Compare commits
222 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87d3d0ca14 | ||
|
|
b330440371 | ||
|
|
2a173ef804 | ||
|
|
bceabae06f | ||
|
|
69667c135d | ||
|
|
587870ad71 | ||
|
|
609fab345a | ||
|
|
29c47cee78 | ||
|
|
6a9795f561 | ||
|
|
72e7f0aa9f | ||
|
|
acfa384c0c | ||
|
|
c2d27ddd04 | ||
|
|
0a6950e34a | ||
|
|
1c3d77b95d | ||
|
|
fb11208bbe | ||
|
|
94f05127b6 | ||
|
|
25a3046c3c | ||
|
|
f479fc37ba | ||
|
|
3ee19a8f08 | ||
|
|
b8d2ad68dd | ||
|
|
8434c488da | ||
|
|
41f251ad50 | ||
|
|
8e99ff1173 | ||
|
|
a921b983f5 | ||
|
|
81eeda0a68 | ||
|
|
1a54f61355 | ||
|
|
382cd5b73d | ||
|
|
e236485bc7 | ||
|
|
0e69e23606 | ||
|
|
0ef85f2551 | ||
|
|
8d3990124e | ||
|
|
7f912a1781 | ||
|
|
d9ae1dd97a | ||
|
|
307db51aec | ||
|
|
347a704b54 | ||
|
|
9e242fb902 | ||
|
|
192cd60a4f | ||
|
|
a98d8511d6 | ||
|
|
811b79610c | ||
|
|
14129e8f21 | ||
|
|
16de044d3d | ||
|
|
e573a8a9c0 | ||
|
|
108648cabf | ||
|
|
8e4ce07d19 | ||
|
|
09cebd70ae | ||
|
|
6a615c408b | ||
|
|
9ebc054c8c | ||
|
|
b2c520bd91 | ||
|
|
6506afa732 | ||
|
|
d1c74b9389 | ||
|
|
61ca7bc1cb | ||
|
|
f94cb2a4b5 | ||
|
|
4c56fcd6a8 | ||
|
|
d5bb2ec165 | ||
|
|
27bc68f264 | ||
|
|
516eed76b7 | ||
|
|
4190c17cdc | ||
|
|
3d0fedfb61 | ||
|
|
9d57deffb4 | ||
|
|
cc1951c721 | ||
|
|
1cd8d4913d | ||
|
|
19890e29e9 | ||
|
|
f759dba7da | ||
|
|
fb6f92a70b | ||
|
|
f22cad42d7 | ||
|
|
cfb6c674ea | ||
|
|
415c68a570 | ||
|
|
15b949bb9c | ||
|
|
316a9809e4 | ||
|
|
3f5aa03056 | ||
|
|
6bc33795a3 | ||
|
|
3191ae27a5 | ||
|
|
b25de52b9e | ||
|
|
a08b4648d5 | ||
|
|
e7a7c945de | ||
|
|
571f358280 | ||
|
|
7ddc3adfaa | ||
|
|
957c326148 | ||
|
|
0b246d03a6 | ||
|
|
5dd3ff32b6 | ||
|
|
a48d262637 | ||
|
|
b5d17b5161 | ||
|
|
980733ba22 | ||
|
|
7043fdc35d | ||
|
|
81713a6bc4 | ||
|
|
959bcfaa30 | ||
|
|
fa465f792d | ||
|
|
74bf78b9cd | ||
|
|
8c408e59f6 | ||
|
|
25d27e42ed | ||
|
|
0f2b0cc5f2 | ||
|
|
00ea34f45d | ||
|
|
ec936c1821 | ||
|
|
2ff84857e7 | ||
|
|
84ed34f3a7 | ||
|
|
f0508e1bc2 | ||
|
|
8ea7bf36d7 | ||
|
|
8819461eed | ||
|
|
7c30668c87 | ||
|
|
a3424950ca | ||
|
|
ebc42eb05e | ||
|
|
fc6ff44471 | ||
|
|
be03377449 | ||
|
|
e52934093a | ||
|
|
ebfd98488e | ||
|
|
6a68af77dc | ||
|
|
e5c0fb7cb2 | ||
|
|
d9611cce80 | ||
|
|
4b48aba2ae | ||
|
|
47fbb6cd0e | ||
|
|
dae71d427c | ||
|
|
46bc2ceb78 | ||
|
|
6f98200179 | ||
|
|
a57b4c56a5 | ||
|
|
6c3062ba2d | ||
|
|
064c8a9dfe | ||
|
|
7dd47736fb | ||
|
|
84cc65489f | ||
|
|
2b9cb54d91 | ||
|
|
54c7fef217 | ||
|
|
ba74cb8c01 | ||
|
|
675932aa69 | ||
|
|
d996e0a7dd | ||
|
|
b6279f702a | ||
|
|
695a89b91a | ||
|
|
09753b6bbd | ||
|
|
6502b97388 | ||
|
|
95d84647b7 | ||
|
|
8f5065332e | ||
|
|
71c8cbb8da | ||
|
|
3217731486 | ||
|
|
a4e63fe0e8 | ||
|
|
cdb8f6845c | ||
|
|
0a7d286c47 | ||
|
|
e17fd0071d | ||
|
|
a9ba30b9b9 | ||
|
|
4267a3b8e0 | ||
|
|
50022ae635 | ||
|
|
0991621152 | ||
|
|
192f8924c8 | ||
|
|
51a12d7266 | ||
|
|
1417fcda6e | ||
|
|
6114e6f033 | ||
|
|
bc2dbc59ce | ||
|
|
7c5075413e | ||
|
|
52175b0b69 | ||
|
|
f0a3515279 | ||
|
|
3c2ccf7d40 | ||
|
|
abd01e1a27 | ||
|
|
0f63976a00 | ||
|
|
6817eb6e56 | ||
|
|
a951d42623 | ||
|
|
8f5c2bf3ba | ||
|
|
7744217d17 | ||
|
|
a84c153bdd | ||
|
|
31a0a90ba4 | ||
|
|
383ce777e3 | ||
|
|
e7ac88ffb1 | ||
|
|
9657c77999 | ||
|
|
afd508f28b | ||
|
|
634612c1a2 | ||
|
|
76545397d4 | ||
|
|
d2f586a7f1 | ||
|
|
7cfebcb6ed | ||
|
|
9a3766e7b7 | ||
|
|
588a06bece | ||
|
|
e45a126862 | ||
|
|
a581945c9e | ||
|
|
331fd0d619 | ||
|
|
c6e229d571 | ||
|
|
b2c3b1ff95 | ||
|
|
3fc12fcc09 | ||
|
|
e098631553 | ||
|
|
0fcd55755e | ||
|
|
65e7c0c4f4 | ||
|
|
70aecd1e38 | ||
|
|
a40c773491 | ||
|
|
b4abb58a41 | ||
|
|
e368436bc6 | ||
|
|
6c96b3a7f5 | ||
|
|
149f0c082d | ||
|
|
994199b5a2 | ||
|
|
8db3d80151 | ||
|
|
93af1e7c44 | ||
|
|
54e90bd986 | ||
|
|
ca16551917 | ||
|
|
f4977d2855 | ||
|
|
bd91fb7c6d | ||
|
|
e1805896f4 | ||
|
|
31a639400e | ||
|
|
d6b24561fa | ||
|
|
7ba9646d38 | ||
|
|
58a7cf1e75 | ||
|
|
1a327198f7 | ||
|
|
e4fe91ef92 | ||
|
|
afcf817c4f | ||
|
|
21550815db | ||
|
|
02af3a18ff | ||
|
|
a5a13b45cf | ||
|
|
0fd232ab3a | ||
|
|
8d83143ca6 | ||
|
|
74452cd622 | ||
|
|
cf2e8cec54 | ||
|
|
5231faf254 | ||
|
|
959d4de78a | ||
|
|
f5d7dc6b6a | ||
|
|
b761c35d10 | ||
|
|
7f3f7f6bdd | ||
|
|
c5877b7c5e | ||
|
|
9e4187ef17 | ||
|
|
bf7ce3e5a2 | ||
|
|
2c55314491 | ||
|
|
d28efef672 | ||
|
|
49b631d560 | ||
|
|
896510f852 | ||
|
|
1d8e469a46 | ||
|
|
caee40a5d9 | ||
|
|
7de051e6bb | ||
|
|
18cec3c752 | ||
|
|
3b46655051 | ||
|
|
f72ad572fb | ||
|
|
a57ea2adb6 |
16
README.md
@@ -47,6 +47,18 @@
|
||||
|
||||
## feat
|
||||
|
||||
- [x] 屏蔽带货动态/评论
|
||||
- [x] 互动视频
|
||||
- [x] 发评/动态反诈
|
||||
- [x] 高能进度条
|
||||
- [x] 滑动跳转预览视频缩略图
|
||||
- [x] Live Photo
|
||||
- [x] 复制/移动收藏夹/稍后再看视频
|
||||
- [x] 超分辨率
|
||||
- [x] 合并弹幕
|
||||
- [x] 会员彩色弹幕
|
||||
- [x] 播放全部/继续播放/倒序播放
|
||||
- [x] Cookie登录
|
||||
- [x] 显示视频分段信息
|
||||
- [x] 调节字幕大小
|
||||
- [x] 调节全屏弹幕大小
|
||||
@@ -73,8 +85,8 @@
|
||||
- [x] 筛选搜索
|
||||
- [x] 转发动态
|
||||
- [x] 合集图片
|
||||
- [x] 删除/置顶私信
|
||||
- [x] 举报用户/评论/视频
|
||||
- [x] 删除/置顶/撤回私信
|
||||
- [x] 举报用户/评论/视频/动态
|
||||
- [x] 删除/发布文本/图片动态
|
||||
- [x] 其他
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.example.piliplus">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission
|
||||
android:name="android.permission.INTERNET"
|
||||
/>
|
||||
|
||||
<application
|
||||
android:label="PiliPlus Debug"
|
||||
tools:replace="android:label">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:resizeableActivity="true"
|
||||
>
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter android:label="PiliPlus Debug">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="http"/>
|
||||
<data android:scheme="https"/>
|
||||
<data android:host="*.bilibili.com"/>
|
||||
<data android:host="*.bilibili.cn"/>
|
||||
<data android:host="*.bilibili.tv"/>
|
||||
<data android:host="bilibili.com"/>
|
||||
<data android:host="bilibili.cn"/>
|
||||
<data android:host="bilibili.tv"/>
|
||||
<data android:host="b23.tv" />
|
||||
<!--<data android:host="live.bilibili.com"/>-->
|
||||
<!--<data android:host="www.bilibili.com"/>-->
|
||||
<!--<data android:host="www.bilibili.tv"/>-->
|
||||
<!--<data android:host="www.bilibili.cn"/>-->
|
||||
<!--<data android:host="m.bilibili.cn"/>-->
|
||||
<!--<data android:host="m.bilibili.com"/>-->
|
||||
<!--<data android:host="bilibili.cn"/>-->
|
||||
<!--<data android:host="bilibili.com"/>-->
|
||||
<!--<data android:host="bangumi.bilibili.com"/>-->
|
||||
<!--<data android:host="space.bilibili.com"/>-->
|
||||
</intent-filter>
|
||||
<intent-filter android:label="PiliPlus Debug">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="bilibili"/>
|
||||
<data android:host="forward" />
|
||||
<data android:host="comment"
|
||||
android:pathPattern="/detail/.*/.*/.*" />
|
||||
<data android:host="uper" />
|
||||
<data android:host="article"
|
||||
android:pathPattern="/readlist" />
|
||||
<data android:host="advertise" android:path="/home" />
|
||||
<data android:host="clip" />
|
||||
<data android:host="search" />
|
||||
<data android:host="stardust-search" />
|
||||
<data android:host="music" />
|
||||
<data android:host="bangumi"
|
||||
android:pathPattern="/season.*" />
|
||||
<data android:host="bangumi" android:pathPattern="/.*" />
|
||||
<data android:host="pictureshow"
|
||||
android:pathPrefix="/creative_center" />
|
||||
<data android:host="cliparea" />
|
||||
<data android:host="im" />
|
||||
<data android:host="im" android:path="/notifications" />
|
||||
<data android:host="following" />
|
||||
<data android:host="following"
|
||||
android:pathPattern="/detail/.*" />
|
||||
<data android:host="following"
|
||||
android:path="/publishInfo/" />
|
||||
<data android:host="laser" android:pathPattern="/.*" />
|
||||
<data android:host="livearea" />
|
||||
<data android:host="live" />
|
||||
<data android:host="catalog" />
|
||||
<data android:host="browser" />
|
||||
<data android:host="user_center" />
|
||||
<data android:host="login" />
|
||||
<data android:host="space" />
|
||||
<data android:host="author" />
|
||||
<data android:host="tag" />
|
||||
<data android:host="rank" />
|
||||
<data android:host="external" />
|
||||
<data android:host="blank" />
|
||||
<data android:host="home" />
|
||||
<data android:host="root" />
|
||||
<data android:host="video" />
|
||||
<data android:host="story" />
|
||||
<data android:host="podcast" />
|
||||
<data android:host="search" />
|
||||
<data android:host="main" android:path="/favorite" />
|
||||
<data android:host="pgc" android:path="/theater/match" />
|
||||
<data android:host="pgc" android:path="/theater/square" />
|
||||
<data android:host="m.bilibili.com"
|
||||
android:path="/topic-detail" />
|
||||
<data android:host="article" />
|
||||
<data android:host="pegasus"
|
||||
android:pathPattern="/channel/v2/.*" />
|
||||
<data android:host="feed" android:pathPattern="/channel" />
|
||||
<data android:host="vip" />
|
||||
<data android:host="user_center" android:path="/vip" />
|
||||
<data android:host="history" />
|
||||
<data android:host="charge" android:path="/rank" />
|
||||
<data android:host="assistant" />
|
||||
<data android:host="assistant" />
|
||||
<data android:host="feedback" />
|
||||
<data android:host="auth" android:path="/launch" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="512dp"
|
||||
android:width="512dp"
|
||||
android:viewportWidth="512.0"
|
||||
android:viewportHeight="512.0">
|
||||
<path
|
||||
android:fillColor="#FF5CB67B"
|
||||
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
|
||||
android:strokeWidth="0.783784"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
|
||||
android:fillType="evenOdd" />
|
||||
</vector>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FF5CB67B</color>
|
||||
</resources>
|
||||
@@ -4,6 +4,7 @@ import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import com.ryanheise.audioservice.AudioServiceActivity
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
@@ -21,6 +22,39 @@ class MainActivity : AudioServiceActivity() {
|
||||
methodChannel.setMethodCallHandler { call, result ->
|
||||
if (call.method == "back") {
|
||||
back()
|
||||
} else if (call.method == "biliSendCommAntifraud") {
|
||||
try {
|
||||
val action = call.argument<Int>("action") ?: 0
|
||||
val oid = call.argument<Number>("oid") ?: 0L
|
||||
val type = call.argument<Int>("type") ?: 0
|
||||
val rpid = call.argument<Number>("rpid") ?: 0L
|
||||
val root = call.argument<Number>("root") ?: 0L
|
||||
val parent = call.argument<Number>("parent") ?: 0L
|
||||
val ctime = call.argument<Number>("ctime") ?: 0L
|
||||
val commentText = call.argument<String>("comment_text") ?: ""
|
||||
val pictures = call.argument<String?>("pictures")
|
||||
val sourceId = call.argument<String>("source_id") ?: ""
|
||||
val uid = call.argument<Number>("uid") ?: 0L
|
||||
val cookies = call.argument<List<String>>("cookies") ?: emptyList<String>()
|
||||
|
||||
val intent = Intent().apply {
|
||||
component = ComponentName("icu.freedomIntrovert.biliSendCommAntifraud", "icu.freedomIntrovert.biliSendCommAntifraud.ByXposedLaunchedActivity")
|
||||
putExtra("action", action)
|
||||
putExtra("oid", oid.toLong())
|
||||
putExtra("type", type)
|
||||
putExtra("rpid", rpid.toLong())
|
||||
putExtra("root", root.toLong())
|
||||
putExtra("parent", parent.toLong())
|
||||
putExtra("ctime", ctime.toLong())
|
||||
putExtra("comment_text", commentText)
|
||||
if(pictures != null)
|
||||
putExtra("pictures", pictures)
|
||||
putExtra("source_id", sourceId)
|
||||
putExtra("uid", uid.toLong())
|
||||
putStringArrayListExtra("cookies", ArrayList(cookies))
|
||||
}
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {}
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 8.8 KiB |
@@ -1,16 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="512dp"
|
||||
android:width="512dp"
|
||||
android:viewportWidth="512.0"
|
||||
android:viewportHeight="512.0">
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportWidth="108.0"
|
||||
android:viewportHeight="108.0">
|
||||
<path
|
||||
android:fillColor="#FF5CB67B"
|
||||
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
|
||||
android:strokeWidth="0.783784"
|
||||
android:fillColor="@color/ic_launcher_foreground"
|
||||
android:pathData="M56,54L39.78,54l2.22,-10.94h14c3.02,0 5.47,2.45 5.47,5.47 0,3.02 -2.45,5.47 -5.47,5.47zM56,35.77h-9.62l-7.13,36.45h7.51L48.92,61.29h7.08c7.05,0 12.76,-5.71 12.76,-12.76 0,-7.05 -5.71,-12.76 -12.76,-12.76z"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
|
||||
android:fillType="evenOdd" />
|
||||
</vector>
|
||||
</vector>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="512dp"
|
||||
android:width="512dp"
|
||||
android:viewportWidth="512.0"
|
||||
android:viewportHeight="512.0">
|
||||
<path
|
||||
android:fillColor="#FF5CB67B"
|
||||
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
|
||||
android:strokeWidth="0.783784"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
|
||||
android:fillType="evenOdd" />
|
||||
</vector>
|
||||
@@ -2,15 +2,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="512.0"
|
||||
android:viewportHeight="512.0">
|
||||
android:viewportWidth="108.0"
|
||||
android:viewportHeight="108.0">
|
||||
<path
|
||||
android:fillColor="#FF5CB67B"
|
||||
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
|
||||
android:strokeWidth="0.783784"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
|
||||
android:pathData="M57.54,54L28.82,54l3.93,-19.36h24.78c5.35,0 9.68,4.33 9.68,9.68 0,5.35 -4.33,9.68 -9.68,9.68zM57.54,21.73L40.5,21.73L27.88,86.27h13.3l3.83,-19.36h12.54c12.48,0 22.59,-10.11 22.59,-22.59 0,-12.48 -10.11,-22.59 -22.59,-22.59z"
|
||||
android:strokeWidth="0.252073"
|
||||
android:fillType="evenOdd" />
|
||||
</vector>
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_foreground"
|
||||
android:inset="16%" />
|
||||
</foreground>
|
||||
<monochrome>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_monochrome"
|
||||
android:inset="16%" />
|
||||
</monochrome>
|
||||
</adaptive-icon>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 914 B |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 3.4 KiB |
3
android/app/src/main/res/raw/keep.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:keep="@drawable/*" />
|
||||
5
android/app/src/main/res/values-night-v31/colors.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_foreground">@android:color/system_accent1_100</color>
|
||||
<color name="ic_launcher_background">@android:color/system_neutral1_800</color>
|
||||
</resources>
|
||||
5
android/app/src/main/res/values-v31/colors.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_foreground">@android:color/system_neutral2_700</color>
|
||||
<color name="ic_launcher_background">@android:color/system_accent1_100</color>
|
||||
</resources>
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FF5CB67B</color>
|
||||
<color name="ic_launcher_foreground">#FF5CB67B</color>
|
||||
<color name="ic_launcher_background">#FFFFFFFF</color>
|
||||
</resources>
|
||||
BIN
assets/fonts/digital_id_num.ttf
Normal file
BIN
assets/images/logo/logo_2.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
assets/images/logo/logo_3.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
assets/images/paycoins/ic_panel_close.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
@@ -23,8 +23,13 @@ class Constants {
|
||||
static const String userAgent =
|
||||
'Mozilla/5.0 BiliDroid/1.46.2 (bbcallen@gmail.com) os/android model/vivo mobi_app/android_hd build/2001100 channel/yingyongbao innerVer/2001100 osVer/14 network/2';
|
||||
static const String statistics =
|
||||
'%7B%22appId%22%3A5%2C%22platform%22%3A3%2C%22version%22%3A%221.46.2%22%2C%22abtest%22%3A%22%22%7D';
|
||||
//Uri.encodeComponent('{"appId": 5,"platform": 3,"version": "1.46.2","abtest": ""}');
|
||||
'{"appId":5,"platform":3,"version":"1.46.2","abtest":""}';
|
||||
// 请求时会自动encodeComponent
|
||||
|
||||
static const urlPattern =
|
||||
r'https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]';
|
||||
|
||||
static const goodsUrlPrefix = "https://gaoneng.bilibili.com/tetris";
|
||||
|
||||
// 超分辨率滤镜
|
||||
static const List<String> mpvAnime4KShaders = [
|
||||
|
||||
@@ -39,51 +39,55 @@ class VideoCardHSkeleton extends StatelessWidget {
|
||||
),
|
||||
// VideoContent(videoItem: videoItem)
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 200,
|
||||
height: 11,
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 150,
|
||||
height: 13,
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 100,
|
||||
height: 13,
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
width: 40,
|
||||
height: 13,
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
width: 40,
|
||||
height: 13,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 200,
|
||||
height: 11,
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
),
|
||||
Container(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 150,
|
||||
height: 13,
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 100,
|
||||
height: 13,
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
width: 40,
|
||||
height: 13,
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
width: 40,
|
||||
height: 13,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -31,13 +31,17 @@ class AnimatedDialogState extends State<AnimatedDialog>
|
||||
opacityAnimation = Tween<double>(begin: 0.0, end: 0.6)
|
||||
.animate(CurvedAnimation(parent: controller, curve: Curves.linear));
|
||||
scaleAnimation = CurvedAnimation(parent: controller, curve: Curves.linear);
|
||||
controller.addListener(() => setState(() {}));
|
||||
controller.addListener(listener);
|
||||
controller.forward();
|
||||
}
|
||||
|
||||
void listener() {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller.removeListener(() {});
|
||||
controller.removeListener(listener);
|
||||
controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel;
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/dynamics/article_content_model.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
@@ -9,7 +11,9 @@ Widget articleContent({
|
||||
required BuildContext context,
|
||||
required List<ArticleContentModel> list,
|
||||
Function(List<String>, int)? callback,
|
||||
required double maxWidth,
|
||||
}) {
|
||||
debugPrint('articleContent');
|
||||
List<String>? imgList = list
|
||||
.where((item) => item.pic != null)
|
||||
.toList()
|
||||
@@ -55,30 +59,28 @@ Widget articleContent({
|
||||
),
|
||||
);
|
||||
} else if (item.pic != null) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) => Hero(
|
||||
tag: item.pic!.pics!.first.url!,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (callback != null) {
|
||||
callback(
|
||||
imgList,
|
||||
imgList.indexOf(item.pic!.pics!.first.url!),
|
||||
);
|
||||
} else {
|
||||
context.imageView(
|
||||
initialPage: imgList.indexOf(item.pic!.pics!.first.url!),
|
||||
imgList: imgList,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: NetworkImgLayer(
|
||||
width: constraints.maxWidth,
|
||||
height: constraints.maxWidth *
|
||||
item.pic!.pics!.first.height! /
|
||||
item.pic!.pics!.first.width!,
|
||||
src: item.pic!.pics!.first.url,
|
||||
),
|
||||
return Hero(
|
||||
tag: item.pic!.pics!.first.url!,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (callback != null) {
|
||||
callback(
|
||||
imgList,
|
||||
imgList.indexOf(item.pic!.pics!.first.url!),
|
||||
);
|
||||
} else {
|
||||
context.imageView(
|
||||
initialPage: imgList.indexOf(item.pic!.pics!.first.url!),
|
||||
imgList: imgList.map((url) => SourceModel(url: url)).toList(),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: NetworkImgLayer(
|
||||
width: maxWidth,
|
||||
height: maxWidth *
|
||||
item.pic!.pics!.first.height! /
|
||||
item.pic!.pics!.first.width!,
|
||||
src: item.pic!.pics!.first.url,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ class PBadge extends StatelessWidget {
|
||||
final double? fs;
|
||||
final String? semanticsLabel;
|
||||
final bool bold;
|
||||
final double? textScaleFactor;
|
||||
|
||||
const PBadge({
|
||||
super.key,
|
||||
@@ -26,6 +27,7 @@ class PBadge extends StatelessWidget {
|
||||
this.fs = 11,
|
||||
this.semanticsLabel,
|
||||
this.bold = true,
|
||||
this.textScaleFactor,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -71,6 +73,9 @@ class PBadge extends StatelessWidget {
|
||||
),
|
||||
child: Text(
|
||||
text ?? "",
|
||||
textScaler: textScaleFactor != null
|
||||
? TextScaler.linear(textScaleFactor!)
|
||||
: null,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: fs ?? fontSize,
|
||||
|
||||
@@ -19,8 +19,16 @@ class CustomSliverPersistentHeaderDelegate
|
||||
//创建child子组件
|
||||
//shrinkOffset:child偏移值minExtent~maxExtent
|
||||
//overlapsContent:SliverPersistentHeader覆盖其他子组件返回true,否则返回false
|
||||
return ColoredBox(
|
||||
color: bgColor,
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: bgColor,
|
||||
offset: const Offset(0, -2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
32
lib/common/widgets/dialog.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
void showConfirmDialog({
|
||||
required BuildContext context,
|
||||
required String title,
|
||||
String? content,
|
||||
required VoidCallback onConfirm,
|
||||
}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(title),
|
||||
content: content == null ? null : Text(content),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: onConfirm,
|
||||
child: Text('确认'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
@@ -11,6 +13,7 @@ Widget htmlRender({
|
||||
required double constrainedWidth,
|
||||
Function(List<String>, int)? callback,
|
||||
}) {
|
||||
debugPrint('htmlRender');
|
||||
return SelectionArea(
|
||||
child: Html(
|
||||
data: htmlContent,
|
||||
@@ -54,7 +57,7 @@ Widget htmlRender({
|
||||
callback([imgUrl], 0);
|
||||
} else {
|
||||
context.imageView(
|
||||
imgList: [imgUrl],
|
||||
imgList: [SourceModel(url: imgUrl)],
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,9 +2,12 @@ import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel, SourceType;
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/nine_grid_view.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ImageModel {
|
||||
@@ -12,16 +15,20 @@ class ImageModel {
|
||||
required this.width,
|
||||
required this.height,
|
||||
required this.url,
|
||||
this.liveUrl,
|
||||
});
|
||||
|
||||
dynamic width;
|
||||
dynamic height;
|
||||
String url;
|
||||
String? liveUrl;
|
||||
bool? _isLongPic;
|
||||
bool? _isLivePhoto;
|
||||
|
||||
dynamic get safeWidth => width ?? 1;
|
||||
dynamic get safeHeight => height ?? 1;
|
||||
bool get isLongPic => _isLongPic ??= (safeHeight / safeWidth) > (22 / 9);
|
||||
bool get isLivePhoto => _isLivePhoto ??= liveUrl?.isNotEmpty == true;
|
||||
}
|
||||
|
||||
Widget imageview(
|
||||
@@ -83,6 +90,17 @@ Widget imageview(
|
||||
);
|
||||
}
|
||||
|
||||
late final enableLivePhoto = GStorage.enableLivePhoto;
|
||||
|
||||
int parseSize(size) {
|
||||
return switch (size) {
|
||||
int() => size,
|
||||
double() => size.round(),
|
||||
String() => int.tryParse(size) ?? 1,
|
||||
_ => 1,
|
||||
};
|
||||
}
|
||||
|
||||
return NineGridView(
|
||||
type: NineGridType.weiBo,
|
||||
margin: const EdgeInsets.only(top: 6),
|
||||
@@ -102,7 +120,19 @@ Widget imageview(
|
||||
onViewImage?.call();
|
||||
context.imageView(
|
||||
initialPage: index,
|
||||
imgList: picArr.map((item) => item.url).toList(),
|
||||
imgList: picArr.map(
|
||||
(item) {
|
||||
bool isLive = item.isLivePhoto && enableLivePhoto;
|
||||
return SourceModel(
|
||||
sourceType:
|
||||
isLive ? SourceType.livePhoto : SourceType.networkImage,
|
||||
url: item.url,
|
||||
liveUrl: isLive ? item.liveUrl : null,
|
||||
width: isLive ? parseSize(item.width) : null,
|
||||
height: isLive ? parseSize(item.height) : null,
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
onDismissed: onDismissed,
|
||||
);
|
||||
}
|
||||
@@ -143,7 +173,14 @@ Widget imageview(
|
||||
},
|
||||
),
|
||||
),
|
||||
if (picArr[index].isLongPic)
|
||||
if (picArr[index].isLivePhoto)
|
||||
const PBadge(
|
||||
text: 'Live',
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
type: 'gray',
|
||||
)
|
||||
else if (picArr[index].isLongPic)
|
||||
const PBadge(
|
||||
text: '长图',
|
||||
right: 8,
|
||||
|
||||
@@ -10,6 +10,8 @@ import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:status_bar_control/status_bar_control.dart';
|
||||
@@ -33,6 +35,24 @@ typedef IndexedFocusedWidgetBuilder = Widget Function(
|
||||
|
||||
typedef IndexedTagStringBuilder = String Function(int index);
|
||||
|
||||
enum SourceType { fileImage, networkImage, livePhoto }
|
||||
|
||||
class SourceModel {
|
||||
final SourceType sourceType;
|
||||
final String url;
|
||||
final String? liveUrl;
|
||||
final int? width;
|
||||
final int? height;
|
||||
|
||||
const SourceModel({
|
||||
this.sourceType = SourceType.networkImage,
|
||||
required this.url,
|
||||
this.liveUrl,
|
||||
this.width,
|
||||
this.height,
|
||||
});
|
||||
}
|
||||
|
||||
class InteractiveviewerGallery<T> extends StatefulWidget {
|
||||
const InteractiveviewerGallery({
|
||||
super.key,
|
||||
@@ -45,17 +65,14 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
|
||||
this.onDismissed,
|
||||
this.setStatusBar,
|
||||
this.onClose,
|
||||
this.isFile,
|
||||
});
|
||||
|
||||
final bool? isFile;
|
||||
|
||||
final VoidCallback? onClose;
|
||||
final ValueChanged? onClose;
|
||||
|
||||
final bool? setStatusBar;
|
||||
|
||||
/// The sources to show.
|
||||
final List<String> sources;
|
||||
final List<SourceModel> sources;
|
||||
|
||||
/// The index of the first source in [sources] to show.
|
||||
final int initIndex;
|
||||
@@ -92,7 +109,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
|
||||
late Offset _doubleTapLocalPosition;
|
||||
|
||||
int? currentIndex;
|
||||
late final RxInt currentIndex = widget.initIndex.obs;
|
||||
|
||||
late List<bool> _thumbList;
|
||||
late final int _quality = GStorage.previewQ;
|
||||
@@ -110,15 +127,19 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
_animationController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
)..addListener(() {
|
||||
_transformationController!.value =
|
||||
_animation?.value ?? Matrix4.identity();
|
||||
});
|
||||
)..addListener(listener);
|
||||
|
||||
currentIndex = widget.initIndex;
|
||||
if (widget.setStatusBar != false) {
|
||||
setStatusBar();
|
||||
}
|
||||
|
||||
if (widget.sources[currentIndex.value].sourceType == SourceType.livePhoto) {
|
||||
_onPlay(currentIndex.value);
|
||||
}
|
||||
}
|
||||
|
||||
void listener() {
|
||||
_transformationController!.value = _animation?.value ?? Matrix4.identity();
|
||||
}
|
||||
|
||||
setStatusBar() async {
|
||||
@@ -132,16 +153,18 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
|
||||
@override
|
||||
void dispose() async {
|
||||
widget.onClose?.call(true);
|
||||
_player?.dispose();
|
||||
_pageController?.dispose();
|
||||
_animationController.removeListener(() {});
|
||||
_animationController.removeListener(listener);
|
||||
_animationController.dispose();
|
||||
if (widget.setStatusBar != false) {
|
||||
if (Platform.isIOS || Platform.isAndroid) {
|
||||
StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE);
|
||||
}
|
||||
}
|
||||
if (widget.isFile != true) {
|
||||
for (int index = 0; index < widget.sources.length; index++) {
|
||||
for (int index = 0; index < widget.sources.length; index++) {
|
||||
if (widget.sources[index].sourceType == SourceType.networkImage) {
|
||||
CachedNetworkImageProvider(_getActualUrl(index)).evict();
|
||||
}
|
||||
}
|
||||
@@ -201,14 +224,22 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
}
|
||||
}
|
||||
|
||||
void _onPlay(int index) {
|
||||
_player ??= Player();
|
||||
_videoController ??= VideoController(_player!);
|
||||
_player!.open(Media(widget.sources[index].liveUrl!));
|
||||
}
|
||||
|
||||
/// When the page view changed its page, the source will animate back into the
|
||||
/// original scale if it was scaled up.
|
||||
///
|
||||
/// Additionally the swipe up / down to dismiss gets enabled.
|
||||
void _onPageChanged(int page) {
|
||||
setState(() {
|
||||
currentIndex = page;
|
||||
});
|
||||
_player?.pause();
|
||||
currentIndex.value = page;
|
||||
if (widget.sources[page].sourceType == SourceType.livePhoto) {
|
||||
_onPlay(page);
|
||||
}
|
||||
widget.onPageChanged?.call(page);
|
||||
if (_transformationController!.value != Matrix4.identity()) {
|
||||
// animate the reset for the transformation of the interactive viewer
|
||||
@@ -225,18 +256,21 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
}
|
||||
|
||||
String _getActualUrl(int index) => _thumbList[index] && _quality != 100
|
||||
? '${widget.sources[index]}@${_quality}q.webp'.http2https
|
||||
: widget.sources[index].http2https;
|
||||
? '${widget.sources[index].url}@${_quality}q.webp'.http2https
|
||||
: widget.sources[index].url.http2https;
|
||||
|
||||
void onClose() {
|
||||
if (widget.onClose != null) {
|
||||
widget.onClose!();
|
||||
widget.onClose!(false);
|
||||
} else {
|
||||
Get.back();
|
||||
widget.onDismissed?.call(_pageController!.page!.floor());
|
||||
}
|
||||
}
|
||||
|
||||
Player? _player;
|
||||
VideoController? _videoController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
@@ -272,12 +306,15 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
_doubleTapLocalPosition = details.localPosition;
|
||||
},
|
||||
onDoubleTap: onDoubleTap,
|
||||
onLongPress: widget.isFile == true ? null : onLongPress,
|
||||
onLongPress:
|
||||
widget.sources[index].sourceType == SourceType.fileImage
|
||||
? null
|
||||
: onLongPress,
|
||||
child: widget.itemBuilder != null
|
||||
? widget.itemBuilder!(
|
||||
context,
|
||||
index,
|
||||
index == currentIndex,
|
||||
index == currentIndex.value,
|
||||
_enablePageView,
|
||||
)
|
||||
: _itemBuilder(index),
|
||||
@@ -321,51 +358,70 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
if (widget.sources.length > 1)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
"${currentIndex! + 1}/${widget.sources.length}",
|
||||
style: const TextStyle(color: Colors.white),
|
||||
child: Obx(
|
||||
() => Text(
|
||||
"${currentIndex.value + 1}/${widget.sources.length}",
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.isFile != true)
|
||||
if (widget.sources[currentIndex.value].sourceType !=
|
||||
SourceType.fileImage)
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: PopupMenuButton(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
value: 0,
|
||||
onTap: () =>
|
||||
onShareImg(widget.sources[currentIndex!]),
|
||||
onTap: () => onShareImg(
|
||||
widget.sources[currentIndex.value].url),
|
||||
child: const Text("分享图片"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
onTap: () {
|
||||
Utils.copyText(widget.sources[currentIndex!]);
|
||||
Utils.copyText(
|
||||
widget.sources[currentIndex.value].url);
|
||||
},
|
||||
child: const Text("复制链接"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
onTap: () {
|
||||
DownloadUtils.downloadImg(
|
||||
context,
|
||||
[widget.sources[currentIndex!]],
|
||||
[widget.sources[currentIndex.value].url],
|
||||
);
|
||||
},
|
||||
child: const Text("保存图片"),
|
||||
),
|
||||
if (widget.sources.length > 1)
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
onTap: () {
|
||||
DownloadUtils.downloadImg(
|
||||
context,
|
||||
widget.sources,
|
||||
widget.sources
|
||||
.map((item) => item.url)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
child: const Text("保存全部图片"),
|
||||
),
|
||||
if (widget.sources[currentIndex.value].sourceType ==
|
||||
SourceType.livePhoto)
|
||||
PopupMenuItem(
|
||||
onTap: () {
|
||||
DownloadUtils.downloadLivePhoto(
|
||||
context: context,
|
||||
url: widget.sources[currentIndex.value].url,
|
||||
liveUrl: widget
|
||||
.sources[currentIndex.value].liveUrl!,
|
||||
width:
|
||||
widget.sources[currentIndex.value].width!,
|
||||
height: widget
|
||||
.sources[currentIndex.value].height!,
|
||||
);
|
||||
},
|
||||
child: const Text("保存 Live Photo"),
|
||||
),
|
||||
];
|
||||
},
|
||||
child: const Icon(Icons.more_horiz, color: Colors.white),
|
||||
@@ -381,49 +437,72 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
|
||||
// 图片分享
|
||||
void onShareImg(String imgUrl) async {
|
||||
SmartDialog.showLoading();
|
||||
var response = await Request()
|
||||
.get(imgUrl, options: Options(responseType: ResponseType.bytes));
|
||||
final temp = await getTemporaryDirectory();
|
||||
SmartDialog.dismiss();
|
||||
String imgName =
|
||||
"plpl_pic_${DateTime.now().toString().split('-').join()}.jpg";
|
||||
var path = '${temp.path}/$imgName';
|
||||
File(path).writeAsBytesSync(response.data);
|
||||
Share.shareXFiles([XFile(path)], subject: imgUrl);
|
||||
try {
|
||||
SmartDialog.showLoading();
|
||||
var response = await Request()
|
||||
.get(imgUrl, options: Options(responseType: ResponseType.bytes));
|
||||
final temp = await getTemporaryDirectory();
|
||||
SmartDialog.dismiss();
|
||||
String imgName =
|
||||
"plpl_pic_${DateTime.now().toString().split('-').join()}.jpg";
|
||||
var path = '${temp.path}/$imgName';
|
||||
File(path).writeAsBytesSync(response.data);
|
||||
|
||||
Rect? sharePositionOrigin;
|
||||
if (Platform.isIOS && (await Utils.isIpad())) {
|
||||
sharePositionOrigin = Rect.fromLTWH(0, 0, Get.width, Get.height / 2);
|
||||
}
|
||||
|
||||
Share.shareXFiles(
|
||||
[XFile(path)],
|
||||
subject: imgUrl,
|
||||
sharePositionOrigin: sharePositionOrigin,
|
||||
);
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Widget _itemBuilder(index) {
|
||||
return Center(
|
||||
child: Hero(
|
||||
tag: widget.sources[index],
|
||||
child: widget.isFile == true
|
||||
? Image(
|
||||
filterQuality: FilterQuality.low,
|
||||
image: FileImage(File(widget.sources[index])),
|
||||
)
|
||||
: CachedNetworkImage(
|
||||
fadeInDuration: const Duration(milliseconds: 0),
|
||||
fadeOutDuration: const Duration(milliseconds: 0),
|
||||
imageUrl: _getActualUrl(index),
|
||||
// fit: BoxFit.contain,
|
||||
progressIndicatorBuilder: (context, url, progress) {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 150.0,
|
||||
child: LinearProgressIndicator(
|
||||
value: progress.progress ?? 0),
|
||||
),
|
||||
);
|
||||
},
|
||||
// errorListener: (value) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// setState(() {
|
||||
// _thumbList[index] = false;
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
),
|
||||
tag: widget.sources[index].url,
|
||||
child: switch (widget.sources[index].sourceType) {
|
||||
SourceType.fileImage => Image(
|
||||
filterQuality: FilterQuality.low,
|
||||
image: FileImage(File(widget.sources[index].url)),
|
||||
),
|
||||
SourceType.networkImage => CachedNetworkImage(
|
||||
fadeInDuration: const Duration(milliseconds: 0),
|
||||
fadeOutDuration: const Duration(milliseconds: 0),
|
||||
imageUrl: _getActualUrl(index),
|
||||
// fit: BoxFit.contain,
|
||||
progressIndicatorBuilder: (context, url, progress) {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 150.0,
|
||||
child:
|
||||
LinearProgressIndicator(value: progress.progress ?? 0),
|
||||
),
|
||||
);
|
||||
},
|
||||
// errorListener: (value) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// setState(() {
|
||||
// _thumbList[index] = false;
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
),
|
||||
SourceType.livePhoto => Obx(() => currentIndex.value == index
|
||||
? IgnorePointer(
|
||||
child: Video(
|
||||
controller: _videoController!,
|
||||
fill: Colors.transparent,
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink()),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -487,7 +566,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: () {
|
||||
onShareImg(widget.sources[currentIndex!]);
|
||||
onShareImg(widget.sources[currentIndex.value].url);
|
||||
Get.back();
|
||||
},
|
||||
dense: true,
|
||||
@@ -496,7 +575,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
Utils.copyText(widget.sources[currentIndex!]);
|
||||
Utils.copyText(widget.sources[currentIndex.value].url);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text('复制链接', style: TextStyle(fontSize: 14)),
|
||||
@@ -506,7 +585,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
Get.back();
|
||||
DownloadUtils.downloadImg(
|
||||
context,
|
||||
[widget.sources[currentIndex!]],
|
||||
[widget.sources[currentIndex.value].url],
|
||||
);
|
||||
},
|
||||
dense: true,
|
||||
@@ -518,12 +597,31 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
Get.back();
|
||||
DownloadUtils.downloadImg(
|
||||
context,
|
||||
widget.sources,
|
||||
widget.sources.map((item) => item.url).toList(),
|
||||
);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text('保存全部图片', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
if (widget.sources[currentIndex.value].sourceType ==
|
||||
SourceType.livePhoto)
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
DownloadUtils.downloadLivePhoto(
|
||||
context: context,
|
||||
url: widget.sources[currentIndex.value].url,
|
||||
liveUrl: widget.sources[currentIndex.value].liveUrl!,
|
||||
width: widget.sources[currentIndex.value].width!,
|
||||
height: widget.sources[currentIndex.value].height!,
|
||||
);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'保存 Live Photo',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
import '../../utils/storage.dart';
|
||||
import '../../utils/utils.dart';
|
||||
import 'package:PiliPlus/common/widgets/spring_physics.dart';
|
||||
|
||||
class ListSheetContent extends StatefulWidget {
|
||||
const ListSheetContent({
|
||||
@@ -104,18 +105,31 @@ class _ListSheetContentState extends State<ListSheetContent>
|
||||
.indexWhere((e) => e.cid == widget.currentCid)
|
||||
: episodes.indexWhere((e) => e.cid == widget.currentCid));
|
||||
|
||||
void listener() {
|
||||
_indexStream?.add(_ctr?.index);
|
||||
}
|
||||
|
||||
late bool _isInit = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (GStorage.collapsibleVideoPage) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isInit = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (_isList) {
|
||||
_indexStream ??= StreamController<int>.broadcast();
|
||||
_ctr = TabController(
|
||||
vsync: this,
|
||||
length: widget.season.sections.length,
|
||||
initialIndex: _index,
|
||||
)..addListener(() {
|
||||
_indexStream?.add(_ctr?.index);
|
||||
});
|
||||
)..addListener(listener);
|
||||
}
|
||||
itemScrollController = _isList
|
||||
? List.generate(
|
||||
@@ -124,7 +138,7 @@ class _ListSheetContentState extends State<ListSheetContent>
|
||||
reverse = _isList
|
||||
? List.generate(widget.season.sections.length, (_) => false)
|
||||
: [false];
|
||||
if (widget.bvid != null && widget.season != null) {
|
||||
if (GStorage.isLogin && widget.bvid != null && widget.season != null) {
|
||||
_favStream ??= StreamController<int>();
|
||||
() async {
|
||||
dynamic result = await VideoHttp.videoRelation(bvid: widget.bvid);
|
||||
@@ -147,7 +161,7 @@ class _ListSheetContentState extends State<ListSheetContent>
|
||||
_favStream = null;
|
||||
_indexStream?.close();
|
||||
_indexStream = null;
|
||||
_ctr?.removeListener(() {});
|
||||
_ctr?.removeListener(listener);
|
||||
_ctr?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
@@ -275,168 +289,174 @@ class _ListSheetContentState extends State<ListSheetContent>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: Utils.getSheetHeight(context),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 45,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: widget.showTitle != false ? 14 : 6),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.showTitle != false)
|
||||
Text(
|
||||
'合集(${_isList ? widget.season.epCount : episodes?.length ?? ''})',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
StreamBuilder(
|
||||
stream: _favStream?.stream,
|
||||
builder: (context, snapshot) => snapshot.hasData
|
||||
? mediumButton(
|
||||
tooltip: _seasonFav == 1 ? '取消订阅' : '订阅',
|
||||
icon: _seasonFav == 1
|
||||
? Icons.notifications_off_outlined
|
||||
: Icons.notifications_active_outlined,
|
||||
onPressed: () async {
|
||||
dynamic result = await VideoHttp.seasonFav(
|
||||
isFav: _seasonFav == 1,
|
||||
seasonId: widget.season.id,
|
||||
);
|
||||
if (result['status']) {
|
||||
SmartDialog.showToast(
|
||||
'${_seasonFav == 1 ? '取消' : ''}订阅成功');
|
||||
_seasonFav = _seasonFav == 1 ? 0 : 1;
|
||||
_favStream?.add(_seasonFav);
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
},
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
if (GStorage.collapsibleVideoPage && _isInit) {
|
||||
return CustomScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 45,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: widget.showTitle != false ? 14 : 6),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.showTitle != false)
|
||||
Text(
|
||||
'合集(${_isList ? widget.season.epCount : episodes?.length ?? ''})',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
mediumButton(
|
||||
tooltip: '跳至顶部',
|
||||
icon: Icons.vertical_align_top,
|
||||
onPressed: () {
|
||||
try {
|
||||
itemScrollController[_ctr?.index ?? 0].scrollTo(
|
||||
index: !reverse[_ctr?.index ?? 0]
|
||||
? 0
|
||||
: _isList
|
||||
? widget.season.sections[_ctr?.index].episodes
|
||||
.length -
|
||||
1
|
||||
: episodes.length - 1,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
mediumButton(
|
||||
tooltip: '跳至底部',
|
||||
icon: Icons.vertical_align_bottom,
|
||||
onPressed: () {
|
||||
try {
|
||||
itemScrollController[_ctr?.index ?? 0].scrollTo(
|
||||
index: !reverse[_ctr?.index ?? 0]
|
||||
? _isList
|
||||
? widget.season.sections[_ctr?.index].episodes
|
||||
.length -
|
||||
1
|
||||
: episodes.length - 1
|
||||
: 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
mediumButton(
|
||||
tooltip: '跳至当前',
|
||||
icon: Icons.my_location,
|
||||
onPressed: () async {
|
||||
if (_ctr != null && _ctr?.index != (_index)) {
|
||||
_ctr?.animateTo(_index);
|
||||
await Future.delayed(const Duration(milliseconds: 225));
|
||||
}
|
||||
try {
|
||||
itemScrollController[_ctr?.index ?? 0].scrollTo(
|
||||
index: currentIndex,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
if (widget.isSupportReverse == true)
|
||||
if (!_isList)
|
||||
_reverseButton
|
||||
else
|
||||
StreamBuilder(
|
||||
stream: _indexStream?.stream,
|
||||
initialData: _index,
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.data == _index
|
||||
? _reverseButton
|
||||
: const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
const Spacer(),
|
||||
StreamBuilder(
|
||||
stream: _indexStream?.stream,
|
||||
initialData: _index,
|
||||
builder: (context, snapshot) => mediumButton(
|
||||
tooltip: reverse[snapshot.data] ? '顺序' : '倒序',
|
||||
icon: !reverse[snapshot.data]
|
||||
? MdiIcons.sortNumericAscending
|
||||
: MdiIcons.sortNumericDescending,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
reverse[_ctr?.index ?? 0] = !reverse[_ctr?.index ?? 0];
|
||||
});
|
||||
StreamBuilder(
|
||||
stream: _favStream?.stream,
|
||||
builder: (context, snapshot) => snapshot.hasData
|
||||
? mediumButton(
|
||||
tooltip: _seasonFav == 1 ? '取消订阅' : '订阅',
|
||||
icon: _seasonFav == 1
|
||||
? Icons.notifications_off_outlined
|
||||
: Icons.notifications_active_outlined,
|
||||
onPressed: () async {
|
||||
dynamic result = await VideoHttp.seasonFav(
|
||||
isFav: _seasonFav == 1,
|
||||
seasonId: widget.season.id,
|
||||
);
|
||||
if (result['status']) {
|
||||
SmartDialog.showToast(
|
||||
'${_seasonFav == 1 ? '取消' : ''}订阅成功');
|
||||
_seasonFav = _seasonFav == 1 ? 0 : 1;
|
||||
_favStream?.add(_seasonFav);
|
||||
} else {
|
||||
SmartDialog.showToast(result['msg']);
|
||||
}
|
||||
},
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
mediumButton(
|
||||
tooltip: '跳至顶部',
|
||||
icon: Icons.vertical_align_top,
|
||||
onPressed: () {
|
||||
try {
|
||||
itemScrollController[_ctr?.index ?? 0].scrollTo(
|
||||
index: !reverse[_ctr?.index ?? 0]
|
||||
? 0
|
||||
: _isList
|
||||
? widget.season.sections[_ctr?.index].episodes
|
||||
.length -
|
||||
1
|
||||
: episodes.length - 1,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
mediumButton(
|
||||
tooltip: '跳至底部',
|
||||
icon: Icons.vertical_align_bottom,
|
||||
onPressed: () {
|
||||
try {
|
||||
itemScrollController[_ctr?.index ?? 0].scrollTo(
|
||||
index: !reverse[_ctr?.index ?? 0]
|
||||
? _isList
|
||||
? widget.season.sections[_ctr?.index].episodes
|
||||
.length -
|
||||
1
|
||||
: episodes.length - 1
|
||||
: 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
mediumButton(
|
||||
tooltip: '跳至当前',
|
||||
icon: Icons.my_location,
|
||||
onPressed: () async {
|
||||
if (_ctr != null && _ctr?.index != (_index)) {
|
||||
_ctr?.animateTo(_index);
|
||||
await Future.delayed(const Duration(milliseconds: 225));
|
||||
}
|
||||
try {
|
||||
itemScrollController[_ctr?.index ?? 0].scrollTo(
|
||||
index: currentIndex,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
} catch (_) {}
|
||||
},
|
||||
),
|
||||
if (widget.isSupportReverse == true)
|
||||
if (!_isList)
|
||||
_reverseButton
|
||||
else
|
||||
StreamBuilder(
|
||||
stream: _indexStream?.stream,
|
||||
initialData: _index,
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.data == _index
|
||||
? _reverseButton
|
||||
: const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
const Spacer(),
|
||||
StreamBuilder(
|
||||
stream: _indexStream?.stream,
|
||||
initialData: _index,
|
||||
builder: (context, snapshot) => mediumButton(
|
||||
tooltip: reverse[snapshot.data] ? '顺序' : '倒序',
|
||||
icon: !reverse[snapshot.data]
|
||||
? MdiIcons.sortNumericAscending
|
||||
: MdiIcons.sortNumericDescending,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
reverse[_ctr?.index ?? 0] = !reverse[_ctr?.index ?? 0];
|
||||
});
|
||||
},
|
||||
),
|
||||
if (widget.onClose != null)
|
||||
mediumButton(
|
||||
tooltip: '关闭',
|
||||
icon: Icons.close,
|
||||
onPressed: widget.onClose,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
),
|
||||
if (_isList)
|
||||
Material(
|
||||
child: TabBar(
|
||||
controller: _ctr,
|
||||
padding: const EdgeInsets.only(right: 60),
|
||||
isScrollable: true,
|
||||
tabs: (widget.season.sections as List)
|
||||
.map((item) => Tab(text: item.title))
|
||||
.toList(),
|
||||
dividerHeight: 1,
|
||||
dividerColor: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _isList
|
||||
? TabBarView(
|
||||
if (widget.onClose != null)
|
||||
mediumButton(
|
||||
tooltip: '关闭',
|
||||
icon: Icons.close,
|
||||
onPressed: widget.onClose,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
),
|
||||
if (_isList)
|
||||
TabBar(
|
||||
controller: _ctr,
|
||||
padding: const EdgeInsets.only(right: 60),
|
||||
isScrollable: true,
|
||||
tabs: (widget.season.sections as List)
|
||||
.map((item) => Tab(text: item.title))
|
||||
.toList(),
|
||||
dividerHeight: 1,
|
||||
dividerColor: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
),
|
||||
Expanded(
|
||||
child: _isList
|
||||
? Material(
|
||||
color: Colors.transparent,
|
||||
child: tabBarView(
|
||||
controller: _ctr,
|
||||
children: List.generate(
|
||||
widget.season.sections.length,
|
||||
(index) => _buildBody(
|
||||
index, widget.season.sections[index].episodes),
|
||||
),
|
||||
)
|
||||
: _buildBody(null, episodes),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: Material(
|
||||
color: Colors.transparent,
|
||||
child: _buildBody(null, episodes),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -464,30 +484,29 @@ class _ListSheetContentState extends State<ListSheetContent>
|
||||
},
|
||||
);
|
||||
|
||||
Widget _buildBody(i, episodes) => Material(
|
||||
child: ScrollablePositionedList.separated(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||
),
|
||||
reverse: reverse[i ?? 0],
|
||||
itemCount: episodes.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return buildEpisodeListItem(
|
||||
episodes[index],
|
||||
index,
|
||||
episodes.length,
|
||||
i != null
|
||||
? i == (_index)
|
||||
? currentIndex == index
|
||||
: false
|
||||
: currentIndex == index,
|
||||
);
|
||||
},
|
||||
itemScrollController: itemScrollController[i ?? 0],
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
),
|
||||
Widget _buildBody(i, episodes) => ScrollablePositionedList.separated(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||
),
|
||||
reverse: reverse[i ?? 0],
|
||||
itemCount: episodes.length,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return buildEpisodeListItem(
|
||||
episodes[index],
|
||||
index,
|
||||
episodes.length,
|
||||
i != null
|
||||
? i == (_index)
|
||||
? currentIndex == index
|
||||
: false
|
||||
: currentIndex == index,
|
||||
);
|
||||
},
|
||||
itemScrollController: itemScrollController[i ?? 0],
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ class _NineGridViewState extends State<NineGridView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget? child = Container();
|
||||
Widget? child;
|
||||
double? realWidth = widget.width;
|
||||
double? realHeight = widget.height;
|
||||
switch (widget.type) {
|
||||
|
||||
25
lib/common/widgets/radio_widget.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Widget radioWidget<T>({
|
||||
required T value,
|
||||
T? groupValue,
|
||||
required ValueChanged onChanged,
|
||||
required String title,
|
||||
double? paddingStart,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: () => onChanged(value),
|
||||
child: Row(
|
||||
children: [
|
||||
if (paddingStart != null) SizedBox(width: paddingStart),
|
||||
Radio(
|
||||
value: value,
|
||||
groupValue: groupValue,
|
||||
onChanged: onChanged,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
Text(title),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class Segment {
|
||||
|
||||
class SegmentProgressBar extends CustomPainter {
|
||||
final List<Segment> segmentColors;
|
||||
late double _defHeight;
|
||||
double? _defHeight;
|
||||
|
||||
SegmentProgressBar({
|
||||
required this.segmentColors,
|
||||
@@ -42,6 +42,18 @@ class SegmentProgressBar extends CustomPainter {
|
||||
if (segmentColors[i].title != null) {
|
||||
double fontSize = 10;
|
||||
|
||||
_defHeight ??= (TextPainter(
|
||||
text: TextSpan(
|
||||
text: segmentColors[i].title,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
),
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout())
|
||||
.height +
|
||||
2;
|
||||
|
||||
TextPainter getTextPainter() => TextPainter(
|
||||
text: TextSpan(
|
||||
text: segmentColors[i].title,
|
||||
@@ -51,14 +63,12 @@ class SegmentProgressBar extends CustomPainter {
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
strutStyle: StrutStyle(height: 1, leading: 0),
|
||||
strutStyle:
|
||||
StrutStyle(leading: 0, height: 1, fontSize: fontSize),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
|
||||
TextPainter textPainter = getTextPainter();
|
||||
if (i == 0) {
|
||||
_defHeight = textPainter.height;
|
||||
}
|
||||
|
||||
late double prevStart;
|
||||
if (i != 0) {
|
||||
@@ -75,7 +85,7 @@ class SegmentProgressBar extends CustomPainter {
|
||||
canvas.drawRect(
|
||||
Rect.fromLTRB(
|
||||
0,
|
||||
-_defHeight - 2,
|
||||
-_defHeight!,
|
||||
size.width,
|
||||
0,
|
||||
),
|
||||
@@ -86,9 +96,9 @@ class SegmentProgressBar extends CustomPainter {
|
||||
canvas.drawRect(
|
||||
Rect.fromLTWH(
|
||||
segmentStart,
|
||||
-_defHeight - 2,
|
||||
-_defHeight!,
|
||||
segmentEnd == segmentStart ? 2 : segmentEnd - segmentStart,
|
||||
size.height + _defHeight + 2,
|
||||
size.height + _defHeight!,
|
||||
),
|
||||
paint,
|
||||
);
|
||||
@@ -98,7 +108,7 @@ class SegmentProgressBar extends CustomPainter {
|
||||
: (segmentStart - prevStart - textPainter.width) / 2 +
|
||||
prevStart +
|
||||
1;
|
||||
double textY = (-_defHeight - textPainter.height) / 2 - 1;
|
||||
double textY = (-_defHeight! - textPainter.height) / 2;
|
||||
textPainter.paint(canvas, Offset(textX, textY));
|
||||
} else {
|
||||
canvas.drawRect(
|
||||
|
||||
@@ -39,7 +39,13 @@ class _SelfSizedHorizontalListState extends State<SelfSizedHorizontalList> {
|
||||
WidgetsBinding.instance.addPostFrameCallback((v) => setState(() {}));
|
||||
}
|
||||
if (widget.itemCount == 0) return const SizedBox();
|
||||
if (isInit) return Container(key: infoKey, child: widget.childBuilder(0));
|
||||
if (isInit) {
|
||||
return Container(
|
||||
key: infoKey,
|
||||
padding: widget.padding,
|
||||
child: widget.childBuilder(0),
|
||||
);
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
height: height,
|
||||
|
||||
54
lib/common/widgets/spring_physics.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Widget videoTabBarView({
|
||||
required List<Widget> children,
|
||||
TabController? controller,
|
||||
}) =>
|
||||
TabBarView(
|
||||
physics: const CustomTabBarViewClampingScrollPhysics(),
|
||||
controller: controller,
|
||||
children: children,
|
||||
);
|
||||
|
||||
Widget tabBarView({
|
||||
required List<Widget> children,
|
||||
TabController? controller,
|
||||
}) =>
|
||||
TabBarView(
|
||||
physics: const CustomTabBarViewScrollPhysics(),
|
||||
controller: controller,
|
||||
children: children,
|
||||
);
|
||||
|
||||
class CustomTabBarViewScrollPhysics extends ScrollPhysics {
|
||||
const CustomTabBarViewScrollPhysics({super.parent});
|
||||
|
||||
@override
|
||||
CustomTabBarViewScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||
return CustomTabBarViewScrollPhysics(parent: buildParent(ancestor));
|
||||
}
|
||||
|
||||
@override
|
||||
SpringDescription get spring => SpringDescription(
|
||||
mass: GStorage.springDescription[0],
|
||||
stiffness: GStorage.springDescription[1],
|
||||
damping: GStorage.springDescription[2],
|
||||
);
|
||||
}
|
||||
|
||||
class CustomTabBarViewClampingScrollPhysics extends ClampingScrollPhysics {
|
||||
const CustomTabBarViewClampingScrollPhysics({super.parent});
|
||||
|
||||
@override
|
||||
CustomTabBarViewClampingScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||
return CustomTabBarViewClampingScrollPhysics(parent: buildParent(ancestor));
|
||||
}
|
||||
|
||||
@override
|
||||
SpringDescription get spring => SpringDescription(
|
||||
mass: GStorage.springDescription[0],
|
||||
stiffness: GStorage.springDescription[1],
|
||||
damping: GStorage.springDescription[2],
|
||||
);
|
||||
}
|
||||
@@ -6,13 +6,14 @@ Widget statDanMu({
|
||||
String? theme,
|
||||
dynamic danmu,
|
||||
String? size,
|
||||
Color? textColor,
|
||||
}) {
|
||||
Map<String, Color> colorObject = {
|
||||
'white': Colors.white,
|
||||
'gray': Theme.of(context).colorScheme.outline.withOpacity(0.8),
|
||||
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
|
||||
};
|
||||
Color color = colorObject[theme]!;
|
||||
Color color = textColor ?? colorObject[theme]!;
|
||||
return Row(
|
||||
children: [
|
||||
Icon(
|
||||
|
||||
@@ -7,13 +7,14 @@ Widget statView({
|
||||
dynamic view,
|
||||
String? size,
|
||||
String? goto,
|
||||
Color? textColor,
|
||||
}) {
|
||||
Map<String, Color> colorObject = {
|
||||
'white': Colors.white,
|
||||
'gray': Theme.of(context).colorScheme.outline.withOpacity(0.8),
|
||||
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
|
||||
};
|
||||
Color color = colorObject[theme]!;
|
||||
Color color = textColor ?? colorObject[theme]!;
|
||||
return Row(
|
||||
children: [
|
||||
Icon(
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||
import 'package:PiliPlus/models/model_hot_video_item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../http/search.dart';
|
||||
import '../../utils/utils.dart';
|
||||
import '../constants.dart';
|
||||
@@ -42,132 +41,135 @@ class VideoCardH extends StatelessWidget {
|
||||
try {
|
||||
type = videoItem.type;
|
||||
} catch (_) {}
|
||||
return Stack(children: [
|
||||
Semantics(
|
||||
label: Utils.videoItemSemantics(videoItem),
|
||||
excludeSemantics: true,
|
||||
// customSemanticsActions: <CustomSemanticsAction, void Function()>{
|
||||
// for (var item in actions)
|
||||
// CustomSemanticsAction(
|
||||
// label: item.title.isEmpty ? 'label' : item.title): item.onTap!,
|
||||
// },
|
||||
child: InkWell(
|
||||
onLongPress: () {
|
||||
if (onLongPress != null) {
|
||||
onLongPress!();
|
||||
} else {
|
||||
imageSaveDialog(
|
||||
context: context,
|
||||
title: videoItem.title is String
|
||||
? videoItem.title
|
||||
: videoItem.title is List
|
||||
? (videoItem.title as List)
|
||||
.map((item) => item['text'])
|
||||
.join()
|
||||
: '',
|
||||
cover: videoItem.pic,
|
||||
);
|
||||
}
|
||||
},
|
||||
onTap: () async {
|
||||
if (onTap != null) {
|
||||
onTap?.call();
|
||||
return;
|
||||
}
|
||||
if (type == 'ketang') {
|
||||
SmartDialog.showToast('课堂视频暂不支持播放');
|
||||
return;
|
||||
}
|
||||
if (videoItem is HotVideoItemModel &&
|
||||
videoItem.redirectUrl?.isNotEmpty == true) {
|
||||
if (Utils.viewPgcFromUri(videoItem.redirectUrl!)) {
|
||||
return Stack(
|
||||
children: [
|
||||
Semantics(
|
||||
label: Utils.videoItemSemantics(videoItem),
|
||||
excludeSemantics: true,
|
||||
// customSemanticsActions: <CustomSemanticsAction, void Function()>{
|
||||
// for (var item in actions)
|
||||
// CustomSemanticsAction(
|
||||
// label: item.title.isEmpty ? 'label' : item.title): item.onTap!,
|
||||
// },
|
||||
child: InkWell(
|
||||
onLongPress: () {
|
||||
if (onLongPress != null) {
|
||||
onLongPress!();
|
||||
} else {
|
||||
imageSaveDialog(
|
||||
context: context,
|
||||
title: videoItem.title is String
|
||||
? videoItem.title
|
||||
: videoItem.title is List
|
||||
? (videoItem.title as List)
|
||||
.map((item) => item['text'])
|
||||
.join()
|
||||
: '',
|
||||
cover: videoItem.pic,
|
||||
);
|
||||
}
|
||||
},
|
||||
onTap: () async {
|
||||
if (onTap != null) {
|
||||
onTap?.call();
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
final int cid =
|
||||
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
||||
Get.toNamed(
|
||||
'/video?bvid=$bvid&cid=$cid',
|
||||
arguments: {
|
||||
'videoItem': videoItem,
|
||||
'heroTag': Utils.makeHeroTag(aid)
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
SmartDialog.showToast(err.toString());
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
vertical: 5,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(
|
||||
builder:
|
||||
(BuildContext context, BoxConstraints boxConstraints) {
|
||||
final double maxWidth = boxConstraints.maxWidth;
|
||||
final double maxHeight = boxConstraints.maxHeight;
|
||||
return Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: videoItem.pic as String,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
if (videoItem is HotVideoItemModel &&
|
||||
videoItem.pgcLabel?.isNotEmpty == true)
|
||||
PBadge(
|
||||
text: videoItem.pgcLabel,
|
||||
top: 6.0,
|
||||
right: 6.0,
|
||||
if (type == 'ketang') {
|
||||
SmartDialog.showToast('课堂视频暂不支持播放');
|
||||
return;
|
||||
}
|
||||
if (videoItem is HotVideoItemModel &&
|
||||
videoItem.redirectUrl?.isNotEmpty == true) {
|
||||
if (Utils.viewPgcFromUri(videoItem.redirectUrl!)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
final int cid = videoItem.cid ??
|
||||
await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
||||
Utils.toViewPage(
|
||||
'bvid=$bvid&cid=$cid',
|
||||
arguments: {
|
||||
'videoItem': videoItem,
|
||||
'heroTag': Utils.makeHeroTag(aid)
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
SmartDialog.showToast(err.toString());
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
vertical: 5,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context,
|
||||
BoxConstraints boxConstraints) {
|
||||
final double maxWidth = boxConstraints.maxWidth;
|
||||
final double maxHeight = boxConstraints.maxHeight;
|
||||
return Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: videoItem.pic as String,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
if (videoItem.duration != 0)
|
||||
PBadge(
|
||||
text: Utils.timeFormat(videoItem.duration!),
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: 'gray',
|
||||
),
|
||||
if (type != 'video')
|
||||
PBadge(
|
||||
text: type,
|
||||
left: 6.0,
|
||||
bottom: 6.0,
|
||||
type: 'primary',
|
||||
),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// pBadge(videoItem.rcmdReason.content, context,
|
||||
// 6.0, 6.0, null, null),
|
||||
],
|
||||
);
|
||||
},
|
||||
if (videoItem is HotVideoItemModel &&
|
||||
videoItem.pgcLabel?.isNotEmpty == true)
|
||||
PBadge(
|
||||
text: videoItem.pgcLabel,
|
||||
top: 6.0,
|
||||
right: 6.0,
|
||||
),
|
||||
if (videoItem.duration != 0)
|
||||
PBadge(
|
||||
text: Utils.timeFormat(videoItem.duration!),
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: 'gray',
|
||||
),
|
||||
if (type != 'video')
|
||||
PBadge(
|
||||
text: type,
|
||||
left: 6.0,
|
||||
bottom: 6.0,
|
||||
type: 'primary',
|
||||
),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// pBadge(videoItem.rcmdReason.content, context,
|
||||
// 6.0, 6.0, null, null),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
],
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (source == 'normal')
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 12,
|
||||
child: VideoPopupMenu(
|
||||
size: 29,
|
||||
iconSize: 17,
|
||||
videoItem: videoItem,
|
||||
if (source == 'normal')
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 12,
|
||||
child: VideoPopupMenu(
|
||||
size: 29,
|
||||
iconSize: 17,
|
||||
videoItem: videoItem,
|
||||
),
|
||||
),
|
||||
),
|
||||
]);
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoContent(context) {
|
||||
@@ -176,107 +178,102 @@ class VideoCardH extends StatelessWidget {
|
||||
: '';
|
||||
if (pubdate != '') pubdate += ' ';
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (videoItem.title is String)
|
||||
Expanded(
|
||||
child: Text(
|
||||
videoItem.title as String,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (videoItem.title is String)
|
||||
Expanded(
|
||||
child: Text(
|
||||
videoItem.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: RichText(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textScaler: MediaQuery.textScalerOf(context),
|
||||
text: TextSpan(
|
||||
children: [
|
||||
for (final i in videoItem.title) ...[
|
||||
TextSpan(
|
||||
text: i['text'] as String,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.fontSize,
|
||||
letterSpacing: 0.3,
|
||||
color: i['type'] == 'em'
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
TextSpan(
|
||||
children: [
|
||||
for (final i in videoItem.title) ...[
|
||||
TextSpan(
|
||||
text: i['text'] as String,
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
color: i['type'] == 'em'
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
// const Spacer(),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// child: Text(
|
||||
// videoItem.rcmdReason.content,
|
||||
// style: TextStyle(
|
||||
// fontSize: 9,
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 4),
|
||||
if (showOwner || showPubdate)
|
||||
Expanded(
|
||||
flex: 0,
|
||||
child: Text(
|
||||
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Row(
|
||||
children: [
|
||||
if (showView) ...[
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
view: videoItem.stat.view as int,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
if (showDanmaku)
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
danmu: videoItem.stat.danmu as int,
|
||||
),
|
||||
const Spacer(),
|
||||
if (source == 'normal') const SizedBox(width: 24),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
// const Spacer(),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// child: Text(
|
||||
// videoItem.rcmdReason.content,
|
||||
// style: TextStyle(
|
||||
// fontSize: 9,
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 4),
|
||||
if (showOwner || showPubdate)
|
||||
Expanded(
|
||||
flex: 0,
|
||||
child: Text(
|
||||
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Row(
|
||||
children: [
|
||||
if (showView) ...[
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
view: videoItem.stat.view as int,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
if (showDanmaku)
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
danmu: videoItem.stat.danmu as int,
|
||||
),
|
||||
const Spacer(),
|
||||
if (source == 'normal') const SizedBox(width: 24),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class VideoCardHGrpc extends StatelessWidget {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
PiliScheme.routePush(Uri.parse(videoItem.smallCoverV5.base.uri));
|
||||
PiliScheme.routePushFromUrl(videoItem.smallCoverV5.base.uri);
|
||||
} catch (err) {
|
||||
SmartDialog.showToast(err.toString());
|
||||
}
|
||||
@@ -111,7 +111,8 @@ class VideoCardHGrpc extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -133,58 +134,43 @@ class VideoCardHGrpc extends StatelessWidget {
|
||||
|
||||
Widget videoContent(context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...[
|
||||
Expanded(
|
||||
child: Text(
|
||||
videoItem.smallCoverV5.base.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
videoItem.smallCoverV5.base.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
],
|
||||
// const Spacer(),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// child: Text(
|
||||
// videoItem.rcmdReason.content,
|
||||
// style: TextStyle(
|
||||
// fontSize: 9,
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 4),
|
||||
if (showOwner || showPubdate)
|
||||
Text(
|
||||
videoItem.smallCoverV5.rightDesc1,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
// const Spacer(),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// child: Text(
|
||||
// videoItem.rcmdReason.content,
|
||||
// style: TextStyle(
|
||||
// fontSize: 9,
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 4),
|
||||
if (showOwner || showPubdate)
|
||||
Text(
|
||||
videoItem.smallCoverV5.rightDesc2,
|
||||
videoItem.smallCoverV5.rightDesc1,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
@@ -193,26 +179,36 @@ class VideoCardHGrpc extends StatelessWidget {
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
// Row(
|
||||
// children: [
|
||||
// if (showView) ...[
|
||||
// StatView(
|
||||
// theme: 'gray',
|
||||
// view: videoItem.stat.view as int,
|
||||
// ),
|
||||
// const SizedBox(width: 8),
|
||||
// ],
|
||||
// if (showDanmaku)
|
||||
// StatDanMu(
|
||||
// theme: 'gray',
|
||||
// danmu: videoItem.stat.danmu as int,
|
||||
// ),
|
||||
// const Spacer(),
|
||||
// if (source == 'normal') const SizedBox(width: 24),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
videoItem.smallCoverV5.rightDesc2,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
// Row(
|
||||
// children: [
|
||||
// if (showView) ...[
|
||||
// StatView(
|
||||
// theme: 'gray',
|
||||
// view: videoItem.stat.view as int,
|
||||
// ),
|
||||
// const SizedBox(width: 8),
|
||||
// ],
|
||||
// if (showDanmaku)
|
||||
// StatDanMu(
|
||||
// theme: 'gray',
|
||||
// danmu: videoItem.stat.danmu as int,
|
||||
// ),
|
||||
// const Spacer(),
|
||||
// if (source == 'normal') const SizedBox(width: 24),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/danmu.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/view.dart';
|
||||
import 'package:PiliPlus/common/widgets/video_popup_menu.dart';
|
||||
import 'package:PiliPlus/common/widgets/video_progress_indicator.dart';
|
||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../utils/utils.dart';
|
||||
import '../constants.dart';
|
||||
import 'badge.dart';
|
||||
@@ -47,8 +47,8 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Get.toNamed(
|
||||
'/video?bvid=${videoItem.bvid}&cid=${videoItem.firstCid}',
|
||||
Utils.toViewPage(
|
||||
'bvid=${videoItem.bvid}&cid=${videoItem.firstCid}',
|
||||
arguments: {
|
||||
'heroTag': Utils.makeHeroTag(videoItem.bvid),
|
||||
},
|
||||
@@ -98,12 +98,29 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
||||
bottom: 6.0,
|
||||
type: 'gray',
|
||||
),
|
||||
if (videoItem.history != null)
|
||||
Builder(builder: (context) {
|
||||
try {
|
||||
return Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: videoProgressIndicator(
|
||||
videoItem.history!['progress'] /
|
||||
videoItem.history!['duration'],
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -125,66 +142,61 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
||||
|
||||
Widget videoContent(context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...[
|
||||
Expanded(
|
||||
child: Text(
|
||||
// videoItem.season?['title'] ?? videoItem.title ?? '',
|
||||
videoItem.title ?? '',
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontWeight: videoItem.bvid != null && videoItem.bvid == bvid
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
color: videoItem.bvid != null && videoItem.bvid == bvid
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
// videoItem.season?['title'] ?? videoItem.title ?? '',
|
||||
videoItem.title ?? '',
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontWeight: videoItem.bvid != null && videoItem.bvid == bvid
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
color: videoItem.bvid != null && videoItem.bvid == bvid
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
videoItem.season != null
|
||||
? Utils.dateFormat(videoItem.season?['mtime'])
|
||||
: videoItem.publishTimeText ?? '',
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Row(
|
||||
children: [
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
// view: videoItem.season?['view_content'] ??
|
||||
// videoItem.viewContent,
|
||||
view: videoItem.viewContent,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
|
||||
danmu: videoItem.danmaku,
|
||||
),
|
||||
],
|
||||
Text(
|
||||
videoItem.season != null
|
||||
? Utils.dateFormat(videoItem.season?['mtime'])
|
||||
: videoItem.publishTimeText ?? '',
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Row(
|
||||
children: [
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
// view: videoItem.season?['view_content'] ??
|
||||
// videoItem.viewContent,
|
||||
view: videoItem.viewContent,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
|
||||
danmu: videoItem.danmaku,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,44 +35,7 @@ class VideoCardV extends StatelessWidget {
|
||||
String goto = videoItem.goto;
|
||||
switch (goto) {
|
||||
case 'bangumi':
|
||||
// if (videoItem.bangumiBadge == '电影') {
|
||||
// SmartDialog.showToast('暂不支持电影观看');
|
||||
// return;
|
||||
// }
|
||||
Utils.viewBangumi(epId: videoItem.param);
|
||||
// SmartDialog.showLoading(msg: '资源获取中');
|
||||
// var result = await SearchHttp.bangumiInfo(seasonId: null, epId: epId);
|
||||
// SmartDialog.dismiss();
|
||||
// if (result['status']) {
|
||||
// var bangumiDetail = result['data'];
|
||||
// EpisodeItem episode = result['data'].episodes.first;
|
||||
// int? epId = result['data'].userStatus?.progress?.lastEpId;
|
||||
// if (epId == null) {
|
||||
// epId = episode.epId;
|
||||
// } else {
|
||||
// for (var item in result['data'].episodes) {
|
||||
// if (item.epId == epId) {
|
||||
// episode = item;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// String bvid = episode.bvid!;
|
||||
// int cid = episode.cid!;
|
||||
// String pic = episode.cover!;
|
||||
// String seasonId = bangumiDetail.seasonId;
|
||||
// dynamic heroTag = Utils.makeHeroTag(cid);
|
||||
// Get.toNamed(
|
||||
// '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId&epId=$epId',
|
||||
// arguments: {
|
||||
// 'pic': pic,
|
||||
// 'heroTag': heroTag,
|
||||
// 'videoType': SearchType.media_bangumi,
|
||||
// },
|
||||
// );
|
||||
// } else {
|
||||
// SmartDialog.showToast(result['msg']);
|
||||
// }
|
||||
break;
|
||||
case 'av':
|
||||
String bvid = videoItem.bvid ?? IdUtils.av2bv(videoItem.aid);
|
||||
@@ -80,8 +43,8 @@ class VideoCardV extends StatelessWidget {
|
||||
if (cid == -1) {
|
||||
cid = await SearchHttp.ab2c(aid: videoItem.aid, bvid: bvid);
|
||||
}
|
||||
Get.toNamed(
|
||||
'/video?bvid=$bvid&cid=$cid',
|
||||
Utils.toViewPage(
|
||||
'bvid=$bvid&cid=$cid',
|
||||
arguments: {
|
||||
// 'videoItem': videoItem,
|
||||
'pic': videoItem.pic,
|
||||
@@ -314,10 +277,9 @@ class VideoCardV extends StatelessWidget {
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
flex: 0,
|
||||
child: RichText(
|
||||
child: Text.rich(
|
||||
maxLines: 1,
|
||||
textScaler: MediaQuery.textScalerOf(context),
|
||||
text: TextSpan(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
@@ -337,10 +299,9 @@ class VideoCardV extends StatelessWidget {
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
flex: 0,
|
||||
child: RichText(
|
||||
child: Text.rich(
|
||||
maxLines: 1,
|
||||
textScaler: MediaQuery.textScalerOf(context),
|
||||
text: TextSpan(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||
import 'package:PiliPlus/models/space/item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../utils/utils.dart';
|
||||
import '../constants.dart';
|
||||
import 'badge.dart';
|
||||
@@ -21,47 +20,8 @@ class VideoCardVMemberHome extends StatelessWidget {
|
||||
String goto = videoItem.goto ?? '';
|
||||
switch (goto) {
|
||||
case 'bangumi':
|
||||
// if (videoItem.bangumiBadge == '电影') {
|
||||
// SmartDialog.showToast('暂不支持电影观看');
|
||||
// return;
|
||||
// }
|
||||
// int epId = videoItem.param;
|
||||
Utils.viewBangumi(epId: videoItem.param);
|
||||
|
||||
// SmartDialog.showLoading(msg: '资源获取中');
|
||||
// var result = await SearchHttp.bangumiInfo(seasonId: null, epId: epId);
|
||||
// SmartDialog.dismiss();
|
||||
// if (result['status']) {
|
||||
// var bangumiDetail = result['data'];
|
||||
// EpisodeItem episode = result['data'].episodes.first;
|
||||
// int? epId = result['data'].userStatus?.progress?.lastEpId;
|
||||
// if (epId == null) {
|
||||
// epId = episode.epId;
|
||||
// } else {
|
||||
// for (var item in result['data'].episodes) {
|
||||
// if (item.epId == epId) {
|
||||
// episode = item;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// String bvid = episode.bvid!;
|
||||
// int cid = episode.cid!;
|
||||
// String pic = episode.cover!;
|
||||
// String seasonId = bangumiDetail.seasonId;
|
||||
// dynamic heroTag = Utils.makeHeroTag(cid);
|
||||
// Get.toNamed(
|
||||
// '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId&epId=$epId',
|
||||
// arguments: {
|
||||
// 'pic': pic,
|
||||
// 'heroTag': heroTag,
|
||||
// 'videoType': SearchType.media_bangumi,
|
||||
// },
|
||||
// );
|
||||
// } else {
|
||||
// SmartDialog.showToast(result['msg']);
|
||||
// }
|
||||
// break;
|
||||
break;
|
||||
case 'av':
|
||||
if (videoItem.isPgc == true && videoItem.uri?.isNotEmpty == true) {
|
||||
if (Utils.viewPgcFromUri(videoItem.uri!)) {
|
||||
@@ -69,8 +29,8 @@ class VideoCardVMemberHome extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
String bvid = videoItem.bvid ?? '';
|
||||
Get.toNamed(
|
||||
'/video?bvid=$bvid&cid=${videoItem.firstCid}',
|
||||
Utils.toViewPage(
|
||||
'bvid=$bvid&cid=${videoItem.firstCid}',
|
||||
arguments: {
|
||||
// 'videoItem': videoItem,
|
||||
'pic': videoItem.cover,
|
||||
|
||||
27
lib/common/widgets/video_progress_indicator.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Widget videoProgressIndicator(double progress) => ClipRect(
|
||||
clipper: ProgressClipper(),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10),
|
||||
),
|
||||
child: LinearProgressIndicator(
|
||||
minHeight: 10,
|
||||
value: progress,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
class ProgressClipper extends CustomClipper<Rect> {
|
||||
@override
|
||||
Rect getClip(Size size) {
|
||||
return Rect.fromLTWH(0, 6, size.width, size.height - 6);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(CustomClipper<Rect> oldClipper) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: bilibili/app/dynamic/v1/dynamic.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:grpc/service_api.dart' as $grpc;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'dynamic.pb.dart' as $0;
|
||||
|
||||
export 'dynamic.pb.dart';
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.app.dynamic.v1.Dynamic')
|
||||
class DynamicClient extends $grpc.Client {
|
||||
static final _$dynVideo = $grpc.ClientMethod<$0.DynVideoReq, $0.DynVideoReqReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynVideo',
|
||||
($0.DynVideoReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DynVideoReqReply.fromBuffer(value));
|
||||
static final _$dynDetails = $grpc.ClientMethod<$0.DynDetailsReq, $0.DynDetailsReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynDetails',
|
||||
($0.DynDetailsReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DynDetailsReply.fromBuffer(value));
|
||||
static final _$sVideo = $grpc.ClientMethod<$0.SVideoReq, $0.SVideoReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/SVideo',
|
||||
($0.SVideoReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.SVideoReply.fromBuffer(value));
|
||||
static final _$dynTab = $grpc.ClientMethod<$0.DynTabReq, $0.DynTabReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynTab',
|
||||
($0.DynTabReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DynTabReply.fromBuffer(value));
|
||||
static final _$dynOurCitySwitch = $grpc.ClientMethod<$0.DynOurCitySwitchReq, $0.NoReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynOurCitySwitch',
|
||||
($0.DynOurCitySwitchReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.NoReply.fromBuffer(value));
|
||||
static final _$dynOurCity = $grpc.ClientMethod<$0.DynOurCityReq, $0.DynOurCityReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynOurCity',
|
||||
($0.DynOurCityReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DynOurCityReply.fromBuffer(value));
|
||||
static final _$dynVideoPersonal = $grpc.ClientMethod<$0.DynVideoPersonalReq, $0.DynVideoPersonalReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynVideoPersonal',
|
||||
($0.DynVideoPersonalReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DynVideoPersonalReply.fromBuffer(value));
|
||||
static final _$dynUpdOffset = $grpc.ClientMethod<$0.DynUpdOffsetReq, $0.NoReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynUpdOffset',
|
||||
($0.DynUpdOffsetReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.NoReply.fromBuffer(value));
|
||||
static final _$dynRed = $grpc.ClientMethod<$0.DynRedReq, $0.DynRedReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynRed',
|
||||
($0.DynRedReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DynRedReply.fromBuffer(value));
|
||||
static final _$dynMixUpListViewMore = $grpc.ClientMethod<$0.NoReq, $0.DynMixUpListViewMoreReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynMixUpListViewMore',
|
||||
($0.NoReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DynMixUpListViewMoreReply.fromBuffer(value));
|
||||
static final _$dynMixUpListSearch = $grpc.ClientMethod<$0.DynMixUpListSearchReq, $0.DynMixUpListSearchReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/DynMixUpListSearch',
|
||||
($0.DynMixUpListSearchReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DynMixUpListSearchReply.fromBuffer(value));
|
||||
static final _$ourCityClickReport = $grpc.ClientMethod<$0.OurCityClickReportReq, $0.OurCityClickReportReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/OurCityClickReport',
|
||||
($0.OurCityClickReportReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.OurCityClickReportReply.fromBuffer(value));
|
||||
static final _$geoCoder = $grpc.ClientMethod<$0.GeoCoderReq, $0.GeoCoderReply>(
|
||||
'/bilibili.app.dynamic.v1.Dynamic/GeoCoder',
|
||||
($0.GeoCoderReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.GeoCoderReply.fromBuffer(value));
|
||||
|
||||
DynamicClient($grpc.ClientChannel channel,
|
||||
{$grpc.CallOptions? options,
|
||||
$core.Iterable<$grpc.ClientInterceptor>? interceptors})
|
||||
: super(channel, options: options,
|
||||
interceptors: interceptors);
|
||||
|
||||
$grpc.ResponseFuture<$0.DynVideoReqReply> dynVideo($0.DynVideoReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynVideo, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DynDetailsReply> dynDetails($0.DynDetailsReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynDetails, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.SVideoReply> sVideo($0.SVideoReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$sVideo, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DynTabReply> dynTab($0.DynTabReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynTab, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.NoReply> dynOurCitySwitch($0.DynOurCitySwitchReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynOurCitySwitch, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DynOurCityReply> dynOurCity($0.DynOurCityReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynOurCity, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DynVideoPersonalReply> dynVideoPersonal($0.DynVideoPersonalReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynVideoPersonal, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.NoReply> dynUpdOffset($0.DynUpdOffsetReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynUpdOffset, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DynRedReply> dynRed($0.DynRedReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynRed, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DynMixUpListViewMoreReply> dynMixUpListViewMore($0.NoReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynMixUpListViewMore, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DynMixUpListSearchReply> dynMixUpListSearch($0.DynMixUpListSearchReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dynMixUpListSearch, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.OurCityClickReportReply> ourCityClickReport($0.OurCityClickReportReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$ourCityClickReport, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.GeoCoderReply> geoCoder($0.GeoCoderReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$geoCoder, request, options: options);
|
||||
}
|
||||
}
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.app.dynamic.v1.Dynamic')
|
||||
abstract class DynamicServiceBase extends $grpc.Service {
|
||||
$core.String get $name => 'bilibili.app.dynamic.v1.Dynamic';
|
||||
|
||||
DynamicServiceBase() {
|
||||
$addMethod($grpc.ServiceMethod<$0.DynVideoReq, $0.DynVideoReqReply>(
|
||||
'DynVideo',
|
||||
dynVideo_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DynVideoReq.fromBuffer(value),
|
||||
($0.DynVideoReqReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DynDetailsReq, $0.DynDetailsReply>(
|
||||
'DynDetails',
|
||||
dynDetails_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DynDetailsReq.fromBuffer(value),
|
||||
($0.DynDetailsReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.SVideoReq, $0.SVideoReply>(
|
||||
'SVideo',
|
||||
sVideo_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.SVideoReq.fromBuffer(value),
|
||||
($0.SVideoReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DynTabReq, $0.DynTabReply>(
|
||||
'DynTab',
|
||||
dynTab_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DynTabReq.fromBuffer(value),
|
||||
($0.DynTabReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DynOurCitySwitchReq, $0.NoReply>(
|
||||
'DynOurCitySwitch',
|
||||
dynOurCitySwitch_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DynOurCitySwitchReq.fromBuffer(value),
|
||||
($0.NoReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DynOurCityReq, $0.DynOurCityReply>(
|
||||
'DynOurCity',
|
||||
dynOurCity_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DynOurCityReq.fromBuffer(value),
|
||||
($0.DynOurCityReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DynVideoPersonalReq, $0.DynVideoPersonalReply>(
|
||||
'DynVideoPersonal',
|
||||
dynVideoPersonal_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DynVideoPersonalReq.fromBuffer(value),
|
||||
($0.DynVideoPersonalReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DynUpdOffsetReq, $0.NoReply>(
|
||||
'DynUpdOffset',
|
||||
dynUpdOffset_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DynUpdOffsetReq.fromBuffer(value),
|
||||
($0.NoReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DynRedReq, $0.DynRedReply>(
|
||||
'DynRed',
|
||||
dynRed_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DynRedReq.fromBuffer(value),
|
||||
($0.DynRedReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.NoReq, $0.DynMixUpListViewMoreReply>(
|
||||
'DynMixUpListViewMore',
|
||||
dynMixUpListViewMore_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.NoReq.fromBuffer(value),
|
||||
($0.DynMixUpListViewMoreReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DynMixUpListSearchReq, $0.DynMixUpListSearchReply>(
|
||||
'DynMixUpListSearch',
|
||||
dynMixUpListSearch_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DynMixUpListSearchReq.fromBuffer(value),
|
||||
($0.DynMixUpListSearchReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.OurCityClickReportReq, $0.OurCityClickReportReply>(
|
||||
'OurCityClickReport',
|
||||
ourCityClickReport_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.OurCityClickReportReq.fromBuffer(value),
|
||||
($0.OurCityClickReportReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.GeoCoderReq, $0.GeoCoderReply>(
|
||||
'GeoCoder',
|
||||
geoCoder_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.GeoCoderReq.fromBuffer(value),
|
||||
($0.GeoCoderReply value) => value.writeToBuffer()));
|
||||
}
|
||||
|
||||
$async.Future<$0.DynVideoReqReply> dynVideo_Pre($grpc.ServiceCall call, $async.Future<$0.DynVideoReq> request) async {
|
||||
return dynVideo(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DynDetailsReply> dynDetails_Pre($grpc.ServiceCall call, $async.Future<$0.DynDetailsReq> request) async {
|
||||
return dynDetails(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.SVideoReply> sVideo_Pre($grpc.ServiceCall call, $async.Future<$0.SVideoReq> request) async {
|
||||
return sVideo(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DynTabReply> dynTab_Pre($grpc.ServiceCall call, $async.Future<$0.DynTabReq> request) async {
|
||||
return dynTab(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.NoReply> dynOurCitySwitch_Pre($grpc.ServiceCall call, $async.Future<$0.DynOurCitySwitchReq> request) async {
|
||||
return dynOurCitySwitch(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DynOurCityReply> dynOurCity_Pre($grpc.ServiceCall call, $async.Future<$0.DynOurCityReq> request) async {
|
||||
return dynOurCity(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DynVideoPersonalReply> dynVideoPersonal_Pre($grpc.ServiceCall call, $async.Future<$0.DynVideoPersonalReq> request) async {
|
||||
return dynVideoPersonal(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.NoReply> dynUpdOffset_Pre($grpc.ServiceCall call, $async.Future<$0.DynUpdOffsetReq> request) async {
|
||||
return dynUpdOffset(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DynRedReply> dynRed_Pre($grpc.ServiceCall call, $async.Future<$0.DynRedReq> request) async {
|
||||
return dynRed(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DynMixUpListViewMoreReply> dynMixUpListViewMore_Pre($grpc.ServiceCall call, $async.Future<$0.NoReq> request) async {
|
||||
return dynMixUpListViewMore(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DynMixUpListSearchReply> dynMixUpListSearch_Pre($grpc.ServiceCall call, $async.Future<$0.DynMixUpListSearchReq> request) async {
|
||||
return dynMixUpListSearch(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.OurCityClickReportReply> ourCityClickReport_Pre($grpc.ServiceCall call, $async.Future<$0.OurCityClickReportReq> request) async {
|
||||
return ourCityClickReport(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.GeoCoderReply> geoCoder_Pre($grpc.ServiceCall call, $async.Future<$0.GeoCoderReq> request) async {
|
||||
return geoCoder(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DynVideoReqReply> dynVideo($grpc.ServiceCall call, $0.DynVideoReq request);
|
||||
$async.Future<$0.DynDetailsReply> dynDetails($grpc.ServiceCall call, $0.DynDetailsReq request);
|
||||
$async.Future<$0.SVideoReply> sVideo($grpc.ServiceCall call, $0.SVideoReq request);
|
||||
$async.Future<$0.DynTabReply> dynTab($grpc.ServiceCall call, $0.DynTabReq request);
|
||||
$async.Future<$0.NoReply> dynOurCitySwitch($grpc.ServiceCall call, $0.DynOurCitySwitchReq request);
|
||||
$async.Future<$0.DynOurCityReply> dynOurCity($grpc.ServiceCall call, $0.DynOurCityReq request);
|
||||
$async.Future<$0.DynVideoPersonalReply> dynVideoPersonal($grpc.ServiceCall call, $0.DynVideoPersonalReq request);
|
||||
$async.Future<$0.NoReply> dynUpdOffset($grpc.ServiceCall call, $0.DynUpdOffsetReq request);
|
||||
$async.Future<$0.DynRedReply> dynRed($grpc.ServiceCall call, $0.DynRedReq request);
|
||||
$async.Future<$0.DynMixUpListViewMoreReply> dynMixUpListViewMore($grpc.ServiceCall call, $0.NoReq request);
|
||||
$async.Future<$0.DynMixUpListSearchReply> dynMixUpListSearch($grpc.ServiceCall call, $0.DynMixUpListSearchReq request);
|
||||
$async.Future<$0.OurCityClickReportReply> ourCityClickReport($grpc.ServiceCall call, $0.OurCityClickReportReq request);
|
||||
$async.Future<$0.GeoCoderReply> geoCoder($grpc.ServiceCall call, $0.GeoCoderReq request);
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: bilibili/main/community/reply/v1/reply.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:grpc/service_api.dart' as $grpc;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'reply.pb.dart' as $0;
|
||||
|
||||
export 'reply.pb.dart';
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.main.community.reply.v1.Reply')
|
||||
class ReplyClient extends $grpc.Client {
|
||||
static final _$mainList = $grpc.ClientMethod<$0.MainListReq, $0.MainListReply>(
|
||||
'/bilibili.main.community.reply.v1.Reply/MainList',
|
||||
($0.MainListReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.MainListReply.fromBuffer(value));
|
||||
static final _$detailList = $grpc.ClientMethod<$0.DetailListReq, $0.DetailListReply>(
|
||||
'/bilibili.main.community.reply.v1.Reply/DetailList',
|
||||
($0.DetailListReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DetailListReply.fromBuffer(value));
|
||||
static final _$dialogList = $grpc.ClientMethod<$0.DialogListReq, $0.DialogListReply>(
|
||||
'/bilibili.main.community.reply.v1.Reply/DialogList',
|
||||
($0.DialogListReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DialogListReply.fromBuffer(value));
|
||||
static final _$previewList = $grpc.ClientMethod<$0.PreviewListReq, $0.PreviewListReply>(
|
||||
'/bilibili.main.community.reply.v1.Reply/PreviewList',
|
||||
($0.PreviewListReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.PreviewListReply.fromBuffer(value));
|
||||
static final _$searchItemPreHook = $grpc.ClientMethod<$0.SearchItemPreHookReq, $0.SearchItemPreHookReply>(
|
||||
'/bilibili.main.community.reply.v1.Reply/SearchItemPreHook',
|
||||
($0.SearchItemPreHookReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.SearchItemPreHookReply.fromBuffer(value));
|
||||
static final _$searchItem = $grpc.ClientMethod<$0.SearchItemReq, $0.SearchItemReply>(
|
||||
'/bilibili.main.community.reply.v1.Reply/SearchItem',
|
||||
($0.SearchItemReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.SearchItemReply.fromBuffer(value));
|
||||
static final _$atSearch = $grpc.ClientMethod<$0.AtSearchReq, $0.AtSearchReply>(
|
||||
'/bilibili.main.community.reply.v1.Reply/AtSearch',
|
||||
($0.AtSearchReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.AtSearchReply.fromBuffer(value));
|
||||
static final _$replyInfo = $grpc.ClientMethod<$0.ReplyInfoReq, $0.ReplyInfoReply>(
|
||||
'/bilibili.main.community.reply.v1.Reply/ReplyInfo',
|
||||
($0.ReplyInfoReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.ReplyInfoReply.fromBuffer(value));
|
||||
static final _$userCallback = $grpc.ClientMethod<$0.UserCallbackReq, $0.UserCallbackReply>(
|
||||
'/bilibili.main.community.reply.v1.Reply/UserCallback',
|
||||
($0.UserCallbackReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.UserCallbackReply.fromBuffer(value));
|
||||
static final _$shareRepliesInfo = $grpc.ClientMethod<$0.ShareRepliesInfoReq, $0.ShareRepliesInfoResp>(
|
||||
'/bilibili.main.community.reply.v1.Reply/ShareRepliesInfo',
|
||||
($0.ShareRepliesInfoReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.ShareRepliesInfoResp.fromBuffer(value));
|
||||
static final _$suggestEmotes = $grpc.ClientMethod<$0.SuggestEmotesReq, $0.SuggestEmotesResp>(
|
||||
'/bilibili.main.community.reply.v1.Reply/SuggestEmotes',
|
||||
($0.SuggestEmotesReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.SuggestEmotesResp.fromBuffer(value));
|
||||
|
||||
ReplyClient($grpc.ClientChannel channel,
|
||||
{$grpc.CallOptions? options,
|
||||
$core.Iterable<$grpc.ClientInterceptor>? interceptors})
|
||||
: super(channel, options: options,
|
||||
interceptors: interceptors);
|
||||
|
||||
$grpc.ResponseFuture<$0.MainListReply> mainList($0.MainListReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$mainList, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DetailListReply> detailList($0.DetailListReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$detailList, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DialogListReply> dialogList($0.DialogListReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dialogList, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.PreviewListReply> previewList($0.PreviewListReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$previewList, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.SearchItemPreHookReply> searchItemPreHook($0.SearchItemPreHookReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$searchItemPreHook, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.SearchItemReply> searchItem($0.SearchItemReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$searchItem, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.AtSearchReply> atSearch($0.AtSearchReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$atSearch, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.ReplyInfoReply> replyInfo($0.ReplyInfoReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$replyInfo, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.UserCallbackReply> userCallback($0.UserCallbackReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$userCallback, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.ShareRepliesInfoResp> shareRepliesInfo($0.ShareRepliesInfoReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$shareRepliesInfo, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.SuggestEmotesResp> suggestEmotes($0.SuggestEmotesReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$suggestEmotes, request, options: options);
|
||||
}
|
||||
}
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.main.community.reply.v1.Reply')
|
||||
abstract class ReplyServiceBase extends $grpc.Service {
|
||||
$core.String get $name => 'bilibili.main.community.reply.v1.Reply';
|
||||
|
||||
ReplyServiceBase() {
|
||||
$addMethod($grpc.ServiceMethod<$0.MainListReq, $0.MainListReply>(
|
||||
'MainList',
|
||||
mainList_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.MainListReq.fromBuffer(value),
|
||||
($0.MainListReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DetailListReq, $0.DetailListReply>(
|
||||
'DetailList',
|
||||
detailList_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DetailListReq.fromBuffer(value),
|
||||
($0.DetailListReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DialogListReq, $0.DialogListReply>(
|
||||
'DialogList',
|
||||
dialogList_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DialogListReq.fromBuffer(value),
|
||||
($0.DialogListReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.PreviewListReq, $0.PreviewListReply>(
|
||||
'PreviewList',
|
||||
previewList_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.PreviewListReq.fromBuffer(value),
|
||||
($0.PreviewListReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.SearchItemPreHookReq, $0.SearchItemPreHookReply>(
|
||||
'SearchItemPreHook',
|
||||
searchItemPreHook_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.SearchItemPreHookReq.fromBuffer(value),
|
||||
($0.SearchItemPreHookReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.SearchItemReq, $0.SearchItemReply>(
|
||||
'SearchItem',
|
||||
searchItem_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.SearchItemReq.fromBuffer(value),
|
||||
($0.SearchItemReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.AtSearchReq, $0.AtSearchReply>(
|
||||
'AtSearch',
|
||||
atSearch_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.AtSearchReq.fromBuffer(value),
|
||||
($0.AtSearchReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.ReplyInfoReq, $0.ReplyInfoReply>(
|
||||
'ReplyInfo',
|
||||
replyInfo_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.ReplyInfoReq.fromBuffer(value),
|
||||
($0.ReplyInfoReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.UserCallbackReq, $0.UserCallbackReply>(
|
||||
'UserCallback',
|
||||
userCallback_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.UserCallbackReq.fromBuffer(value),
|
||||
($0.UserCallbackReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.ShareRepliesInfoReq, $0.ShareRepliesInfoResp>(
|
||||
'ShareRepliesInfo',
|
||||
shareRepliesInfo_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.ShareRepliesInfoReq.fromBuffer(value),
|
||||
($0.ShareRepliesInfoResp value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.SuggestEmotesReq, $0.SuggestEmotesResp>(
|
||||
'SuggestEmotes',
|
||||
suggestEmotes_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.SuggestEmotesReq.fromBuffer(value),
|
||||
($0.SuggestEmotesResp value) => value.writeToBuffer()));
|
||||
}
|
||||
|
||||
$async.Future<$0.MainListReply> mainList_Pre($grpc.ServiceCall call, $async.Future<$0.MainListReq> request) async {
|
||||
return mainList(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DetailListReply> detailList_Pre($grpc.ServiceCall call, $async.Future<$0.DetailListReq> request) async {
|
||||
return detailList(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DialogListReply> dialogList_Pre($grpc.ServiceCall call, $async.Future<$0.DialogListReq> request) async {
|
||||
return dialogList(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.PreviewListReply> previewList_Pre($grpc.ServiceCall call, $async.Future<$0.PreviewListReq> request) async {
|
||||
return previewList(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.SearchItemPreHookReply> searchItemPreHook_Pre($grpc.ServiceCall call, $async.Future<$0.SearchItemPreHookReq> request) async {
|
||||
return searchItemPreHook(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.SearchItemReply> searchItem_Pre($grpc.ServiceCall call, $async.Future<$0.SearchItemReq> request) async {
|
||||
return searchItem(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.AtSearchReply> atSearch_Pre($grpc.ServiceCall call, $async.Future<$0.AtSearchReq> request) async {
|
||||
return atSearch(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.ReplyInfoReply> replyInfo_Pre($grpc.ServiceCall call, $async.Future<$0.ReplyInfoReq> request) async {
|
||||
return replyInfo(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.UserCallbackReply> userCallback_Pre($grpc.ServiceCall call, $async.Future<$0.UserCallbackReq> request) async {
|
||||
return userCallback(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.ShareRepliesInfoResp> shareRepliesInfo_Pre($grpc.ServiceCall call, $async.Future<$0.ShareRepliesInfoReq> request) async {
|
||||
return shareRepliesInfo(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.SuggestEmotesResp> suggestEmotes_Pre($grpc.ServiceCall call, $async.Future<$0.SuggestEmotesReq> request) async {
|
||||
return suggestEmotes(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.MainListReply> mainList($grpc.ServiceCall call, $0.MainListReq request);
|
||||
$async.Future<$0.DetailListReply> detailList($grpc.ServiceCall call, $0.DetailListReq request);
|
||||
$async.Future<$0.DialogListReply> dialogList($grpc.ServiceCall call, $0.DialogListReq request);
|
||||
$async.Future<$0.PreviewListReply> previewList($grpc.ServiceCall call, $0.PreviewListReq request);
|
||||
$async.Future<$0.SearchItemPreHookReply> searchItemPreHook($grpc.ServiceCall call, $0.SearchItemPreHookReq request);
|
||||
$async.Future<$0.SearchItemReply> searchItem($grpc.ServiceCall call, $0.SearchItemReq request);
|
||||
$async.Future<$0.AtSearchReply> atSearch($grpc.ServiceCall call, $0.AtSearchReq request);
|
||||
$async.Future<$0.ReplyInfoReply> replyInfo($grpc.ServiceCall call, $0.ReplyInfoReq request);
|
||||
$async.Future<$0.UserCallbackReply> userCallback($grpc.ServiceCall call, $0.UserCallbackReq request);
|
||||
$async.Future<$0.ShareRepliesInfoResp> shareRepliesInfo($grpc.ServiceCall call, $0.ShareRepliesInfoReq request);
|
||||
$async.Future<$0.SuggestEmotesResp> suggestEmotes($grpc.ServiceCall call, $0.SuggestEmotesReq request);
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: playeronline.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:grpc/service_api.dart' as $grpc;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'playeronline.pb.dart' as $0;
|
||||
|
||||
export 'playeronline.pb.dart';
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.app.playeronline.v1.PlayerOnline')
|
||||
class PlayerOnlineClient extends $grpc.Client {
|
||||
static final _$playerOnline = $grpc.ClientMethod<$0.PlayerOnlineReq, $0.PlayerOnlineReply>(
|
||||
'/bilibili.app.playeronline.v1.PlayerOnline/PlayerOnline',
|
||||
($0.PlayerOnlineReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.PlayerOnlineReply.fromBuffer(value));
|
||||
static final _$premiereInfo = $grpc.ClientMethod<$0.PremiereInfoReq, $0.PremiereInfoReply>(
|
||||
'/bilibili.app.playeronline.v1.PlayerOnline/PremiereInfo',
|
||||
($0.PremiereInfoReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.PremiereInfoReply.fromBuffer(value));
|
||||
static final _$reportWatch = $grpc.ClientMethod<$0.ReportWatchReq, $0.NoReply>(
|
||||
'/bilibili.app.playeronline.v1.PlayerOnline/ReportWatch',
|
||||
($0.ReportWatchReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.NoReply.fromBuffer(value));
|
||||
|
||||
PlayerOnlineClient($grpc.ClientChannel channel,
|
||||
{$grpc.CallOptions? options,
|
||||
$core.Iterable<$grpc.ClientInterceptor>? interceptors})
|
||||
: super(channel, options: options,
|
||||
interceptors: interceptors);
|
||||
|
||||
$grpc.ResponseFuture<$0.PlayerOnlineReply> playerOnline($0.PlayerOnlineReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$playerOnline, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.PremiereInfoReply> premiereInfo($0.PremiereInfoReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$premiereInfo, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.NoReply> reportWatch($0.ReportWatchReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$reportWatch, request, options: options);
|
||||
}
|
||||
}
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.app.playeronline.v1.PlayerOnline')
|
||||
abstract class PlayerOnlineServiceBase extends $grpc.Service {
|
||||
$core.String get $name => 'bilibili.app.playeronline.v1.PlayerOnline';
|
||||
|
||||
PlayerOnlineServiceBase() {
|
||||
$addMethod($grpc.ServiceMethod<$0.PlayerOnlineReq, $0.PlayerOnlineReply>(
|
||||
'PlayerOnline',
|
||||
playerOnline_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.PlayerOnlineReq.fromBuffer(value),
|
||||
($0.PlayerOnlineReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.PremiereInfoReq, $0.PremiereInfoReply>(
|
||||
'PremiereInfo',
|
||||
premiereInfo_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.PremiereInfoReq.fromBuffer(value),
|
||||
($0.PremiereInfoReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.ReportWatchReq, $0.NoReply>(
|
||||
'ReportWatch',
|
||||
reportWatch_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.ReportWatchReq.fromBuffer(value),
|
||||
($0.NoReply value) => value.writeToBuffer()));
|
||||
}
|
||||
|
||||
$async.Future<$0.PlayerOnlineReply> playerOnline_Pre($grpc.ServiceCall call, $async.Future<$0.PlayerOnlineReq> request) async {
|
||||
return playerOnline(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.PremiereInfoReply> premiereInfo_Pre($grpc.ServiceCall call, $async.Future<$0.PremiereInfoReq> request) async {
|
||||
return premiereInfo(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.NoReply> reportWatch_Pre($grpc.ServiceCall call, $async.Future<$0.ReportWatchReq> request) async {
|
||||
return reportWatch(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.PlayerOnlineReply> playerOnline($grpc.ServiceCall call, $0.PlayerOnlineReq request);
|
||||
$async.Future<$0.PremiereInfoReply> premiereInfo($grpc.ServiceCall call, $0.PremiereInfoReq request);
|
||||
$async.Future<$0.NoReply> reportWatch($grpc.ServiceCall call, $0.ReportWatchReq request);
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: bilibili/app/show/popular/v1/popular.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:grpc/service_api.dart' as $grpc;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'popular.pb.dart' as $0;
|
||||
|
||||
export 'popular.pb.dart';
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.app.show.v1.Popular')
|
||||
class PopularClient extends $grpc.Client {
|
||||
static final _$index = $grpc.ClientMethod<$0.PopularResultReq, $0.PopularReply>(
|
||||
'/bilibili.app.show.v1.Popular/Index',
|
||||
($0.PopularResultReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.PopularReply.fromBuffer(value));
|
||||
|
||||
PopularClient($grpc.ClientChannel channel,
|
||||
{$grpc.CallOptions? options,
|
||||
$core.Iterable<$grpc.ClientInterceptor>? interceptors})
|
||||
: super(channel, options: options,
|
||||
interceptors: interceptors);
|
||||
|
||||
$grpc.ResponseFuture<$0.PopularReply> index($0.PopularResultReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$index, request, options: options);
|
||||
}
|
||||
}
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.app.show.v1.Popular')
|
||||
abstract class PopularServiceBase extends $grpc.Service {
|
||||
$core.String get $name => 'bilibili.app.show.v1.Popular';
|
||||
|
||||
PopularServiceBase() {
|
||||
$addMethod($grpc.ServiceMethod<$0.PopularResultReq, $0.PopularReply>(
|
||||
'Index',
|
||||
index_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.PopularResultReq.fromBuffer(value),
|
||||
($0.PopularReply value) => value.writeToBuffer()));
|
||||
}
|
||||
|
||||
$async.Future<$0.PopularReply> index_Pre($grpc.ServiceCall call, $async.Future<$0.PopularResultReq> request) async {
|
||||
return index(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.PopularReply> index($grpc.ServiceCall call, $0.PopularResultReq request);
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
//
|
||||
// Generated code. Do not modify.
|
||||
// source: bilibili/community/service/dm/v1/dm.proto
|
||||
//
|
||||
// @dart = 2.12
|
||||
|
||||
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
import 'dart:async' as $async;
|
||||
import 'dart:core' as $core;
|
||||
|
||||
import 'package:grpc/service_api.dart' as $grpc;
|
||||
import 'package:protobuf/protobuf.dart' as $pb;
|
||||
|
||||
import 'dm.pb.dart' as $0;
|
||||
|
||||
export 'dm.pb.dart';
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.community.service.dm.v1.DM')
|
||||
class DMClient extends $grpc.Client {
|
||||
static final _$dmSegMobile = $grpc.ClientMethod<$0.DmSegMobileReq, $0.DmSegMobileReply>(
|
||||
'/bilibili.community.service.dm.v1.DM/DmSegMobile',
|
||||
($0.DmSegMobileReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DmSegMobileReply.fromBuffer(value));
|
||||
static final _$dmView = $grpc.ClientMethod<$0.DmViewReq, $0.DmViewReply>(
|
||||
'/bilibili.community.service.dm.v1.DM/DmView',
|
||||
($0.DmViewReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DmViewReply.fromBuffer(value));
|
||||
static final _$dmPlayerConfig = $grpc.ClientMethod<$0.DmPlayerConfigReq, $0.Response>(
|
||||
'/bilibili.community.service.dm.v1.DM/DmPlayerConfig',
|
||||
($0.DmPlayerConfigReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.Response.fromBuffer(value));
|
||||
static final _$dmSegOtt = $grpc.ClientMethod<$0.DmSegOttReq, $0.DmSegOttReply>(
|
||||
'/bilibili.community.service.dm.v1.DM/DmSegOtt',
|
||||
($0.DmSegOttReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DmSegOttReply.fromBuffer(value));
|
||||
static final _$dmSegSDK = $grpc.ClientMethod<$0.DmSegSDKReq, $0.DmSegSDKReply>(
|
||||
'/bilibili.community.service.dm.v1.DM/DmSegSDK',
|
||||
($0.DmSegSDKReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DmSegSDKReply.fromBuffer(value));
|
||||
static final _$dmExpoReport = $grpc.ClientMethod<$0.DmExpoReportReq, $0.DmExpoReportRes>(
|
||||
'/bilibili.community.service.dm.v1.DM/DmExpoReport',
|
||||
($0.DmExpoReportReq value) => value.writeToBuffer(),
|
||||
($core.List<$core.int> value) => $0.DmExpoReportRes.fromBuffer(value));
|
||||
|
||||
DMClient($grpc.ClientChannel channel,
|
||||
{$grpc.CallOptions? options,
|
||||
$core.Iterable<$grpc.ClientInterceptor>? interceptors})
|
||||
: super(channel, options: options,
|
||||
interceptors: interceptors);
|
||||
|
||||
$grpc.ResponseFuture<$0.DmSegMobileReply> dmSegMobile($0.DmSegMobileReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dmSegMobile, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DmViewReply> dmView($0.DmViewReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dmView, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.Response> dmPlayerConfig($0.DmPlayerConfigReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dmPlayerConfig, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DmSegOttReply> dmSegOtt($0.DmSegOttReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dmSegOtt, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DmSegSDKReply> dmSegSDK($0.DmSegSDKReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dmSegSDK, request, options: options);
|
||||
}
|
||||
|
||||
$grpc.ResponseFuture<$0.DmExpoReportRes> dmExpoReport($0.DmExpoReportReq request, {$grpc.CallOptions? options}) {
|
||||
return $createUnaryCall(_$dmExpoReport, request, options: options);
|
||||
}
|
||||
}
|
||||
|
||||
@$pb.GrpcServiceName('bilibili.community.service.dm.v1.DM')
|
||||
abstract class DMServiceBase extends $grpc.Service {
|
||||
$core.String get $name => 'bilibili.community.service.dm.v1.DM';
|
||||
|
||||
DMServiceBase() {
|
||||
$addMethod($grpc.ServiceMethod<$0.DmSegMobileReq, $0.DmSegMobileReply>(
|
||||
'DmSegMobile',
|
||||
dmSegMobile_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DmSegMobileReq.fromBuffer(value),
|
||||
($0.DmSegMobileReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DmViewReq, $0.DmViewReply>(
|
||||
'DmView',
|
||||
dmView_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DmViewReq.fromBuffer(value),
|
||||
($0.DmViewReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DmPlayerConfigReq, $0.Response>(
|
||||
'DmPlayerConfig',
|
||||
dmPlayerConfig_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DmPlayerConfigReq.fromBuffer(value),
|
||||
($0.Response value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DmSegOttReq, $0.DmSegOttReply>(
|
||||
'DmSegOtt',
|
||||
dmSegOtt_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DmSegOttReq.fromBuffer(value),
|
||||
($0.DmSegOttReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DmSegSDKReq, $0.DmSegSDKReply>(
|
||||
'DmSegSDK',
|
||||
dmSegSDK_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DmSegSDKReq.fromBuffer(value),
|
||||
($0.DmSegSDKReply value) => value.writeToBuffer()));
|
||||
$addMethod($grpc.ServiceMethod<$0.DmExpoReportReq, $0.DmExpoReportRes>(
|
||||
'DmExpoReport',
|
||||
dmExpoReport_Pre,
|
||||
false,
|
||||
false,
|
||||
($core.List<$core.int> value) => $0.DmExpoReportReq.fromBuffer(value),
|
||||
($0.DmExpoReportRes value) => value.writeToBuffer()));
|
||||
}
|
||||
|
||||
$async.Future<$0.DmSegMobileReply> dmSegMobile_Pre($grpc.ServiceCall call, $async.Future<$0.DmSegMobileReq> request) async {
|
||||
return dmSegMobile(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DmViewReply> dmView_Pre($grpc.ServiceCall call, $async.Future<$0.DmViewReq> request) async {
|
||||
return dmView(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.Response> dmPlayerConfig_Pre($grpc.ServiceCall call, $async.Future<$0.DmPlayerConfigReq> request) async {
|
||||
return dmPlayerConfig(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DmSegOttReply> dmSegOtt_Pre($grpc.ServiceCall call, $async.Future<$0.DmSegOttReq> request) async {
|
||||
return dmSegOtt(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DmSegSDKReply> dmSegSDK_Pre($grpc.ServiceCall call, $async.Future<$0.DmSegSDKReq> request) async {
|
||||
return dmSegSDK(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DmExpoReportRes> dmExpoReport_Pre($grpc.ServiceCall call, $async.Future<$0.DmExpoReportReq> request) async {
|
||||
return dmExpoReport(call, await request);
|
||||
}
|
||||
|
||||
$async.Future<$0.DmSegMobileReply> dmSegMobile($grpc.ServiceCall call, $0.DmSegMobileReq request);
|
||||
$async.Future<$0.DmViewReply> dmView($grpc.ServiceCall call, $0.DmViewReq request);
|
||||
$async.Future<$0.Response> dmPlayerConfig($grpc.ServiceCall call, $0.DmPlayerConfigReq request);
|
||||
$async.Future<$0.DmSegOttReply> dmSegOtt($grpc.ServiceCall call, $0.DmSegOttReq request);
|
||||
$async.Future<$0.DmSegSDKReply> dmSegSDK($grpc.ServiceCall call, $0.DmSegSDKReq request);
|
||||
$async.Future<$0.DmExpoReportRes> dmExpoReport($grpc.ServiceCall call, $0.DmExpoReportReq request);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import 'package:PiliPlus/grpc/app/dynamic/v1/dynamic.pbgrpc.dart' as v1;
|
||||
import 'package:PiliPlus/grpc/app/dynamic/v2/dynamic.pbgrpc.dart' as v2;
|
||||
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pbgrpc.dart';
|
||||
import 'package:PiliPlus/grpc/app/playeronline/v1/playeronline.pbgrpc.dart';
|
||||
import 'package:PiliPlus/grpc/app/show/popular/v1/popular.pbgrpc.dart';
|
||||
import 'package:grpc/grpc.dart';
|
||||
|
||||
class GrpcClient {
|
||||
ClientChannel? _channel;
|
||||
PlayerOnlineClient? _playerOnlineClient;
|
||||
PopularClient? _popularClient;
|
||||
ReplyClient? _replyClient;
|
||||
v2.DynamicClient? _dynamicClientV2;
|
||||
v1.DynamicClient? _dynamicClientV1;
|
||||
|
||||
GrpcClient._internal() {
|
||||
_channel = ClientChannel(
|
||||
'grpc.biliapi.net',
|
||||
port: 443,
|
||||
options: const ChannelOptions(
|
||||
credentials: ChannelCredentials.secure(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static final GrpcClient _instance = GrpcClient._internal();
|
||||
static GrpcClient get instance => _instance;
|
||||
|
||||
PlayerOnlineClient get playerOnlineClient {
|
||||
_playerOnlineClient ??= PlayerOnlineClient(_channel!);
|
||||
return _playerOnlineClient!;
|
||||
}
|
||||
|
||||
PopularClient get popularClient {
|
||||
_popularClient ??= PopularClient(_channel!);
|
||||
return _popularClient!;
|
||||
}
|
||||
|
||||
ReplyClient get replyClient {
|
||||
_replyClient ??= ReplyClient(_channel!);
|
||||
return _replyClient!;
|
||||
}
|
||||
|
||||
v2.DynamicClient get dynamicClientV2 {
|
||||
_dynamicClientV2 ??= v2.DynamicClient(_channel!);
|
||||
return _dynamicClientV2!;
|
||||
}
|
||||
|
||||
v1.DynamicClient get dynamicClientV1 {
|
||||
_dynamicClientV1 ??= v1.DynamicClient(_channel!);
|
||||
return _dynamicClientV1!;
|
||||
}
|
||||
|
||||
Future<void> shutdown() async {
|
||||
await _channel?.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,48 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/grpc/app/dynamic/v1/dynamic.pb.dart';
|
||||
import 'package:PiliPlus/grpc/app/dynamic/v2/dynamic.pb.dart';
|
||||
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
||||
import 'package:PiliPlus/grpc/app/playeronline/v1/playeronline.pbgrpc.dart';
|
||||
import 'package:PiliPlus/grpc/app/playeronline/v1/playeronline.pb.dart';
|
||||
import 'package:PiliPlus/grpc/app/show/popular/v1/popular.pb.dart';
|
||||
import 'package:PiliPlus/grpc/device/device.pb.dart';
|
||||
import 'package:PiliPlus/grpc/dm/v1/dm.pb.dart';
|
||||
import 'package:PiliPlus/grpc/fawkes/fawkes.pb.dart';
|
||||
import 'package:PiliPlus/grpc/grpc_client.dart';
|
||||
import 'package:PiliPlus/grpc/locale/locale.pb.dart';
|
||||
import 'package:PiliPlus/grpc/metadata/metadata.pb.dart';
|
||||
import 'package:PiliPlus/grpc/network/network.pb.dart' as network;
|
||||
import 'package:PiliPlus/grpc/restriction/restriction.pb.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/utils/login.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:fixnum/src/int64.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:grpc/grpc.dart';
|
||||
import 'package:protobuf/protobuf.dart' show GeneratedMessage;
|
||||
|
||||
class GrpcUrl {
|
||||
static const playerOnline =
|
||||
'/bilibili.app.playeronline.v1.PlayerOnline/PlayerOnline';
|
||||
static const popular = '/bilibili.app.show.v1.Popular/Index';
|
||||
static const dialogList =
|
||||
'/bilibili.main.community.reply.v1.Reply/DialogList';
|
||||
static const detailList =
|
||||
'/bilibili.main.community.reply.v1.Reply/DetailList';
|
||||
static const replyInfo = '/bilibili.main.community.reply.v1.Reply/ReplyInfo';
|
||||
static const mainList = '/bilibili.main.community.reply.v1.Reply/MainList';
|
||||
static const dynSpace = '/bilibili.app.dynamic.v2.Dynamic/DynSpace';
|
||||
static const dynRed = '/bilibili.app.dynamic.v1.Dynamic/DynRed';
|
||||
static const dmSegMobile = '/bilibili.community.service.dm.v1.DM/DmSegMobile';
|
||||
}
|
||||
|
||||
class GrpcRepo {
|
||||
static const gzipEncoder = GZipEncoder();
|
||||
static const gzipDecoder = GZipDecoder();
|
||||
|
||||
static final bool _isLogin = GStorage.userInfo.get('userInfoCache') != null;
|
||||
static final int? _mid = GStorage.userInfo.get('userInfoCache')?.mid;
|
||||
static final String? _accessKey = GStorage.localCache
|
||||
@@ -31,11 +53,14 @@ class GrpcRepo {
|
||||
static const _phone = 'phone';
|
||||
|
||||
static final _eId = _isLogin ? Utils.genAuroraEid(_mid!) : '';
|
||||
static final _buvid = LoginUtils.buvid();
|
||||
static final _buvid = LoginUtils.buvid;
|
||||
static final _traceId = Utils.genTraceId();
|
||||
static final _sessionId = Utils.generateRandomString(8);
|
||||
|
||||
static final Map<String, String> metadata = {
|
||||
static final Map<String, String> headers = {
|
||||
Headers.contentTypeHeader: 'application/grpc',
|
||||
'grpc-encoding': 'gzip',
|
||||
'gzip-accept-encoding': 'gzip,identity',
|
||||
'user-agent': '${Constants.userAgent} grpc-java-cronet/1.36.1',
|
||||
'x-bili-gaia-vtoken': '',
|
||||
'x-bili-aurora-eid': _isLogin ? _eId : '',
|
||||
@@ -46,85 +71,118 @@ class GrpcRepo {
|
||||
'buvid': _buvid,
|
||||
'bili-http-engine': 'cronet',
|
||||
'te': 'trailers',
|
||||
'x-bili-fawkes-req-bin': base64Encode((FawkesReq()
|
||||
..appkey = _mobiApp
|
||||
..env = 'prod'
|
||||
..sessionId = _sessionId)
|
||||
'x-bili-fawkes-req-bin': base64Encode(
|
||||
FawkesReq(appkey: _mobiApp, env: 'prod', sessionId: _sessionId)
|
||||
.writeToBuffer()),
|
||||
'x-bili-metadata-bin': base64Encode(Metadata(
|
||||
accessKey: _accessKey ?? '',
|
||||
mobiApp: _mobiApp,
|
||||
device: _phone,
|
||||
build: _build,
|
||||
channel: _biliChannel,
|
||||
buvid: _buvid,
|
||||
platform: _mobiApp,
|
||||
).writeToBuffer()),
|
||||
'x-bili-device-bin': base64Encode(Device(
|
||||
appId: 1,
|
||||
build: _build,
|
||||
buvid: _buvid,
|
||||
mobiApp: _mobiApp,
|
||||
platform: _mobiApp,
|
||||
device: _phone,
|
||||
channel: _biliChannel,
|
||||
brand: _phone,
|
||||
model: _phone,
|
||||
osver: '14',
|
||||
fpLocal: '',
|
||||
fpRemote: '',
|
||||
versionName: _build.toString(),
|
||||
fp: '',
|
||||
fts: Int64())
|
||||
.writeToBuffer()),
|
||||
'x-bili-metadata-bin': base64Encode((Metadata()
|
||||
..accessKey = _accessKey ?? ''
|
||||
..mobiApp = _mobiApp
|
||||
..device = _phone
|
||||
..build = _build
|
||||
..channel = _biliChannel
|
||||
..buvid = _buvid
|
||||
..platform = _mobiApp)
|
||||
'x-bili-network-bin': base64Encode(network.Network(
|
||||
type: network.NetworkType.WIFI,
|
||||
tf: network.TFType.TF_UNKNOWN,
|
||||
oid: '')
|
||||
.writeToBuffer()),
|
||||
'x-bili-device-bin': base64Encode((Device()
|
||||
..appId = 1
|
||||
..build = _build
|
||||
..buvid = _buvid
|
||||
..mobiApp = _mobiApp
|
||||
..platform = _mobiApp
|
||||
..device = _phone
|
||||
..channel = _biliChannel
|
||||
..brand = _phone
|
||||
..model = _phone
|
||||
..osver = '14'
|
||||
..fpLocal = ''
|
||||
..fpRemote = ''
|
||||
..versionName = _build.toString()
|
||||
..fp = ''
|
||||
..fts = Int64())
|
||||
'x-bili-restriction-bin': base64Encode(Restriction(
|
||||
teenagersMode: false,
|
||||
lessonsMode: false,
|
||||
mode: ModeType.NORMAL,
|
||||
review: false,
|
||||
disableRcmd: false,
|
||||
basicMode: false)
|
||||
.writeToBuffer()),
|
||||
'x-bili-network-bin': base64Encode((network.Network()
|
||||
..type = network.NetworkType.WIFI
|
||||
..tf = network.TFType.TF_UNKNOWN
|
||||
..oid = '')
|
||||
.writeToBuffer()),
|
||||
'x-bili-restriction-bin': base64Encode((Restriction()
|
||||
..teenagersMode = false
|
||||
..lessonsMode = false
|
||||
..mode = ModeType.NORMAL
|
||||
..review = false
|
||||
..disableRcmd = false
|
||||
..basicMode = false)
|
||||
.writeToBuffer()),
|
||||
'x-bili-locale-bin': base64Encode((Locale()
|
||||
..cLocale = LocaleIds(language: 'zh', region: 'CN')
|
||||
..sLocale = LocaleIds(language: 'zh', region: 'CN')
|
||||
..simCode = ''
|
||||
..timezone = 'Asia/Shanghai')
|
||||
'x-bili-locale-bin': base64Encode(Locale(
|
||||
cLocale: LocaleIds(language: 'zh', region: 'CN'),
|
||||
sLocale: LocaleIds(language: 'zh', region: 'CN'),
|
||||
simCode: '',
|
||||
timezone: 'Asia/Shanghai')
|
||||
.writeToBuffer()),
|
||||
'x-bili-exps-bin': '',
|
||||
};
|
||||
|
||||
static final CallOptions options = CallOptions(metadata: metadata);
|
||||
static final unprintableRegExp = RegExp(r"[^\u4e00-\u9fa5,。;!?UP]");
|
||||
|
||||
static Future _request(Function request) async {
|
||||
try {
|
||||
return await request();
|
||||
} catch (e) {
|
||||
dynamic defMsg() => {'status': false, 'msg': e.toString()};
|
||||
if (e is GrpcError) {
|
||||
try {
|
||||
String msg = utf8.decode(
|
||||
e.details?.firstOrNull?.getFieldOrNull(2),
|
||||
allowMalformed: true,
|
||||
);
|
||||
msg =
|
||||
msg.replaceAll(RegExp(r"[^a-zA-Z0-9\u4e00-\u9fa5,.;?,。;!?]"), '');
|
||||
if (msg.isNotEmpty) {
|
||||
return {'status': false, 'msg': msg};
|
||||
} else {
|
||||
return defMsg();
|
||||
}
|
||||
} catch (e1) {
|
||||
debugPrint(e1.toString());
|
||||
return defMsg();
|
||||
}
|
||||
static Uint8List compressProtobuf(Uint8List proto) {
|
||||
proto = gzipEncoder.encodeBytes(proto, level: 0);
|
||||
var byteLength = ByteData(4);
|
||||
byteLength.setInt32(0, proto.length, Endian.big);
|
||||
var compressed = Uint8List(5 + proto.length);
|
||||
compressed[0] = 1;
|
||||
compressed.setRange(1, 5, byteLength.buffer.asUint8List());
|
||||
compressed.setAll(5, proto);
|
||||
return compressed;
|
||||
}
|
||||
|
||||
static Uint8List decompressProtobuf(Uint8List data) {
|
||||
var length = ByteData.sublistView(data, 1, 5).getInt32(0, Endian.big);
|
||||
|
||||
if (data[0] == 1) {
|
||||
return gzipDecoder.decodeBytes(data.sublist(5, length + 5));
|
||||
} else {
|
||||
return data.sublist(5, length + 5);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> _request(
|
||||
url, GeneratedMessage request, Function grpcParser,
|
||||
{Function? onSuccess}) async {
|
||||
final response = await Request().post(HttpString.appBaseUrl + url,
|
||||
data: compressProtobuf(request.writeToBuffer()),
|
||||
options: Options(headers: headers, responseType: ResponseType.bytes));
|
||||
|
||||
if (response.data is Map) {
|
||||
return {'status': false, 'msg': response.data['message']};
|
||||
}
|
||||
|
||||
if (response.headers.value('Grpc-Status') == '0') {
|
||||
try {
|
||||
Uint8List data = response.data;
|
||||
data = decompressProtobuf(data);
|
||||
final grpcResponse = grpcParser(data);
|
||||
return {
|
||||
'status': true,
|
||||
'data': onSuccess == null ? grpcResponse : onSuccess(grpcResponse),
|
||||
};
|
||||
} catch (e) {
|
||||
return {'status': false, 'msg': e.toString()};
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
String msg = response.headers.value('Grpc-Status-Details-Bin') ?? '';
|
||||
if (msg != '') {
|
||||
while (msg.length % 4 != 0) {
|
||||
msg += '=';
|
||||
}
|
||||
msg = utf8
|
||||
.decode(base64Decode(msg), allowMalformed: true)
|
||||
.replaceAll(unprintableRegExp, '');
|
||||
}
|
||||
return {'status': false, 'msg': msg};
|
||||
} catch (e) {
|
||||
return {'status': false, 'msg': e.toString()};
|
||||
}
|
||||
return defMsg();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,22 +190,16 @@ class GrpcRepo {
|
||||
int aid = 0,
|
||||
int cid = 0,
|
||||
}) async {
|
||||
return await _request(() async {
|
||||
final request = PlayerOnlineReq()
|
||||
..aid = Int64(aid)
|
||||
..cid = Int64(cid)
|
||||
..playOpen = true;
|
||||
final response = await GrpcClient.instance.playerOnlineClient
|
||||
.playerOnline(request, options: options);
|
||||
return {'status': true, 'data': response.totalNumberText};
|
||||
});
|
||||
return await _request(
|
||||
GrpcUrl.playerOnline,
|
||||
PlayerOnlineReq(aid: Int64(aid), cid: Int64(cid), playOpen: true),
|
||||
PlayerOnlineReply.fromBuffer,
|
||||
onSuccess: (response) => response.totalNumberText);
|
||||
}
|
||||
|
||||
static Future popular(int idx) async {
|
||||
return await _request(() async {
|
||||
final request = PopularResultReq()..idx = Int64(idx);
|
||||
final response = await GrpcClient.instance.popularClient
|
||||
.index(request, options: options);
|
||||
return await _request(GrpcUrl.popular, PopularResultReq(idx: Int64(idx)),
|
||||
PopularReply.fromBuffer, onSuccess: (response) {
|
||||
response.items.retainWhere((item) => item.smallCoverV5.base.goto == 'av');
|
||||
return {'status': true, 'data': response.items};
|
||||
});
|
||||
@@ -161,17 +213,15 @@ class GrpcRepo {
|
||||
required CursorReq cursor,
|
||||
DetailListScene scene = DetailListScene.REPLY,
|
||||
}) async {
|
||||
return await _request(() async {
|
||||
final request = DialogListReq()
|
||||
..oid = Int64(oid)
|
||||
..type = Int64(type)
|
||||
..root = Int64(root)
|
||||
..rpid = Int64(rpid)
|
||||
..cursor = cursor;
|
||||
final response = await GrpcClient.instance.replyClient
|
||||
.dialogList(request, options: options);
|
||||
return {'status': true, 'data': response};
|
||||
});
|
||||
return await _request(
|
||||
GrpcUrl.dialogList,
|
||||
DialogListReq(
|
||||
oid: Int64(oid),
|
||||
type: Int64(type),
|
||||
root: Int64(root),
|
||||
rpid: Int64(rpid),
|
||||
cursor: cursor),
|
||||
DialogListReply.fromBuffer);
|
||||
}
|
||||
|
||||
static Future detailList({
|
||||
@@ -182,29 +232,22 @@ class GrpcRepo {
|
||||
required CursorReq cursor,
|
||||
DetailListScene scene = DetailListScene.REPLY,
|
||||
}) async {
|
||||
return await _request(() async {
|
||||
final request = DetailListReq()
|
||||
..oid = Int64(oid)
|
||||
..type = Int64(type)
|
||||
..root = Int64(root)
|
||||
..rpid = Int64(rpid)
|
||||
..cursor = cursor
|
||||
..scene = scene;
|
||||
final response = await GrpcClient.instance.replyClient
|
||||
.detailList(request, options: options);
|
||||
return {'status': true, 'data': response};
|
||||
});
|
||||
return await _request(
|
||||
GrpcUrl.detailList,
|
||||
DetailListReq(
|
||||
oid: Int64(oid),
|
||||
type: Int64(type),
|
||||
root: Int64(root),
|
||||
rpid: Int64(rpid),
|
||||
cursor: cursor,
|
||||
scene: scene),
|
||||
DetailListReply.fromBuffer);
|
||||
}
|
||||
|
||||
static Future replyInfo({
|
||||
required int rpid,
|
||||
}) async {
|
||||
return await _request(() async {
|
||||
final request = ReplyInfoReq()..rpid = Int64(rpid);
|
||||
final response = await GrpcClient.instance.replyClient
|
||||
.replyInfo(request, options: options);
|
||||
return {'status': true, 'data': response.reply};
|
||||
});
|
||||
static Future replyInfo({required int rpid}) async {
|
||||
return await _request(GrpcUrl.replyInfo, ReplyInfoReq(rpid: Int64(rpid)),
|
||||
ReplyInfoReply.fromBuffer,
|
||||
onSuccess: (response) => response.reply);
|
||||
}
|
||||
|
||||
static Future mainList({
|
||||
@@ -212,40 +255,39 @@ class GrpcRepo {
|
||||
required int oid,
|
||||
required CursorReq cursor,
|
||||
}) async {
|
||||
return await _request(() async {
|
||||
final request = MainListReq()
|
||||
..oid = Int64(oid)
|
||||
..type = Int64(type)
|
||||
..rpid = Int64(0)
|
||||
..cursor = cursor;
|
||||
final response = await GrpcClient.instance.replyClient
|
||||
.mainList(request, options: options);
|
||||
return {'status': true, 'data': response};
|
||||
});
|
||||
return await _request(
|
||||
GrpcUrl.mainList,
|
||||
MainListReq(
|
||||
oid: Int64(oid), type: Int64(type), rpid: Int64(0), cursor: cursor),
|
||||
MainListReply.fromBuffer);
|
||||
}
|
||||
|
||||
static Future dynSpace({
|
||||
required int uid,
|
||||
required int page,
|
||||
}) async {
|
||||
return await _request(() async {
|
||||
final request = DynSpaceReq()
|
||||
..hostUid = Int64(uid)
|
||||
..localTime = 8
|
||||
..page = Int64(page)
|
||||
..from = 'space';
|
||||
final DynSpaceRsp response = await GrpcClient.instance.dynamicClientV2
|
||||
.dynSpace(request, options: options);
|
||||
return {'status': true, 'data': response};
|
||||
});
|
||||
return await _request(
|
||||
GrpcUrl.dynSpace,
|
||||
DynSpaceReq(
|
||||
hostUid: Int64(uid),
|
||||
localTime: 8,
|
||||
page: Int64(page),
|
||||
from: 'space'),
|
||||
DynSpaceRsp.fromBuffer);
|
||||
}
|
||||
|
||||
static Future dynRed() async {
|
||||
return await _request(() async {
|
||||
final request = DynRedReq()..tabOffset.add(TabOffset(tab: 1));
|
||||
final DynRedReply response = await GrpcClient.instance.dynamicClientV1
|
||||
.dynRed(request, options: options);
|
||||
return {'status': true, 'data': response.dynRedItem.count.toInt()};
|
||||
});
|
||||
return await _request(GrpcUrl.dynRed,
|
||||
DynRedReq(tabOffset: [TabOffset(tab: 1)]), DynRedReply.fromBuffer,
|
||||
onSuccess: (response) => response.dynRedItem.count.toInt());
|
||||
}
|
||||
|
||||
static Future dmSegMobile(
|
||||
{required int cid, required int segmentIndex, int type = 1}) async {
|
||||
return await _request(
|
||||
GrpcUrl.dmSegMobile,
|
||||
DmSegMobileReq(
|
||||
oid: Int64(cid), segmentIndex: Int64(segmentIndex), type: type),
|
||||
DmSegMobileReply.fromBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,15 @@ class Api {
|
||||
// up_mid num 目标用户mid 必要
|
||||
// type num 目标内容属性 非必要 默认为全部 0:全部 2:视频稿件
|
||||
// rid num 目标 视频稿件avid
|
||||
static const String videoInFolder = '/x/v3/fav/folder/created/list-all';
|
||||
static const String favFolder = '/x/v3/fav/folder/created/list-all';
|
||||
|
||||
static const String copyFav = '/x/v3/fav/resource/copy';
|
||||
|
||||
static const String moveFav = '/x/v3/fav/resource/move';
|
||||
|
||||
static const String copyToview = '/x/v2/history/toview/copy';
|
||||
|
||||
static const String moveToview = '/x/v2/history/toview/move';
|
||||
|
||||
// 视频详情页 相关视频
|
||||
static const String relatedList = '/x/web-interface/archive/related';
|
||||
@@ -182,6 +190,8 @@ class Api {
|
||||
|
||||
static const String deleteFolder = '/x/v3/fav/folder/del';
|
||||
|
||||
static const String cleanFav = '/x/v3/fav/resource/clean';
|
||||
|
||||
/// 收藏夹 详情
|
||||
/// media_id 当前收藏夹id 搜索全部时为默认收藏夹id
|
||||
/// pn int 当前页
|
||||
@@ -409,7 +419,7 @@ class Api {
|
||||
// https://api.bilibili.com/x/player/online/total?aid=913663681&cid=1203559746&bvid=BV1MM4y1s7NZ&ts=56427838
|
||||
static const String onlineTotal = '/x/player/online/total';
|
||||
|
||||
static const String webDanmaku = '/x/v2/dm/web/seg.so';
|
||||
// static const String webDanmaku = '/x/v2/dm/web/seg.so';
|
||||
|
||||
// 发送视频弹幕
|
||||
//https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/action.md
|
||||
@@ -609,7 +619,7 @@ class Api {
|
||||
|
||||
/// 申请二维码(TV端)
|
||||
static const getTVCode =
|
||||
'https://passport.bilibili.com/x/passport-tv-login/qrcode/auth_code';
|
||||
'${HttpString.passBaseUrl}/x/passport-tv-login/qrcode/auth_code';
|
||||
|
||||
///扫码登录(TV端)
|
||||
static const qrcodePoll =
|
||||
@@ -652,7 +662,7 @@ class Api {
|
||||
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
|
||||
|
||||
/// 用户动态主页
|
||||
static const dynamicSpmPrefix = 'https://space.bilibili.com/1/dynamic';
|
||||
static const dynamicSpmPrefix = '${HttpString.spaceBaseUrl}/1/dynamic';
|
||||
|
||||
/// 激活buvid3
|
||||
static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi';
|
||||
@@ -708,4 +718,8 @@ class Api {
|
||||
/// 我的关注 - 正在直播
|
||||
static const String getFollowingLive =
|
||||
'${HttpString.liveBaseUrl}/xlive/web-ucenter/user/following';
|
||||
|
||||
static const String pgcIndexCondition = '/pgc/season/index/condition';
|
||||
|
||||
static const String pgcIndexResult = '/pgc/season/index/result';
|
||||
}
|
||||
|
||||
@@ -1,9 +1,55 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
|
||||
import '../models/bangumi/list.dart';
|
||||
import '../models/bangumi/pgc_index/condition.dart';
|
||||
import 'index.dart';
|
||||
|
||||
class BangumiHttp {
|
||||
static Future<LoadingState> pgcIndexResult({
|
||||
required int page,
|
||||
required Map<String, dynamic> params,
|
||||
seasonType,
|
||||
type,
|
||||
indexType,
|
||||
}) async {
|
||||
dynamic res = await Request().get(
|
||||
Api.pgcIndexResult,
|
||||
queryParameters: {
|
||||
...params,
|
||||
if (seasonType != null) 'season_type': seasonType,
|
||||
if (type != null) 'type': type,
|
||||
if (indexType != null) 'index_type': indexType,
|
||||
'page': page,
|
||||
'pagesize': 21,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(res.data['data']);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> pgcIndexCondition({
|
||||
seasonType,
|
||||
type,
|
||||
indexType,
|
||||
}) async {
|
||||
dynamic res = await Request().get(
|
||||
Api.pgcIndexCondition,
|
||||
queryParameters: {
|
||||
if (seasonType != null) 'season_type': seasonType,
|
||||
if (type != null) 'type': type,
|
||||
if (indexType != null) 'index_type': indexType,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(Condition.fromJson(res.data['data']));
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<LoadingState> bangumiList({
|
||||
int? page,
|
||||
int? indexType,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:PiliPlus/grpc/dm/v1/dm.pb.dart';
|
||||
import 'package:PiliPlus/grpc/grpc_repo.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'index.dart';
|
||||
|
||||
class DanmakaHttp {
|
||||
class DanmakuHttp {
|
||||
// 获取视频弹幕
|
||||
static Future queryDanmaku({
|
||||
required int cid,
|
||||
@@ -11,20 +12,12 @@ class DanmakaHttp {
|
||||
required bool mergeDanmaku,
|
||||
}) async {
|
||||
// 构建参数对象
|
||||
Map<String, int> params = {
|
||||
'type': 1,
|
||||
'oid': cid,
|
||||
'segment_index': segmentIndex,
|
||||
};
|
||||
var response = await Request().get(
|
||||
Api.webDanmaku,
|
||||
queryParameters: params,
|
||||
options: Options(responseType: ResponseType.bytes),
|
||||
);
|
||||
if (response.statusCode != 200 || response.data == null) {
|
||||
final response =
|
||||
await GrpcRepo.dmSegMobile(cid: cid, segmentIndex: segmentIndex);
|
||||
if (!response['status']) {
|
||||
return DmSegMobileReply();
|
||||
}
|
||||
DmSegMobileReply data = DmSegMobileReply.fromBuffer(response.data);
|
||||
DmSegMobileReply data = response['data'];
|
||||
if (mergeDanmaku) {
|
||||
data.elems.unique((item) => item.content);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../models/dynamics/result.dart';
|
||||
import '../models/dynamics/up.dart';
|
||||
@@ -9,12 +10,13 @@ class DynamicsHttp {
|
||||
String? type,
|
||||
String? offset,
|
||||
int? mid,
|
||||
required bool antiGoodsDyn,
|
||||
}) async {
|
||||
Map<String, dynamic> data = {
|
||||
'type': type ?? 'all',
|
||||
'timezone_offset': '-480',
|
||||
'offset': offset,
|
||||
'features': 'itemOpusStyle'
|
||||
'features': 'itemOpusStyle,listOnlyfans'
|
||||
};
|
||||
if (mid != -1) {
|
||||
data['host_mid'] = mid;
|
||||
@@ -24,6 +26,15 @@ class DynamicsHttp {
|
||||
if (res.data['code'] == 0) {
|
||||
try {
|
||||
DynamicsDataModel data = DynamicsDataModel.fromJson(res.data['data']);
|
||||
if (antiGoodsDyn) {
|
||||
data.items?.removeWhere(
|
||||
(item) =>
|
||||
item.orig?.modules?.moduleDynamic?.additional?.type ==
|
||||
'ADDITIONAL_TYPE_GOODS' ||
|
||||
item.modules?.moduleDynamic?.additional?.type ==
|
||||
'ADDITIONAL_TYPE_GOODS',
|
||||
);
|
||||
}
|
||||
return LoadingState.success(data);
|
||||
} catch (err) {
|
||||
return LoadingState.error(err.toString());
|
||||
@@ -78,13 +89,23 @@ class DynamicsHttp {
|
||||
|
||||
//
|
||||
static Future dynamicDetail({
|
||||
String? id,
|
||||
dynamic id,
|
||||
dynamic rid,
|
||||
dynamic type,
|
||||
bool? clearCookie,
|
||||
}) async {
|
||||
var res = await Request().get(Api.dynamicDetail, queryParameters: {
|
||||
'timezone_offset': -480,
|
||||
'id': id,
|
||||
'features': 'itemOpusStyle',
|
||||
});
|
||||
var res = await Request().get(
|
||||
Api.dynamicDetail,
|
||||
queryParameters: {
|
||||
'timezone_offset': -480,
|
||||
if (id != null) 'id': id,
|
||||
if (rid != null) 'rid': rid,
|
||||
if (type != null) 'type': type,
|
||||
'features': 'itemOpusStyle',
|
||||
},
|
||||
options:
|
||||
clearCookie == true ? Options(extra: {'clearCookie': true}) : null,
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
try {
|
||||
return {
|
||||
|
||||
@@ -3,10 +3,14 @@ import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'dart:math' show Random;
|
||||
import 'package:PiliPlus/build_config.dart';
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:brotli/brotli.dart';
|
||||
import 'package:cookie_jar/cookie_jar.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dio/io.dart';
|
||||
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import '../utils/storage.dart';
|
||||
@@ -14,10 +18,12 @@ import '../utils/utils.dart';
|
||||
import 'api.dart';
|
||||
import 'constants.dart';
|
||||
import 'interceptor.dart';
|
||||
import 'interceptor_anonymity.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web;
|
||||
|
||||
class Request {
|
||||
static const gzipDecoder = GZipDecoder();
|
||||
static const brotilDecoder = BrotliDecoder();
|
||||
|
||||
static final Request _instance = Request._internal();
|
||||
static late CookieManager cookieManager;
|
||||
static late final Dio dio;
|
||||
@@ -37,7 +43,7 @@ class Request {
|
||||
);
|
||||
cookieManager = CookieManager(cookieJar);
|
||||
dio.interceptors.add(cookieManager);
|
||||
dio.interceptors.add(AnonymityInterceptor());
|
||||
dio.interceptors.add(ApiInterceptor());
|
||||
final List<Cookie> cookies = await cookieManager.cookieJar
|
||||
.loadForRequest(Uri.parse(HttpString.baseUrl));
|
||||
for (Cookie item in cookies) {
|
||||
@@ -52,18 +58,18 @@ class Request {
|
||||
);
|
||||
}
|
||||
final userInfo = GStorage.userInfo.get('userInfoCache');
|
||||
if (userInfo != null && userInfo.mid != null) {
|
||||
final List<Cookie> cookie2 = await cookieManager.cookieJar
|
||||
if (userInfo?.mid != null) {
|
||||
final List<Cookie> tUrlCookies = await cookieManager.cookieJar
|
||||
.loadForRequest(Uri.parse(HttpString.tUrl));
|
||||
if (cookie2.isEmpty) {
|
||||
if (tUrlCookies.isEmpty) {
|
||||
try {
|
||||
await Request().get(HttpString.tUrl);
|
||||
await dio.head(HttpString.tUrl);
|
||||
} catch (e) {
|
||||
log("setCookie, ${e.toString()}");
|
||||
}
|
||||
}
|
||||
setOptionsHeaders(userInfo);
|
||||
}
|
||||
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
|
||||
|
||||
try {
|
||||
await buvidActivate();
|
||||
@@ -81,23 +87,15 @@ class Request {
|
||||
static Future<String> getCsrf() async {
|
||||
List<Cookie> cookies = await cookieManager.cookieJar
|
||||
.loadForRequest(Uri.parse(HttpString.apiBaseUrl));
|
||||
String token = '';
|
||||
if (cookies.where((e) => e.name == 'bili_jct').isNotEmpty) {
|
||||
token = cookies.firstWhere((e) => e.name == 'bili_jct').value;
|
||||
}
|
||||
return token;
|
||||
return cookies
|
||||
.firstWhere((e) => e.name == 'bili_jct', orElse: () => Cookie('', ''))
|
||||
.value;
|
||||
}
|
||||
|
||||
static setOptionsHeaders(userInfo, bool status) {
|
||||
if (status) {
|
||||
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
|
||||
dio.options.headers['x-bili-aurora-eid'] =
|
||||
IdUtils.genAuroraEid(userInfo.mid);
|
||||
}
|
||||
dio.options.headers['env'] = 'prod';
|
||||
dio.options.headers['app-key'] = 'android64';
|
||||
dio.options.headers['x-bili-aurora-zone'] = 'sh001';
|
||||
dio.options.headers['referer'] = 'https://www.bilibili.com/';
|
||||
static setOptionsHeaders(userInfo) {
|
||||
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
|
||||
dio.options.headers['x-bili-aurora-eid'] =
|
||||
IdUtils.genAuroraEid(userInfo.mid);
|
||||
}
|
||||
|
||||
static Future buvidActivate() async {
|
||||
@@ -121,7 +119,7 @@ class Request {
|
||||
|
||||
await Request().post(Api.activateBuvidApi,
|
||||
data: {'payload': jsonData},
|
||||
options: Options(contentType: 'application/json'));
|
||||
options: Options(contentType: Headers.jsonContentType));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -130,15 +128,24 @@ class Request {
|
||||
Request._internal() {
|
||||
//BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
|
||||
BaseOptions options = BaseOptions(
|
||||
//请求基地址,可以包含子路径
|
||||
baseUrl: HttpString.apiBaseUrl,
|
||||
//连接服务器超时时间,单位是毫秒.
|
||||
connectTimeout: const Duration(milliseconds: 12000),
|
||||
//响应流上前后两次接受到数据的间隔,单位为毫秒。
|
||||
receiveTimeout: const Duration(milliseconds: 12000),
|
||||
//Http请求头.
|
||||
headers: {},
|
||||
);
|
||||
//请求基地址,可以包含子路径
|
||||
baseUrl: HttpString.apiBaseUrl,
|
||||
//连接服务器超时时间,单位是毫秒.
|
||||
connectTimeout: const Duration(milliseconds: 12000),
|
||||
//响应流上前后两次接受到数据的间隔,单位为毫秒。
|
||||
receiveTimeout: const Duration(milliseconds: 12000),
|
||||
//Http请求头.
|
||||
headers: {
|
||||
'connection': 'keep-alive',
|
||||
'accept-encoding': 'br,gzip',
|
||||
'user-agent': 'Dart/3.6 (dart:io)', // Http2Adapter不会自动添加标头
|
||||
'referer': HttpString.baseUrl,
|
||||
'env': 'prod',
|
||||
'app-key': 'android64',
|
||||
'x-bili-aurora-zone': 'sh001',
|
||||
},
|
||||
responseDecoder: responseDecoder, // Http2Adapter没有自动解压
|
||||
persistentConnection: true);
|
||||
|
||||
enableSystemProxy = GStorage.setting
|
||||
.get(SettingBoxKey.enableSystemProxy, defaultValue: false) as bool;
|
||||
@@ -147,48 +154,49 @@ class Request {
|
||||
systemProxyPort =
|
||||
GStorage.setting.get(SettingBoxKey.systemProxyPort, defaultValue: '');
|
||||
|
||||
dio = Dio(options);
|
||||
final http11Adapter = IOHttpClientAdapter(createHttpClient: () {
|
||||
final client = HttpClient()
|
||||
..idleTimeout = const Duration(seconds: 30)
|
||||
..autoUncompress = false; // Http2Adapter没有自动解压, 统一行为
|
||||
// 设置代理
|
||||
if (enableSystemProxy) {
|
||||
client.findProxy = (_) => 'PROXY $systemProxyHost:$systemProxyPort';
|
||||
client.badCertificateCallback =
|
||||
(X509Certificate cert, String host, int port) => true;
|
||||
}
|
||||
return client;
|
||||
});
|
||||
|
||||
/// fix 第三方登录 302重定向 跟iOS代理问题冲突
|
||||
// ..httpClientAdapter = Http2Adapter(
|
||||
// ConnectionManager(
|
||||
// idleTimeout: const Duration(milliseconds: 10000),
|
||||
// onClientCreate: (context, ClientSetting config) =>
|
||||
// config.onBadCertificate = (_) => true,
|
||||
// ),
|
||||
// );
|
||||
|
||||
/// 设置代理
|
||||
if (enableSystemProxy) {
|
||||
dio.httpClientAdapter = IOHttpClientAdapter(
|
||||
createHttpClient: () {
|
||||
final HttpClient client = HttpClient();
|
||||
// Config the client.
|
||||
client.findProxy = (Uri uri) {
|
||||
// return 'PROXY host:port';
|
||||
return 'PROXY $systemProxyHost:$systemProxyPort';
|
||||
};
|
||||
client.badCertificateCallback =
|
||||
(X509Certificate cert, String host, int port) => true;
|
||||
return client;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
//添加拦截器
|
||||
dio.interceptors.add(ApiInterceptor());
|
||||
dio = Dio(options)
|
||||
..httpClientAdapter =
|
||||
GStorage.setting.get(SettingBoxKey.enableHttp2, defaultValue: false)
|
||||
? Http2Adapter(
|
||||
ConnectionManager(
|
||||
idleTimeout: const Duration(seconds: 30),
|
||||
onClientCreate: (_, ClientSetting config) {
|
||||
config.onBadCertificate = (_) => true;
|
||||
if (enableSystemProxy) {
|
||||
config.proxy = Uri(
|
||||
scheme: 'http',
|
||||
host: systemProxyHost,
|
||||
port: int.parse(systemProxyPort));
|
||||
}
|
||||
}),
|
||||
fallbackAdapter: http11Adapter)
|
||||
: http11Adapter;
|
||||
|
||||
// 日志拦截器 输出请求、响应内容
|
||||
dio.interceptors.add(LogInterceptor(
|
||||
request: false,
|
||||
requestHeader: false,
|
||||
responseHeader: false,
|
||||
));
|
||||
if (BuildConfig.isDebug) {
|
||||
dio.interceptors.add(LogInterceptor(
|
||||
request: false,
|
||||
requestHeader: false,
|
||||
responseHeader: false,
|
||||
));
|
||||
}
|
||||
|
||||
dio.transformer = BackgroundTransformer();
|
||||
dio.options.validateStatus = (int? status) {
|
||||
return status! >= 200 && status < 300 ||
|
||||
HttpString.validateStatusCodes.contains(status);
|
||||
return status! >= 200 && status < 300;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -285,20 +293,25 @@ class Request {
|
||||
token.cancel("cancelled");
|
||||
}
|
||||
|
||||
String headerUa({type = 'mob'}) {
|
||||
String headerUa = '';
|
||||
if (type == 'mob') {
|
||||
if (Platform.isIOS) {
|
||||
headerUa =
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Mobile/15E148 Safari/604.1';
|
||||
} else {
|
||||
headerUa =
|
||||
'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36';
|
||||
}
|
||||
} else {
|
||||
headerUa =
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15';
|
||||
static String headerUa({type = 'mob'}) {
|
||||
return type == 'mob'
|
||||
? Platform.isIOS
|
||||
? 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Mobile/15E148 Safari/604.1'
|
||||
: 'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36'
|
||||
: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15';
|
||||
}
|
||||
|
||||
static String responseDecoder(List<int> responseBytes, RequestOptions options,
|
||||
ResponseBody responseBody) {
|
||||
switch (responseBody.headers['content-encoding']?.firstOrNull) {
|
||||
case 'gzip':
|
||||
return utf8.decode(gzipDecoder.decodeBytes(responseBytes),
|
||||
allowMalformed: true);
|
||||
case 'br':
|
||||
return utf8.decode(brotilDecoder.convert(responseBytes),
|
||||
allowMalformed: true);
|
||||
default:
|
||||
return utf8.decode(responseBytes, allowMalformed: true);
|
||||
}
|
||||
return headerUa;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,71 @@
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
class ApiInterceptor extends Interceptor {
|
||||
// @override
|
||||
// void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
// debugPrint("请求之前");
|
||||
// // 在请求之前添加头部或认证信息
|
||||
// options.headers['Authorization'] = 'Bearer token';
|
||||
// options.headers['Content-Type'] = 'application/json';
|
||||
// handler.next(options);
|
||||
// }
|
||||
static const List<String> anonymityList = [
|
||||
Api.videoUrl,
|
||||
Api.videoIntro,
|
||||
Api.relatedList,
|
||||
Api.replyList,
|
||||
Api.replyReplyList,
|
||||
Api.searchSuggest,
|
||||
Api.searchByType,
|
||||
Api.heartBeat,
|
||||
Api.ab2c,
|
||||
Api.bangumiInfo,
|
||||
Api.liveRoomInfo,
|
||||
Api.onlineTotal,
|
||||
Api.dynamicDetail,
|
||||
Api.aiConclusion,
|
||||
Api.getSeasonDetailApi,
|
||||
Api.liveRoomDmToken,
|
||||
Api.liveRoomDmPrefetch,
|
||||
];
|
||||
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
void onRemoveCookie() {
|
||||
options.headers.remove('x-bili-mid');
|
||||
options.headers.remove('x-bili-aurora-eid');
|
||||
options.headers.remove('x-bili-aurora-zone');
|
||||
options.headers.remove('cookie');
|
||||
options.queryParameters.remove('access_key');
|
||||
options.queryParameters.remove('csrf');
|
||||
options.queryParameters.remove('csrf_token');
|
||||
if (options.data is Map) {
|
||||
options.data.remove('access_key');
|
||||
options.data.remove('csrf');
|
||||
options.data.remove('csrf_token');
|
||||
}
|
||||
}
|
||||
|
||||
// app端不需要cookie
|
||||
if (options.uri.host == 'app.bilibili.com') {
|
||||
options.headers.remove('cookie');
|
||||
}
|
||||
|
||||
if (options.extra['clearCookie'] == true) {
|
||||
onRemoveCookie();
|
||||
} else if (MineController.anonymity.value) {
|
||||
String uri = options.uri.toString();
|
||||
for (var i in anonymityList) {
|
||||
// 如果请求的url包含无痕列表中的url,则清空cookie
|
||||
// 但需要保证匹配到url的后半部分不再出现/符号,否则会误伤
|
||||
int index = uri.indexOf(i);
|
||||
if (index == -1) continue;
|
||||
if (uri.lastIndexOf('/') >= index + i.length) continue;
|
||||
//SmartDialog.showToast('触发无痕模式\n\n$i\n\n${options.uri}');
|
||||
onRemoveCookie();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handler.next(options);
|
||||
}
|
||||
|
||||
// @override
|
||||
// void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||
@@ -53,8 +107,8 @@ class ApiInterceptor extends Interceptor {
|
||||
} else {
|
||||
SmartDialog.showToast(
|
||||
await dioError(err) + url,
|
||||
displayType: SmartToastType.onlyRefresh,
|
||||
displayTime: const Duration(milliseconds: 1200),
|
||||
// displayType: SmartToastType.onlyRefresh,
|
||||
// displayTime: const Duration(milliseconds: 1200),
|
||||
);
|
||||
}
|
||||
super.onError(err, handler);
|
||||
@@ -85,5 +139,5 @@ class ApiInterceptor extends Interceptor {
|
||||
}
|
||||
|
||||
extension _ConnectivityResultExt on ConnectivityResult {
|
||||
String get title => ['蓝牙', 'Wi-Fi', '局域', '流量', '无', '代理', '其他'][index];
|
||||
String get title => const ['蓝牙', 'Wi-Fi', '局域', '流量', '无', '代理', '其他'][index];
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'dart:io';
|
||||
import 'package:dio/dio.dart';
|
||||
// import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import '../pages/mine/controller.dart';
|
||||
import 'api.dart';
|
||||
|
||||
class AnonymityInterceptor extends Interceptor {
|
||||
static const List<String> anonymityList = [
|
||||
Api.videoUrl,
|
||||
Api.videoIntro,
|
||||
Api.relatedList,
|
||||
Api.replyList,
|
||||
Api.replyReplyList,
|
||||
Api.searchSuggest,
|
||||
Api.searchByType,
|
||||
Api.heartBeat,
|
||||
Api.ab2c,
|
||||
Api.bangumiInfo,
|
||||
Api.liveRoomInfo,
|
||||
Api.onlineTotal,
|
||||
Api.webDanmaku,
|
||||
Api.dynamicDetail,
|
||||
Api.aiConclusion,
|
||||
Api.getSeasonDetailApi,
|
||||
Api.liveRoomDmToken,
|
||||
Api.liveRoomDmPrefetch,
|
||||
];
|
||||
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
if (MineController.anonymity.value) {
|
||||
String uri = options.uri.toString();
|
||||
for (var i in anonymityList) {
|
||||
// 如果请求的url包含无痕列表中的url,则清空cookie
|
||||
// 但需要保证匹配到url的后半部分不再出现/符号,否则会误伤
|
||||
int index = uri.indexOf(i);
|
||||
if (index == -1) continue;
|
||||
if (uri.lastIndexOf('/') >= index + i.length) continue;
|
||||
//SmartDialog.showToast('触发无痕模式\n\n$i\n\n${options.uri}');
|
||||
options.headers[HttpHeaders.cookieHeader] = "";
|
||||
if (options.data != null && options.data.csrf != null) {
|
||||
options.data.csrf = "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
handler.next(options);
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,10 @@ import '../utils/utils.dart';
|
||||
import 'index.dart';
|
||||
|
||||
class LoginHttp {
|
||||
static String deviceId = LoginUtils.genDeviceId();
|
||||
static String buvid = LoginUtils.buvid();
|
||||
static String host = 'passport.bilibili.com';
|
||||
static Map<String, String> headers = {
|
||||
static final String deviceId = LoginUtils.genDeviceId();
|
||||
static final String buvid = LoginUtils.buvid;
|
||||
static const String host = 'passport.bilibili.com';
|
||||
static final Map<String, String> headers = {
|
||||
'Host': host,
|
||||
'buvid': buvid,
|
||||
'env': 'prod',
|
||||
@@ -224,7 +224,7 @@ class LoginHttp {
|
||||
'device_platform': 'Android14vivo',
|
||||
'disable_rcmd': '0',
|
||||
'dt': Uri.encodeComponent(Encrypter(RSA(publicKey: publicKey))
|
||||
.encrypt(LoginUtils.generateRandomString(16))
|
||||
.encrypt(Utils.generateRandomString(16))
|
||||
.base64),
|
||||
'from_pv': 'main.homepage.avatar-nologin.all.click',
|
||||
'from_url': Uri.encodeComponent('bilibili://pegasus/promo'),
|
||||
@@ -304,7 +304,7 @@ class LoginHttp {
|
||||
// 'device_tourist_id': '',
|
||||
'disable_rcmd': '0',
|
||||
'dt': Uri.encodeComponent(Encrypter(RSA(publicKey: publicKey))
|
||||
.encrypt(LoginUtils.generateRandomString(16))
|
||||
.encrypt(Utils.generateRandomString(16))
|
||||
.base64),
|
||||
'from_pv': 'main.my-information.my-login.0.click',
|
||||
'from_url': Uri.encodeComponent('bilibili://user_center/mine'),
|
||||
|
||||
@@ -88,15 +88,11 @@ class MemberHttp {
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
int? _mid = GStorage.userInfo.get('userInfoCache')?.mid;
|
||||
dynamic res = await Request().get(
|
||||
Api.spaceArticle,
|
||||
queryParameters: data,
|
||||
options: Options(
|
||||
headers: {
|
||||
'env': 'prod',
|
||||
'app-key': 'android_hd',
|
||||
'x-bili-mid': _mid,
|
||||
'bili-http-engine': 'cronet',
|
||||
'user-agent': Constants.userAgent,
|
||||
},
|
||||
@@ -133,15 +129,11 @@ class MemberHttp {
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
int? _mid = GStorage.userInfo.get('userInfoCache')?.mid;
|
||||
dynamic res = await Request().get(
|
||||
Api.spaceFav,
|
||||
queryParameters: data,
|
||||
options: Options(
|
||||
headers: {
|
||||
'env': 'prod',
|
||||
'app-key': 'android_hd',
|
||||
'x-bili-mid': _mid,
|
||||
'bili-http-engine': 'cronet',
|
||||
'user-agent': Constants.userAgent,
|
||||
},
|
||||
@@ -214,7 +206,6 @@ class MemberHttp {
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
int? _mid = GStorage.userInfo.get('userInfoCache')?.mid;
|
||||
dynamic res = await Request().get(
|
||||
type == ContributeType.video
|
||||
? Api.spaceArchive
|
||||
@@ -228,9 +219,6 @@ class MemberHttp {
|
||||
queryParameters: data,
|
||||
options: Options(
|
||||
headers: {
|
||||
'env': 'prod',
|
||||
'app-key': 'android_hd',
|
||||
'x-bili-mid': _mid,
|
||||
'bili-http-engine': 'cronet',
|
||||
'user-agent': Constants.userAgent,
|
||||
},
|
||||
@@ -267,15 +255,11 @@ class MemberHttp {
|
||||
Constants.appSec,
|
||||
);
|
||||
data['sign'] = sign;
|
||||
int? _mid = GStorage.userInfo.get('userInfoCache')?.mid;
|
||||
dynamic res = await Request().get(
|
||||
Api.space,
|
||||
queryParameters: data,
|
||||
options: Options(
|
||||
headers: {
|
||||
'env': 'prod',
|
||||
'app-key': 'android_hd',
|
||||
'x-bili-mid': _mid,
|
||||
'bili-http-engine': 'cronet',
|
||||
'user-agent': Constants.userAgent,
|
||||
},
|
||||
@@ -294,7 +278,7 @@ class MemberHttp {
|
||||
dynamic wwebid,
|
||||
}) async {
|
||||
space(mid: mid);
|
||||
Map params = await WbiSign().makSign({
|
||||
Map params = await WbiSign.makSign({
|
||||
'mid': mid,
|
||||
'token': token,
|
||||
'platform': 'web',
|
||||
@@ -334,8 +318,13 @@ class MemberHttp {
|
||||
}
|
||||
|
||||
static Future memberCardInfo({int? mid}) async {
|
||||
var res = await Request()
|
||||
.get(Api.memberCardInfo, queryParameters: {'mid': mid, 'photo': true});
|
||||
var res = await Request().get(
|
||||
Api.memberCardInfo,
|
||||
queryParameters: {
|
||||
'mid': mid,
|
||||
'photo': false,
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': res.data['data']};
|
||||
} else {
|
||||
@@ -359,7 +348,7 @@ class MemberHttp {
|
||||
}) async {
|
||||
String dmImgStr = Utils.base64EncodeRandomString(16, 64);
|
||||
String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128);
|
||||
Map params = await WbiSign().makSign({
|
||||
Map params = await WbiSign.makSign({
|
||||
'mid': mid,
|
||||
'ps': ps,
|
||||
'tid': tid,
|
||||
@@ -397,14 +386,18 @@ class MemberHttp {
|
||||
}
|
||||
|
||||
// 用户动态
|
||||
static Future<LoadingState> memberDynamic({String? offset, int? mid}) async {
|
||||
static Future<LoadingState> memberDynamic({
|
||||
String? offset,
|
||||
int? mid,
|
||||
required bool antiGoodsDyn,
|
||||
}) async {
|
||||
String dmImgStr = Utils.base64EncodeRandomString(16, 64);
|
||||
String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128);
|
||||
Map params = await WbiSign().makSign({
|
||||
Map params = await WbiSign.makSign({
|
||||
'offset': offset ?? '',
|
||||
'host_mid': mid,
|
||||
'timezone_offset': '-480',
|
||||
'features': 'itemOpusStyle',
|
||||
'features': 'itemOpusStyle,listOnlyfans',
|
||||
'platform': 'web',
|
||||
'web_location': '333.999',
|
||||
'dm_img_list': '[]',
|
||||
@@ -416,7 +409,15 @@ class MemberHttp {
|
||||
});
|
||||
var res = await Request().get(Api.memberDynamic, queryParameters: params);
|
||||
if (res.data['code'] == 0) {
|
||||
return LoadingState.success(DynamicsDataModel.fromJson(res.data['data']));
|
||||
DynamicsDataModel data = DynamicsDataModel.fromJson(res.data['data']);
|
||||
if (antiGoodsDyn) {
|
||||
data.items?.removeWhere((item) =>
|
||||
item.orig?.modules?.moduleDynamic?.additional?.type ==
|
||||
'ADDITIONAL_TYPE_GOODS' ||
|
||||
item.modules?.moduleDynamic?.additional?.type ==
|
||||
'ADDITIONAL_TYPE_GOODS');
|
||||
}
|
||||
return LoadingState.success(data);
|
||||
} else {
|
||||
Map errMap = {
|
||||
-352: '风控校验失败,请检查登录状态',
|
||||
@@ -593,8 +594,8 @@ class MemberHttp {
|
||||
}
|
||||
|
||||
// 最近投币
|
||||
static Future getRecentCoinVideo({required int mid}) async {
|
||||
Map params = await WbiSign().makSign({
|
||||
static Future<LoadingState> getRecentCoinVideo({required int mid}) async {
|
||||
Map params = await WbiSign.makSign({
|
||||
'mid': mid,
|
||||
'gaia_source': 'main_web',
|
||||
'web_location': 333.999,
|
||||
@@ -610,24 +611,17 @@ class MemberHttp {
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data']
|
||||
.map<MemberCoinsDataModel>((e) => MemberCoinsDataModel.fromJson(e))
|
||||
.toList(),
|
||||
};
|
||||
return LoadingState.success(res.data['data']
|
||||
.map<MemberCoinsDataModel>((e) => MemberCoinsDataModel.fromJson(e))
|
||||
.toList());
|
||||
} else {
|
||||
return {
|
||||
'status': false,
|
||||
'data': [],
|
||||
'msg': res.data['message'],
|
||||
};
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
// 最近点赞
|
||||
static Future getRecentLikeVideo({required int mid}) async {
|
||||
Map params = await WbiSign().makSign({
|
||||
static Future<LoadingState> getRecentLikeVideo({required int mid}) async {
|
||||
Map params = await WbiSign.makSign({
|
||||
'mid': mid,
|
||||
'gaia_source': 'main_web',
|
||||
'web_location': 333.999,
|
||||
@@ -643,16 +637,11 @@ class MemberHttp {
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': MemberSeasonsDataModel.fromJson(res.data['data']['items_lists'])
|
||||
};
|
||||
return LoadingState.success(res.data['data']['list']
|
||||
.map<MemberCoinsDataModel>((e) => MemberCoinsDataModel.fromJson(e))
|
||||
.toList());
|
||||
} else {
|
||||
return {
|
||||
'status': false,
|
||||
'data': [],
|
||||
'msg': res.data['message'],
|
||||
};
|
||||
return LoadingState.error(res.data['message']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,7 +713,7 @@ class MemberHttp {
|
||||
'name': name,
|
||||
'web_location': 333.999,
|
||||
};
|
||||
Map params = await WbiSign().makSign(data);
|
||||
Map params = await WbiSign.makSign(data);
|
||||
var res = await Request().get(Api.followSearch, queryParameters: {
|
||||
...data,
|
||||
'w_rid': params['w_rid'],
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/view.dart' show ReplyOption;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../models/msg/account.dart';
|
||||
import '../models/msg/session.dart';
|
||||
@@ -144,11 +145,13 @@ class MsgHttp {
|
||||
|
||||
static Future createDynamic({
|
||||
dynamic mid,
|
||||
dynamic dynIdStr, // repost
|
||||
dynamic dynIdStr, // repost dyn
|
||||
dynamic rid, // repost video
|
||||
dynamic dynType,
|
||||
dynamic rawText,
|
||||
List? pics,
|
||||
int? publishTime,
|
||||
ReplyOption replyOption = ReplyOption.allow,
|
||||
ReplyOption? replyOption,
|
||||
int? privatePub,
|
||||
}) async {
|
||||
String csrf = await Request.getCsrf();
|
||||
@@ -171,17 +174,21 @@ class MsgHttp {
|
||||
}
|
||||
]
|
||||
},
|
||||
if (dynIdStr == null)
|
||||
if (replyOption != null || publishTime != null)
|
||||
"option": {
|
||||
if (publishTime != null) "timer_pub_time": publishTime,
|
||||
if (replyOption == ReplyOption.close) "close_comment": 1,
|
||||
if (replyOption == ReplyOption.choose) "up_choose_comment": 1,
|
||||
if (replyOption == ReplyOption.close)
|
||||
"close_comment": 1
|
||||
else if (replyOption == ReplyOption.choose)
|
||||
"up_choose_comment": 1,
|
||||
},
|
||||
"scene": dynIdStr != null
|
||||
? 4
|
||||
: pics != null
|
||||
? 2
|
||||
: 1,
|
||||
"scene": rid != null
|
||||
? 5
|
||||
: dynIdStr != null
|
||||
? 4
|
||||
: pics != null
|
||||
? 2
|
||||
: 1,
|
||||
if (privatePub != null)
|
||||
'create_option': {
|
||||
'private_pub': privatePub,
|
||||
@@ -189,16 +196,27 @@ class MsgHttp {
|
||||
if (pics != null) 'pics': pics,
|
||||
"attach_card": null,
|
||||
"upload_id":
|
||||
"${mid}_${DateTime.now().millisecondsSinceEpoch ~/ 1000}_${Random().nextInt(9000) + 1000}",
|
||||
"${rid != null ? 0 : mid}_${DateTime.now().millisecondsSinceEpoch ~/ 1000}_${Random().nextInt(9000) + 1000}",
|
||||
"meta": {
|
||||
"app_meta": {"from": "create.dynamic.web", "mobi_app": "web"}
|
||||
}
|
||||
},
|
||||
if (dynIdStr != null) "web_repost_src": {"dyn_id_str": dynIdStr}
|
||||
if (dynIdStr != null || rid != null)
|
||||
"web_repost_src": {
|
||||
if (dynIdStr != null) "dyn_id_str": dynIdStr,
|
||||
if (rid != null)
|
||||
"revs_id": {
|
||||
"dyn_type": dynType,
|
||||
"rid": rid,
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data'],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': false,
|
||||
@@ -240,7 +258,7 @@ class MsgHttp {
|
||||
String? biz,
|
||||
}) async {
|
||||
String csrf = await Request.getCsrf();
|
||||
Map<String, dynamic> data = await WbiSign().makSign({
|
||||
Map<String, dynamic> data = await WbiSign.makSign({
|
||||
'file_up': await MultipartFile.fromFile(path),
|
||||
if (category != null) 'category': category,
|
||||
if (biz != null) 'biz': biz,
|
||||
@@ -267,7 +285,7 @@ class MsgHttp {
|
||||
dynamic content,
|
||||
) async {
|
||||
String csrf = await Request.getCsrf();
|
||||
Map<String, dynamic> data = await WbiSign().makSign({
|
||||
Map<String, dynamic> data = await WbiSign.makSign({
|
||||
'dynamic_id': 0,
|
||||
'type': 4,
|
||||
'rid': 0,
|
||||
@@ -293,7 +311,7 @@ class MsgHttp {
|
||||
dynamic dynamicId,
|
||||
) async {
|
||||
String csrf = await Request.getCsrf();
|
||||
Map<String, dynamic> data = await WbiSign().makSign({
|
||||
Map<String, dynamic> data = await WbiSign.makSign({
|
||||
'dynamic_id': dynamicId,
|
||||
'csrf_token': csrf,
|
||||
'csrf': csrf,
|
||||
@@ -316,7 +334,7 @@ class MsgHttp {
|
||||
dynamic talkerId,
|
||||
) async {
|
||||
String csrf = await Request.getCsrf();
|
||||
Map<String, dynamic> data = await WbiSign().makSign({
|
||||
Map<String, dynamic> data = await WbiSign.makSign({
|
||||
'talker_id': talkerId,
|
||||
'session_type': 1,
|
||||
'build': 0,
|
||||
@@ -369,7 +387,7 @@ class MsgHttp {
|
||||
int opType,
|
||||
) async {
|
||||
String csrf = await Request.getCsrf();
|
||||
Map<String, dynamic> data = await WbiSign().makSign({
|
||||
Map<String, dynamic> data = await WbiSign.makSign({
|
||||
'talker_id': talkerId,
|
||||
'session_type': 1,
|
||||
'op_type': opType,
|
||||
@@ -404,7 +422,7 @@ class MsgHttp {
|
||||
params['end_ts'] = endTs;
|
||||
}
|
||||
|
||||
Map signParams = await WbiSign().makSign(params);
|
||||
Map signParams = await WbiSign.makSign(params);
|
||||
var res = await Request().get(Api.sessionList, queryParameters: signParams);
|
||||
if (res.data['code'] == 0) {
|
||||
try {
|
||||
@@ -457,7 +475,7 @@ class MsgHttp {
|
||||
static Future sessionMsg({
|
||||
int? talkerId,
|
||||
}) async {
|
||||
Map params = await WbiSign().makSign({
|
||||
Map params = await WbiSign.makSign({
|
||||
'talker_id': talkerId,
|
||||
'session_type': 1,
|
||||
'size': 20,
|
||||
@@ -490,7 +508,7 @@ class MsgHttp {
|
||||
int? ackSeqno,
|
||||
}) async {
|
||||
String csrf = await Request.getCsrf();
|
||||
Map params = await WbiSign().makSign({
|
||||
Map params = await WbiSign.makSign({
|
||||
'talker_id': talkerId,
|
||||
'session_type': 1,
|
||||
'ack_seqno': ackSeqno,
|
||||
@@ -541,7 +559,7 @@ class MsgHttp {
|
||||
'csrf_token': csrf,
|
||||
'csrf': csrf,
|
||||
};
|
||||
Map<String, dynamic> params = await WbiSign().makSign(base);
|
||||
Map<String, dynamic> params = await WbiSign.makSign(base);
|
||||
var res = await Request().post(Api.sendMsg,
|
||||
queryParameters: <String, dynamic>{
|
||||
'w_sender_uid': params['msg[sender_uid]'],
|
||||
@@ -560,44 +578,43 @@ class MsgHttp {
|
||||
return {
|
||||
'status': false,
|
||||
'date': [],
|
||||
'msg': "message: ${res.data['message']},"
|
||||
" msg: ${res.data['msg']},"
|
||||
" code: ${res.data['code']}",
|
||||
'msg': res.data['message'] ?? res.data['msg'],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static String getDevId() {
|
||||
final List<String> b = [
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F'
|
||||
];
|
||||
final List<String> s = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".split('');
|
||||
for (int i = 0; i < s.length; i++) {
|
||||
if ('-' == s[i] || '4' == s[i]) {
|
||||
continue;
|
||||
}
|
||||
final int randomInt = Random().nextInt(16);
|
||||
if ('x' == s[i]) {
|
||||
s[i] = b[randomInt];
|
||||
} else {
|
||||
s[i] = b[3 & randomInt | 8];
|
||||
}
|
||||
}
|
||||
return s.join();
|
||||
return Uuid().v4();
|
||||
// final List<String> b = [
|
||||
// '0',
|
||||
// '1',
|
||||
// '2',
|
||||
// '3',
|
||||
// '4',
|
||||
// '5',
|
||||
// '6',
|
||||
// '7',
|
||||
// '8',
|
||||
// '9',
|
||||
// 'A',
|
||||
// 'B',
|
||||
// 'C',
|
||||
// 'D',
|
||||
// 'E',
|
||||
// 'F'
|
||||
// ];
|
||||
// final List<String> s = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".split('');
|
||||
// for (int i = 0; i < s.length; i++) {
|
||||
// if ('-' == s[i] || '4' == s[i]) {
|
||||
// continue;
|
||||
// }
|
||||
// final int randomInt = Random().nextInt(16);
|
||||
// if ('x' == s[i]) {
|
||||
// s[i] = b[randomInt];
|
||||
// } else {
|
||||
// s[i] = b[3 & randomInt | 8];
|
||||
// }
|
||||
// }
|
||||
// return s.join();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
|
||||
import 'package:PiliPlus/grpc/grpc_repo.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/video/reply/item.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
@@ -10,10 +10,14 @@ import 'package:dio/dio.dart';
|
||||
import '../models/video/reply/data.dart';
|
||||
import '../models/video/reply/emote.dart';
|
||||
import 'api.dart';
|
||||
import 'constants.dart';
|
||||
import 'init.dart';
|
||||
|
||||
class ReplyHttp {
|
||||
static Options get _options => Options(extra: {'clearCookie': true});
|
||||
|
||||
static RegExp replyRegExp =
|
||||
RegExp(GStorage.banWordForReply, caseSensitive: false);
|
||||
|
||||
static Future<LoadingState> replyList({
|
||||
required bool isLogin,
|
||||
required int oid,
|
||||
@@ -21,15 +25,12 @@ class ReplyHttp {
|
||||
required int type,
|
||||
required int page,
|
||||
int sort = 1,
|
||||
required String banWordForReply,
|
||||
required bool antiGoodsReply,
|
||||
bool? enableFilter,
|
||||
}) async {
|
||||
Options? options = !isLogin
|
||||
? Options(
|
||||
headers: {HttpHeaders.cookieHeader: "buvid3= ; b_nut= ; sid= "})
|
||||
: null;
|
||||
var res = !isLogin
|
||||
? await Request().get(
|
||||
'${HttpString.apiBaseUrl}${Api.replyList}/main',
|
||||
'${Api.replyList}/main',
|
||||
queryParameters: {
|
||||
'oid': oid,
|
||||
'type': type,
|
||||
@@ -37,10 +38,10 @@ class ReplyHttp {
|
||||
'{"offset":"${nextOffset.replaceAll('"', '\\"')}"}',
|
||||
'mode': sort + 2, //2:按时间排序;3:按热度排序
|
||||
},
|
||||
options: options,
|
||||
options: isLogin.not ? _options : null,
|
||||
)
|
||||
: await Request().get(
|
||||
'${HttpString.apiBaseUrl}${Api.replyList}',
|
||||
Api.replyList,
|
||||
queryParameters: {
|
||||
'oid': oid,
|
||||
'type': type,
|
||||
@@ -48,22 +49,20 @@ class ReplyHttp {
|
||||
'pn': page,
|
||||
'ps': 20,
|
||||
},
|
||||
options: options,
|
||||
options: isLogin.not ? _options : null,
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
ReplyData replyData = ReplyData.fromJson(res.data['data']);
|
||||
if (banWordForReply.isNotEmpty) {
|
||||
if (enableFilter != false && replyRegExp.pattern.isNotEmpty) {
|
||||
// topReplies
|
||||
if (replyData.topReplies?.isNotEmpty == true) {
|
||||
replyData.topReplies!.removeWhere((item) {
|
||||
bool hasMatch = RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(item.content?.message ?? '');
|
||||
bool hasMatch = replyRegExp.hasMatch(item.content?.message ?? '');
|
||||
// remove subreplies
|
||||
if (hasMatch.not) {
|
||||
if (item.replies?.isNotEmpty == true) {
|
||||
item.replies!.removeWhere((item) =>
|
||||
RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(item.content?.message ?? ''));
|
||||
replyRegExp.hasMatch(item.content?.message ?? ''));
|
||||
}
|
||||
}
|
||||
return hasMatch;
|
||||
@@ -73,14 +72,43 @@ class ReplyHttp {
|
||||
// replies
|
||||
if (replyData.replies?.isNotEmpty == true) {
|
||||
replyData.replies!.removeWhere((item) {
|
||||
bool hasMatch = RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(item.content?.message ?? '');
|
||||
bool hasMatch = replyRegExp.hasMatch(item.content?.message ?? '');
|
||||
// remove subreplies
|
||||
if (hasMatch.not) {
|
||||
if (item.replies?.isNotEmpty == true) {
|
||||
item.replies!.removeWhere((item) =>
|
||||
RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(item.content?.message ?? ''));
|
||||
replyRegExp.hasMatch(item.content?.message ?? ''));
|
||||
}
|
||||
}
|
||||
return hasMatch;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// antiGoodsReply
|
||||
if (antiGoodsReply) {
|
||||
// topReplies
|
||||
if (replyData.topReplies?.isNotEmpty == true) {
|
||||
replyData.topReplies!.removeWhere((item) {
|
||||
bool hasMatch = needRemove(item);
|
||||
// remove subreplies
|
||||
if (hasMatch.not) {
|
||||
if (item.replies?.isNotEmpty == true) {
|
||||
item.replies!.removeWhere(needRemove);
|
||||
}
|
||||
}
|
||||
return hasMatch;
|
||||
});
|
||||
}
|
||||
|
||||
// replies
|
||||
if (replyData.replies?.isNotEmpty == true) {
|
||||
replyData.replies!.removeWhere((item) {
|
||||
bool hasMatch = needRemove(item);
|
||||
// remove subreplies
|
||||
if (hasMatch.not) {
|
||||
if (item.replies?.isNotEmpty == true) {
|
||||
item.replies!.removeWhere(needRemove);
|
||||
}
|
||||
}
|
||||
return hasMatch;
|
||||
@@ -97,30 +125,50 @@ class ReplyHttp {
|
||||
int type = 1,
|
||||
required int oid,
|
||||
required CursorReq cursor,
|
||||
required String banWordForReply,
|
||||
required bool antiGoodsReply,
|
||||
}) async {
|
||||
dynamic res = await GrpcRepo.mainList(type: type, oid: oid, cursor: cursor);
|
||||
if (res['status']) {
|
||||
MainListReply mainListReply = res['data'];
|
||||
if (banWordForReply.isNotEmpty) {
|
||||
// keyword filter
|
||||
if (replyRegExp.pattern.isNotEmpty) {
|
||||
// upTop
|
||||
if (mainListReply.hasUpTop() &&
|
||||
RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(mainListReply.upTop.content.message)) {
|
||||
replyRegExp.hasMatch(mainListReply.upTop.content.message)) {
|
||||
mainListReply.clearUpTop();
|
||||
}
|
||||
|
||||
// replies
|
||||
if (mainListReply.replies.isNotEmpty) {
|
||||
mainListReply.replies.removeWhere((item) {
|
||||
bool hasMatch = RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(item.content.message);
|
||||
bool hasMatch = replyRegExp.hasMatch(item.content.message);
|
||||
// remove subreplies
|
||||
if (hasMatch.not) {
|
||||
if (item.replies.isNotEmpty) {
|
||||
item.replies.removeWhere((item) =>
|
||||
RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(item.content.message));
|
||||
item.replies.removeWhere(
|
||||
(item) => replyRegExp.hasMatch(item.content.message));
|
||||
}
|
||||
}
|
||||
return hasMatch;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// antiGoodsReply
|
||||
if (antiGoodsReply) {
|
||||
// upTop
|
||||
if (mainListReply.hasUpTop() && needRemoveGrpc(mainListReply.upTop)) {
|
||||
mainListReply.clearUpTop();
|
||||
}
|
||||
|
||||
// replies
|
||||
if (mainListReply.replies.isNotEmpty) {
|
||||
mainListReply.replies.removeWhere((item) {
|
||||
bool hasMatch = needRemoveGrpc(item);
|
||||
// remove subreplies
|
||||
if (hasMatch.not) {
|
||||
if (item.replies.isNotEmpty) {
|
||||
item.replies.removeWhere(needRemoveGrpc);
|
||||
}
|
||||
}
|
||||
return hasMatch;
|
||||
@@ -133,42 +181,79 @@ class ReplyHttp {
|
||||
}
|
||||
}
|
||||
|
||||
// ref BiliRoamingX
|
||||
static bool needRemoveGrpc(ReplyInfo reply) {
|
||||
if ((reply.content.url.isNotEmpty &&
|
||||
reply.content.url.values.any((url) {
|
||||
return url.hasExtra() &&
|
||||
(url.extra.goodsCmControl == 1 ||
|
||||
url.extra.goodsItemId != 0 ||
|
||||
url.extra.goodsPrefetchedCache.isNotEmpty);
|
||||
})) ||
|
||||
reply.content.message.contains(Constants.goodsUrlPrefix)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool needRemove(ReplyItemModel reply) {
|
||||
try {
|
||||
if ((reply.content?.jumpUrl?.isNotEmpty == true &&
|
||||
reply.content!.jumpUrl!.values.any((url) {
|
||||
return url['extra'] != null &&
|
||||
(url['extra']['goods_cm_control'] == 1 ||
|
||||
url['extra']['goods_item_id'] != 0 ||
|
||||
url['extra']['goods_prefetched_cache'].isNotEmpty);
|
||||
})) ||
|
||||
reply.content?.message?.contains(Constants.goodsUrlPrefix) == true) {
|
||||
return true;
|
||||
}
|
||||
} catch (_) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<LoadingState> replyReplyList({
|
||||
required bool isLogin,
|
||||
required int oid,
|
||||
required int root,
|
||||
required int pageNum,
|
||||
required int type,
|
||||
int sort = 1,
|
||||
required String banWordForReply,
|
||||
required bool antiGoodsReply,
|
||||
bool? isCheck,
|
||||
bool? filterBanWord,
|
||||
}) async {
|
||||
Options? options = GStorage.userInfo.get('userInfoCache') == null
|
||||
? Options(
|
||||
headers: {HttpHeaders.cookieHeader: "buvid3= ; b_nut= ; sid= "})
|
||||
: null;
|
||||
var res = await Request().get(
|
||||
'${HttpString.apiBaseUrl}${Api.replyReplyList}',
|
||||
Api.replyReplyList,
|
||||
queryParameters: {
|
||||
'oid': oid,
|
||||
'root': root,
|
||||
'pn': pageNum,
|
||||
'type': type,
|
||||
'sort': 1,
|
||||
'csrf': await Request.getCsrf(),
|
||||
if (isLogin) 'csrf': await Request.getCsrf(),
|
||||
},
|
||||
options: options,
|
||||
options: isLogin.not ? _options : null,
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
ReplyReplyData replyData = ReplyReplyData.fromJson(res.data['data']);
|
||||
if (banWordForReply.isNotEmpty) {
|
||||
if (filterBanWord != false && replyRegExp.pattern.isNotEmpty) {
|
||||
if (replyData.replies?.isNotEmpty == true) {
|
||||
replyData.replies!.removeWhere((item) =>
|
||||
RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(item.content?.message ?? ''));
|
||||
replyData.replies!.removeWhere(
|
||||
(item) => replyRegExp.hasMatch(item.content?.message ?? ''));
|
||||
}
|
||||
}
|
||||
if (antiGoodsReply) {
|
||||
if (replyData.replies?.isNotEmpty == true) {
|
||||
replyData.replies!.removeWhere(needRemove);
|
||||
}
|
||||
}
|
||||
return LoadingState.success(replyData);
|
||||
} else {
|
||||
return LoadingState.error(res.data['message']);
|
||||
return LoadingState.error(
|
||||
isCheck == true
|
||||
? '${res.data['code']}${res.data['message']}'
|
||||
: res.data['message'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +263,7 @@ class ReplyHttp {
|
||||
required int root,
|
||||
required int rpid,
|
||||
required CursorReq cursor,
|
||||
required String banWordForReply,
|
||||
required bool antiGoodsReply,
|
||||
}) async {
|
||||
dynamic res = await GrpcRepo.detailList(
|
||||
type: type,
|
||||
@@ -189,11 +274,15 @@ class ReplyHttp {
|
||||
);
|
||||
if (res['status']) {
|
||||
DetailListReply detailListReply = res['data'];
|
||||
if (banWordForReply.isNotEmpty) {
|
||||
if (replyRegExp.pattern.isNotEmpty) {
|
||||
if (detailListReply.root.replies.isNotEmpty) {
|
||||
detailListReply.root.replies.removeWhere((item) =>
|
||||
RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(item.content.message));
|
||||
detailListReply.root.replies.removeWhere(
|
||||
(item) => replyRegExp.hasMatch(item.content.message));
|
||||
}
|
||||
}
|
||||
if (antiGoodsReply) {
|
||||
if (detailListReply.root.replies.isNotEmpty) {
|
||||
detailListReply.root.replies.removeWhere(needRemoveGrpc);
|
||||
}
|
||||
}
|
||||
return LoadingState.success(detailListReply);
|
||||
@@ -208,7 +297,7 @@ class ReplyHttp {
|
||||
required int root,
|
||||
required int rpid,
|
||||
required CursorReq cursor,
|
||||
required String banWordForReply,
|
||||
required bool antiGoodsReply,
|
||||
}) async {
|
||||
dynamic res = await GrpcRepo.dialogList(
|
||||
type: type,
|
||||
@@ -219,11 +308,15 @@ class ReplyHttp {
|
||||
);
|
||||
if (res['status']) {
|
||||
DialogListReply dialogListReply = res['data'];
|
||||
if (banWordForReply.isNotEmpty) {
|
||||
if (replyRegExp.pattern.isNotEmpty) {
|
||||
if (dialogListReply.replies.isNotEmpty) {
|
||||
dialogListReply.replies.removeWhere((item) =>
|
||||
RegExp(banWordForReply, caseSensitive: false)
|
||||
.hasMatch(item.content.message));
|
||||
dialogListReply.replies.removeWhere(
|
||||
(item) => replyRegExp.hasMatch(item.content.message));
|
||||
}
|
||||
}
|
||||
if (antiGoodsReply) {
|
||||
if (dialogListReply.replies.isNotEmpty) {
|
||||
dialogListReply.replies.removeWhere(needRemoveGrpc);
|
||||
}
|
||||
}
|
||||
return LoadingState.success(dialogListReply);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
@@ -140,7 +141,7 @@ class SearchHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<int> ab2c({dynamic aid, dynamic bvid}) async {
|
||||
static Future<int> ab2c({dynamic aid, dynamic bvid, int? part}) async {
|
||||
Map<String, dynamic> data = {};
|
||||
if (aid != null) {
|
||||
data['aid'] = aid;
|
||||
@@ -150,7 +151,10 @@ class SearchHttp {
|
||||
final dynamic res = await Request()
|
||||
.get(Api.ab2c, queryParameters: <String, dynamic>{...data});
|
||||
if (res.data['code'] == 0) {
|
||||
return res.data['data'].first['cid'];
|
||||
return part != null
|
||||
? ((res.data['data'] as List).getOrNull(part - 1)?['cid'] ??
|
||||
res.data['data'].first['cid'])
|
||||
: res.data['data'].first['cid'];
|
||||
} else {
|
||||
SmartDialog.showToast("ab2c error: ${res.data['message']}");
|
||||
return -1;
|
||||
|
||||
@@ -62,6 +62,27 @@ class UserHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future cleanFav({
|
||||
required dynamic mediaId,
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
Api.cleanFav,
|
||||
data: {
|
||||
'media_id': mediaId,
|
||||
'platform': 'web',
|
||||
'csrf': await Request.getCsrf(),
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': res.data['data']};
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
static Future deleteFolder({
|
||||
required List<dynamic> mediaIds,
|
||||
}) async {
|
||||
@@ -153,7 +174,10 @@ class UserHttp {
|
||||
var res = await Request().get(Api.seeYouLater);
|
||||
if (res.data['code'] == 0) {
|
||||
if (res.data['data']['count'] == 0) {
|
||||
return LoadingState.success([]);
|
||||
return LoadingState.success({
|
||||
'list': [],
|
||||
'count': 0,
|
||||
});
|
||||
}
|
||||
List<HotVideoItemModel> list = [];
|
||||
for (var i in res.data['data']['list']) {
|
||||
@@ -332,7 +356,7 @@ class UserHttp {
|
||||
}
|
||||
// // 相互关系查询
|
||||
// static Future relationSearch(int mid) async {
|
||||
// Map params = await WbiSign().makSign({
|
||||
// Map params = await WbiSign.makSign({
|
||||
// 'mid': mid,
|
||||
// 'token': '',
|
||||
// 'platform': 'web',
|
||||
|
||||
@@ -32,6 +32,8 @@ import 'login.dart';
|
||||
class VideoHttp {
|
||||
static bool enableRcmdDynamic =
|
||||
GStorage.setting.get(SettingBoxKey.enableRcmdDynamic, defaultValue: true);
|
||||
static RegExp zoneRegExp =
|
||||
RegExp(GStorage.banWordForZone, caseSensitive: false);
|
||||
|
||||
// 首页推荐视频
|
||||
static Future<LoadingState> rcmdVideoList(
|
||||
@@ -118,7 +120,6 @@ class VideoHttp {
|
||||
Api.recommendListApp,
|
||||
queryParameters: data,
|
||||
options: Options(headers: {
|
||||
'Host': 'app.bilibili.com',
|
||||
'buvid': LoginHttp.buvid,
|
||||
'fp_local':
|
||||
'1111111111111111111111111111111111111111111111111111111111111111',
|
||||
@@ -145,12 +146,11 @@ class VideoHttp {
|
||||
(!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) &&
|
||||
(i['args'] != null &&
|
||||
!blackMidsList.contains(i['args']['up_id']))) {
|
||||
String banWordForZone = GStorage.banWordForZone;
|
||||
if (banWordForZone.isNotEmpty &&
|
||||
RegExp(banWordForZone, caseSensitive: false)
|
||||
.hasMatch(i['args']['rname'])) {
|
||||
continue;
|
||||
}
|
||||
// if (zoneRegExp.pattern.isNotEmpty &&
|
||||
// i['args']?['rname'] != null &&
|
||||
// zoneRegExp.hasMatch(i['args']['rname'])) {
|
||||
// continue;
|
||||
// }
|
||||
RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i);
|
||||
if (!RecommendFilter.filter(videoItem)) {
|
||||
list.add(videoItem);
|
||||
@@ -178,10 +178,9 @@ class VideoHttp {
|
||||
!RecommendFilter.filterTitle(i['title']) &&
|
||||
!RecommendFilter.filterLikeRatio(
|
||||
i['stat']['like'], i['stat']['view'])) {
|
||||
String banWordForZone = GStorage.banWordForZone;
|
||||
if (banWordForZone.isNotEmpty &&
|
||||
RegExp(banWordForZone, caseSensitive: false)
|
||||
.hasMatch(i['tname'])) {
|
||||
if (zoneRegExp.pattern.isNotEmpty &&
|
||||
i['tname'] != null &&
|
||||
zoneRegExp.hasMatch(i['tname'])) {
|
||||
continue;
|
||||
}
|
||||
list.add(HotVideoItemModel.fromJson(i));
|
||||
@@ -237,7 +236,7 @@ class VideoHttp {
|
||||
data['try_look'] = 1;
|
||||
}
|
||||
|
||||
Map params = await WbiSign().makSign({
|
||||
Map params = await WbiSign.makSign({
|
||||
...data,
|
||||
'fourk': 1,
|
||||
'voice_balance': 1,
|
||||
@@ -421,14 +420,18 @@ class VideoHttp {
|
||||
}
|
||||
|
||||
// 投币
|
||||
static Future coinVideo({required String bvid, required int multiply}) async {
|
||||
static Future coinVideo({
|
||||
required String bvid,
|
||||
required int multiply,
|
||||
int selectLike = 0,
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
Api.coinVideo,
|
||||
queryParameters: {
|
||||
'aid': IdUtils.bv2av(bvid),
|
||||
// 'bvid': bvid,
|
||||
'multiply': multiply,
|
||||
'select_like': 0,
|
||||
'select_like': selectLike,
|
||||
'access_key': GStorage.localCache
|
||||
.get(LocalCacheKey.accessKey, defaultValue: {})['value'],
|
||||
// 'csrf': await Request.getCsrf(),
|
||||
@@ -453,7 +456,7 @@ class VideoHttp {
|
||||
}
|
||||
|
||||
// 一键三连 bangumi
|
||||
static Future triple({dynamic epId}) async {
|
||||
static Future triple({dynamic epId, required dynamic seasonId}) async {
|
||||
var res = await Request().post(
|
||||
Api.triple,
|
||||
data: {
|
||||
@@ -461,8 +464,11 @@ class VideoHttp {
|
||||
'csrf': await Request.getCsrf(),
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
headers: {
|
||||
'Content-Type': Headers.formUrlEncodedContentType,
|
||||
'origin': 'https://www.bilibili.com',
|
||||
'referer': 'https://www.bilibili.com/bangumi/play/ss$seasonId',
|
||||
'user-agent': Request.headerUa(type: 'pc'),
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -487,6 +493,11 @@ class VideoHttp {
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
headers: {
|
||||
'origin': 'https://www.bilibili.com',
|
||||
'referer': 'https://www.bilibili.com/video/$bvid',
|
||||
'user-agent': Request.headerUa(type: 'pc'),
|
||||
},
|
||||
),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
@@ -676,6 +687,52 @@ class VideoHttp {
|
||||
// }
|
||||
// }
|
||||
|
||||
static Future copyOrMoveFav({
|
||||
required bool isCopy,
|
||||
required bool isFav,
|
||||
required dynamic srcMediaId,
|
||||
required dynamic tarMediaId,
|
||||
dynamic mid,
|
||||
required List resources,
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
isFav
|
||||
? isCopy
|
||||
? Api.copyFav
|
||||
: Api.moveFav
|
||||
: isCopy
|
||||
? Api.copyToview
|
||||
: Api.moveToview,
|
||||
data: {
|
||||
if (srcMediaId != null) 'src_media_id': srcMediaId,
|
||||
'tar_media_id': tarMediaId,
|
||||
if (mid != null) 'mid': mid,
|
||||
'resources': resources.join(','),
|
||||
'platform': 'web',
|
||||
'csrf': await Request.getCsrf(),
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
static Future allFavFolders(mid) async {
|
||||
var res = await Request().get(
|
||||
Api.favFolder,
|
||||
queryParameters: {'up_mid': mid},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
FavFolderData data = FavFolderData.fromJson(res.data['data']);
|
||||
return {'status': true, 'data': data};
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
// 查看视频被收藏在哪个文件夹
|
||||
static Future videoInFolder({
|
||||
dynamic mid,
|
||||
@@ -683,7 +740,7 @@ class VideoHttp {
|
||||
dynamic type,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
Api.videoInFolder,
|
||||
Api.favFolder,
|
||||
queryParameters: {
|
||||
'up_mid': mid,
|
||||
'rid': rid,
|
||||
@@ -713,6 +770,7 @@ class VideoHttp {
|
||||
int? root,
|
||||
int? parent,
|
||||
List? pictures,
|
||||
bool? syncToDynamic,
|
||||
}) async {
|
||||
if (message == '') {
|
||||
return {'status': false, 'data': [], 'msg': '请输入评论内容'};
|
||||
@@ -720,10 +778,11 @@ class VideoHttp {
|
||||
Map<String, dynamic> data = {
|
||||
'type': type.index,
|
||||
'oid': oid,
|
||||
'root': root == null || root == 0 ? '' : root,
|
||||
'parent': parent == null || parent == 0 ? '' : parent,
|
||||
if (root != null && root != 0) 'root': root,
|
||||
if (parent != null && parent != 0) 'parent': parent,
|
||||
'message': message,
|
||||
if (pictures != null) 'pictures': jsonEncode(pictures),
|
||||
if (syncToDynamic == true) 'sync_to_dynamic': 1,
|
||||
'csrf': await Request.getCsrf(),
|
||||
};
|
||||
var res = await Request().post(
|
||||
@@ -770,12 +829,30 @@ class VideoHttp {
|
||||
// 操作用户关系
|
||||
static Future relationMod(
|
||||
{required int mid, required int act, required int reSrc}) async {
|
||||
var res = await Request().post(Api.relationMod, queryParameters: {
|
||||
'fid': mid,
|
||||
'act': act,
|
||||
're_src': reSrc,
|
||||
'csrf': await Request.getCsrf(),
|
||||
});
|
||||
var res = await Request().post(
|
||||
Api.relationMod,
|
||||
data: {
|
||||
'fid': mid,
|
||||
'act': act,
|
||||
're_src': reSrc,
|
||||
'gaia_source': 'web_main',
|
||||
'spmid': '333.999.0.0',
|
||||
'extend_content': {
|
||||
"entity": "user",
|
||||
"entity_id": mid,
|
||||
'fp': Request.headerUa(type: 'pc'),
|
||||
},
|
||||
'csrf': await Request.getCsrf(),
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
headers: {
|
||||
'origin': 'https://space.bilibili.com',
|
||||
'referer': 'https://space.bilibili.com/$mid/dynamic',
|
||||
'user-agent': Request.headerUa(type: 'pc'),
|
||||
},
|
||||
),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
} else {
|
||||
@@ -900,7 +977,7 @@ class VideoHttp {
|
||||
int? cid,
|
||||
int? upMid,
|
||||
}) async {
|
||||
Map params = await WbiSign().makSign({
|
||||
Map params = await WbiSign.makSign({
|
||||
'bvid': bvid,
|
||||
'cid': cid,
|
||||
'up_mid': upMid,
|
||||
@@ -929,7 +1006,6 @@ class VideoHttp {
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
dynamic data = res.data['data'];
|
||||
List subtitlesJson = data['subtitle']['subtitles'];
|
||||
/*
|
||||
[
|
||||
{
|
||||
@@ -947,10 +1023,11 @@ class VideoHttp {
|
||||
*/
|
||||
return {
|
||||
'status': true,
|
||||
'data': subtitlesJson,
|
||||
'data': data['subtitle']['subtitles'],
|
||||
'view_points': data['view_points'],
|
||||
// 'last_play_time': data['last_play_time'],
|
||||
'last_play_cid': data['last_play_cid'],
|
||||
'interaction': data['interaction'],
|
||||
};
|
||||
} else {
|
||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||
@@ -1044,10 +1121,9 @@ class VideoHttp {
|
||||
!RecommendFilter.filterTitle(i['title']) &&
|
||||
!RecommendFilter.filterLikeRatio(
|
||||
i['stat']['like'], i['stat']['view'])) {
|
||||
String banWordForZone = GStorage.banWordForZone;
|
||||
if (banWordForZone.isNotEmpty &&
|
||||
RegExp(banWordForZone, caseSensitive: false)
|
||||
.hasMatch(i['tname'])) {
|
||||
if (zoneRegExp.pattern.isNotEmpty &&
|
||||
i['tname'] != null &&
|
||||
zoneRegExp.hasMatch(i['tname'])) {
|
||||
continue;
|
||||
}
|
||||
list.add(HotVideoItemModel.fromJson(i));
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:PiliPlus/build_config.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/view_v.dart';
|
||||
import 'package:PiliPlus/utils/cache_manage.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flex_seed_scheme/flex_seed_scheme.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||
@@ -52,9 +54,7 @@ void main() async {
|
||||
],
|
||||
);
|
||||
}
|
||||
if (GStorage.badCertificateCallback) {
|
||||
HttpOverrides.global = _CustomHttpOverrides();
|
||||
}
|
||||
HttpOverrides.global = _CustomHttpOverrides();
|
||||
await setupServiceLocator();
|
||||
Request();
|
||||
await Request.setCookie();
|
||||
@@ -177,6 +177,7 @@ class MyApp extends StatelessWidget {
|
||||
// tones: FlexTones.soft(Brightness.dark),
|
||||
);
|
||||
}
|
||||
|
||||
// 图片缓存
|
||||
// PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20;
|
||||
return GetMaterialApp(
|
||||
@@ -216,6 +217,7 @@ class MyApp extends StatelessWidget {
|
||||
},
|
||||
navigatorObservers: [
|
||||
VideoDetailPage.routeObserver,
|
||||
VideoDetailPageV.routeObserver,
|
||||
MainApp.routeObserver,
|
||||
],
|
||||
);
|
||||
@@ -229,7 +231,7 @@ class MyApp extends StatelessWidget {
|
||||
bool isDark = false,
|
||||
required FlexSchemeVariant variant,
|
||||
}) {
|
||||
return ThemeData(
|
||||
ThemeData themeData = ThemeData(
|
||||
colorScheme: colorScheme,
|
||||
useMaterial3: true,
|
||||
appBarTheme: AppBarTheme(
|
||||
@@ -275,15 +277,33 @@ class MyApp extends StatelessWidget {
|
||||
progressIndicatorTheme: ProgressIndicatorThemeData(
|
||||
refreshBackgroundColor: colorScheme.onSecondary,
|
||||
),
|
||||
dialogTheme: DialogTheme(
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: 18,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (isDark && GStorage.isPureBlackTheme) {
|
||||
themeData = Utils.darkenTheme(themeData);
|
||||
}
|
||||
return themeData;
|
||||
}
|
||||
}
|
||||
|
||||
class _CustomHttpOverrides extends HttpOverrides {
|
||||
static final badCertificateCallback =
|
||||
BuildConfig.isDebug || GStorage.badCertificateCallback;
|
||||
|
||||
@override
|
||||
HttpClient createHttpClient(SecurityContext? context) {
|
||||
return super.createHttpClient(context)
|
||||
..badCertificateCallback =
|
||||
final client = super.createHttpClient(context)
|
||||
..maxConnectionsPerHost = 32
|
||||
..idleTimeout = const Duration(seconds: 30);
|
||||
if (badCertificateCallback) {
|
||||
client.badCertificateCallback =
|
||||
(X509Certificate cert, String host, int port) => true;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +175,7 @@ class EpisodeItem {
|
||||
this.subtitle,
|
||||
this.title,
|
||||
this.vid,
|
||||
this.showTitle,
|
||||
});
|
||||
|
||||
int? aid;
|
||||
@@ -205,6 +206,7 @@ class EpisodeItem {
|
||||
String? subtitle;
|
||||
String? title;
|
||||
String? vid;
|
||||
String? showTitle;
|
||||
|
||||
EpisodeItem.fromJson(Map<String, dynamic> json) {
|
||||
aid = json['aid'];
|
||||
@@ -235,6 +237,7 @@ class EpisodeItem {
|
||||
subtitle = json['subtitle'];
|
||||
title = json['title'];
|
||||
vid = json['vid'];
|
||||
showTitle = json['show_title'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,9 @@ class BangumiListDataModel {
|
||||
|
||||
BangumiListDataModel.fromJson(Map<String, dynamic> json) {
|
||||
hasNext = json['has_next'];
|
||||
list = json['list'] != null
|
||||
? json['list']
|
||||
.map<BangumiListItemModel>((e) => BangumiListItemModel.fromJson(e))
|
||||
.toList()
|
||||
: [];
|
||||
list = (json['list'] as List?)
|
||||
?.map<BangumiListItemModel>((e) => BangumiListItemModel.fromJson(e))
|
||||
.toList();
|
||||
num = json['num'];
|
||||
size = json['size'];
|
||||
total = json['total'];
|
||||
|
||||
70
lib/models/bangumi/pgc_index/condition.dart
Normal file
@@ -0,0 +1,70 @@
|
||||
class Condition {
|
||||
List<Filter>? filter;
|
||||
List<Order>? order;
|
||||
|
||||
Condition({
|
||||
this.filter,
|
||||
this.order,
|
||||
});
|
||||
|
||||
Condition.fromJson(Map json) {
|
||||
filter = (json['filter'] as List?)
|
||||
?.map((item) => Filter.fromJson(item))
|
||||
.toList();
|
||||
order =
|
||||
(json['order'] as List?)?.map((item) => Order.fromJson(item)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
class Order {
|
||||
String? field;
|
||||
String? name;
|
||||
String? sort;
|
||||
|
||||
Order({
|
||||
this.field,
|
||||
this.name,
|
||||
this.sort,
|
||||
});
|
||||
|
||||
Order.fromJson(Map json) {
|
||||
field = json['field'];
|
||||
name = json['name'];
|
||||
sort = json['sort'];
|
||||
}
|
||||
}
|
||||
|
||||
class Filter {
|
||||
String? field;
|
||||
String? name;
|
||||
List<Values>? values;
|
||||
|
||||
Filter({
|
||||
this.field,
|
||||
this.name,
|
||||
this.values,
|
||||
});
|
||||
|
||||
Filter.fromJson(Map json) {
|
||||
field = json['field'];
|
||||
name = json['name'];
|
||||
values = (json['values'] as List?)
|
||||
?.map((item) => Values.fromJson(item))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
class Values {
|
||||
String? keyword;
|
||||
String? name;
|
||||
|
||||
Values({
|
||||
this.keyword,
|
||||
this.name,
|
||||
});
|
||||
|
||||
Values.fromJson(Map json) {
|
||||
keyword = json['keyword'];
|
||||
name = json['name'];
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
|
||||
class DynamicsDataModel {
|
||||
DynamicsDataModel({
|
||||
this.hasMore,
|
||||
@@ -12,7 +14,7 @@ class DynamicsDataModel {
|
||||
|
||||
DynamicsDataModel.fromJson(Map<String, dynamic> json) {
|
||||
hasMore = json['has_more'];
|
||||
items = json['items']
|
||||
items = (json['items'] as List?)
|
||||
?.map<DynamicItemModel>((e) => DynamicItemModel.fromJson(e))
|
||||
.toList();
|
||||
offset = json['offset'];
|
||||
@@ -33,47 +35,23 @@ class DynamicItemModel {
|
||||
Map? basic;
|
||||
dynamic idStr;
|
||||
ItemModulesModel? modules;
|
||||
ItemOrigModel? orig;
|
||||
DynamicItemModel? orig;
|
||||
String? type;
|
||||
bool? visible;
|
||||
bool? isForwarded;
|
||||
|
||||
DynamicItemModel.fromJson(Map<String, dynamic> json) {
|
||||
basic = json['basic'];
|
||||
idStr = json['id_str'];
|
||||
modules = ItemModulesModel.fromJson(json['modules']);
|
||||
orig = json['orig'] != null ? ItemOrigModel.fromJson(json['orig']) : null;
|
||||
orig =
|
||||
json['orig'] != null ? DynamicItemModel.fromJson(json['orig']) : null;
|
||||
orig?.isForwarded = true;
|
||||
type = json['type'];
|
||||
visible = json['visible'];
|
||||
}
|
||||
}
|
||||
|
||||
class ItemOrigModel {
|
||||
ItemOrigModel({
|
||||
this.basic,
|
||||
this.isStr,
|
||||
this.modules,
|
||||
this.type,
|
||||
this.visible,
|
||||
this.idStr,
|
||||
});
|
||||
|
||||
Map? basic;
|
||||
String? isStr;
|
||||
ItemModulesModel? modules;
|
||||
String? type;
|
||||
bool? visible;
|
||||
dynamic idStr;
|
||||
|
||||
ItemOrigModel.fromJson(Map<String, dynamic> json) {
|
||||
basic = json['basic'];
|
||||
isStr = json['is_str'];
|
||||
modules = ItemModulesModel.fromJson(json['modules']);
|
||||
type = json['type'];
|
||||
visible = json['visible'];
|
||||
idStr = json['id_str'];
|
||||
}
|
||||
}
|
||||
|
||||
// 单个动态详情
|
||||
class ItemModulesModel {
|
||||
ItemModulesModel({
|
||||
@@ -126,6 +104,8 @@ class ModuleAuthorModel {
|
||||
this.pubTs,
|
||||
this.type,
|
||||
this.vip,
|
||||
this.decorate,
|
||||
this.pendant,
|
||||
});
|
||||
|
||||
String? face;
|
||||
@@ -139,6 +119,8 @@ class ModuleAuthorModel {
|
||||
int? pubTs;
|
||||
String? type;
|
||||
Map? vip;
|
||||
Map? decorate;
|
||||
Map? pendant;
|
||||
|
||||
ModuleAuthorModel.fromJson(Map<String, dynamic> json) {
|
||||
face = json['face'];
|
||||
@@ -152,7 +134,13 @@ class ModuleAuthorModel {
|
||||
pubTs = json['pub_ts'] == 0 ? null : json['pub_ts'];
|
||||
type = json['type'];
|
||||
vip = json['vip'];
|
||||
if (showDynDecorate) {
|
||||
decorate = json['decorate'];
|
||||
pendant = json['pendant'];
|
||||
}
|
||||
}
|
||||
|
||||
static bool showDynDecorate = GStorage.showDynDecorate;
|
||||
}
|
||||
|
||||
// 单个动态详情 - 动态信息
|
||||
@@ -352,7 +340,9 @@ class Good {
|
||||
Good.fromJson(Map<String, dynamic> json) {
|
||||
headIcon = json['head_icon'];
|
||||
headText = json['head_text'];
|
||||
items = json['items'].map<GoodItem>((e) => GoodItem.fromJson(e)).toList();
|
||||
items = (json['items'] as List?)
|
||||
?.map<GoodItem>((e) => GoodItem.fromJson(e))
|
||||
.toList();
|
||||
jumpUrl = json['jump_url'];
|
||||
}
|
||||
}
|
||||
@@ -397,11 +387,9 @@ class DynamicDescModel {
|
||||
String? text;
|
||||
|
||||
DynamicDescModel.fromJson(Map<String, dynamic> json) {
|
||||
richTextNodes = json['rich_text_nodes'] != null
|
||||
? json['rich_text_nodes']
|
||||
.map<RichTextNodeItem>((e) => RichTextNodeItem.fromJson(e))
|
||||
.toList()
|
||||
: [];
|
||||
richTextNodes = (json['rich_text_nodes'] as List?)
|
||||
?.map<RichTextNodeItem>((e) => RichTextNodeItem.fromJson(e))
|
||||
.toList();
|
||||
text = json['text'];
|
||||
}
|
||||
}
|
||||
@@ -421,6 +409,7 @@ class DynamicMajorModel {
|
||||
this.courses,
|
||||
this.common,
|
||||
this.music,
|
||||
this.blocked,
|
||||
});
|
||||
|
||||
DynamicArchiveModel? archive;
|
||||
@@ -438,6 +427,7 @@ class DynamicMajorModel {
|
||||
Map? courses;
|
||||
Map? common;
|
||||
Map? music;
|
||||
Map? blocked;
|
||||
|
||||
DynamicMajorModel.fromJson(Map<String, dynamic> json) {
|
||||
archive = json['archive'] != null
|
||||
@@ -463,6 +453,7 @@ class DynamicMajorModel {
|
||||
courses = json['courses'] ?? {};
|
||||
common = json['common'] ?? {};
|
||||
music = json['music'] ?? {};
|
||||
blocked = json['blocked'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,12 +533,9 @@ class DynamicDrawModel {
|
||||
|
||||
DynamicDrawModel.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
// ignore: prefer_null_aware_operators
|
||||
items = json['items'] != null
|
||||
? json['items']
|
||||
.map<DynamicDrawItemModel>((e) => DynamicDrawItemModel.fromJson(e))
|
||||
.toList()
|
||||
: null;
|
||||
items = (json['items'] as List?)
|
||||
?.map<DynamicDrawItemModel>((e) => DynamicDrawItemModel.fromJson(e))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,7 +553,7 @@ class DynamicOpusModel {
|
||||
String? title;
|
||||
DynamicOpusModel.fromJson(Map<String, dynamic> json) {
|
||||
jumpUrl = json['jump_url'];
|
||||
pics = json['pics']
|
||||
pics = (json['pics'] as List?)
|
||||
?.map<OpusPicsModel>((e) => OpusPicsModel.fromJson(e))
|
||||
.toList();
|
||||
summary =
|
||||
@@ -584,8 +572,8 @@ class SummaryModel {
|
||||
String? text;
|
||||
|
||||
SummaryModel.fromJson(Map<String, dynamic> json) {
|
||||
richTextNodes = json['rich_text_nodes']
|
||||
.map<RichTextNodeItem>((e) => RichTextNodeItem.fromJson(e))
|
||||
richTextNodes = (json['rich_text_nodes'] as List?)
|
||||
?.map<RichTextNodeItem>((e) => RichTextNodeItem.fromJson(e))
|
||||
.toList();
|
||||
text = json['text'];
|
||||
}
|
||||
@@ -623,11 +611,15 @@ class Emoji {
|
||||
});
|
||||
|
||||
String? iconUrl;
|
||||
String? webpUrl;
|
||||
String? gifUrl;
|
||||
double? size;
|
||||
String? text;
|
||||
int? type;
|
||||
Emoji.fromJson(Map<String, dynamic> json) {
|
||||
iconUrl = json['icon_url'];
|
||||
webpUrl = json['webp_url'];
|
||||
gifUrl = json['gif_url'];
|
||||
size = json['size'].toDouble();
|
||||
text = json['text'];
|
||||
type = json['type'];
|
||||
@@ -658,6 +650,7 @@ class OpusPicsModel {
|
||||
int? size;
|
||||
String? src;
|
||||
String? url;
|
||||
String? liveUrl;
|
||||
|
||||
OpusPicsModel.fromJson(Map<String, dynamic> json) {
|
||||
width = json['width'];
|
||||
@@ -665,6 +658,7 @@ class OpusPicsModel {
|
||||
size = json['size'] != null ? json['size'].toInt() : 0;
|
||||
src = json['src'];
|
||||
url = json['url'];
|
||||
liveUrl = json['live_url'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ class FollowUpModel {
|
||||
liveUsers = json['live_users'] != null
|
||||
? LiveUsers.fromJson(json['live_users'])
|
||||
: null;
|
||||
upList = json['up_list'] != null
|
||||
? json['up_list'].map<UpItem>((e) => UpItem.fromJson(e)).toList()
|
||||
: [];
|
||||
upList = (json['up_list'] as List?)
|
||||
?.map<UpItem>((e) => UpItem.fromJson(e))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ class LiveUsers {
|
||||
LiveUsers.fromJson(Map<String, dynamic> json) {
|
||||
count = json['count'];
|
||||
group = json['group'];
|
||||
items = json['items']
|
||||
.map<LiveUserItem>((e) => LiveUserItem.fromJson(e))
|
||||
items = (json['items'] as List?)
|
||||
?.map<LiveUserItem>((e) => LiveUserItem.fromJson(e))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ class FansDataModel {
|
||||
|
||||
FansDataModel.fromJson(Map<String, dynamic> json) {
|
||||
total = json['total'];
|
||||
list = json['list']
|
||||
.map<FansItemModel>((e) => FansItemModel.fromJson(e))
|
||||
list = (json['list'] as List?)
|
||||
?.map<FansItemModel>((e) => FansItemModel.fromJson(e))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||