Compare commits

..

183 Commits

Author SHA1 Message Date
bggRGjQaUbCoE
c88776c4a0 mod: try-catch nextplay
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-07 11:51:20 +08:00
My-Responsitories
b7cb977f2b remove duplicated code (#396)
* remove duplicated code

* partical revert

---------

Co-authored-by: dom <githubaccount56556@proton.me>
2025-03-07 11:49:46 +08:00
bggRGjQaUbCoE
3048e36d2f mod: requery dm
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-06 16:27:51 +08:00
bggRGjQaUbCoE
64f37fa743 opt: triple
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-06 13:52:37 +08:00
bggRGjQaUbCoE
737b7d0507 fix: numformat
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-06 13:32:44 +08:00
bggRGjQaUbCoE
973dad4176 fix: video header
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-06 12:26:34 +08:00
bggRGjQaUbCoE
46110adb8f fix: #391
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-06 12:06:00 +08:00
bggRGjQaUbCoE
b5c7ed1c34 opt: video: check title width
Closes #206

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-06 11:58:39 +08:00
bggRGjQaUbCoE
86678ec15a opt: triple
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-06 11:03:58 +08:00
bggRGjQaUbCoE
893fb63a72 mod: video appbar
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-06 10:48:37 +08:00
bggRGjQaUbCoE
3b717cfc58 fix: video minScale
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-06 10:11:10 +08:00
bggRGjQaUbCoE
ed40a91a52 opt: fullscreen triple
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 23:49:03 +08:00
bggRGjQaUbCoE
8b1bec6ed2 mod: blacklist item
Closes #390

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 23:02:32 +08:00
bggRGjQaUbCoE
a6a3476cb2 mod: video minScale
Closes #388

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 22:48:01 +08:00
bggRGjQaUbCoE
fac3c19d3f mod: show fullscreen action item
Closes #367

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 21:50:26 +08:00
bggRGjQaUbCoE
dc1451c3af opt: at me item
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 17:57:51 +08:00
bggRGjQaUbCoE
08b0a93064 mod: msg top: show time
Closes #387

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 17:39:09 +08:00
bggRGjQaUbCoE
72dd0b9e81 opt: report panel
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 17:04:16 +08:00
bggRGjQaUbCoE
8236b93717 mod: msg top item
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 17:01:22 +08:00
My-Responsitories
c4c5eee2eb fix banUid (#386) 2025-03-05 17:01:08 +08:00
My-Responsitories
5cc9c59c76 report panel (#385) 2025-03-05 15:42:04 +08:00
bggRGjQaUbCoE
bf4ecc85dd mod: article req
Closes #305

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 14:25:54 +08:00
bggRGjQaUbCoE
da5c2148ad opt: horizontal image view
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 13:42:35 +08:00
bggRGjQaUbCoE
bfcea11320 opt: dyndetail/html ctr listener
related #305

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 13:22:06 +08:00
bggRGjQaUbCoE
ed19e13630 fix: video appbar
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 11:23:51 +08:00
bggRGjQaUbCoE
6497fb6cd0 mod: common slide page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 10:45:02 +08:00
bggRGjQaUbCoE
9c21f03df8 opt: tabbar
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-05 00:04:38 +08:00
bggRGjQaUbCoE
7667e73d9d fix: #383
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-04 23:38:38 +08:00
bggRGjQaUbCoE
ff2ed0421c mod: pages
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-04 22:32:09 +08:00
bggRGjQaUbCoE
56c5ad360a mod: more slide dismiss pages
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-04 20:35:15 +08:00
bggRGjQaUbCoE
ef644d2837 fix: #381
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-04 20:34:45 +08:00
bggRGjQaUbCoE
4642eda98d refa: msg top page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-04 14:37:58 +08:00
bggRGjQaUbCoE
8ef163dd38 mod: refresh
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-04 12:06:50 +08:00
bggRGjQaUbCoE
5986add7dd feat: show video note list
Closes #376

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-04 11:42:41 +08:00
bggRGjQaUbCoE
c990cf1660 mod: progressbar
Closes #379

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-04 09:37:10 +08:00
bggRGjQaUbCoE
76c16c035e mod: show dm merged count
related #359

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-03 21:38:02 +08:00
My-Responsitories
d5a244ce7f update msg api (#375) 2025-03-03 17:23:19 +08:00
bggRGjQaUbCoE
432c5133e6 mod: show clear search history dialog
related #359

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-03 11:40:39 +08:00
bggRGjQaUbCoE
a9f9b324a9 opt: slide gesture
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-02 13:59:45 +08:00
bggRGjQaUbCoE
4735297285 opt: seek indicator
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-02 12:13:48 +08:00
bggRGjQaUbCoE
3abff4b9da opt: string ext
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-02 12:04:13 +08:00
bggRGjQaUbCoE
c32b98fa7f fix: thumbnail image url
Closes #360

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-02 11:53:51 +08:00
bggRGjQaUbCoE
a605c0fcfb mod: reenable multi seek
Closes #365

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-02 10:23:45 +08:00
bggRGjQaUbCoE
1e83b4557f mod: extra jump to reply
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-02 00:47:02 +08:00
bggRGjQaUbCoE
c3d729fc77 mod: scheme: jump to reply
Closes #362

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-02 00:39:39 +08:00
bggRGjQaUbCoE
00ea891784 fix: clear audio noti
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-01 17:53:24 +08:00
bggRGjQaUbCoE
c98dbccbd7 fix: #359
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-01 16:40:40 +08:00
bggRGjQaUbCoE
4a68122c31 opt: live bottom control
Closes #349

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-01 13:52:26 +08:00
bggRGjQaUbCoE
9c4a52de87 opt: video toolbar icon
Closes #329

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-01 13:32:54 +08:00
bggRGjQaUbCoE
6c11140f43 fix: #352
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-01 09:36:41 +08:00
bggRGjQaUbCoE
11398ca64b opt: handle response
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-01 09:36:41 +08:00
bggRGjQaUbCoE
312ce6e639 opt: pages
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 21:54:53 +08:00
bggRGjQaUbCoE
139b48c457 opt: dm widget
Closes #350

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 21:47:42 +08:00
bggRGjQaUbCoE
f1f478e193 fix: live: show statusbar
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 21:47:42 +08:00
bggRGjQaUbCoE
1abaf3db3f fix: #343
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 21:47:42 +08:00
bggRGjQaUbCoE
f6bfbc3ed6 mod: video sheet panel
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 20:20:39 +08:00
bggRGjQaUbCoE
d2890d72e5 fix: #341 #343 #339
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 17:48:04 +08:00
bggRGjQaUbCoE
5c029c8f64 Revert "opt: video page"
This reverts commit 82030b8d06.
2025-02-28 17:30:50 +08:00
bggRGjQaUbCoE
c37a631df2 fix: #345
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 15:57:31 +08:00
bggRGjQaUbCoE
82030b8d06 opt: video page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 15:36:39 +08:00
bggRGjQaUbCoE
e362f75dac opt: slide gesture
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 13:10:14 +08:00
bggRGjQaUbCoE
3fecf7c0a4 fix: #339
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 11:26:03 +08:00
bggRGjQaUbCoE
801043468d fix: getPlayerKey
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 10:16:02 +08:00
bggRGjQaUbCoE
1b4f588671 fix: #339 #341
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 10:05:27 +08:00
bggRGjQaUbCoE
7ad48570f0 mod: fullscreen debounce
Closes #340

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-28 00:37:57 +08:00
bggRGjQaUbCoE
5b8c68303f fix: #337
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 23:52:54 +08:00
bggRGjQaUbCoE
87d3d0ca14 fix: video page v
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 21:28:14 +08:00
bggRGjQaUbCoE
b330440371 fix: video page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 21:00:49 +08:00
bggRGjQaUbCoE
2a173ef804 fix: #334
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 20:06:46 +08:00
bggRGjQaUbCoE
bceabae06f fix: video play
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 19:05:57 +08:00
bggRGjQaUbCoE
69667c135d feat: slide to dismiss subreply page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 18:05:34 +08:00
bggRGjQaUbCoE
587870ad71 fix: player key
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 16:52:33 +08:00
bggRGjQaUbCoE
609fab345a fix: video page v
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 16:26:54 +08:00
bggRGjQaUbCoE
29c47cee78 fix: #333
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 15:51:03 +08:00
bggRGjQaUbCoE
6a9795f561 opt: video page v
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 11:38:44 +08:00
bggRGjQaUbCoE
72e7f0aa9f Revert "fix: #333"
This reverts commit acfa384c0c.
2025-02-27 11:38:43 +08:00
bggRGjQaUbCoE
acfa384c0c fix: #333
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 09:51:21 +08:00
bggRGjQaUbCoE
c2d27ddd04 opt: video page v
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-27 09:28:43 +08:00
My-Responsitories
0a6950e34a enable http2 (#331) 2025-02-26 22:02:19 +08:00
My-Responsitories
1c3d77b95d opt: wbiSign (#332) 2025-02-26 22:01:38 +08:00
bggRGjQaUbCoE
fb11208bbe fix: video toolbar
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-26 20:23:26 +08:00
bggRGjQaUbCoE
94f05127b6 fix: #330
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-26 19:53:06 +08:00
bggRGjQaUbCoE
25a3046c3c fix: video page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-26 18:53:31 +08:00
bggRGjQaUbCoE
f479fc37ba mod: follow tabbarview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-26 18:36:25 +08:00
bggRGjQaUbCoE
3ee19a8f08 mod: show followed user verif
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-26 18:27:50 +08:00
bggRGjQaUbCoE
b8d2ad68dd fix: video page v
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-26 17:43:35 +08:00
dom
8434c488da refa: vertical video page (#328)
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-26 17:21:24 +08:00
bggRGjQaUbCoE
41f251ad50 fix: dyn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-26 11:39:56 +08:00
bggRGjQaUbCoE
8e99ff1173 mod: search: show user verf
Closes #322

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-25 18:27:15 +08:00
bggRGjQaUbCoE
a921b983f5 opt: cancel seek
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-25 18:03:26 +08:00
bggRGjQaUbCoE
81eeda0a68 mod: video: cancellable slide seek
Closes #319

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-25 16:53:35 +08:00
bggRGjQaUbCoE
1a54f61355 mod: player: enable long press feedback
Closes #318

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-25 16:53:01 +08:00
bggRGjQaUbCoE
382cd5b73d mod: error toast
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-25 15:20:52 +08:00
bggRGjQaUbCoE
e236485bc7 mod: listener
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-25 15:20:52 +08:00
bggRGjQaUbCoE
0e69e23606 mod: video tabbarview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-24 22:25:37 +08:00
bggRGjQaUbCoE
0ef85f2551 fix: search ctr
related #306

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-24 21:04:28 +08:00
My-Responsitories
8d3990124e revert genTraceId (#314) 2025-02-24 17:13:15 +08:00
bggRGjQaUbCoE
7f912a1781 mod: refresh
related #306

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-24 17:10:43 +08:00
bggRGjQaUbCoE
d9ae1dd97a fix: handle grpc response
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-24 16:31:04 +08:00
bggRGjQaUbCoE
307db51aec mod: filter goods reposted dyn
Closes #309

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-24 15:00:54 +08:00
My-Responsitories
347a704b54 replace grpc to dio (#313)
* replace grpc to dio

* load danmaku from grpc
2025-02-24 14:55:28 +08:00
My-Responsitories
9e242fb902 remove duplicate code (#312)
* remove duplicate code

* Update login.dart
2025-02-24 14:55:08 +08:00
bggRGjQaUbCoE
192cd60a4f fix: get video progress
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-24 10:31:14 +08:00
bggRGjQaUbCoE
a98d8511d6 Update README.md
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-23 21:03:05 +08:00
bggRGjQaUbCoE
811b79610c fix: live room
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-23 18:10:51 +08:00
bggRGjQaUbCoE
14129e8f21 mod: horizontal live room
Closes #62

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-23 17:15:25 +08:00
bggRGjQaUbCoE
16de044d3d mod: triple
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-23 15:10:31 +08:00
bggRGjQaUbCoE
e573a8a9c0 mod: fs
related #306

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-23 14:58:12 +08:00
bggRGjQaUbCoE
108648cabf mod: seek from url
related #208

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-23 11:28:13 +08:00
bggRGjQaUbCoE
8e4ce07d19 opt: parse scheme
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-23 11:05:50 +08:00
bggRGjQaUbCoE
09cebd70ae mod: seek from dm
Closes #208

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-23 10:43:35 +08:00
bggRGjQaUbCoE
6a615c408b opt: nav icon color
ref orz12/main
2025-02-22 20:37:51 +08:00
bggRGjQaUbCoE
9ebc054c8c opt: spring
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-22 20:17:38 +08:00
My-Responsitories
b2c520bd91 feat: custom spring (#304) 2025-02-22 17:56:36 +08:00
bggRGjQaUbCoE
6506afa732 mod: add expand dyn live panel option
Closes #302

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-22 16:35:25 +08:00
bggRGjQaUbCoE
d1c74b9389 Revert "mod: set user-agent"
This reverts commit 4c56fcd6a8.
2025-02-22 16:10:11 +08:00
bggRGjQaUbCoE
61ca7bc1cb opt: horizontal preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-22 12:08:36 +08:00
bggRGjQaUbCoE
f94cb2a4b5 opt: pages
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-21 21:27:19 +08:00
bggRGjQaUbCoE
4c56fcd6a8 mod: set user-agent
Closes #299

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-21 20:56:15 +08:00
bggRGjQaUbCoE
d5bb2ec165 opt: del fav folder
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-21 18:20:08 +08:00
bggRGjQaUbCoE
27bc68f264 opt: follow page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-21 18:18:16 +08:00
bggRGjQaUbCoE
516eed76b7 mod: video sheet
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-21 18:04:31 +08:00
bggRGjQaUbCoE
4190c17cdc fix: #295
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-21 17:43:50 +08:00
bggRGjQaUbCoE
3d0fedfb61 mod: convert forEach
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-21 11:39:32 +08:00
bggRGjQaUbCoE
9d57deffb4 fix: filter dm midhash
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-21 11:17:54 +08:00
My-Responsitories
cc1951c721 fix regex & use set in uid (#296)
* fix regex count & use set in uid

* fix regex
2025-02-21 10:42:28 +08:00
bggRGjQaUbCoE
1cd8d4913d fix: #294
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-20 23:15:06 +08:00
bggRGjQaUbCoE
19890e29e9 mod: restore video duration
Closes #293

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-20 21:09:53 +08:00
bggRGjQaUbCoE
f759dba7da opt: filter danmaku
related #283

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-20 18:09:51 +08:00
bggRGjQaUbCoE
fb6f92a70b opt: #284
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-19 16:18:21 +08:00
bggRGjQaUbCoE
f22cad42d7 opt: filter data
Closes #283

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-19 15:42:24 +08:00
My-Responsitories
cfb6c674ea skip mcdn on parsing (#281) 2025-02-19 14:55:02 +08:00
bggRGjQaUbCoE
415c68a570 fix: typo
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-19 12:16:20 +08:00
bggRGjQaUbCoE
15b949bb9c mod: webview jump
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-19 11:42:05 +08:00
bggRGjQaUbCoE
316a9809e4 mod: delay checking dyn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-18 22:42:39 +08:00
bggRGjQaUbCoE
3f5aa03056 mod: insert dyn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-18 22:03:59 +08:00
bggRGjQaUbCoE
6bc33795a3 feat: create dyn antifraud
Closes #278

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-18 21:51:09 +08:00
bggRGjQaUbCoE
3191ae27a5 mod: repost panel
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-18 18:54:46 +08:00
bggRGjQaUbCoE
b25de52b9e feat: repost video
Closes #279

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-18 18:28:59 +08:00
bggRGjQaUbCoE
a08b4648d5 mod: try-catch biliSendCommAntifraud
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-18 17:07:00 +08:00
bggRGjQaUbCoE
e7a7c945de fix: biliSendCommAntifraud
related #275

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-18 15:48:39 +08:00
bggRGjQaUbCoE
571f358280 mod: user search widget
Closes #280

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-17 23:11:16 +08:00
bggRGjQaUbCoE
7ddc3adfaa feat: bili comm antifraud
Closes #275

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-17 21:31:55 +08:00
bggRGjQaUbCoE
957c326148 feat: anti goods reply
Closes #276

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-17 18:49:20 +08:00
bggRGjQaUbCoE
0b246d03a6 feat: anti goods dyn
Closes #277

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-17 18:49:03 +08:00
bggRGjQaUbCoE
5dd3ff32b6 fix: view forwarded dyn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 22:00:55 +08:00
bggRGjQaUbCoE
a48d262637 mod: show member coin/like archives(web)
Closes #265

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 20:55:41 +08:00
bggRGjQaUbCoE
b5d17b5161 mod: pay coins page
related #245

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 20:34:06 +08:00
bggRGjQaUbCoE
980733ba22 fix: member contribute page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 19:49:39 +08:00
bggRGjQaUbCoE
7043fdc35d mod: debug logout
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 15:09:53 +08:00
bggRGjQaUbCoE
81713a6bc4 mod: article: add action panel
related #235

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 15:00:49 +08:00
bggRGjQaUbCoE
959bcfaa30 mod: keep pgc index page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 13:37:34 +08:00
bggRGjQaUbCoE
fa465f792d opt: video width
Closes #267

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 13:37:34 +08:00
bggRGjQaUbCoE
74bf78b9cd feat: pgc index page
Closes #216

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 13:15:00 +08:00
bggRGjQaUbCoE
8c408e59f6 opt: post segment panel
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 09:25:33 +08:00
bggRGjQaUbCoE
25d27e42ed fix: #263
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-16 09:22:09 +08:00
bggRGjQaUbCoE
0f2b0cc5f2 Revert "fix: #263"
This reverts commit 84ed34f3a7.
2025-02-16 01:01:01 +08:00
bggRGjQaUbCoE
00ea34f45d opt: video bs
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 23:42:02 +08:00
bggRGjQaUbCoE
ec936c1821 opt: video bs
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 23:00:47 +08:00
bggRGjQaUbCoE
2ff84857e7 refa: video bottom sheet
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 22:19:43 +08:00
bggRGjQaUbCoE
84ed34f3a7 fix: #263
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 21:07:56 +08:00
bggRGjQaUbCoE
f0508e1bc2 mod: disable version check when debug
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 20:40:39 +08:00
bggRGjQaUbCoE
8ea7bf36d7 fix: dyn detail: repost btn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 20:22:47 +08:00
bggRGjQaUbCoE
8819461eed mod: dyn detail: add action panel
Closes #235

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 18:04:43 +08:00
bggRGjQaUbCoE
7c30668c87 fix: #261
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 16:41:49 +08:00
bggRGjQaUbCoE
a3424950ca fix: push dyn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 14:33:58 +08:00
bggRGjQaUbCoE
ebc42eb05e fix: logout
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 14:25:10 +08:00
bggRGjQaUbCoE
fc6ff44471 fix: ai conclusion
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 14:23:15 +08:00
bggRGjQaUbCoE
be03377449 fix: emote
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 13:52:12 +08:00
bggRGjQaUbCoE
e52934093a opt: member tab
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 13:30:50 +08:00
bggRGjQaUbCoE
ebfd98488e mod: show staff verf
Closes #259

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 13:26:08 +08:00
bggRGjQaUbCoE
6a68af77dc mod: member tab
Closes #260

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 13:14:25 +08:00
bggRGjQaUbCoE
e5c0fb7cb2 fix: in-app webview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 12:57:04 +08:00
bggRGjQaUbCoE
d9611cce80 opt: in-app webview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 12:26:44 +08:00
bggRGjQaUbCoE
4b48aba2ae opt: data parse
related #258

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 12:03:43 +08:00
bggRGjQaUbCoE
47fbb6cd0e opt: whisper: msg preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-15 00:06:35 +08:00
bggRGjQaUbCoE
dae71d427c feat: whisper: revoke msg
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-14 23:28:48 +08:00
bggRGjQaUbCoE
46bc2ceb78 mod: scheme match
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-14 21:52:57 +08:00
bggRGjQaUbCoE
6f98200179 mod: whisper pic type
Closes #253

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-14 21:35:15 +08:00
bggRGjQaUbCoE
a57b4c56a5 fix: #257
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-14 21:07:56 +08:00
bggRGjQaUbCoE
6c3062ba2d feat: pure black theme
Closes #254

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-14 16:10:22 +08:00
bggRGjQaUbCoE
064c8a9dfe mod: page observer
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-14 11:15:50 +08:00
bggRGjQaUbCoE
7dd47736fb opt: better url pattern
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-13 22:27:16 +08:00
244 changed files with 15065 additions and 11213 deletions

View File

@@ -47,8 +47,9 @@
## feat
- [x] 屏蔽带货动态/评论
- [x] 互动视频
- [x] 发评反诈
- [x] 发评/动态反诈
- [x] 高能进度条
- [x] 滑动跳转预览视频缩略图
- [x] Live Photo
@@ -84,7 +85,7 @@
- [x] 筛选搜索
- [x] 转发动态
- [x] 合集图片
- [x] 删除/置顶私信
- [x] 删除/置顶/撤回私信
- [x] 举报用户/评论/视频/动态
- [x] 删除/发布文本/图片动态
- [x] 其他

View File

@@ -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()
}

View File

@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="108dp"
android:width="108dp"
android:height="24dp"
android:width="24dp"
android:viewportWidth="108.0"
android:viewportHeight="108.0">
<path
android:fillColor="#FF5CB67B"
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: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>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/*" />

View File

@@ -23,246 +23,251 @@ 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 List<String> mpvAnime4KShaders = [
'Anime4K_Clamp_Highlights.glsl',
'Anime4K_Restore_CNN_VL.glsl',
'Anime4K_Upscale_CNN_x2_VL.glsl',
'Anime4K_AutoDownscalePre_x2.glsl',
'Anime4K_AutoDownscalePre_x4.glsl',
'Anime4K_Upscale_CNN_x2_M.glsl'
];
static const urlPattern =
r'https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]';
static get goodsUrlPrefix => "https://gaoneng.bilibili.com/tetris";
// 超分辨率滤镜
static List<String> get mpvAnime4KShaders => [
'Anime4K_Clamp_Highlights.glsl',
'Anime4K_Restore_CNN_VL.glsl',
'Anime4K_Upscale_CNN_x2_VL.glsl',
'Anime4K_AutoDownscalePre_x2.glsl',
'Anime4K_AutoDownscalePre_x4.glsl',
'Anime4K_Upscale_CNN_x2_M.glsl'
];
// 超分辨率滤镜 (轻量)
static const List<String> mpvAnime4KShadersLite = [
'Anime4K_Clamp_Highlights.glsl',
'Anime4K_Restore_CNN_M.glsl',
'Anime4K_Restore_CNN_S.glsl',
'Anime4K_Upscale_CNN_x2_M.glsl',
'Anime4K_AutoDownscalePre_x2.glsl',
'Anime4K_AutoDownscalePre_x4.glsl',
'Anime4K_Upscale_CNN_x2_S.glsl'
];
static List<String> get mpvAnime4KShadersLite => [
'Anime4K_Clamp_Highlights.glsl',
'Anime4K_Restore_CNN_M.glsl',
'Anime4K_Restore_CNN_S.glsl',
'Anime4K_Upscale_CNN_x2_M.glsl',
'Anime4K_AutoDownscalePre_x2.glsl',
'Anime4K_AutoDownscalePre_x4.glsl',
'Anime4K_Upscale_CNN_x2_S.glsl'
];
//内容来自 https://passport.bilibili.com/web/generic/country/list
static const List<Map<String, dynamic>> internationalDialingPrefix = [
{"id": 1, "cname": "中国大陆", "country_id": "86"},
{"id": 5, "cname": "中国香港特别行政区", "country_id": "852"},
{"id": 2, "cname": "中国澳门特别行政区", "country_id": "853"},
{"id": 3, "cname": "中国台湾", "country_id": "886"},
{"id": 4, "cname": "美国", "country_id": "1"},
{"id": 6, "cname": "比利时", "country_id": "32"},
{"id": 7, "cname": "澳大利亚", "country_id": "61"},
{"id": 8, "cname": "法国", "country_id": "33"},
{"id": 9, "cname": "加拿大", "country_id": "1"},
{"id": 10, "cname": "日本", "country_id": "81"},
{"id": 11, "cname": "新加坡", "country_id": "65"},
{"id": 12, "cname": "韩国", "country_id": "82"},
{"id": 13, "cname": "马来西亚", "country_id": "60"},
{"id": 14, "cname": "英国", "country_id": "44"},
{"id": 15, "cname": "意大利", "country_id": "39"},
{"id": 16, "cname": "德国", "country_id": "49"},
{"id": 18, "cname": "俄罗斯", "country_id": "7"},
{"id": 19, "cname": "新西兰", "country_id": "64"}, //common:1-19
{"id": 153, "cname": "瓦利斯群岛和富图纳群岛", "country_id": "1681"},
{"id": 152, "cname": "葡萄牙", "country_id": "351"},
{"id": 151, "cname": "帕劳", "country_id": "680"},
{"id": 150, "cname": "诺福克岛", "country_id": "672"},
{"id": 149, "cname": "挪威", "country_id": "47"},
{"id": 148, "cname": "纽埃岛", "country_id": "683"},
{"id": 147, "cname": "尼日利亚", "country_id": "234"},
{"id": 146, "cname": "尼日尔", "country_id": "227"},
{"id": 145, "cname": "尼加拉瓜", "country_id": "505"},
{"id": 144, "cname": "尼泊尔", "country_id": "977"},
{"id": 143, "cname": "瑙鲁", "country_id": "674"},
{"id": 154, "cname": "格鲁吉亚", "country_id": "995"},
{"id": 155, "cname": "瑞典", "country_id": "46"},
{"id": 165, "cname": "沙特阿拉伯", "country_id": "966"},
{"id": 164, "cname": "桑给巴尔岛", "country_id": "259"},
{"id": 163, "cname": "塞舌尔共和国", "country_id": "248"},
{"id": 162, "cname": "塞浦路斯", "country_id": "357"},
{"id": 161, "cname": "塞内加尔", "country_id": "221"},
{"id": 160, "cname": "塞拉利昂", "country_id": "232"},
{"id": 159, "cname": "萨摩亚,东部", "country_id": "684"},
{"id": 158, "cname": "萨摩亚,西部", "country_id": "685"},
{"id": 157, "cname": "萨尔瓦多", "country_id": "503"},
{"id": 156, "cname": "瑞士", "country_id": "41"},
{"id": 166, "cname": "圣多美和普林西比", "country_id": "239"},
{"id": 142, "cname": "塞尔维亚", "country_id": "381"},
{"id": 141, "cname": "南非", "country_id": "27"},
{"id": 128, "cname": "毛里塔尼亚", "country_id": "222"},
{"id": 127, "cname": "毛里求斯", "country_id": "230"},
{"id": 126, "cname": "马歇尔岛", "country_id": "692"},
{"id": 125, "cname": "马提尼克岛", "country_id": "596"},
{"id": 124, "cname": "马其顿", "country_id": "389"},
{"id": 123, "cname": "马里亚纳岛", "country_id": "1670"},
{"id": 122, "cname": "马里", "country_id": "223"},
{"id": 121, "cname": "马拉维", "country_id": "265"},
{"id": 120, "cname": "马耳他", "country_id": "356"},
{"id": 119, "cname": "马尔代夫", "country_id": "960"},
{"id": 129, "cname": "蒙古", "country_id": "976"},
{"id": 130, "cname": "蒙特塞拉特岛", "country_id": "1664"},
{"id": 140, "cname": "纳米比亚", "country_id": "264"},
{"id": 139, "cname": "墨西哥", "country_id": "52"},
{"id": 138, "cname": "莫桑比克", "country_id": "258"},
{"id": 137, "cname": "摩纳哥", "country_id": "377"},
{"id": 136, "cname": "摩洛哥", "country_id": "212"},
{"id": 135, "cname": "摩尔多瓦", "country_id": "373"},
{"id": 134, "cname": "缅甸", "country_id": "95"},
{"id": 133, "cname": "密克罗尼西亚", "country_id": "691"},
{"id": 132, "cname": "秘鲁", "country_id": "51"},
{"id": 131, "cname": "孟加拉国", "country_id": "880"},
{"id": 118, "cname": "马达加斯加", "country_id": "261"},
{"id": 167, "cname": "圣卢西亚", "country_id": "1784"},
{"id": 216, "cname": "智利", "country_id": "56"},
{"id": 203, "cname": "牙买加", "country_id": "1876"},
{"id": 202, "cname": "叙利亚", "country_id": "963"},
{"id": 201, "cname": "匈牙利", "country_id": "36"},
{"id": 200, "cname": "科特迪瓦", "country_id": "225"},
{"id": 199, "cname": "希腊", "country_id": "30"},
{"id": 198, "cname": "西班牙", "country_id": "34"},
{"id": 197, "cname": "乌兹别克斯坦", "country_id": "998"},
{"id": 196, "cname": "乌拉圭", "country_id": "598"},
{"id": 195, "cname": "乌克兰", "country_id": "380"},
{"id": 194, "cname": "乌干达", "country_id": "256"},
{"id": 204, "cname": "亚美尼亚", "country_id": "374"},
{"id": 205, "cname": "也门", "country_id": "967"},
{"id": 215, "cname": "直布罗陀", "country_id": "350"},
{"id": 214, "cname": "乍得", "country_id": "235"},
{"id": 213, "cname": "赞比亚", "country_id": "260"},
{"id": 212, "cname": "越南", "country_id": "84"},
{"id": 211, "cname": "约旦", "country_id": "962"},
{"id": 210, "cname": "印尼", "country_id": "62"},
{"id": 209, "cname": "印度", "country_id": "91"},
{"id": 208, "cname": "以色列", "country_id": "972"},
{"id": 207, "cname": "伊朗", "country_id": "98"},
{"id": 206, "cname": "伊拉克", "country_id": "964"},
{"id": 193, "cname": "文莱", "country_id": "673"},
{"id": 192, "cname": "委内瑞拉", "country_id": "58"},
{"id": 191, "cname": "维珍群岛(英属)", "country_id": "1284"},
{"id": 178, "cname": "泰国", "country_id": "66"},
{"id": 177, "cname": "索马里", "country_id": "252"},
{"id": 176, "cname": "所罗门群岛", "country_id": "677"},
{"id": 175, "cname": "苏里南", "country_id": "597"},
{"id": 174, "cname": "苏丹", "country_id": "249"},
{"id": 173, "cname": "斯威士兰", "country_id": "268"},
{"id": 172, "cname": "斯洛文尼亚", "country_id": "386"},
{"id": 171, "cname": "斯洛伐克", "country_id": "421"},
{"id": 170, "cname": "斯里兰卡", "country_id": "94"},
{"id": 169, "cname": "圣皮埃尔和密克隆群岛", "country_id": "508"},
{"id": 179, "cname": "坦桑尼亚", "country_id": "255"},
{"id": 180, "cname": "汤加", "country_id": "676"},
{"id": 190, "cname": "维珍群岛(美属)", "country_id": "1340"},
{"id": 189, "cname": "瓦努阿图", "country_id": "678"},
{"id": 188, "cname": "托克劳岛", "country_id": "690"},
{"id": 187, "cname": "土库曼斯坦", "country_id": "993"},
{"id": 186, "cname": "土耳其", "country_id": "90"},
{"id": 185, "cname": "图瓦卢", "country_id": "688"},
{"id": 184, "cname": "突尼斯", "country_id": "216"},
{"id": 183, "cname": "阿森松岛", "country_id": "247"},
{"id": 182, "cname": "特立尼达和多巴哥", "country_id": "1868"},
{"id": 181, "cname": "特克斯和凯科斯", "country_id": "1649"},
{"id": 168, "cname": "圣马力诺", "country_id": "378"},
{"id": 67, "cname": "法属圭亚那", "country_id": "594"},
{"id": 54, "cname": "不丹", "country_id": "975"},
{"id": 53, "cname": "博茨瓦纳", "country_id": "267"},
{"id": 52, "cname": "伯利兹", "country_id": "501"},
{"id": 51, "cname": "玻利维亚", "country_id": "591"},
{"id": 50, "cname": "波兰", "country_id": "48"},
{"id": 49, "cname": "波黑", "country_id": "387"},
{"id": 48, "cname": "波多黎各", "country_id": "1787"},
{"id": 47, "cname": "冰岛", "country_id": "354"},
{"id": 46, "cname": "贝宁", "country_id": "229"},
{"id": 45, "cname": "保加利亚", "country_id": "359"},
{"id": 55, "cname": "布基纳法索", "country_id": "226"},
{"id": 56, "cname": "布隆迪", "country_id": "257"},
{"id": 66, "cname": "法属波利尼西亚", "country_id": "689"},
{"id": 65, "cname": "法罗岛", "country_id": "298"},
{"id": 64, "cname": "厄立特里亚", "country_id": "291"},
{"id": 63, "cname": "厄瓜多尔", "country_id": "593"},
{"id": 62, "cname": "多米尼加代表", "country_id": "1809"},
{"id": 61, "cname": "多米尼加", "country_id": "1767"},
{"id": 60, "cname": "多哥", "country_id": "228"},
{"id": 59, "cname": "迪戈加西亚岛", "country_id": "246"},
{"id": 58, "cname": "丹麦", "country_id": "45"},
{"id": 57, "cname": "赤道几内亚", "country_id": "240"},
{"id": 44, "cname": "百慕大群岛", "country_id": "1441"},
{"id": 43, "cname": "白俄罗斯", "country_id": "375"},
{"id": 42, "cname": "巴西", "country_id": "55"},
{"id": 29, "cname": "爱尔兰", "country_id": "353"},
{"id": 28, "cname": "埃塞俄比亚", "country_id": "251"},
{"id": 27, "cname": "埃及", "country_id": "20"},
{"id": 26, "cname": "阿塞拜疆", "country_id": "994"},
{"id": 25, "cname": "阿曼", "country_id": "968"},
{"id": 24, "cname": "阿联酋", "country_id": "971"},
{"id": 23, "cname": "阿根廷", "country_id": "54"},
{"id": 22, "cname": "阿富汗", "country_id": "93"},
{"id": 21, "cname": "阿尔及利亚", "country_id": "213"},
{"id": 20, "cname": "阿尔巴尼亚", "country_id": "355"},
{"id": 30, "cname": "爱沙尼亚", "country_id": "372"},
{"id": 31, "cname": "安道尔", "country_id": "376"},
{"id": 41, "cname": "巴拿马", "country_id": "507"},
{"id": 40, "cname": "巴林", "country_id": "973"},
{"id": 39, "cname": "巴拉圭", "country_id": "595"},
{"id": 38, "cname": "巴基斯坦", "country_id": "92"},
{"id": 37, "cname": "巴哈马群岛", "country_id": "1242"},
{"id": 36, "cname": "巴布亚新几内亚", "country_id": "675"},
{"id": 35, "cname": "巴巴多斯", "country_id": "1246"},
{"id": 34, "cname": "奥地利", "country_id": "43"},
{"id": 33, "cname": "安提瓜岛和巴布达", "country_id": "1268"},
{"id": 32, "cname": "安哥拉", "country_id": "244"},
{"id": 68, "cname": "非洲中部", "country_id": "236"},
{"id": 117, "cname": "罗马尼亚", "country_id": "40"},
{"id": 104, "cname": "科威特", "country_id": "965"},
{"id": 103, "cname": "科摩罗", "country_id": "269"},
{"id": 102, "cname": "开曼群岛", "country_id": "1345"},
{"id": 101, "cname": "卡塔尔", "country_id": "974"},
{"id": 100, "cname": "喀麦隆", "country_id": "237"},
{"id": 99, "cname": "聚会岛", "country_id": "262"},
{"id": 98, "cname": "津巴布韦", "country_id": "263"},
{"id": 97, "cname": "捷克", "country_id": "420"},
{"id": 96, "cname": "柬埔寨", "country_id": "855"},
{"id": 95, "cname": "加蓬", "country_id": "241"},
{"id": 105, "cname": "克罗地亚", "country_id": "385"},
{"id": 106, "cname": "肯尼亚", "country_id": "254"},
{"id": 116, "cname": "卢旺达", "country_id": "250"},
{"id": 115, "cname": "卢森堡", "country_id": "352"},
{"id": 114, "cname": "利比亚", "country_id": "218"},
{"id": 113, "cname": "利比里亚", "country_id": "231"},
{"id": 112, "cname": "立陶宛", "country_id": "370"},
{"id": 111, "cname": "黎巴嫩", "country_id": "961"},
{"id": 110, "cname": "老挝", "country_id": "856"},
{"id": 109, "cname": "莱索托", "country_id": "266"},
{"id": 108, "cname": "拉脱维亚", "country_id": "371"},
{"id": 107, "cname": "库克岛", "country_id": "682"},
{"id": 94, "cname": "加纳", "country_id": "233"},
{"id": 93, "cname": "几内亚比绍", "country_id": "245"},
{"id": 92, "cname": "几内亚", "country_id": "224"},
{"id": 79, "cname": "格林纳达", "country_id": "1473"},
{"id": 78, "cname": "哥斯达黎加", "country_id": "506"},
{"id": 77, "cname": "哥伦比亚", "country_id": "57"},
{"id": 76, "cname": "刚果(金)", "country_id": "243"},
{"id": 75, "cname": "刚果", "country_id": "242"},
{"id": 74, "cname": "冈比亚", "country_id": "220"},
{"id": 73, "cname": "福克兰岛", "country_id": "500"},
{"id": 72, "cname": "佛得角", "country_id": "238"},
{"id": 71, "cname": "芬兰", "country_id": "358"},
{"id": 70, "cname": "斐济", "country_id": "679"},
{"id": 80, "cname": "格陵兰岛", "country_id": "299"},
{"id": 81, "cname": "古巴", "country_id": "53"},
{"id": 91, "cname": "吉尔吉斯斯坦", "country_id": "996"},
{"id": 90, "cname": "吉布提", "country_id": "253"},
{"id": 89, "cname": "基里巴斯", "country_id": "686"},
{"id": 88, "cname": "维克岛", "country_id": "1808"},
{"id": 87, "cname": "洪都拉斯", "country_id": "504"},
{"id": 86, "cname": "荷兰", "country_id": "31"},
{"id": 85, "cname": "朝鲜", "country_id": "850"},
{"id": 84, "cname": "海地", "country_id": "509"},
{"id": 83, "cname": "关岛", "country_id": "1671"},
{"id": 82, "cname": "瓜德罗普岛", "country_id": "590"},
{"id": 69, "cname": "菲律宾", "country_id": "63"}
];
static List<Map<String, dynamic>> get internationalDialingPrefix => [
{"id": 1, "cname": "中国大陆", "country_id": "86"},
{"id": 5, "cname": "中国香港特别行政区", "country_id": "852"},
{"id": 2, "cname": "中国澳门特别行政区", "country_id": "853"},
{"id": 3, "cname": "中国台湾", "country_id": "886"},
{"id": 4, "cname": "美国", "country_id": "1"},
{"id": 6, "cname": "比利时", "country_id": "32"},
{"id": 7, "cname": "澳大利亚", "country_id": "61"},
{"id": 8, "cname": "法国", "country_id": "33"},
{"id": 9, "cname": "加拿大", "country_id": "1"},
{"id": 10, "cname": "日本", "country_id": "81"},
{"id": 11, "cname": "新加坡", "country_id": "65"},
{"id": 12, "cname": "韩国", "country_id": "82"},
{"id": 13, "cname": "马来西亚", "country_id": "60"},
{"id": 14, "cname": "英国", "country_id": "44"},
{"id": 15, "cname": "意大利", "country_id": "39"},
{"id": 16, "cname": "德国", "country_id": "49"},
{"id": 18, "cname": "俄罗斯", "country_id": "7"},
{"id": 19, "cname": "新西兰", "country_id": "64"}, //common:1-19
{"id": 153, "cname": "瓦利斯群岛和富图纳群岛", "country_id": "1681"},
{"id": 152, "cname": "葡萄牙", "country_id": "351"},
{"id": 151, "cname": "帕劳", "country_id": "680"},
{"id": 150, "cname": "诺福克岛", "country_id": "672"},
{"id": 149, "cname": "挪威", "country_id": "47"},
{"id": 148, "cname": "纽埃岛", "country_id": "683"},
{"id": 147, "cname": "尼日利亚", "country_id": "234"},
{"id": 146, "cname": "尼日尔", "country_id": "227"},
{"id": 145, "cname": "尼加拉瓜", "country_id": "505"},
{"id": 144, "cname": "尼泊尔", "country_id": "977"},
{"id": 143, "cname": "瑙鲁", "country_id": "674"},
{"id": 154, "cname": "格鲁吉亚", "country_id": "995"},
{"id": 155, "cname": "瑞典", "country_id": "46"},
{"id": 165, "cname": "沙特阿拉伯", "country_id": "966"},
{"id": 164, "cname": "桑给巴尔岛", "country_id": "259"},
{"id": 163, "cname": "塞舌尔共和国", "country_id": "248"},
{"id": 162, "cname": "塞浦路斯", "country_id": "357"},
{"id": 161, "cname": "塞内加尔", "country_id": "221"},
{"id": 160, "cname": "塞拉利昂", "country_id": "232"},
{"id": 159, "cname": "萨摩亚,东部", "country_id": "684"},
{"id": 158, "cname": "萨摩亚,西部", "country_id": "685"},
{"id": 157, "cname": "萨尔瓦多", "country_id": "503"},
{"id": 156, "cname": "瑞士", "country_id": "41"},
{"id": 166, "cname": "圣多美和普林西比", "country_id": "239"},
{"id": 142, "cname": "塞尔维亚", "country_id": "381"},
{"id": 141, "cname": "南非", "country_id": "27"},
{"id": 128, "cname": "毛里塔尼亚", "country_id": "222"},
{"id": 127, "cname": "毛里求斯", "country_id": "230"},
{"id": 126, "cname": "马歇尔岛", "country_id": "692"},
{"id": 125, "cname": "马提尼克岛", "country_id": "596"},
{"id": 124, "cname": "马其顿", "country_id": "389"},
{"id": 123, "cname": "马里亚纳岛", "country_id": "1670"},
{"id": 122, "cname": "马里", "country_id": "223"},
{"id": 121, "cname": "马拉维", "country_id": "265"},
{"id": 120, "cname": "马耳他", "country_id": "356"},
{"id": 119, "cname": "马尔代夫", "country_id": "960"},
{"id": 129, "cname": "蒙古", "country_id": "976"},
{"id": 130, "cname": "蒙特塞拉特岛", "country_id": "1664"},
{"id": 140, "cname": "纳米比亚", "country_id": "264"},
{"id": 139, "cname": "墨西哥", "country_id": "52"},
{"id": 138, "cname": "莫桑比克", "country_id": "258"},
{"id": 137, "cname": "摩纳哥", "country_id": "377"},
{"id": 136, "cname": "摩洛哥", "country_id": "212"},
{"id": 135, "cname": "摩尔多瓦", "country_id": "373"},
{"id": 134, "cname": "缅甸", "country_id": "95"},
{"id": 133, "cname": "密克罗尼西亚", "country_id": "691"},
{"id": 132, "cname": "秘鲁", "country_id": "51"},
{"id": 131, "cname": "孟加拉国", "country_id": "880"},
{"id": 118, "cname": "马达加斯加", "country_id": "261"},
{"id": 167, "cname": "圣卢西亚", "country_id": "1784"},
{"id": 216, "cname": "智利", "country_id": "56"},
{"id": 203, "cname": "牙买加", "country_id": "1876"},
{"id": 202, "cname": "叙利亚", "country_id": "963"},
{"id": 201, "cname": "匈牙利", "country_id": "36"},
{"id": 200, "cname": "科特迪瓦", "country_id": "225"},
{"id": 199, "cname": "希腊", "country_id": "30"},
{"id": 198, "cname": "西班牙", "country_id": "34"},
{"id": 197, "cname": "乌兹别克斯坦", "country_id": "998"},
{"id": 196, "cname": "乌拉圭", "country_id": "598"},
{"id": 195, "cname": "乌克兰", "country_id": "380"},
{"id": 194, "cname": "乌干达", "country_id": "256"},
{"id": 204, "cname": "亚美尼亚", "country_id": "374"},
{"id": 205, "cname": "也门", "country_id": "967"},
{"id": 215, "cname": "直布罗陀", "country_id": "350"},
{"id": 214, "cname": "乍得", "country_id": "235"},
{"id": 213, "cname": "赞比亚", "country_id": "260"},
{"id": 212, "cname": "越南", "country_id": "84"},
{"id": 211, "cname": "约旦", "country_id": "962"},
{"id": 210, "cname": "印尼", "country_id": "62"},
{"id": 209, "cname": "印度", "country_id": "91"},
{"id": 208, "cname": "以色列", "country_id": "972"},
{"id": 207, "cname": "伊朗", "country_id": "98"},
{"id": 206, "cname": "伊拉克", "country_id": "964"},
{"id": 193, "cname": "文莱", "country_id": "673"},
{"id": 192, "cname": "委内瑞拉", "country_id": "58"},
{"id": 191, "cname": "维珍群岛(英属)", "country_id": "1284"},
{"id": 178, "cname": "泰国", "country_id": "66"},
{"id": 177, "cname": "索马里", "country_id": "252"},
{"id": 176, "cname": "所罗门群岛", "country_id": "677"},
{"id": 175, "cname": "苏里南", "country_id": "597"},
{"id": 174, "cname": "苏丹", "country_id": "249"},
{"id": 173, "cname": "斯威士兰", "country_id": "268"},
{"id": 172, "cname": "斯洛文尼亚", "country_id": "386"},
{"id": 171, "cname": "斯洛伐克", "country_id": "421"},
{"id": 170, "cname": "斯里兰卡", "country_id": "94"},
{"id": 169, "cname": "圣皮埃尔和密克隆群岛", "country_id": "508"},
{"id": 179, "cname": "坦桑尼亚", "country_id": "255"},
{"id": 180, "cname": "汤加", "country_id": "676"},
{"id": 190, "cname": "维珍群岛(美属)", "country_id": "1340"},
{"id": 189, "cname": "瓦努阿图", "country_id": "678"},
{"id": 188, "cname": "托克劳岛", "country_id": "690"},
{"id": 187, "cname": "土库曼斯坦", "country_id": "993"},
{"id": 186, "cname": "土耳其", "country_id": "90"},
{"id": 185, "cname": "图瓦卢", "country_id": "688"},
{"id": 184, "cname": "突尼斯", "country_id": "216"},
{"id": 183, "cname": "阿森松岛", "country_id": "247"},
{"id": 182, "cname": "特立尼达和多巴哥", "country_id": "1868"},
{"id": 181, "cname": "特克斯和凯科斯", "country_id": "1649"},
{"id": 168, "cname": "圣马力诺", "country_id": "378"},
{"id": 67, "cname": "法属圭亚那", "country_id": "594"},
{"id": 54, "cname": "不丹", "country_id": "975"},
{"id": 53, "cname": "博茨瓦纳", "country_id": "267"},
{"id": 52, "cname": "伯利兹", "country_id": "501"},
{"id": 51, "cname": "玻利维亚", "country_id": "591"},
{"id": 50, "cname": "波兰", "country_id": "48"},
{"id": 49, "cname": "波黑", "country_id": "387"},
{"id": 48, "cname": "波多黎各", "country_id": "1787"},
{"id": 47, "cname": "冰岛", "country_id": "354"},
{"id": 46, "cname": "贝宁", "country_id": "229"},
{"id": 45, "cname": "保加利亚", "country_id": "359"},
{"id": 55, "cname": "布基纳法索", "country_id": "226"},
{"id": 56, "cname": "布隆迪", "country_id": "257"},
{"id": 66, "cname": "法属波利尼西亚", "country_id": "689"},
{"id": 65, "cname": "法罗岛", "country_id": "298"},
{"id": 64, "cname": "厄立特里亚", "country_id": "291"},
{"id": 63, "cname": "厄瓜多尔", "country_id": "593"},
{"id": 62, "cname": "多米尼加代表", "country_id": "1809"},
{"id": 61, "cname": "多米尼加", "country_id": "1767"},
{"id": 60, "cname": "多哥", "country_id": "228"},
{"id": 59, "cname": "迪戈加西亚岛", "country_id": "246"},
{"id": 58, "cname": "丹麦", "country_id": "45"},
{"id": 57, "cname": "赤道几内亚", "country_id": "240"},
{"id": 44, "cname": "百慕大群岛", "country_id": "1441"},
{"id": 43, "cname": "白俄罗斯", "country_id": "375"},
{"id": 42, "cname": "巴西", "country_id": "55"},
{"id": 29, "cname": "爱尔兰", "country_id": "353"},
{"id": 28, "cname": "埃塞俄比亚", "country_id": "251"},
{"id": 27, "cname": "埃及", "country_id": "20"},
{"id": 26, "cname": "阿塞拜疆", "country_id": "994"},
{"id": 25, "cname": "阿曼", "country_id": "968"},
{"id": 24, "cname": "阿联酋", "country_id": "971"},
{"id": 23, "cname": "阿根廷", "country_id": "54"},
{"id": 22, "cname": "阿富汗", "country_id": "93"},
{"id": 21, "cname": "阿尔及利亚", "country_id": "213"},
{"id": 20, "cname": "阿尔巴尼亚", "country_id": "355"},
{"id": 30, "cname": "爱沙尼亚", "country_id": "372"},
{"id": 31, "cname": "安道尔", "country_id": "376"},
{"id": 41, "cname": "巴拿马", "country_id": "507"},
{"id": 40, "cname": "巴林", "country_id": "973"},
{"id": 39, "cname": "巴拉圭", "country_id": "595"},
{"id": 38, "cname": "巴基斯坦", "country_id": "92"},
{"id": 37, "cname": "巴哈马群岛", "country_id": "1242"},
{"id": 36, "cname": "巴布亚新几内亚", "country_id": "675"},
{"id": 35, "cname": "巴巴多斯", "country_id": "1246"},
{"id": 34, "cname": "奥地利", "country_id": "43"},
{"id": 33, "cname": "安提瓜岛和巴布达", "country_id": "1268"},
{"id": 32, "cname": "安哥拉", "country_id": "244"},
{"id": 68, "cname": "非洲中部", "country_id": "236"},
{"id": 117, "cname": "罗马尼亚", "country_id": "40"},
{"id": 104, "cname": "科威特", "country_id": "965"},
{"id": 103, "cname": "科摩罗", "country_id": "269"},
{"id": 102, "cname": "开曼群岛", "country_id": "1345"},
{"id": 101, "cname": "卡塔尔", "country_id": "974"},
{"id": 100, "cname": "喀麦隆", "country_id": "237"},
{"id": 99, "cname": "聚会岛", "country_id": "262"},
{"id": 98, "cname": "津巴布韦", "country_id": "263"},
{"id": 97, "cname": "捷克", "country_id": "420"},
{"id": 96, "cname": "柬埔寨", "country_id": "855"},
{"id": 95, "cname": "加蓬", "country_id": "241"},
{"id": 105, "cname": "克罗地亚", "country_id": "385"},
{"id": 106, "cname": "肯尼亚", "country_id": "254"},
{"id": 116, "cname": "卢旺达", "country_id": "250"},
{"id": 115, "cname": "卢森堡", "country_id": "352"},
{"id": 114, "cname": "利比亚", "country_id": "218"},
{"id": 113, "cname": "利比里亚", "country_id": "231"},
{"id": 112, "cname": "立陶宛", "country_id": "370"},
{"id": 111, "cname": "黎巴嫩", "country_id": "961"},
{"id": 110, "cname": "老挝", "country_id": "856"},
{"id": 109, "cname": "莱索托", "country_id": "266"},
{"id": 108, "cname": "拉脱维亚", "country_id": "371"},
{"id": 107, "cname": "库克岛", "country_id": "682"},
{"id": 94, "cname": "加纳", "country_id": "233"},
{"id": 93, "cname": "几内亚比绍", "country_id": "245"},
{"id": 92, "cname": "几内亚", "country_id": "224"},
{"id": 79, "cname": "格林纳达", "country_id": "1473"},
{"id": 78, "cname": "哥斯达黎加", "country_id": "506"},
{"id": 77, "cname": "哥伦比亚", "country_id": "57"},
{"id": 76, "cname": "刚果(金)", "country_id": "243"},
{"id": 75, "cname": "刚果", "country_id": "242"},
{"id": 74, "cname": "冈比亚", "country_id": "220"},
{"id": 73, "cname": "福克兰岛", "country_id": "500"},
{"id": 72, "cname": "佛得角", "country_id": "238"},
{"id": 71, "cname": "芬兰", "country_id": "358"},
{"id": 70, "cname": "斐济", "country_id": "679"},
{"id": 80, "cname": "格陵兰岛", "country_id": "299"},
{"id": 81, "cname": "古巴", "country_id": "53"},
{"id": 91, "cname": "吉尔吉斯斯坦", "country_id": "996"},
{"id": 90, "cname": "吉布提", "country_id": "253"},
{"id": 89, "cname": "基里巴斯", "country_id": "686"},
{"id": 88, "cname": "维克岛", "country_id": "1808"},
{"id": 87, "cname": "洪都拉斯", "country_id": "504"},
{"id": 86, "cname": "荷兰", "country_id": "31"},
{"id": 85, "cname": "朝鲜", "country_id": "850"},
{"id": 84, "cname": "海地", "country_id": "509"},
{"id": 83, "cname": "关岛", "country_id": "1671"},
{"id": 82, "cname": "瓜德罗普岛", "country_id": "590"},
{"id": 69, "cname": "菲律宾", "country_id": "63"}
];
}

View File

@@ -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();
}

View File

@@ -484,6 +484,9 @@ class _RenderProgressBar extends RenderBox {
}
void _onDragStart(DragStartDetails details) {
if (onDragStart == null) {
return;
}
_userIsDraggingThumb = true;
_updateThumbPosition(details.localPosition);
onDragStart?.call(ThumbDragDetails(
@@ -494,6 +497,9 @@ class _RenderProgressBar extends RenderBox {
}
void _onDragUpdate(DragUpdateDetails details) {
if (onDragUpdate == null) {
return;
}
_updateThumbPosition(details.localPosition);
onDragUpdate?.call(ThumbDragDetails(
timeStamp: _currentThumbDuration(),
@@ -503,6 +509,9 @@ class _RenderProgressBar extends RenderBox {
}
void _onDragEnd(DragEndDetails details) {
if (onSeek == null) {
return;
}
onDragEnd?.call();
onSeek?.call(_currentThumbDuration());
_finishDrag();

View File

@@ -28,12 +28,6 @@ Widget htmlRender({
String imgUrl = key.contains('src')
? attributes['src'] as String
: attributes['data-src'] as String;
if (imgUrl.startsWith('//')) {
imgUrl = 'https:$imgUrl';
}
if (imgUrl.startsWith('http://')) {
imgUrl = imgUrl.replaceAll('http://', 'https://');
}
imgUrl = imgUrl.contains('@') ? imgUrl.split('@').first : imgUrl;
final bool isEmote = imgUrl.contains('/emote/');
final bool isMall = imgUrl.contains('/mall/');

View File

@@ -6,15 +6,16 @@ import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.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';
import 'interactive_viewer_boundary.dart';
import 'interactive_viewer.dart' as custom;
@@ -67,7 +68,7 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
this.onClose,
});
final VoidCallback? onClose;
final ValueChanged? onClose;
final bool? setStatusBar;
@@ -111,15 +112,12 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
late final RxInt currentIndex = widget.initIndex.obs;
late List<bool> _thumbList;
late final int _quality = GStorage.previewQ;
@override
void initState() {
super.initState();
_thumbList = List.generate(widget.sources.length, (_) => true);
_pageController = PageController(initialPage: widget.initIndex);
_transformationController = custom.TransformationController();
@@ -127,10 +125,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
)..addListener(() {
_transformationController!.value =
_animation?.value ?? Matrix4.identity();
});
)..addListener(listener);
if (widget.setStatusBar != false) {
setStatusBar();
@@ -141,24 +136,36 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
}
}
void listener() {
_transformationController!.value = _animation?.value ?? Matrix4.identity();
}
SystemUiMode? mode;
setStatusBar() async {
if (Platform.isIOS || Platform.isAndroid) {
await StatusBarControl.setHidden(
true,
animation: StatusBarAnimation.FADE,
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.immersiveSticky,
);
}
if (Platform.isAndroid &&
(await DeviceInfoPlugin().androidInfo).version.sdkInt < 29) {
mode = SystemUiMode.manual;
}
}
@override
void dispose() async {
void dispose() {
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);
SystemChrome.setEnabledSystemUIMode(
mode ?? SystemUiMode.edgeToEdge,
overlays: SystemUiOverlay.values,
);
}
}
for (int index = 0; index < widget.sources.length; index++) {
@@ -253,13 +260,15 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
}
}
String _getActualUrl(int index) => _thumbList[index] && _quality != 100
? '${widget.sources[index].url}@${_quality}q.webp'.http2https
: widget.sources[index].url.http2https;
String _getActualUrl(int index) {
return _quality != 100
? Utils.thumbnailImgUrl(widget.sources[index].url)
: 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());
@@ -435,16 +444,30 @@ 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) {

View File

@@ -7,6 +7,7 @@ import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models/bangumi/info.dart' as bangumi;
import 'package:PiliPlus/models/video_detail_res.dart' as video;
import 'package:PiliPlus/pages/common/common_slide_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
@@ -14,8 +15,9 @@ 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 {
class ListSheetContent extends CommonSlidePage {
const ListSheetContent({
super.key,
this.index, // tab index
@@ -30,6 +32,7 @@ class ListSheetContent extends StatefulWidget {
this.showTitle,
this.isSupportReverse,
this.isReversed,
super.enableSlide,
});
final dynamic index;
@@ -49,7 +52,7 @@ class ListSheetContent extends StatefulWidget {
State<ListSheetContent> createState() => _ListSheetContentState();
}
class _ListSheetContentState extends State<ListSheetContent>
class _ListSheetContentState extends CommonSlidePageState<ListSheetContent>
with TickerProviderStateMixin {
late List<ItemScrollController> itemScrollController = [];
late int currentIndex = _currentIndex;
@@ -104,6 +107,12 @@ 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();
@@ -113,9 +122,7 @@ class _ListSheetContentState extends State<ListSheetContent>
vsync: this,
length: widget.season.sections.length,
initialIndex: _index,
)..addListener(() {
_indexStream?.add(_ctr?.index);
});
)..addListener(listener);
}
itemScrollController = _isList
? List.generate(
@@ -124,7 +131,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);
@@ -135,9 +142,22 @@ class _ListSheetContentState extends State<ListSheetContent>
}();
}
WidgetsBinding.instance.addPostFrameCallback((_) {
try {
itemScrollController[_index].jumpTo(index: currentIndex);
} catch (_) {}
if (enableSlide && GStorage.collapsibleVideoPage) {
if (mounted) {
setState(() {
_isInit = false;
});
WidgetsBinding.instance.addPostFrameCallback((_) {
try {
itemScrollController[_index].jumpTo(index: currentIndex);
} catch (_) {}
});
}
} else {
try {
itemScrollController[_index].jumpTo(index: currentIndex);
} catch (_) {}
}
});
}
@@ -147,7 +167,7 @@ class _ListSheetContentState extends State<ListSheetContent>
_favStream = null;
_indexStream?.close();
_indexStream = null;
_ctr?.removeListener(() {});
_ctr?.removeListener(listener);
_ctr?.dispose();
super.dispose();
}
@@ -275,144 +295,160 @@ 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(),
),
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();
},
if (enableSlide && GStorage.collapsibleVideoPage && _isInit) {
return CustomScrollView(
physics: const NeverScrollableScrollPhysics(),
);
}
return enableSlide
? Padding(
padding: EdgeInsets.only(top: padding),
child: buildPage,
)
: buildPage;
}
@override
Widget get buildPage => Material(
color: widget.showTitle == false
? Colors.transparent
: 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,
),
const Spacer(),
StreamBuilder(
stream: _indexStream?.stream,
initialData: _index,
builder: (context, snapshot) => mediumButton(
tooltip: reverse[snapshot.data] ? '顺序' : '倒序',
icon: !reverse[snapshot.data]
? MdiIcons.sortNumericAscending
: MdiIcons.sortNumericDescending,
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: () {
setState(() {
reverse[_ctr?.index ?? 0] = !reverse[_ctr?.index ?? 0];
});
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 (_) {}
},
),
),
if (widget.onClose != null)
mediumButton(
tooltip: '关闭',
icon: Icons.close,
onPressed: widget.onClose,
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(
Divider(
height: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
if (_isList)
TabBar(
controller: _ctr,
padding: const EdgeInsets.only(right: 60),
isScrollable: true,
@@ -422,23 +458,32 @@ class _ListSheetContentState extends State<ListSheetContent>
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),
),
),
)
: enableSlide
? slideList()
: buildList,
),
Expanded(
child: _isList
? TabBarView(
controller: _ctr,
children: List.generate(
widget.season.sections.length,
(index) => _buildBody(
index, widget.season.sections[index].episodes),
),
)
: _buildBody(null, episodes),
),
],
),
);
}
],
),
);
@override
Widget get buildList => Material(
color: Colors.transparent,
child: _buildBody(null, episodes),
);
Widget get _reverseButton => mediumButton(
tooltip: widget.isReversed == true ? '正序播放' : '倒序播放',
@@ -464,30 +509,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),
),
);
}

View File

@@ -11,6 +11,7 @@ Widget errorWidget({errMsg, callback}) => HttpError(
);
Widget scrollErrorWidget({errMsg, callback}) => CustomScrollView(
controller: ScrollController(),
slivers: [
HttpError(
errMsg: errMsg,

View File

@@ -1,7 +1,7 @@
import 'package:PiliPlus/utils/utils.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/global_data.dart';
import '../constants.dart';
class NetworkImgLayer extends StatelessWidget {
@@ -56,8 +56,6 @@ class NetworkImgLayer extends StatelessWidget {
}
Widget _buildImage(context) {
late final int defaultImgQuality = GlobalData().imgQuality;
bool thumbnail = true;
int? memCacheWidth, memCacheHeight;
if (callback?.call() == true || width <= height) {
memCacheWidth = width.cacheSize(context);
@@ -65,8 +63,7 @@ class NetworkImgLayer extends StatelessWidget {
memCacheHeight = height.cacheSize(context);
}
return CachedNetworkImage(
imageUrl:
'${src?.startsWith('//') == true ? 'https:$src' : src?.http2https}${type != 'emote' && type != 'cover' && thumbnail ? '@${quality ?? defaultImgQuality}q.webp' : ''}',
imageUrl: Utils.thumbnailImgUrl(src, quality),
width: width,
height: ignoreHeight == null || ignoreHeight == false ? height : null,
memCacheWidth: memCacheWidth,
@@ -77,17 +74,9 @@ class NetworkImgLayer extends StatelessWidget {
fadeOutDuration: fadeOutDuration ?? const Duration(milliseconds: 120),
fadeInDuration: fadeInDuration ?? const Duration(milliseconds: 120),
filterQuality: FilterQuality.low,
// errorWidget: (BuildContext context, String url, Object error) =>
// placeholder(context),
placeholder: (BuildContext context, String url) =>
getPlaceHolder?.call() ?? placeholder(context),
imageBuilder: imageBuilder,
// errorListener: (value) {
// thumbnail = false;
// if (context.mounted) {
// (context as Element).markNeedsBuild();
// }
// },
);
}

View File

@@ -5,21 +5,26 @@ Widget radioWidget<T>({
T? groupValue,
required ValueChanged onChanged,
required String title,
double? paddingStart,
EdgeInsetsGeometry? padding,
}) {
Widget child() => Row(
children: [
Radio(
value: value,
groupValue: groupValue,
onChanged: onChanged,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
Text(title),
],
);
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),
],
),
child: padding != null
? Padding(
padding: padding,
child: child(),
)
: child(),
);
}

View File

@@ -0,0 +1,218 @@
import 'package:PiliPlus/common/widgets/radio_widget.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
void autoWrapReportDialog(
BuildContext context,
Map<String, Map<int, String>> options,
Future<Map> Function(int, String?, bool) onSuccess,
) {
int? reasonType;
String? reasonDesc;
bool banUid = false;
late final key = GlobalKey<FormState>();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('举报'),
titlePadding: const EdgeInsets.only(left: 22, top: 16, right: 22),
contentPadding: const EdgeInsets.symmetric(vertical: 5),
actionsPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 10),
content: Builder(
builder: (context) {
return SingleChildScrollView(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
left: 22,
right: 22,
bottom: 5,
),
child: const Text('请选择举报的理由:'),
),
...options.entries.map<Widget>((title) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (title.key.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 22),
child: Text(
title.key,
style: TextStyle(
color:
Theme.of(context).colorScheme.outline),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Wrap(
children: title.value.entries.map((opt) {
return IntrinsicWidth(
child: radioWidget<int>(
value: opt.key,
groupValue: reasonType,
title: opt.value,
padding: const EdgeInsets.only(right: 10),
onChanged: (value) {
reasonType = value;
if (context.mounted) {
(context as Element?)?.markNeedsBuild();
}
},
),
);
}).toList(),
),
),
],
);
}),
if (reasonType == 0)
Padding(
padding: const EdgeInsets.only(
left: 22,
top: 5,
right: 22,
),
child: Form(
key: key,
child: TextFormField(
autofocus: true,
minLines: 4,
maxLines: 4,
initialValue: reasonDesc,
inputFormatters: [
LengthLimitingTextInputFormatter(60),
],
decoration: const InputDecoration(
labelText: '为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息',
border: OutlineInputBorder(),
contentPadding: EdgeInsets.all(10),
),
onChanged: (value) => reasonDesc = value,
validator: (value) {
if (value.isNullOrEmpty) {
return '理由不能为空';
}
return null;
},
),
),
),
GestureDetector(
onTap: () {
banUid = !banUid;
(context as Element?)?.markNeedsBuild();
},
child: Padding(
padding: const EdgeInsets.only(left: 18, top: 10),
child: Row(
children: [
Icon(
size: 22,
banUid
? Icons.check_box_outlined
: Icons.check_box_outline_blank,
color: banUid
? Theme.of(context).colorScheme.primary
: Theme.of(context)
.colorScheme
.onSurfaceVariant,
),
Text(
' 拉黑该用户',
style: TextStyle(
color: banUid
? Theme.of(context).colorScheme.primary
: null,
),
),
],
),
),
),
],
),
),
);
},
),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
if (reasonType == null ||
(reasonType == 0 && key.currentState?.validate() != true)) {
return;
}
SmartDialog.showLoading();
try {
final data = await onSuccess(reasonType!, reasonDesc, banUid);
SmartDialog.dismiss();
if (data['code'] == 0) {
Get.back();
SmartDialog.showToast('举报成功');
} else {
SmartDialog.showToast(data['message']);
}
} catch (e) {
SmartDialog.dismiss();
SmartDialog.showToast('提交失败:$e');
}
},
child: const Text('确定'),
),
],
);
},
);
}
class ReportOptions {
// from https://s1.hdslb.com/bfs/seed/jinkela/comment-h5/static/js/605.chunks.js
static Map<String, Map<int, String>> get commentReport => {
'违反法律法规': {9: '违法违规', 2: '色情', 10: '低俗', 12: '赌博诈骗', 23: '违法信息外链'},
'谣言类不实信息': {19: '涉政谣言', 22: '虚假不实信息', 20: '涉社会事件谣言'},
'侵犯个人权益': {7: '人身攻击', 15: '侵犯隐私'},
'有害社区环境': {
1: '垃圾广告',
4: '引战',
5: '剧透',
3: '刷屏',
8: '视频不相关',
18: '违规抽奖',
17: '青少年不良信息',
},
'其他': {0: '其他'},
};
static Map<String, Map<int, String>> get dynamicReport => {
'': {
4: '垃圾广告',
8: '引战',
1: '色情',
5: '人身攻击',
3: '违法信息',
9: '涉政谣言',
10: '涉社会事件谣言',
12: '虚假不实信息',
13: '违法信息外链',
0: '其他',
},
};
}

View File

@@ -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,

View 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],
);
}

View File

@@ -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(

View File

@@ -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(

View File

@@ -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,133 +41,138 @@ 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;
}
}
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 (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),
],
);
return Material(
color: Colors.transparent,
child: 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;
}
}
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,
),
const SizedBox(width: 10),
videoContent(context),
],
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 (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),
],
);
},
),
),
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) {

View File

@@ -6,7 +6,6 @@ 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';
@@ -48,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),
},

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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-\u9fa5UP]");
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);
}
}

View File

@@ -408,9 +408,6 @@ class Api {
// 黑名单
static const String blackLst = '/x/relation/blacks';
// 移除黑名单
static const String removeBlack = '/x/relation/modify';
// github 获取最新版
static const String latestApp =
'https://api.github.com/repos/bggRGjQaUbCoE/PiliPlus/releases';
@@ -419,7 +416,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
@@ -468,12 +465,9 @@ class Api {
static const String msgFeedAt = '/x/msgfeed/at';
//https://api.bilibili.com/x/msgfeed/like?platform=web&build=0&mobi_app=web
static const String msgFeedLike = '/x/msgfeed/like';
//https://message.bilibili.com/x/sys-msg/query_user_notify?csrf=xxxx&csrf=xxxx&page_size=20&build=0&mobi_app=web
static const String msgSysUserNotify =
'${HttpString.messageBaseUrl}/x/sys-msg/query_user_notify';
//https://message.bilibili.com/x/sys-msg/query_unified_notify?csrf=xxxx&csrf=xxxx&page_size=10&build=0&mobi_app=web
static const String msgSysUnifiedNotify =
'${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify';
//https://message.bilibili.com/x/sys-msg/query_notify_list?page_size=20&cursor=xxx
static const String msgSysNotify =
'${HttpString.messageBaseUrl}/x/sys-msg/query_notify_list';
// 系统信息光标更新(已读标记)
//https://message.bilibili.com/x/sys-msg/update_cursor?csrf=xxxx&csrf=xxxx&cursor=1705288500000000000&has_up=0&build=0&mobi_app=web
@@ -619,7 +613,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 =
@@ -662,7 +656,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';
@@ -718,4 +712,10 @@ 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';
static const String noteList = '/x/note/publish/list/archive';
}

View File

@@ -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,

View File

@@ -19,31 +19,4 @@ class BlackHttp {
return LoadingState.error(res.data['message']);
}
}
// 移除黑名单
static Future removeBlack({required int fid}) async {
var res = await Request().post(
Api.removeBlack,
queryParameters: {
'act': 6,
'csrf': await Request.getCsrf(),
'fid': fid,
'jsonp': 'jsonp',
're_src': 116,
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': [],
'msg': '操作成功',
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
}

View File

@@ -9,37 +9,4 @@ class HttpString {
static const String dynamicShareBaseUrl = 'https://t.bilibili.com';
static const String spaceBaseUrl = 'https://space.bilibili.com';
static const String sponsorBlockBaseUrl = 'https://www.bsbsb.top';
static const List<int> validateStatusCodes = [
302,
304,
307,
400,
401,
403,
404,
405,
409,
412,
500,
503,
504,
509,
616,
617,
625,
626,
628,
629,
632,
643,
650,
652,
658,
662,
688,
689,
701,
799,
8888
];
}

View File

@@ -1,32 +1,47 @@
import 'package:PiliPlus/grpc/dm/v1/dm.pb.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/grpc/grpc_repo.dart';
import 'package:dio/dio.dart';
import 'index.dart';
class DanmakaHttp {
class DanmakuHttp {
// 获取视频弹幕
static Future queryDanmaku({
required int cid,
required int segmentIndex,
required bool mergeDanmaku,
int queryCount = 1,
}) 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) {
return DmSegMobileReply();
final response =
await GrpcRepo.dmSegMobile(cid: cid, segmentIndex: segmentIndex);
if (!response['status']) {
if (queryCount >= 3) {
return DmSegMobileReply();
} else {
await Future.delayed(const Duration(seconds: 1));
return await queryDanmaku(
cid: cid,
segmentIndex: segmentIndex,
mergeDanmaku: mergeDanmaku,
queryCount: ++queryCount,
);
}
}
DmSegMobileReply data = DmSegMobileReply.fromBuffer(response.data);
if (mergeDanmaku) {
data.elems.unique((item) => item.content);
DmSegMobileReply data = response['data'];
if (mergeDanmaku && data.elems.isNotEmpty) {
final Map counts = <String, int>{};
data.elems.retainWhere((item) {
int? count = counts[item.content];
counts[item.content] = count != null ? count + 1 : 1;
return count == null;
});
for (DanmakuElem item in data.elems) {
item.clearAttr();
final count = counts[item.content];
if (count != 1) {
item.attr = count;
}
}
}
return data;
}

View File

@@ -1,4 +1,6 @@
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:dio/dio.dart';
import '../models/dynamics/result.dart';
import '../models/dynamics/up.dart';
@@ -24,6 +26,15 @@ class DynamicsHttp {
if (res.data['code'] == 0) {
try {
DynamicsDataModel data = DynamicsDataModel.fromJson(res.data['data']);
if (GStorage.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,17 +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,
if (id != null) 'id': id,
if (rid != null) 'rid': rid,
if (type != null) 'type': type,
'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 {

View File

@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:PiliPlus/models/dynamics/article_content_model.dart';
import 'package:PiliPlus/utils/url_utils.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:html/dom.dart' as dom;
@@ -48,10 +49,11 @@ class HtmlHttp {
String opusContent =
opusDetail.querySelector('.opus-module-content')!.innerHtml;
String? test;
test = opusDetail
.querySelector('.horizontal-scroll-album__pic__img')
?.innerHtml ??
'';
try {
test = opusDetail
.querySelector('.horizontal-scroll-album__pic__img')!
.innerHtml;
} catch (_) {}
String commentId = opusDetail
.querySelector('.bili-comment-container')!
@@ -64,7 +66,7 @@ class HtmlHttp {
'avatar': avatar,
'uname': uname,
'updateTime': updateTime,
'content': test + opusContent,
'content': (test ?? '') + opusContent,
'commentId': int.parse(commentId)
};
} catch (err) {
@@ -74,6 +76,12 @@ class HtmlHttp {
// read
static Future reqReadHtml(id, dynamicType) async {
String? redirectUrl = await UrlUtils.parseRedirectUrl(
'https://www.bilibili.com/$dynamicType/$id/');
if (redirectUrl != null) {
return await reqHtml(redirectUrl.split('/').last, dynamicType);
}
var response = await Request().get(
"https://www.bilibili.com/$dynamicType/$id/",
extra: {'ua': 'pc'},

View File

@@ -4,10 +4,13 @@ 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';
@@ -18,6 +21,9 @@ import 'interceptor.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;
@@ -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,33 +154,36 @@ 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 = 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;
// 日志拦截器 输出请求、响应内容
if (BuildConfig.isDebug) {
@@ -186,8 +196,7 @@ class Request {
dio.transformer = BackgroundTransformer();
dio.options.validateStatus = (int? status) {
return status! >= 200 && status < 300 ||
HttpString.validateStatusCodes.contains(status);
return status! >= 200 && status < 300;
};
}
@@ -284,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;
}
}

View File

@@ -19,7 +19,6 @@ class ApiInterceptor extends Interceptor {
Api.bangumiInfo,
Api.liveRoomInfo,
Api.onlineTotal,
Api.webDanmaku,
Api.dynamicDetail,
Api.aiConclusion,
Api.getSeasonDetailApi,
@@ -44,6 +43,11 @@ class ApiInterceptor extends Interceptor {
}
}
// app端不需要cookie
// if (options.uri.host == 'app.bilibili.com') {
// options.headers.remove('cookie');
// }
if (options.extra['clearCookie'] == true) {
onRemoveCookie();
} else if (MineController.anonymity.value) {
@@ -103,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);
@@ -135,5 +139,5 @@ class ApiInterceptor extends Interceptor {
}
extension _ConnectivityResultExt on ConnectivityResult {
String get title => ['蓝牙', 'Wi-Fi', '局域', '流量', '', '代理', '其他'][index];
String get title => const ['蓝牙', 'Wi-Fi', '局域', '流量', '', '代理', '其他'][index];
}

View File

@@ -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'),

View File

@@ -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',
@@ -364,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,
@@ -402,10 +386,13 @@ class MemberHttp {
}
// 用户动态
static Future<LoadingState> memberDynamic({String? offset, int? mid}) async {
static Future<LoadingState> memberDynamic({
String? offset,
int? mid,
}) 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',
@@ -421,7 +408,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 (GStorage.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: '风控校验失败,请检查登录状态',
@@ -598,8 +593,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,
@@ -615,24 +610,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,
@@ -648,16 +636,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']);
}
}
@@ -729,7 +712,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'],

View File

@@ -1,8 +1,14 @@
import 'dart:math';
import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/msg/msgfeed_at_me.dart';
import 'package:PiliPlus/models/msg/msgfeed_like_me.dart';
import 'package:PiliPlus/models/msg/msgfeed_reply_me.dart';
import 'package:PiliPlus/models/msg/msgfeed_sys_msg.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';
@@ -11,100 +17,70 @@ import 'api.dart';
import 'init.dart';
class MsgHttp {
static Future msgFeedReplyMe({int cursor = -1, int cursorTime = -1}) async {
static Future<LoadingState> msgFeedReplyMe(
{int cursor = -1, int cursorTime = -1}) async {
var res = await Request().get(Api.msgFeedReply, queryParameters: {
'id': cursor == -1 ? null : cursor,
'reply_time': cursorTime == -1 ? null : cursorTime,
'platform': 'android',
'mobi_app': 'android',
'build': '8350200',
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
MsgFeedReplyMe data = MsgFeedReplyMe.fromJson(res.data['data']);
return LoadingState.success(data);
} else {
return {
'status': false,
'date': [],
'msg': res.data['message'],
};
return LoadingState.error(res.data['message']);
}
}
static Future msgFeedAtMe({int cursor = -1, int cursorTime = -1}) async {
static Future<LoadingState> msgFeedAtMe(
{int cursor = -1, int cursorTime = -1}) async {
var res = await Request().get(Api.msgFeedAt, queryParameters: {
'id': cursor == -1 ? null : cursor,
'at_time': cursorTime == -1 ? null : cursorTime,
'platform': 'android',
'mobi_app': 'android',
'build': '8350200',
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
MsgFeedAtMe data = MsgFeedAtMe.fromJson(res.data['data']);
return LoadingState.success(data);
} else {
return {
'status': false,
'date': [],
'msg': res.data['message'],
};
return LoadingState.error(res.data['message']);
}
}
static Future msgFeedLikeMe({int cursor = -1, int cursorTime = -1}) async {
static Future<LoadingState> msgFeedLikeMe(
{int cursor = -1, int cursorTime = -1}) async {
var res = await Request().get(Api.msgFeedLike, queryParameters: {
'id': cursor == -1 ? null : cursor,
'like_time': cursorTime == -1 ? null : cursorTime,
'platform': 'android',
'mobi_app': 'android',
'build': '8350200',
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
MsgFeedLikeMe data = MsgFeedLikeMe.fromJson(res.data['data']);
return LoadingState.success(data);
} else {
return {
'status': false,
'date': [],
'msg': res.data['message'],
};
return LoadingState.error(res.data['message']);
}
}
static Future msgFeedSysUserNotify() async {
String csrf = await Request.getCsrf();
var res = await Request().get(Api.msgSysUserNotify, queryParameters: {
'csrf': csrf,
'page_size': 20,
static Future<LoadingState> msgFeedNotify(
{int cursor = -1, int pageSize = 20}) async {
var res = await Request().get(Api.msgSysNotify, queryParameters: {
'cursor': cursor == -1 ? null : cursor,
'page_size': pageSize,
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
List<SystemNotifyList>? list = (res.data['data'] as List?)
?.map((e) => SystemNotifyList.fromJson(e))
.toList();
return LoadingState.success(list);
} else {
return {
'status': false,
'date': [],
'msg': res.data['message'],
};
}
}
static Future msgFeedSysUnifiedNotify() async {
String csrf = await Request.getCsrf();
var res = await Request().get(Api.msgSysUnifiedNotify, queryParameters: {
'csrf': csrf,
'page_size': 10,
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'date': [],
'msg': res.data['message'],
};
return LoadingState.error(res.data['message']);
}
}
@@ -144,11 +120,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 +149,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 +171,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 +233,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 +260,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 +286,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 +309,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 +362,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 +397,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 +450,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 +483,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 +534,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 +553,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();
}
}

View File

@@ -1,7 +1,10 @@
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';
import '../models/video/reply/data.dart';
@@ -12,6 +15,9 @@ 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,
@@ -19,7 +25,8 @@ class ReplyHttp {
required int type,
required int page,
int sort = 1,
required String banWordForReply,
required bool antiGoodsReply,
bool? enableFilter,
}) async {
var res = !isLogin
? await Request().get(
@@ -46,18 +53,16 @@ class ReplyHttp {
);
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;
@@ -67,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;
@@ -91,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;
@@ -127,14 +181,46 @@ 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,
required String banWordForReply,
required bool antiGoodsReply,
bool? isCheck,
bool? filterBanWord,
}) async {
var res = await Request().get(
Api.replyReplyList,
@@ -150,11 +236,15 @@ class ReplyHttp {
);
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);
@@ -173,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,
@@ -184,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);
@@ -203,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,
@@ -214,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);

View File

@@ -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;

View File

@@ -180,8 +180,10 @@ class UserHttp {
});
}
List<HotVideoItemModel> list = [];
for (var i in res.data['data']['list']) {
list.add(HotVideoItemModel.fromJson(i));
if (res.data['data']?['list'] != null) {
for (var i in res.data['data']['list']) {
list.add(HotVideoItemModel.fromJson(i));
}
}
return LoadingState.success({
'list': list,
@@ -356,7 +358,7 @@ class UserHttp {
}
// // 相互关系查询
// static Future relationSearch(int mid) async {
// Map params = await WbiSign().makSign({
// Map params = await WbiSign.makSign({
// 'mid': mid,
// 'token': '',
// 'platform': 'web',

View File

@@ -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,
@@ -457,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: {
@@ -465,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'),
},
),
);
@@ -491,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) {
@@ -822,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 {
@@ -952,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,
@@ -1096,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));
@@ -1110,4 +1134,27 @@ class VideoHttp {
return LoadingState.error(res.data['message']);
}
}
static Future<LoadingState> getVideoNoteList({
dynamic oid,
dynamic uperMid,
required int page,
}) async {
var res = await Request().get(
Api.noteList,
queryParameters: {
'csrf': await Request.getCsrf(),
'oid': oid,
'oid_type': 0,
'pn': page,
'ps': 10,
if (uperMid != null) 'uper_mid': uperMid,
},
);
if (res.data['code'] == 0) {
return LoadingState.success(res.data['data']);
} else {
return LoadingState.error(res.data['message']);
}
}
}

View File

@@ -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(
@@ -282,14 +284,26 @@ class MyApp extends StatelessWidget {
),
),
);
if (isDark && GStorage.isPureBlackTheme) {
themeData = Utils.darkenTheme(themeData);
}
return themeData;
}
}
class _CustomHttpOverrides extends HttpOverrides {
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;
}
}

View File

@@ -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'];
}
}

View File

@@ -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'];

View 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'];
}
}

View File

@@ -1,5 +1,7 @@
import 'dart:ui';
import 'package:PiliPlus/models/common/sponsor_block/action_type.dart';
enum SegmentType {
sponsor,
selfpromo,
@@ -13,6 +15,74 @@ enum SegmentType {
exclusive_access
}
// List<SegmentType> _actionType2SegmentType(ActionType actionType) {
// return switch (actionType) {
// ActionType.skip => [
// SegmentType.sponsor,
// SegmentType.selfpromo,
// SegmentType.interaction,
// SegmentType.intro,
// SegmentType.outro,
// SegmentType.preview,
// SegmentType.filler,
// ],
// ActionType.mute => [
// SegmentType.sponsor,
// SegmentType.selfpromo,
// SegmentType.interaction,
// SegmentType.intro,
// SegmentType.outro,
// SegmentType.preview,
// SegmentType.music_offtopic,
// SegmentType.filler,
// ],
// ActionType.full => [
// SegmentType.sponsor,
// SegmentType.selfpromo,
// SegmentType.exclusive_access,
// ],
// ActionType.poi => [
// SegmentType.poi_highlight,
// ],
// };
// }
List<ActionType> segmentType2ActionType(SegmentType segmentType) {
return switch (segmentType) {
SegmentType.sponsor => [ActionType.skip, ActionType.mute, ActionType.full],
SegmentType.selfpromo => [
ActionType.skip,
ActionType.mute,
ActionType.full
],
SegmentType.interaction => [
ActionType.skip,
ActionType.mute,
],
SegmentType.intro => [
ActionType.skip,
ActionType.mute,
],
SegmentType.outro => [
ActionType.skip,
ActionType.mute,
],
SegmentType.preview => [
ActionType.skip,
ActionType.mute,
],
SegmentType.music_offtopic => [
ActionType.skip,
],
SegmentType.poi_highlight => [ActionType.poi],
SegmentType.filler => [
ActionType.skip,
ActionType.mute,
],
SegmentType.exclusive_access => [ActionType.full],
};
}
extension SegmentTypeExt on SegmentType {
/// from https://github.com/hanydd/BilibiliSponsorBlock/blob/master/public/_locales/zh_CN/messages.json
String get title => [

View File

@@ -14,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'];
@@ -35,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({
@@ -364,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'];
}
}
@@ -409,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'];
}
}
@@ -557,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();
}
}
@@ -580,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 =
@@ -599,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'];
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -9,8 +9,8 @@ class FollowDataModel {
FollowDataModel.fromJson(Map<String, dynamic> json) {
total = json['total'] ?? 0;
list = json['list']
.map<FollowItemModel>((e) => FollowItemModel.fromJson(e))
list = (json['list'] as List?)
?.map<FollowItemModel>((e) => FollowItemModel.fromJson(e))
.toList();
}
}

View File

@@ -17,9 +17,9 @@ class LatestDataModel {
url = json['url'];
tagName = json['tag_name'];
createdAt = json['created_at'];
assets = json['assets'] != null
? json['assets'].map<AssetItem>((e) => AssetItem.fromJson(e)).toList()
: [];
assets = (json['assets'] as List?)
?.map<AssetItem>((e) => AssetItem.fromJson(e))
.toList();
body = json['body'];
}
}

View File

@@ -45,9 +45,12 @@ class Playurl {
Playurl.fromJson(Map<String, dynamic> json) {
cid = json['cid'];
gQnDesc =
json['g_qn_desc'].map<GQnDesc>((e) => GQnDesc.fromJson(e)).toList();
stream = json['stream'].map<Streams>((e) => Streams.fromJson(e)).toList();
gQnDesc = (json['g_qn_desc'] as List?)
?.map<GQnDesc>((e) => GQnDesc.fromJson(e))
.toList();
stream = (json['stream'] as List?)
?.map<Streams>((e) => Streams.fromJson(e))
.toList();
}
}
@@ -83,8 +86,9 @@ class Streams {
Streams.fromJson(Map<String, dynamic> json) {
protocolName = json['protocol_name'];
format =
json['format'].map<FormatItem>((e) => FormatItem.fromJson(e)).toList();
format = (json['format'] as List?)
?.map<FormatItem>((e) => FormatItem.fromJson(e))
.toList();
}
}
@@ -99,7 +103,9 @@ class FormatItem {
FormatItem.fromJson(Map<String, dynamic> json) {
formatName = json['format_name'];
codec = json['codec'].map<CodecItem>((e) => CodecItem.fromJson(e)).toList();
codec = (json['codec'] as List?)
?.map<CodecItem>((e) => CodecItem.fromJson(e))
.toList();
}
}
@@ -129,8 +135,8 @@ class CodecItem {
currentQn = json['current_qn'];
acceptQn = json['accept_qn'];
baseUrl = json['base_url'];
urlInfo = json['url_info']
.map<UrlInfoItem>((e) => UrlInfoItem.fromJson(e))
urlInfo = (json['url_info'] as List?)
?.map<UrlInfoItem>((e) => UrlInfoItem.fromJson(e))
.toList();
hdrQn = json['hdr_n'];
dolbyType = json['dolby_type'];

View File

@@ -27,8 +27,8 @@ class ArchiveListModel {
? Map.from(json['tlist']).map((k, v) =>
MapEntry<String, TListItemModel>(k, TListItemModel.fromJson(v)))
: {};
vlist = json['vlist']
.map<VListItemModel>((e) => VListItemModel.fromJson(e))
vlist = (json['vlist'] as List?)
?.map<VListItemModel>((e) => VListItemModel.fromJson(e))
.toList();
}
}

View File

@@ -9,11 +9,9 @@ class MemberSeasonsDataModel {
MemberSeasonsDataModel.fromJson(Map<String, dynamic> json) {
page = json['page'];
seasonsList = json['seasons_list'] != null
? json['seasons_list']
.map<MemberSeasonsList>((e) => MemberSeasonsList.fromJson(e))
.toList()
: [];
seasonsList = (json['seasons_list'] as List?)
?.map<MemberSeasonsList>((e) => MemberSeasonsList.fromJson(e))
.toList();
}
}
@@ -31,11 +29,9 @@ class MemberSeasonsList {
Map? page;
MemberSeasonsList.fromJson(Map<String, dynamic> json) {
archives = json['archives'] != null
? json['archives']
.map<MemberArchiveItem>((e) => MemberArchiveItem.fromJson(e))
.toList()
: [];
archives = (json['archives'] as List?)
?.map<MemberArchiveItem>((e) => MemberArchiveItem.fromJson(e))
.toList();
meta = MamberMeta.fromJson(json['meta']);
page = json['page'];
}

View File

@@ -5,8 +5,7 @@ class MsgFeedLikeMe {
MsgFeedLikeMe({latest, total});
MsgFeedLikeMe.fromJson(Map<String, dynamic> json) {
latest =
json['latest'] != null ? Latest.fromJson(json['latest']) : null;
latest = json['latest'] != null ? Latest.fromJson(json['latest']) : null;
total = json['total'] != null ? Total.fromJson(json['total']) : null;
}
@@ -50,13 +49,7 @@ class LikeMeItems {
int? likeTime;
int? noticeState;
LikeMeItems(
{id,
users,
item,
counts,
likeTime,
noticeState});
LikeMeItems({id, users, item, counts, likeTime, noticeState});
LikeMeItems.fromJson(Map<String, dynamic> json) {
id = json['id'];
@@ -92,13 +85,7 @@ class Users {
String? midLink;
bool? follow;
Users(
{mid,
fans,
nickname,
avatar,
midLink,
follow});
Users({mid, fans, nickname, avatar, midLink, follow});
Users.fromJson(Map<String, dynamic> json) {
mid = json['mid'];
@@ -139,19 +126,19 @@ class Item {
Item(
{itemId,
pid,
type,
business,
businessId,
replyBusinessId,
likeBusinessId,
title,
desc,
image,
uri,
detailName,
nativeUri,
ctime});
pid,
type,
business,
businessId,
replyBusinessId,
likeBusinessId,
title,
desc,
image,
uri,
detailName,
nativeUri,
ctime});
Item.fromJson(Map<String, dynamic> json) {
itemId = json['item_id'];
@@ -197,8 +184,7 @@ class Total {
Total({cursor, items});
Total.fromJson(Map<String, dynamic> json) {
cursor =
json['cursor'] != null ? Cursor.fromJson(json['cursor']) : null;
cursor = json['cursor'] != null ? Cursor.fromJson(json['cursor']) : null;
if (json['items'] != null) {
items = <LikeMeItems>[];
json['items'].forEach((v) {
@@ -235,4 +221,4 @@ class Cursor {
data['time'] = time;
return data;
}
}
}

View File

@@ -1,27 +1,3 @@
class MsgFeedSysMsg {
List<SystemNotifyList>? systemNotifyList;
MsgFeedSysMsg({this.systemNotifyList});
MsgFeedSysMsg.fromJson(Map<String, dynamic> json) {
if (json['system_notify_list'] != null) {
systemNotifyList = <SystemNotifyList>[];
json['system_notify_list'].forEach((v) {
systemNotifyList!.add(SystemNotifyList.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (systemNotifyList != null) {
data['system_notify_list'] =
systemNotifyList!.map((v) => v.toJson()).toList();
}
return data;
}
}
class SystemNotifyList {
int? id;
int? cursor;

View File

@@ -12,7 +12,7 @@ class SessionDataModel {
int? hasMore;
SessionDataModel.fromJson(Map<String, dynamic> json) {
sessionList = json['session_list']
sessionList = (json['session_list'] as List?)
?.map<SessionList>((e) => SessionList.fromJson(e))
.toList();
hasMore = json['has_more'];
@@ -175,8 +175,8 @@ class SessionMsgDataModel {
List<dynamic>? eInfos;
SessionMsgDataModel.fromJson(Map<String, dynamic> json) {
messages = json['messages']
.map<MessageItem>((e) => MessageItem.fromJson(e))
messages = (json['messages'] as List?)
?.map<MessageItem>((e) => MessageItem.fromJson(e))
.toList();
hasMore = json['has_more'];
minSeqno = json['min_seqno'];

View File

@@ -12,8 +12,8 @@ class HotSearchModel {
List<HotSearchItem>? list;
HotSearchModel.fromJson(Map<String, dynamic> json) {
list = json['list']
.map<HotSearchItem>((e) => HotSearchItem.fromJson(e))
list = (json['list'] as List?)
?.map<HotSearchItem>((e) => HotSearchItem.fromJson(e))
.toList();
}
}

View File

@@ -12,9 +12,9 @@ class SearchVideoModel {
SearchVideoModel.fromJson(Map<String, dynamic> json) {
numResults = (json['numResults'] as num?)?.toInt();
list = json['result']
list = (json['result'] as List?)
?.where((e) => e['available'] == true)
?.map<SearchVideoItemModel>((e) => SearchVideoItemModel.fromJson(e))
.map<SearchVideoItemModel>((e) => SearchVideoItemModel.fromJson(e))
.toList();
}
}
@@ -161,7 +161,7 @@ class SearchUserModel {
SearchUserModel.fromJson(Map<String, dynamic> json) {
numResults = (json['numResults'] as num?)?.toInt();
list = json['result']
list = (json['result'] as List?)
?.map<SearchUserItemModel>((e) => SearchUserItemModel.fromJson(e))
.toList();
}
@@ -316,12 +316,10 @@ class SearchMBangumiModel {
SearchMBangumiModel.fromJson(Map<String, dynamic> json) {
numResults = (json['numResults'] as num?)?.toInt();
list = json['result'] != null
? json['result']
.map<SearchMBangumiItemModel>(
(e) => SearchMBangumiItemModel.fromJson(e))
.toList()
: [];
list = (json['result'] as List?)
?.map<SearchMBangumiItemModel>(
(e) => SearchMBangumiItemModel.fromJson(e))
.toList();
}
}
@@ -420,12 +418,9 @@ class SearchArticleModel {
SearchArticleModel.fromJson(Map<String, dynamic> json) {
numResults = (json['numResults'] as num?)?.toInt();
list = json['result'] != null
? json['result']
.map<SearchArticleItemModel>(
(e) => SearchArticleItemModel.fromJson(e))
.toList()
: [];
list = (json['result'] as List?)
?.map<SearchArticleItemModel>((e) => SearchArticleItemModel.fromJson(e))
.toList();
}
}

View File

@@ -11,8 +11,8 @@ class SearchSuggestModel {
String? term;
SearchSuggestModel.fromJson(Map<String, dynamic> json) {
tag = json['tag']
.map<SearchSuggestItem>(
tag = (json['tag'] as List?)
?.map<SearchSuggestItem>(
(e) => SearchSuggestItem.fromJson(e, json['term']))
.toList();
}

View File

@@ -8,8 +8,8 @@ class BlackListDataModel {
int? total;
BlackListDataModel.fromJson(Map<String, dynamic> json) {
list = json['list']
.map<BlackListItem>((e) => BlackListItem.fromJson(e))
list = (json['list'] as List?)
?.map<BlackListItem>((e) => BlackListItem.fromJson(e))
.toList();
total = json['total'];
}

View File

@@ -15,11 +15,9 @@ class FavDetailData {
FavDetailData.fromJson(Map<String, dynamic> json) {
info =
json['info'] == null ? null : FavFolderItemData.fromJson(json['info']);
medias = json['medias'] != null
? json['medias']
.map<FavDetailItemData>((e) => FavDetailItemData.fromJson(e))
.toList()
: [];
medias = (json['medias'] as List?)
?.map<FavDetailItemData>((e) => FavDetailItemData.fromJson(e))
.toList();
hasMore = json['has_more'];
}
}

View File

@@ -11,11 +11,10 @@ class FavFolderData {
FavFolderData.fromJson(Map<String, dynamic> json) {
count = json['count'];
list = json['list'] != null
? json['list']
.map<FavFolderItemData>((e) => FavFolderItemData.fromJson(e))
.toList()
: [FavFolderItemData()];
list = (json['list'] as List?)
?.map<FavFolderItemData>((e) => FavFolderItemData.fromJson(e))
.toList() ??
[FavFolderItemData()];
hasMore = json['has_more'];
}
}

View File

@@ -13,12 +13,12 @@ class HistoryData {
HistoryData.fromJson(Map<String, dynamic> json) {
cursor = json['cursor'] != null ? Cursor.fromJson(json['cursor']) : null;
tab = json['tab'] != null
? json['tab'].map<HisTabItem>((e) => HisTabItem.fromJson(e)).toList()
: [];
list = json['list'] != null
? json['list'].map<HisListItem>((e) => HisListItem.fromJson(e)).toList()
: [];
tab = (json['tab'] as List?)
?.map<HisTabItem>((e) => HisTabItem.fromJson(e))
.toList();
list = (json['list'] as List?)
?.map<HisListItem>((e) => HisListItem.fromJson(e))
.toList();
page = json['page'];
}
}

View File

@@ -237,7 +237,7 @@ class Emote {
class EmoteMeta {
int? size;
List<String>? suggest;
List? suggest;
String? alias;
String? gifUrl;
@@ -245,9 +245,7 @@ class EmoteMeta {
EmoteMeta.fromJson(Map<String, dynamic> json) {
size = json['size'];
suggest = json['suggest'] == null
? null
: List<String>.from(json['suggest'].map((x) => x));
suggest = json['suggest'];
alias = json['alias'];
gifUrl = json['gif_url'];
}

View File

@@ -10,11 +10,9 @@ class SubFolderModelData {
factory SubFolderModelData.fromJson(Map<String, dynamic> json) {
return SubFolderModelData(
count: json['count'],
list: json['list'] != null
? (json['list'] as List)
.map<SubFolderItemData>((i) => SubFolderItemData.fromJson(i))
.toList()
: null,
list: (json['list'] as List?)
?.map<SubFolderItemData>((i) => SubFolderItemData.fromJson(i))
.toList(),
);
}
}

View File

@@ -39,8 +39,8 @@ class ModelResult {
ModelResult.fromJson(Map<String, dynamic> json) {
resultType = json['result_type'];
summary = json['summary'];
outline = json['outline']
.map<OutlineItem>((e) => OutlineItem.fromJson(e))
outline = (json['outline'] as List?)
?.map<OutlineItem>((e) => OutlineItem.fromJson(e))
.toList();
}
}
@@ -56,8 +56,8 @@ class OutlineItem {
OutlineItem.fromJson(Map<String, dynamic> json) {
title = json['title'];
partOutline = json['part_outline']
.map<PartOutline>((e) => PartOutline.fromJson(e))
partOutline = (json['part_outline'] as List?)
?.map<PartOutline>((e) => PartOutline.fromJson(e))
.toList();
}
}

View File

@@ -84,10 +84,7 @@ class MediaVideoItemModel {
likeState: json["like_state"],
favState: json["fav_state"],
page: json["page"],
// json["pages"] 可能为null
pages: json["pages"] == null
? []
: List<Page>.from(json["pages"].map((x) => Page.fromJson(x))),
pages: (json["pages"] as List?)?.map((x) => Page.fromJson(x)).toList(),
title: json["title"],
type: json["type"],
upper: Upper.fromJson(json["upper"]),
@@ -149,7 +146,7 @@ class Page {
duration: json["duration"],
link: json["link"],
page: json["page"],
metas: List<Meta>.from(json["metas"].map((x) => Meta.fromJson(x))),
metas: (json["metas"] as List?)?.map((x) => Meta.fromJson(x)).toList(),
from: json["from"],
dimension: Dimension.fromJson(json["dimension"]),
);

View File

@@ -49,17 +49,16 @@ class PlayUrlModel {
timeLength = json['timelength'];
acceptFormat = json['accept_format'];
acceptDesc = json['accept_description'];
acceptQuality = json['accept_quality'].map<int>((e) => e as int).toList();
acceptQuality =
(json['accept_quality'] as List?)?.map<int>((e) => e as int).toList();
videoCodecid = json['video_codecid'];
seekParam = json['seek_param'];
seekType = json['seek_type'];
dash = json['dash'] != null ? Dash.fromJson(json['dash']) : null;
durl = json['durl']?.map<Durl>((e) => Durl.fromJson(e)).toList();
supportFormats = json['support_formats'] != null
? json['support_formats']
.map<FormatItem>((e) => FormatItem.fromJson(e))
.toList()
: [];
durl = (json['durl'] as List?)?.map<Durl>((e) => Durl.fromJson(e)).toList();
supportFormats = (json['support_formats'] as List?)
?.map<FormatItem>((e) => FormatItem.fromJson(e))
.toList();
lastPlayTime = json['last_play_time'];
lastPlayCid = json['last_play_cid'];
}
@@ -85,10 +84,12 @@ class Dash {
Dash.fromJson(Map<String, dynamic> json) {
duration = json['duration'];
minBufferTime = json['minBufferTime'];
video = json['video'].map<VideoItem>((e) => VideoItem.fromJson(e)).toList();
audio = json['audio'] != null
? json['audio'].map<AudioItem>((e) => AudioItem.fromJson(e)).toList()
: [];
video = (json['video'] as List?)
?.map<VideoItem>((e) => VideoItem.fromJson(e))
.toList();
audio = (json['audio'] as List?)
?.map<AudioItem>((e) => AudioItem.fromJson(e))
.toList();
dolby = json['dolby'] != null ? Dolby.fromJson(json['dolby']) : null;
flac = json['flac'] != null ? Flac.fromJson(json['flac']) : null;
}
@@ -128,6 +129,14 @@ class Durl {
}
}
final _ipRegExp = RegExp(r'^https?://\d{1,3}\.\d{1,3}');
bool _isMCDNorPCDN(String url) {
return url.contains("szbdyd.com") ||
url.contains(".mcdn.bilivideo") ||
_ipRegExp.hasMatch(url);
}
class VideoItem {
VideoItem({
this.id,
@@ -164,8 +173,10 @@ class VideoItem {
VideoItem.fromJson(Map<String, dynamic> json) {
id = json['id'];
baseUrl = json['baseUrl'];
backupUrl =
json['backupUrl'] != null ? json['backupUrl'].toList().first : '';
var backupUrls = json['backupUrl']?.toList() ?? [];
backupUrl = backupUrls.isNotEmpty
? backupUrls.firstWhere((i) => !_isMCDNorPCDN(i), orElse: () => backupUrls.first)
: '';
bandWidth = json['bandWidth'];
mimeType = json['mime_type'];
codecs = json['codecs'];
@@ -235,8 +246,10 @@ class AudioItem {
AudioItem.fromJson(Map<String, dynamic> json) {
id = json['id'];
baseUrl = json['baseUrl'];
backupUrl =
json['backupUrl'] != null ? json['backupUrl'].toList().first : '';
var backupUrls = json['backupUrl']?.toList() ?? [];
backupUrl = backupUrls.isNotEmpty
? backupUrls.firstWhere((i) => !_isMCDNorPCDN(i), orElse: () => backupUrls.first)
: '';
bandWidth = json['bandWidth'];
mimeType = json['mime_type'];
codecs = json['codecs'];
@@ -288,9 +301,9 @@ class Dolby {
Dolby.fromJson(Map<String, dynamic> json) {
type = json['type'];
audio = json['audio'] != null
? json['audio'].map<AudioItem>((e) => AudioItem.fromJson(e)).toList()
: [];
audio = (json['audio'] as List?)
?.map<AudioItem>((e) => AudioItem.fromJson(e))
.toList();
}
}

View File

@@ -8,7 +8,6 @@ class ReplyContent {
this.pictures, // {}
this.vote,
this.richText,
this.isText,
this.topicsMeta,
});
@@ -20,27 +19,19 @@ class ReplyContent {
List? pictures;
Map? vote;
Map? richText;
bool? isText;
Map? topicsMeta;
ReplyContent.fromJson(Map<String, dynamic> json) {
message = json['message']
.replaceAll('&gt;', '>')
.replaceAll('&#34;', '"')
.replaceAll('&#39;', "'");
message = json['message'];
atNameToMid = json['at_name_to_mid'] ?? {};
members = json['members'] != null
? json['members']
.map<MemberItemModel>((e) => MemberItemModel.fromJson(e))
.toList()
: [];
members = (json['members'] as List?)
?.map<MemberItemModel>((e) => MemberItemModel.fromJson(e))
.toList();
emote = json['emote'] ?? {};
jumpUrl = json['jump_url'] ?? {};
pictures = json['pictures'] ?? [];
vote = json['vote'] ?? {};
richText = json['rich_text'] ?? {};
// 不包含@ 笔记 图片的时候,文字可折叠
isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty;
topicsMeta = json['topics_meta'] ?? {};
}
}

View File

@@ -27,15 +27,15 @@ class ReplyData {
json['cursor'] == null ? null : ReplyCursor.fromJson(json['cursor']);
config =
json['config'] == null ? null : ReplyConfig.fromJson(json['config']);
replies = json['replies'] != null
? List<ReplyItemModel>.from(json['replies'].map<ReplyItemModel>(
(item) => ReplyItemModel.fromJson(item, json['upper']['mid'])))
: <ReplyItemModel>[];
topReplies = json['top_replies'] != null
? List<ReplyItemModel>.from(json['top_replies'].map<ReplyItemModel>(
(item) => ReplyItemModel.fromJson(item, json['upper']['mid'],
isTopStatus: true)))
: <ReplyItemModel>[];
replies = (json['replies'] as List?)
?.map<ReplyItemModel>(
(item) => ReplyItemModel.fromJson(item, json['upper']['mid']))
.toList();
topReplies = (json['top_replies'] as List?)
?.map<ReplyItemModel>((item) => ReplyItemModel.fromJson(
item, json['upper']['mid'],
isTopStatus: true))
.toList();
upper = json['upper'] == null ? null : ReplyUpper.fromJson(json['upper']);
}
}
@@ -60,15 +60,15 @@ class ReplyReplyData {
ReplyReplyData.fromJson(Map<String, dynamic> json) {
page = ReplyPage.fromJson(json['page']);
config = ReplyConfig.fromJson(json['config']);
replies = json['replies'] != null
? List<ReplyItemModel>.from(json['replies'].map<ReplyItemModel>(
(item) => ReplyItemModel.fromJson(item, json['upper']['mid'])))
: <ReplyItemModel>[];
topReplies = json['top_replies'] != null
? List<ReplyItemModel>.from(json['top_replies'].map<ReplyItemModel>(
(item) => ReplyItemModel.fromJson(item, json['upper']['mid'],
isTopStatus: true)))
: <ReplyItemModel>[];
replies = (json['replies'] as List?)
?.map<ReplyItemModel>(
(item) => ReplyItemModel.fromJson(item, json['upper']['mid']))
.toList();
topReplies = (json['top_replies'] as List?)
?.map<ReplyItemModel>((item) => ReplyItemModel.fromJson(
item, json['upper']['mid'],
isTopStatus: true))
.toList();
upper = ReplyUpper.fromJson(json['upper']);
root = ReplyItemModel.fromJson(json['root'], json['upper']['mid']);
}

View File

@@ -6,7 +6,7 @@ class EmoteModelData {
EmoteModelData.fromJson(Map<String, dynamic> json) {
setting =
json['setting'] != null ? Setting.fromJson(json['setting']) : null;
json['setting'] != null ? Setting.fromJson(json['setting']) : null;
if (json['packages'] != null) {
packages = <Packages>[];
json['packages'].forEach((v) {
@@ -68,17 +68,17 @@ class Packages {
Packages(
{this.id,
this.text,
this.url,
this.mtime,
this.type,
this.attr,
this.meta,
this.emote,
this.flags,
this.label,
this.packageSubTitle,
this.refMid});
this.text,
this.url,
this.mtime,
this.type,
this.attr,
this.meta,
this.emote,
this.flags,
this.label,
this.packageSubTitle,
this.refMid});
Packages.fromJson(Map<String, dynamic> json) {
id = json['id'];
@@ -95,7 +95,7 @@ class Packages {
});
}
flags =
json['flags'] != null ? PackagesFlags.fromJson(json['flags']) : null;
json['flags'] != null ? PackagesFlags.fromJson(json['flags']) : null;
label = json['label'] != null ? Label.fromJson(json['label']) : null;
packageSubTitle = json['package_sub_title'];
refMid = json['ref_mid'];
@@ -189,16 +189,16 @@ class Emote {
Emote(
{this.id,
this.packageId,
this.text,
this.url,
this.mtime,
this.type,
this.attr,
this.meta,
this.flags,
this.activity,
this.gifUrl});
this.packageId,
this.text,
this.url,
this.mtime,
this.type,
this.attr,
this.meta,
this.flags,
this.activity,
this.gifUrl});
Emote.fromJson(Map<String, dynamic> json) {
id = json['id'];
@@ -237,7 +237,7 @@ class Emote {
class EmoteMeta {
int? size;
List<String>? suggest;
List? suggest;
String? alias;
String? gifUrl;
@@ -245,9 +245,7 @@ class EmoteMeta {
EmoteMeta.fromJson(Map<String, dynamic> json) {
size = json['size'];
suggest = json['suggest'] == null
? null
: List<String>.from(json['suggest'].map((x) => x));
suggest = json['suggest'];
alias = json['alias'];
gifUrl = json['gif_url'];
}

View File

@@ -86,10 +86,9 @@ class ReplyItemModel {
action = json['action'];
member = ReplyMember.fromJson(json['member']);
content = ReplyContent.fromJson(json['content']);
replies = json['replies'] != null
? List<ReplyItemModel>.from(json['replies']
.map((item) => ReplyItemModel.fromJson(item, upperMid)))
: <ReplyItemModel>[];
replies = (json['replies'] as List?)
?.map((item) => ReplyItemModel.fromJson(item, upperMid))
.toList();
assist = json['assist'];
upAction = UpAction.fromJson(json['up_action']);
invisible = json['invisible'];
@@ -98,9 +97,8 @@ class ReplyItemModel {
: ReplyControl.fromJson(json['reply_control']);
isUp = upperMid.toString() == json['member']['mid'];
isTop = isTopStatus;
cardLabel = json['card_label'] != null
? json['card_label'].map((e) => e['text_content']).toList()
: [];
cardLabel =
(json['card_label'] as List?)?.map((e) => e['text_content']).toList();
rcount = json['rcount'];
}
}

View File

@@ -54,7 +54,8 @@ class ReplyCursor {
mode = json['mode'];
modeText = json['mode_text'];
allCount = json['all_count'] ?? 0;
supportMode = json['support_mode'].cast<int>();
supportMode =
(json['support_mode'] as List?)?.map((e) => e as int).toList();
name = json['name'];
paginationReply = json['pagination_reply'] != null
? PaginationReply.fromJson(json['pagination_reply'])

View File

@@ -46,7 +46,7 @@ class VideoDetailData {
List<DescV2>? descV2;
int? state;
int? duration;
Map<String, int>? rights;
Map? rights;
Owner? owner;
Stat? stat;
String? argueMsg;
@@ -125,13 +125,11 @@ class VideoDetailData {
pubdate = json["pubdate"];
ctime = json["ctime"];
desc = json["desc"];
descV2 = json["desc_v2"] == null
? []
: List<DescV2>.from(json["desc_v2"]!.map((e) => DescV2.fromJson(e)));
descV2 =
(json["desc_v2"] as List?)?.map((e) => DescV2.fromJson(e)).toList();
state = json["state"];
duration = json["duration"];
rights =
Map.from(json["rights"]!).map((k, v) => MapEntry<String, int>(k, v));
rights = json["rights"];
owner = json["owner"] == null ? null : Owner.fromJson(json["owner"]);
stat = json["stat"] == null ? null : Stat.fromJson(json["stat"]);
argueMsg = json['argue_info']?['argue_msg'];
@@ -145,9 +143,7 @@ class VideoDetailData {
isChargeableSeason = json["is_chargeable_season"];
isStory = json["is_story"];
noCache = json["no_cache"];
pages = json["pages"] == null
? []
: List<Part>.from(json["pages"]!.map((e) => Part.fromJson(e)));
pages = (json["pages"] as List?)?.map((e) => Part.fromJson(e)).toList();
subtitle =
json["subtitle"] == null ? null : Subtitle.fromJson(json["subtitle"]);
ugcSeason = json["ugc_season"] != null
@@ -161,9 +157,8 @@ class VideoDetailData {
: HonorReply.fromJson(json["honor_reply"]);
likeIcon = json["like_icon"];
needJumpBv = json["need_jump_bv"];
staff = json["staff"] == null
? null
: (json["staff"] as List).map((item) => Staff.fromJson(item)).toList();
staff =
(json["staff"] as List?)?.map((item) => Staff.fromJson(item)).toList();
if (json['redirect_url'] != null) {
epId = resolveEpId(json['redirect_url']);
}
@@ -296,6 +291,7 @@ class Staff {
String? name;
String? face;
Vip? vip;
Map? official;
Staff({
this.mid,
@@ -311,6 +307,7 @@ class Staff {
name = json["name"];
face = json["face"];
vip = json["vip"] == null ? null : Vip.fromJson(json["vip"]);
official = json['official'];
}
}
@@ -341,9 +338,7 @@ class HonorReply {
String toRawJson() => json.encode(toJson());
HonorReply.fromJson(Map<String, dynamic> json) {
honor = json["honor"] == null
? []
: List<Honor>.from(json["honor"]!.map((x) => Honor.fromJson(x)));
honor = (json["honor"] as List?)?.map((x) => Honor.fromJson(x)).toList();
}
Map<String, dynamic> toJson() {
@@ -563,9 +558,7 @@ class Subtitle {
Subtitle.fromJson(Map<String, dynamic> json) {
allowSubmit = json["allow_submit"];
list = json["list"] == null
? []
: List<dynamic>.from(json["list"]!.map((x) => x));
list = json["list"];
}
Map<String, dynamic> toJson() {
@@ -634,11 +627,9 @@ class UgcSeason {
intro = json['intro'];
signState = json['sign_state'];
attribute = json['attribute'];
sections = json['sections'] != null
? json['sections']
.map<SectionItem>((e) => SectionItem.fromJson(e))
.toList()
: [];
sections = (json['sections'] as List?)
?.map<SectionItem>((e) => SectionItem.fromJson(e))
.toList();
stat = Stat.fromJson(json['stat']);
epCount = json['ep_count'];
seasonType = json['season_type'];
@@ -680,8 +671,8 @@ class SectionItem {
id = json['id'];
title = json['title'];
type = json['type'];
episodes = json['episodes']
.map<EpisodeItem>((e) => EpisodeItem.fromJson(e))
episodes = (json['episodes'] as List?)
?.map<EpisodeItem>((e) => EpisodeItem.fromJson(e))
.toList();
}
}

View File

@@ -27,11 +27,9 @@ class AboutPage extends StatefulWidget {
class _AboutPageState extends State<AboutPage> {
final AboutController _aboutController = Get.put(AboutController());
static const String _sourceCodeUrl =
'https://github.com/bggRGjQaUbCoE/PiliPlus';
static const String _originSourceCodeUrl =
'https://github.com/guozhigq/pilipala';
static const String _upstreamUrl = 'https://github.com/orz12/PiliPalaX';
final String _sourceCodeUrl = 'https://github.com/bggRGjQaUbCoE/PiliPlus';
final String _originSourceCodeUrl = 'https://github.com/guozhigq/pilipala';
final String _upstreamUrl = 'https://github.com/orz12/PiliPalaX';
late int _pressCount = 0;

View File

@@ -4,6 +4,7 @@ import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/pages/dynamics/repost_dyn_panel.dart';
import 'package:PiliPlus/pages/video/detail/introduction/controller.dart';
import 'package:PiliPlus/pages/video/detail/introduction/pay_coins_page.dart';
import 'package:PiliPlus/utils/extension.dart';
@@ -22,7 +23,6 @@ import 'package:PiliPlus/pages/video/detail/reply/index.dart';
import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart';
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:share_plus/share_plus.dart';
import 'package:html/parser.dart' as html_parser;
import 'package:html/dom.dart' as dom;
@@ -395,7 +395,47 @@ class BangumiIntroController extends CommonController {
),
onTap: () {
Get.back();
Share.share(videoUrl);
Utils.shareText(videoUrl);
},
),
ListTile(
title: const Text(
'分享至动态',
style: TextStyle(fontSize: 14),
),
onTap: () {
Get.back();
EpisodeItem? item = bangumiItem?.episodes
?.firstWhereOrNull((item) => item.epId == epId);
showModalBottomSheet(
context: context,
isScrollControlled: true,
useSafeArea: true,
builder: (context) => RepostPanel(
rid: epId,
/**
* 1番剧 // 4097
2电影 // 4098
3纪录片 // 4101
4国创 // 4100
5电视剧 // 4099
6漫画
7综艺 // 4099
*/
dynType: switch (Get.parameters['type']) {
'1' => 4097,
'2' => 4098,
'3' => 4101,
'4' => 4100,
'5' || '7' => 4099,
_ => -1,
},
pic: bangumiItem?.cover,
title:
'${bangumiItem?.title}${item != null ? '\n${item.showTitle}' : ''}',
uname: '',
),
);
},
),
],
@@ -439,11 +479,9 @@ class BangumiIntroController extends CommonController {
// 重新请求评论
if (videoDetailCtr.showReply) {
try {
/// 未渲染回复组件时可能异常
VideoReplyController videoReplyCtr =
Get.find<VideoReplyController>(tag: Get.arguments['heroTag']);
videoReplyCtr.aid = aid;
videoReplyCtr.onRefresh();
Get.find<VideoReplyController>(tag: Get.arguments['heroTag'])
..aid = aid
..onReload();
} catch (_) {}
}
@@ -538,38 +576,42 @@ class BangumiIntroController extends CommonController {
/// 列表循环或者顺序播放时,自动播放下一个;自动连播时,播放相关视频
bool nextPlay() {
late List episodes;
VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
PlayRepeat playRepeat = videoDetailCtr.plPlayerController.playRepeat;
try {
late List episodes;
VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
PlayRepeat playRepeat = videoDetailCtr.plPlayerController.playRepeat;
if ((loadingState.value as Success).response.episodes != null) {
episodes = (loadingState.value as Success).response.episodes!;
} else {
if (playRepeat == PlayRepeat.autoPlayRelated) {
return playRelated();
}
}
int currentIndex =
episodes.indexWhere((e) => e.cid == videoDetailCtr.cid.value);
int nextIndex = currentIndex + 1;
// 列表循环
if (nextIndex >= episodes.length) {
if (playRepeat == PlayRepeat.listCycle) {
nextIndex = 0;
} else if (playRepeat == PlayRepeat.autoPlayRelated) {
return playRelated();
if ((loadingState.value as Success).response.episodes != null) {
episodes = (loadingState.value as Success).response.episodes!;
} else {
return false;
if (playRepeat == PlayRepeat.autoPlayRelated) {
return playRelated();
}
}
int currentIndex =
episodes.indexWhere((e) => e.cid == videoDetailCtr.cid.value);
int nextIndex = currentIndex + 1;
// 列表循环
if (nextIndex >= episodes.length) {
if (playRepeat == PlayRepeat.listCycle) {
nextIndex = 0;
} else if (playRepeat == PlayRepeat.autoPlayRelated) {
return playRelated();
} else {
return false;
}
}
int epid = episodes[nextIndex].epId;
int cid = episodes[nextIndex].cid;
String bvid = episodes[nextIndex].bvid;
int aid = episodes[nextIndex].aid;
dynamic cover = episodes[nextIndex].cover;
changeSeasonOrbangu(epid, bvid, cid, aid, cover);
return true;
} catch (_) {
return false;
}
int epid = episodes[nextIndex].epId;
int cid = episodes[nextIndex].cid;
String bvid = episodes[nextIndex].bvid;
int aid = episodes[nextIndex].aid;
dynamic cover = episodes[nextIndex].cover;
changeSeasonOrbangu(epid, bvid, cid, aid, cover);
return true;
}
bool playRelated() {
@@ -589,7 +631,7 @@ class BangumiIntroController extends CommonController {
SmartDialog.showToast('已三连');
return false;
}
var result = await VideoHttp.triple(epId: epId);
var result = await VideoHttp.triple(epId: epId, seasonId: seasonId);
if (result['status']) {
hasLike.value = result["data"]["like"] == 1;
hasCoin.value = result["data"]["coin"] == 1;
@@ -624,4 +666,24 @@ class BangumiIntroController extends CommonController {
}
} catch (_) {}
}
// 收藏
showFavBottomSheet(BuildContext context, {type = 'tap'}) {
if (userInfo == null) {
SmartDialog.showToast('账号未登录');
return;
}
// 快速收藏 &
// 点按 收藏至默认文件夹
// 长按选择文件夹
if (enableQuickFav) {
if (type == 'tap') {
actionFavVideo(type: 'default');
} else {
Utils.showFavBottomSheet(context: context, ctr: this);
}
} else if (type != 'longPress') {
Utils.showFavBottomSheet(context: context, ctr: this);
}
}
}

View File

@@ -6,7 +6,7 @@ import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactivevie
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
import 'package:PiliPlus/common/constants.dart';
@@ -173,26 +173,6 @@ class _BangumiInfoState extends State<BangumiInfo>
super.dispose();
}
// 收藏
showFavBottomSheet({type = 'tap'}) {
if (bangumiIntroController.userInfo == null) {
SmartDialog.showToast('账号未登录');
return;
}
// 快速收藏 &
// 点按 收藏至默认文件夹
// 长按选择文件夹
if (bangumiIntroController.enableQuickFav) {
if (type == 'tap') {
bangumiIntroController.actionFavVideo(type: 'default');
} else {
Utils.showFavBottomSheet(context: context, ctr: bangumiIntroController);
}
} else if (type != 'longPress') {
Utils.showFavBottomSheet(context: context, ctr: bangumiIntroController);
}
}
// 视频介绍
showIntroDetail() {
feedBack();
@@ -446,10 +426,12 @@ class _BangumiInfoState extends State<BangumiInfo>
);
}
Widget actionGrid(BuildContext context, bangumiIntroController) {
Widget actionGrid(
BuildContext context, BangumiIntroController bangumiIntroController) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Material(
color: Theme.of(context).colorScheme.surface,
child: Padding(
padding: const EdgeInsets.only(top: 1),
child: SizedBox(
@@ -473,11 +455,12 @@ class _BangumiInfoState extends State<BangumiInfo>
bangumiItem!.stat!['likes']!,
),
needAnim: true,
hasOneThree: bangumiIntroController.hasLike.value &&
hasTriple: bangumiIntroController.hasLike.value &&
bangumiIntroController.hasCoin.value &&
bangumiIntroController.hasFav.value,
callBack: (start) {
if (start) {
HapticFeedback.lightImpact();
_coinKey.currentState?.controller?.forward();
_favKey.currentState?.controller?.forward();
} else {
@@ -510,8 +493,10 @@ class _BangumiInfoState extends State<BangumiInfo>
key: _favKey,
icon: const Icon(FontAwesomeIcons.star),
selectIcon: const Icon(FontAwesomeIcons.solidStar),
onTap: () => showFavBottomSheet(),
onLongPress: () => showFavBottomSheet(type: 'longPress'),
onTap: () =>
bangumiIntroController.showFavBottomSheet(context),
onLongPress: () => bangumiIntroController
.showFavBottomSheet(context, type: 'longPress'),
selectStatus: bangumiIntroController.hasFav.value,
loadingStatus: false,
semanticsLabel: '收藏',
@@ -552,13 +537,17 @@ class _BangumiInfoState extends State<BangumiInfo>
});
}
Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) {
Widget actionRow(
BuildContext context,
BangumiIntroController bangumiIntroController,
VideoDetailController videoDetailCtr,
) {
return Row(children: [
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.thumbsUp),
onTap: () => handleState(videoIntroController.actionLikeVideo),
selectStatus: videoIntroController.hasLike.value,
onTap: () => handleState(bangumiIntroController.actionLikeVideo),
selectStatus: bangumiIntroController.hasLike.value,
loadingStatus: widget.isLoading,
text: !widget.isLoading
? widget.bangumiDetail!.stat!['likes']!.toString()
@@ -569,8 +558,8 @@ class _BangumiInfoState extends State<BangumiInfo>
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.b),
onTap: () => handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin.value,
onTap: () => handleState(bangumiIntroController.actionCoinVideo),
selectStatus: bangumiIntroController.hasCoin.value,
loadingStatus: widget.isLoading,
text: !widget.isLoading
? widget.bangumiDetail!.stat!['coins']!.toString()
@@ -581,9 +570,10 @@ class _BangumiInfoState extends State<BangumiInfo>
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.heart),
onTap: () => showFavBottomSheet(),
onLongPress: () => showFavBottomSheet(type: 'longPress'),
selectStatus: videoIntroController.hasFav.value,
onTap: () => bangumiIntroController.showFavBottomSheet(context),
onLongPress: () => bangumiIntroController.showFavBottomSheet(context,
type: 'longPress'),
selectStatus: bangumiIntroController.hasFav.value,
loadingStatus: widget.isLoading,
text: !widget.isLoading
? widget.bangumiDetail!.stat!['favorite']!.toString()
@@ -605,7 +595,7 @@ class _BangumiInfoState extends State<BangumiInfo>
const SizedBox(width: 8),
ActionRowItem(
icon: const Icon(FontAwesomeIcons.share),
onTap: () => videoIntroController.actionShareVideo(),
onTap: () => bangumiIntroController.actionShareVideo(),
selectStatus: false,
loadingStatus: widget.isLoading,
text: '转发'),

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/pages/common/common_collapse_slide_page.dart';
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
import 'package:flutter/material.dart';
import 'package:PiliPlus/common/widgets/stat/danmu.dart';
@@ -6,7 +7,7 @@ import 'package:get/get.dart';
import '../../../../utils/utils.dart';
class IntroDetail extends StatelessWidget {
class IntroDetail extends CommonCollapseSlidePage {
final dynamic bangumiDetail;
final dynamic videoTags;
@@ -17,21 +18,28 @@ class IntroDetail extends StatelessWidget {
});
@override
Widget build(BuildContext context) {
TextStyle smallTitle = TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurface,
);
return Container(
State<IntroDetail> createState() => _IntroDetailState();
}
class _IntroDetailState extends CommonCollapseSlidePageState<IntroDetail> {
late final TextStyle smallTitle = TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurface,
);
@override
Widget get buildPage {
return Material(
color: Theme.of(context).colorScheme.surface,
padding: const EdgeInsets.only(left: 14, right: 14),
// height: Utils.getSheetHeight(context),
child: Column(
children: [
Container(
height: 35,
padding: const EdgeInsets.only(bottom: 2),
child: Center(
GestureDetector(
onTap: Get.back,
behavior: HitTestBehavior.opaque,
child: Container(
height: 35,
alignment: Alignment.center,
padding: const EdgeInsets.only(bottom: 2),
child: Container(
width: 32,
height: 3,
@@ -45,99 +53,104 @@ class IntroDetail extends StatelessWidget {
),
),
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(
bangumiDetail!.title,
style: const TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 4),
Row(
children: [
statView(
context: context,
theme: 'gray',
view: bangumiDetail!.stat!['views'],
size: 'medium',
),
const SizedBox(width: 6),
statDanMu(
context: context,
theme: 'gray',
danmu: bangumiDetail!.stat!['danmakus'],
size: 'medium',
),
],
),
const SizedBox(height: 4),
Row(
children: [
Text(
bangumiDetail!.areas!.first['name'],
style: smallTitle,
),
const SizedBox(width: 6),
Text(
bangumiDetail!.publish!['pub_time_show'],
style: smallTitle,
),
const SizedBox(width: 6),
Text(
bangumiDetail!.newEp!['desc'],
style: smallTitle,
),
],
),
const SizedBox(height: 20),
Text(
'简介:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
'${bangumiDetail!.evaluate!}',
style: smallTitle.copyWith(fontSize: 13),
),
const SizedBox(height: 20),
Text(
'声优:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
bangumiDetail.actors,
style: smallTitle.copyWith(fontSize: 13),
),
if (videoTags is List && videoTags.isNotEmpty) ...[
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 8,
children: (videoTags as List)
.map(
(item) => SearchText(
fontSize: 13,
text: item['tag_name'],
onTap: (_) => Get.toNamed('/searchResult',
parameters: {'keyword': item['tag_name']}),
onLongPress: (_) =>
Utils.copyText(item['tag_name']),
),
)
.toList(),
)
],
SizedBox(height: MediaQuery.of(context).padding.bottom + 20)
],
),
),
child: enableSlide ? slideList() : buildList,
)
],
),
);
}
@override
Widget get buildList => ListView(
controller: ScrollController(),
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.only(
left: 14,
right: 14,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
children: [
SelectableText(
widget.bangumiDetail!.title,
style: const TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 4),
Row(
children: [
statView(
context: context,
theme: 'gray',
view: widget.bangumiDetail!.stat!['views'],
size: 'medium',
),
const SizedBox(width: 6),
statDanMu(
context: context,
theme: 'gray',
danmu: widget.bangumiDetail!.stat!['danmakus'],
size: 'medium',
),
],
),
const SizedBox(height: 4),
Row(
children: [
Text(
widget.bangumiDetail!.areas!.first['name'],
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.bangumiDetail!.publish!['pub_time_show'],
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.bangumiDetail!.newEp!['desc'],
style: smallTitle,
),
],
),
const SizedBox(height: 20),
Text(
'简介:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
'${widget.bangumiDetail!.evaluate!}',
style: smallTitle.copyWith(fontSize: 14),
),
const SizedBox(height: 20),
Text(
'声优:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
widget.bangumiDetail.actors,
style: smallTitle.copyWith(fontSize: 14),
),
if (widget.videoTags is List && widget.videoTags.isNotEmpty) ...[
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 8,
children: (widget.videoTags as List)
.map(
(item) => SearchText(
fontSize: 13,
text: item['tag_name'],
onTap: (_) => Get.toNamed('/searchResult',
parameters: {'keyword': item['tag_name']}),
onLongPress: (_) => Utils.copyText(item['tag_name']),
),
)
.toList(),
)
],
],
);
}

View File

@@ -0,0 +1,70 @@
import 'package:PiliPlus/http/bangumi.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/bangumi/pgc_index/condition.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:get/get.dart' hide Condition;
class PgcIndexController extends CommonController {
PgcIndexController(this.indexType);
int? indexType;
Rx<LoadingState> conditionState = LoadingState.loading().obs;
late final RxBool isExpand = false.obs;
RxMap<String, dynamic> indexParams = <String, dynamic>{}.obs;
@override
void onInit() {
super.onInit();
getPgcIndexCondition();
}
Future getPgcIndexCondition() async {
dynamic res = await BangumiHttp.pgcIndexCondition(
seasonType: indexType == null ? 1 : null,
type: 0,
indexType: indexType,
);
if (res is Success) {
Condition data = res.response;
if (data.order?.isNotEmpty == true) {
indexParams['order'] = data.order!.first.field;
}
if (data.filter?.isNotEmpty == true) {
for (Filter item in data.filter!) {
indexParams['${item.field}'] = item.values?.firstOrNull?.keyword;
}
}
queryData();
}
conditionState.value = res;
}
@override
Future<LoadingState> customGetData() => BangumiHttp.pgcIndexResult(
page: currentPage,
params: indexParams,
seasonType: indexType == null ? 1 : null,
type: 0,
indexType: indexType,
);
@override
bool customHandleResponse(Success response) {
if (response.response['has_next'] == null ||
response.response['has_next'] == 0) {
isEnd = true;
}
if (isEnd.not && (response.response['list'] as List?).isNullOrEmpty) {
isEnd = true;
}
if (currentPage != 1 && loadingState.value is Success) {
response.response['list'] ??= [];
response.response['list']!
.insertAll(0, (loadingState.value as Success).response);
}
loadingState.value = LoadingState.success(response.response['list']);
return true;
}
}

View File

@@ -0,0 +1,241 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/http_error.dart';
import 'package:PiliPlus/common/widgets/loading_widget.dart';
import 'package:PiliPlus/common/widgets/self_sized_horizontal_list.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/bangumi/pgc_index/pgc_index_controller.dart';
import 'package:PiliPlus/pages/bangumi/widgets/bangumi_card_v_pgc_index.dart';
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/grid.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart' hide Condition;
import '../../../models/bangumi/pgc_index/condition.dart';
class PgcIndexPage extends StatefulWidget {
const PgcIndexPage({super.key, this.indexType});
final int? indexType;
@override
State<PgcIndexPage> createState() => _PgcIndexPageState();
}
class _PgcIndexPageState extends State<PgcIndexPage>
with AutomaticKeepAliveClientMixin {
late final _ctr = Get.put(
PgcIndexController(widget.indexType),
tag: '${widget.indexType}',
);
@override
bool get wantKeepAlive => widget.indexType != null;
@override
Widget build(BuildContext context) {
super.build(context);
return widget.indexType == null
? Scaffold(
appBar: AppBar(title: const Text('索引')),
body: Obx(() => _buildBody(_ctr.conditionState.value)),
)
: Obx(() => _buildBody(_ctr.conditionState.value));
}
Widget _buildBody(LoadingState loadingState) {
return switch (loadingState) {
Loading() => loadingWidget,
Success() => Builder(builder: (context) {
Condition data = loadingState.response;
int count = (data.order?.isNotEmpty == true ? 1 : 0) +
(data.filter?.length ?? 0);
if (count == 0) return const SizedBox.shrink();
return CustomScrollView(
slivers: [
if (widget.indexType != null)
SliverToBoxAdapter(child: const SizedBox(height: 12)),
SliverToBoxAdapter(
child: AnimatedSize(
curve: Curves.easeInOut,
alignment: Alignment.topCenter,
duration: const Duration(milliseconds: 200),
child: count > 5
? Obx(() => _buildSortWidget(count, data))
: _buildSortWidget(count, data),
),
),
SliverPadding(
padding: EdgeInsets.only(
left: StyleString.safeSpace,
right: StyleString.safeSpace,
top: 12,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
sliver: Obx(() => _buildList(_ctr.loadingState.value)),
),
],
);
}),
Error() => scrollErrorWidget(
errMsg: loadingState.errMsg,
callback: () {
_ctr.conditionState.value = LoadingState.loading();
_ctr.getPgcIndexCondition();
},
),
LoadingState() => throw UnimplementedError(),
};
}
Widget _buildSortWidget(count, data) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...List.generate(
count > 5
? _ctr.isExpand.value
? count
: count ~/ 2
: count,
(index) {
List? item = data.order?.isNotEmpty == true
? index == 0
? data.order
: data.filter![index - 1].values
: data.filter![index].values;
return item?.isNotEmpty == true
? Padding(
padding: EdgeInsets.only(
top: index == 0 ? 0 : 10,
),
child: SelfSizedHorizontalList(
gapSize: 12,
padding: const EdgeInsets.symmetric(
horizontal: 12,
),
childBuilder: (childIndex) => Obx(
() => SearchText(
bgColor: (item[childIndex] is Order
? _ctr.indexParams['order']
: _ctr.indexParams[data
.filter![
data.order?.isNotEmpty == true
? index - 1
: index]
.field]) ==
(item[childIndex] is Order
? item[childIndex].field
: item[childIndex].keyword)
? Theme.of(context)
.colorScheme
.secondaryContainer
: Colors.transparent,
textColor: (item[childIndex] is Order
? _ctr.indexParams['order']
: _ctr.indexParams[data
.filter![
data.order?.isNotEmpty == true
? index - 1
: index]
.field]) ==
(item[childIndex] is Order
? item[childIndex].field
: item[childIndex].keyword)
? Theme.of(context)
.colorScheme
.onSecondaryContainer
: Theme.of(context)
.colorScheme
.onSurfaceVariant,
text: item[childIndex].name,
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 3,
),
onTap: (_) {
String name = item[childIndex] is Order
? 'order'
: data
.filter![data.order?.isNotEmpty == true
? index - 1
: index]
.field!;
_ctr.indexParams[name] =
(item[childIndex] is Order
? item[childIndex].field
: item[childIndex].keyword);
_ctr.onReload();
},
),
),
itemCount: item!.length,
),
)
: const SizedBox.shrink();
},
),
if (count > 5) ...[
const SizedBox(height: 8),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
_ctr.isExpand.value = _ctr.isExpand.value.not;
},
child: Container(
alignment: Alignment.center,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_ctr.isExpand.value ? '收起' : '展开',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
),
),
Icon(
_ctr.isExpand.value
? Icons.keyboard_arrow_up
: Icons.keyboard_arrow_down,
color: Theme.of(context).colorScheme.outline,
),
],
),
),
),
],
],
);
Widget _buildList(LoadingState loadingState) {
return switch (loadingState) {
Loading() => HttpError(errMsg: '加载中'),
Success() => (loadingState.response as List?)?.isNotEmpty == true
? SliverGrid(
gridDelegate: SliverGridDelegateWithExtentAndRatio(
mainAxisSpacing: StyleString.cardSpace,
crossAxisSpacing: StyleString.cardSpace,
maxCrossAxisExtent: Grid.smallCardWidth / 3 * 2,
childAspectRatio: 0.75,
mainAxisExtent: MediaQuery.textScalerOf(context).scale(50),
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index == loadingState.response.length - 1) {
_ctr.onLoadMore();
}
return BangumiCardVPgcIndex(
bangumiItem: loadingState.response[index]);
},
childCount: loadingState.response.length,
),
)
: HttpError(callback: _ctr.onReload),
Error() => HttpError(
errMsg: loadingState.errMsg,
callback: _ctr.onReload,
),
LoadingState() => throw UnimplementedError(),
};
}
}

View File

@@ -4,6 +4,7 @@ import 'package:PiliPlus/common/widgets/loading_widget.dart';
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/tab_type.dart';
import 'package:PiliPlus/pages/bangumi/pgc_index/pgc_index_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
@@ -15,6 +16,7 @@ import 'package:PiliPlus/pages/main/index.dart';
import '../../utils/grid.dart';
import 'controller.dart';
import 'widgets/bangumi_card_v.dart';
import 'package:PiliPlus/common/widgets/spring_physics.dart';
class BangumiPage extends StatefulWidget {
const BangumiPage({
@@ -41,28 +43,28 @@ class _BangumiPageState extends State<BangumiPage>
@override
void initState() {
super.initState();
_bangumiController.scrollController.addListener(listener);
}
void listener() {
StreamController<bool> mainStream =
Get.find<MainController>().bottomBarStream;
StreamController<bool> searchBarStream =
Get.find<HomeController>().searchBarStream;
_bangumiController.scrollController.addListener(
() async {
final ScrollDirection direction =
_bangumiController.scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
searchBarStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
searchBarStream.add(false);
}
},
);
final ScrollDirection direction =
_bangumiController.scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
searchBarStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
searchBarStream.add(false);
}
}
@override
void dispose() {
_bangumiController.scrollController.removeListener(() {});
_bangumiController.scrollController.removeListener(listener);
super.dispose();
}
@@ -128,7 +130,12 @@ class _BangumiPageState extends State<BangumiPage>
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(top: 10, bottom: 10, left: 16),
padding: const EdgeInsets.only(
top: 10,
bottom: 10,
left: 16,
right: 10,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -136,6 +143,56 @@ class _BangumiPageState extends State<BangumiPage>
'推荐',
style: Theme.of(context).textTheme.titleMedium,
),
GestureDetector(
onTap: () {
if (widget.tabType == TabType.bangumi) {
Get.to(PgcIndexPage());
} else {
List titles = const ['全部', '电影', '电视剧', '纪录片', '综艺'];
List types = const [102, 2, 5, 3, 7];
Get.to(
Scaffold(
appBar: AppBar(title: const Text('索引')),
body: DefaultTabController(
length: types.length,
child: Column(
children: [
TabBar(
tabs: titles
.map((title) => Tab(text: title))
.toList()),
Expanded(
child: tabBarView(
children: types
.map((type) =>
PgcIndexPage(indexType: type))
.toList()),
)
],
),
),
),
);
}
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'查看更多',
strutStyle: StrutStyle(leading: 0, height: 1),
style: TextStyle(
height: 1,
color: Theme.of(context).colorScheme.secondary,
),
),
Icon(
Icons.chevron_right,
color: Theme.of(context).colorScheme.secondary,
),
],
),
),
],
),
),

View File

@@ -29,45 +29,6 @@ class BangumiCardV extends StatelessWidget {
onTap: () async {
final int seasonId = bangumiItem.seasonId;
Utils.viewBangumi(seasonId: seasonId);
// SmartDialog.showLoading(msg: '获取中...');
// final res = await SearchHttp.bangumiInfo(seasonId: seasonId);
// SmartDialog.dismiss().then((value) {
// if (res['status']) {
// if (res['data'].episodes.isEmpty) {
// SmartDialog.showToast('资源加载失败');
// return;
// }
// EpisodeItem episode = res['data'].episodes.first;
// int? epId = res['data'].userStatus?.progress?.lastEpId;
// if (epId == null) {
// epId = episode.epId;
// } else {
// for (var item in res['data'].episodes) {
// if (item.epId == epId) {
// episode = item;
// break;
// }
// }
// }
// String bvid = episode.bvid!;
// int cid = episode.cid!;
// String pic = episode.cover!;
// // debugPrint('epId');
// // debugPrint(epId);
// String heroTag = Utils.makeHeroTag(cid);
// Get.toNamed(
// '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId&epId=$epId',
// arguments: {
// 'pic': pic,
// 'heroTag': heroTag,
// 'videoType': SearchType.media_bangumi,
// 'bangumiItem': res['data'],
// },
// );
// } else {
// SmartDialog.showToast(res['msg']);
// }
// });
},
child: Column(
children: [

View File

@@ -24,45 +24,6 @@ class BangumiCardVMemberHome extends StatelessWidget {
onTap: () async {
final int seasonId = int.tryParse(bangumiItem.param ?? '') ?? -1;
Utils.viewBangumi(seasonId: seasonId);
// SmartDialog.showLoading(msg: '获取中...');
// final res = await SearchHttp.bangumiInfo(seasonId: seasonId);
// SmartDialog.dismiss().then((value) {
// if (res['status']) {
// if (res['data'].episodes.isEmpty) {
// SmartDialog.showToast('资源加载失败');
// return;
// }
// EpisodeItem episode = res['data'].episodes.first;
// int? epId = res['data'].userStatus?.progress?.lastEpId;
// if (epId == null) {
// epId = episode.epId;
// } else {
// for (var item in res['data'].episodes) {
// if (item.epId == epId) {
// episode = item;
// break;
// }
// }
// }
// String bvid = episode.bvid!;
// int cid = episode.cid!;
// String pic = episode.cover!;
// // debugPrint('epId');
// // debugPrint(epId);
// String heroTag = Utils.makeHeroTag(cid);
// Get.toNamed(
// '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId&epId=$epId',
// arguments: {
// 'pic': pic,
// 'heroTag': heroTag,
// 'videoType': SearchType.media_bangumi,
// 'bangumiItem': res['data'],
// },
// );
// } else {
// SmartDialog.showToast(res['msg']);
// }
// });
},
onLongPress: () => imageSaveDialog(
context: context,

View File

@@ -0,0 +1,127 @@
import 'package:PiliPlus/common/widgets/image_save.dart';
import 'package:flutter/material.dart';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
// 视频卡片 - 垂直布局
class BangumiCardVPgcIndex extends StatelessWidget {
const BangumiCardVPgcIndex({
super.key,
required this.bangumiItem,
});
final dynamic bangumiItem;
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: InkWell(
onLongPress: () => imageSaveDialog(
context: context,
title: bangumiItem['title'],
cover: bangumiItem['cover'],
),
onTap: () {
Utils.viewBangumi(seasonId: bangumiItem['season_id']);
},
child: Column(
children: [
ClipRRect(
borderRadius: StyleString.mdRadius,
child: AspectRatio(
aspectRatio: 0.75,
child: LayoutBuilder(builder: (context, boxConstraints) {
final double maxWidth = boxConstraints.maxWidth;
final double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
NetworkImgLayer(
src: bangumiItem['cover'],
width: maxWidth,
height: maxHeight,
),
if (bangumiItem['badge'] != null &&
bangumiItem['badge'] != '')
PBadge(
text: bangumiItem['badge'],
top: 6,
right: 6,
bottom: null,
left: null,
),
if (bangumiItem['order'] != null &&
bangumiItem['order'] != '')
PBadge(
text: bangumiItem['order'],
top: null,
right: null,
bottom: 6,
left: 6,
type: 'gray',
),
],
);
}),
),
),
bagumiContent(context)
],
),
),
);
}
Widget bagumiContent(context) {
return Expanded(
child: Padding(
// 多列
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
// 单列
// padding: const EdgeInsets.fromLTRB(14, 10, 4, 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Expanded(
child: Text(
bangumiItem['title'],
textAlign: TextAlign.start,
style: const TextStyle(
letterSpacing: 0.3,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
)),
],
),
const SizedBox(height: 1),
if (bangumiItem['index_show'] != null)
Text(
bangumiItem['index_show'],
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
// if (bangumiItem.progress != null)
// Text(
// bangumiItem.progress,
// maxLines: 1,
// style: TextStyle(
// fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
// color: Theme.of(context).colorScheme.outline,
// ),
// ),
],
),
),
);
}
}

View File

@@ -1,7 +1,11 @@
import 'package:PiliPlus/common/widgets/dialog.dart';
import 'package:PiliPlus/common/widgets/loading_widget.dart';
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models/user/black.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
@@ -24,9 +28,9 @@ class _BlackListPageState extends State<BlackListPage> {
void dispose() {
List list = _blackListController.loadingState.value is Success
? (_blackListController.loadingState.value as Success).response
: <int>[];
: <BlackListItem>[];
GStorage.setBlackMidsList(
list.isNotEmpty ? list.map<int>((e) => e.mid!).toList() : <int>[]);
list.isNotEmpty ? list.map((e) => e.mid!).toList() : <int>[]);
super.dispose();
}
@@ -57,7 +61,10 @@ class _BlackListPageState extends State<BlackListPage> {
_blackListController.onLoadMore();
}
return ListTile(
onTap: () {},
onTap: () {
Get.toNamed(
'/member?mid=${loadingState.response[index].mid}');
},
leading: NetworkImgLayer(
width: 45,
height: 45,
@@ -79,8 +86,11 @@ class _BlackListPageState extends State<BlackListPage> {
),
dense: true,
trailing: TextButton(
onPressed: () => _blackListController
.removeBlack(loadingState.response[index].mid),
onPressed: () => _blackListController.removeBlack(
context,
loadingState.response[index].uname,
loadingState.response[index].mid,
),
child: const Text('移除'),
),
);
@@ -111,31 +121,40 @@ class BlackListController extends CommonController {
@override
bool customHandleResponse(Success response) {
total.value = response.response.total;
if (response.response.list.isEmpty) {
if ((response.response.list as List?).isNullOrEmpty) {
isEnd = true;
}
if (currentPage != 1 && loadingState.value is Success) {
response.response.list
?.insertAll(0, (loadingState.value as Success).response);
response.response.list ??= <BlackListItem>[];
response.response.list!
.insertAll(0, (loadingState.value as Success).response);
}
if (response.response.list.length >= total.value) {
if (isEnd.not && response.response.list.length >= total.value) {
isEnd = true;
}
loadingState.value = LoadingState.success(
response.response.list.isNotEmpty ? response.response.list : <int>[]);
loadingState.value = LoadingState.success(response.response.list.isNotEmpty
? response.response.list
: <BlackListItem>[]);
return true;
}
Future removeBlack(mid) async {
var result = await BlackHttp.removeBlack(fid: mid);
if (result['status']) {
List list = (loadingState.value as Success).response;
list.removeWhere((e) => e.mid == mid);
total.value = total.value - 1;
loadingState.value =
LoadingState.success(list.isNotEmpty ? list : <int>[]);
SmartDialog.showToast(result['msg']);
}
Future removeBlack(context, name, mid) async {
showConfirmDialog(
context: context,
title: '确定将 $name 移出黑名单?',
onConfirm: () async {
Get.back();
var result = await VideoHttp.relationMod(mid: mid, act: 6, reSrc: 11);
if (result['status']) {
List list = (loadingState.value as Success).response;
list.removeWhere((e) => e.mid == mid);
total.value = total.value - 1;
loadingState.value =
LoadingState.success(list.isNotEmpty ? list : <BlackListItem>[]);
SmartDialog.showToast('操作成功');
}
},
);
}
@override

View File

@@ -0,0 +1,37 @@
import 'package:PiliPlus/pages/common/common_slide_page.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/material.dart';
abstract class CommonCollapseSlidePage extends CommonSlidePage {
const CommonCollapseSlidePage({super.key, super.enableSlide});
}
abstract class CommonCollapseSlidePageState<T extends CommonCollapseSlidePage>
extends CommonSlidePageState<T> {
late bool _isInit = true;
@override
void initState() {
super.initState();
if (enableSlide && GStorage.collapsibleVideoPage) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
_isInit = false;
});
}
});
}
}
@override
Widget build(BuildContext context) {
if (enableSlide && GStorage.collapsibleVideoPage && _isInit) {
return CustomScrollView(
physics: const NeverScrollableScrollPhysics(),
);
}
return super.build(context);
}
}

View File

@@ -29,7 +29,9 @@ abstract class CommonController extends GetxController {
LoadingState response = await customGetData();
if (response is Success) {
if (!customHandleResponse(response)) {
isEnd = response.response.isEmpty;
if ((response.response as List?).isNullOrEmpty) {
isEnd = true;
}
List currentList = loadingState.value is Success
? (loadingState.value as Success).response
: [];
@@ -64,9 +66,9 @@ abstract class CommonController extends GetxController {
scrollController.animToTop();
}
void onReload() {
Future onReload() async {
loadingState.value = LoadingState.loading();
onRefresh();
await onRefresh();
}
@override

View File

@@ -33,9 +33,6 @@ abstract class CommonPublishPage extends StatefulWidget {
final int? imageLengthLimit;
final ValueChanged<String>? onSave;
final bool autofocus;
@override
State<CommonPublishPage> createState();
}
abstract class CommonPublishPageState<T extends CommonPublishPage>
@@ -72,7 +69,7 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
}
@override
void dispose() async {
void dispose() {
focusNode.dispose();
editController.dispose();
WidgetsBinding.instance.removeObserver(this);

View File

@@ -0,0 +1,91 @@
import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
abstract class CommonSlidePage extends StatefulWidget {
const CommonSlidePage({super.key, this.enableSlide});
final bool? enableSlide;
}
abstract class CommonSlidePageState<T extends CommonSlidePage>
extends State<T> {
Offset? downPos;
bool? isSliding;
late double padding = 0.0;
late final enableSlide =
widget.enableSlide != false && GStorage.slideDismissReplyPage;
@override
Widget build(BuildContext context) {
return enableSlide
? Padding(
padding: EdgeInsets.only(top: padding),
child: buildPage,
)
: buildPage;
}
Widget get buildPage;
Widget get buildList => throw UnimplementedError();
Widget slideList([Widget? buildList]) => GestureDetector(
onPanDown: (event) {
if (event.localPosition.dx > 30) {
isSliding = false;
} else {
downPos = event.localPosition;
}
},
onPanUpdate: (event) {
if (isSliding == false) {
return;
} else if (isSliding == null) {
if (downPos != null) {
Offset cumulativeDelta = event.localPosition - downPos!;
if (cumulativeDelta.dx.abs() >= cumulativeDelta.dy.abs()) {
isSliding = true;
setState(() {
padding = event.localPosition.dx;
});
} else {
isSliding = false;
}
}
} else if (isSliding == true) {
setState(() {
padding = event.localPosition.dx;
});
}
},
onPanCancel: () {
if (isSliding == true) {
if (padding >= 100) {
Get.back();
} else {
setState(() {
padding = 0;
});
}
}
downPos = null;
isSliding = null;
},
onPanEnd: (event) {
if (isSliding == true) {
if (padding >= 100) {
Get.back();
} else {
setState(() {
padding = 0;
});
}
}
downPos = null;
isSliding = null;
},
child: buildList ?? this.buildList,
);
}

View File

@@ -1,4 +1,9 @@
import 'dart:convert';
import 'dart:io';
import 'package:PiliPlus/grpc/app/main/community/reply/v1/reply.pb.dart';
import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/reply.dart';
import 'package:PiliPlus/models/common/reply_type.dart';
@@ -32,8 +37,14 @@ abstract class ReplyController extends CommonController {
late Rx<Mode> mode = Mode.MAIN_LIST_HOT.obs;
late bool hasUpTop = false;
late final banWordForReply = GStorage.banWordForReply;
late final enableCommAntifraud = GStorage.enableCommAntifraud;
late final antiGoodsReply = GStorage.antiGoodsReply;
// comment antifraud
late final _enableCommAntifraud = GStorage.enableCommAntifraud;
late final _biliSendCommAntifraud = GStorage.biliSendCommAntifraud;
bool get enableCommAntifraud =>
_enableCommAntifraud || _biliSendCommAntifraud;
dynamic get sourceId;
@override
void onInit() {
@@ -105,7 +116,7 @@ abstract class ReplyController extends CommonController {
} else if (loadingState.value is Success) {
replies.insertAll(0, (loadingState.value as Success).response.replies);
}
if (replies.length >= count.value) {
if (isEnd.not && replies.length >= count.value) {
isEnd = true;
}
loadingState.value = LoadingState.success(response.response);
@@ -128,8 +139,7 @@ abstract class ReplyController extends CommonController {
break;
}
nextOffset = '';
loadingState.value = LoadingState.loading();
onRefresh();
onReload();
});
}
@@ -209,14 +219,22 @@ abstract class ReplyController extends CommonController {
loadingState.value = LoadingState.success(response);
if (enableCommAntifraud && context.mounted) {
checkReply(
context,
oid ?? replyItem.oid.toInt(),
replyItem?.id.toInt(),
replyItem?.type.toInt() ??
context: context,
oid: oid ?? replyItem.oid.toInt(),
rpid: replyItem?.id.toInt(),
replyType: replyItem?.type.toInt() ??
replyType?.index ??
ReplyType.video.index,
replyInfo.id.toInt(),
replyInfo.content.message,
replyId: replyInfo.id.toInt(),
message: replyInfo.content.message,
//
root: replyInfo.root.toInt(),
parent: replyInfo.parent.toInt(),
ctime: replyInfo.ctime.toInt(),
pictures: replyInfo.content.pictures
.map((item) => item.toProto3Json())
.toList(),
mid: replyInfo.mid.toInt(),
);
}
} else {
@@ -235,14 +253,20 @@ abstract class ReplyController extends CommonController {
loadingState.value = LoadingState.success(response);
if (enableCommAntifraud && context.mounted) {
checkReply(
context,
oid ?? replyItem.oid,
replyItem?.rpid,
replyItem?.type.toInt() ??
context: context,
oid: oid ?? replyItem.oid,
rpid: replyItem?.rpid,
replyType: replyItem?.type.toInt() ??
replyType?.index ??
ReplyType.video.index,
replyInfo.rpid ?? 0,
replyInfo.content?.message ?? '',
replyId: replyInfo.rpid ?? 0,
message: replyInfo.content?.message ?? '',
//
root: replyInfo.root,
parent: replyInfo.parent,
ctime: replyInfo.ctime,
pictures: replyInfo.content?.pictures,
mid: replyInfo.mid,
);
}
}
@@ -290,14 +314,52 @@ abstract class ReplyController extends CommonController {
}
// ref https://github.com/freedom-introvert/biliSendCommAntifraud
void checkReply(
BuildContext context,
dynamic oid,
dynamic rpid,
int replyType,
int replyId,
String message,
) async {
void checkReply({
required BuildContext context,
required dynamic oid,
required dynamic rpid,
required int replyType,
required int replyId,
required String message,
dynamic root,
dynamic parent,
dynamic ctime,
dynamic pictures,
dynamic mid,
}) async {
// biliSendCommAntifraud
if (Platform.isAndroid && _biliSendCommAntifraud) {
try {
List<Cookie> cookies = await Request.cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.apiBaseUrl));
final String cookieString = cookies
.map((Cookie cookie) => '${cookie.name}=${cookie.value}')
.join(';');
Utils.channel.invokeMethod(
'biliSendCommAntifraud',
{
'action': 0,
'oid': oid,
'type': replyType,
'rpid': replyId,
'root': root,
'parent': parent,
'ctime': ctime,
'comment_text': message,
if (pictures.isNotEmpty == true) 'pictures': jsonEncode(pictures),
'source_id': '$sourceId',
'uid': mid,
'cookies': [cookieString],
},
);
} catch (e) {
debugPrint('biliSendCommAntifraud: $e');
}
return;
}
// CommAntifraud
await Future.delayed(const Duration(seconds: 5));
void showReplyCheckResult(String message) {
showDialog(
context: context,
@@ -308,7 +370,6 @@ abstract class ReplyController extends CommonController {
);
}
await Future.delayed(const Duration(seconds: 5));
if (context.mounted.not) return;
// root reply
if (rpid == null) {
@@ -320,7 +381,8 @@ abstract class ReplyController extends CommonController {
type: replyType,
sort: ReplySortType.time.index,
page: 1,
banWordForReply: '',
enableFilter: false,
antiGoodsReply: false,
);
if (context.mounted.not) return;
if (res is Error) {
@@ -347,7 +409,8 @@ abstract class ReplyController extends CommonController {
root: rpid ?? replyId,
pageNum: 1,
type: replyType,
banWordForReply: '',
filterBanWord: false,
antiGoodsReply: false,
);
if (context.mounted.not) return;
if (res1 is Error) {
@@ -367,8 +430,9 @@ abstract class ReplyController extends CommonController {
root: rpid ?? replyId,
pageNum: 1,
type: replyType,
banWordForReply: '',
filterBanWord: false,
isCheck: true,
antiGoodsReply: false,
);
if (context.mounted.not) return;
if (res2 is Error) {
@@ -403,8 +467,9 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
root: rpid ?? replyId,
pageNum: i,
type: replyType,
banWordForReply: '',
filterBanWord: false,
isCheck: true,
antiGoodsReply: false,
);
if (res3 is Error) {
break;
@@ -437,8 +502,9 @@ https://api.bilibili.com/x/v2/reply/reply?oid=$oid&pn=1&ps=20&root=${rpid ?? rep
root: rpid ?? replyId,
pageNum: i,
type: replyType,
banWordForReply: '',
filterBanWord: false,
isCheck: true,
antiGoodsReply: false,
);
if (res4 is Error) {
break;

View File

@@ -44,7 +44,7 @@ class PlDanmakuController {
}
assert(requestedSeg[segmentIndex] == false);
requestedSeg[segmentIndex] = true;
final DmSegMobileReply result = await DanmakaHttp.queryDanmaku(
final DmSegMobileReply result = await DanmakuHttp.queryDanmaku(
cid: cid,
segmentIndex: segmentIndex + 1,
mergeDanmaku: plPlayerController.mergeDanmaku,
@@ -69,7 +69,7 @@ class PlDanmakuController {
queryDanmaku(segmentIndex);
}
if (plPlayerController.danmakuWeight == 0 &&
plPlayerController.danmakuFilterRule.isEmpty) {
plPlayerController.filterCount == 0) {
return dmSegMap[progress ~/ 100];
} else {
return dmSegMap[progress ~/ 100]
@@ -81,26 +81,9 @@ class PlDanmakuController {
}
bool filterDanmaku(DanmakuElem elem) {
for (var filter in plPlayerController.danmakuFilterRule) {
switch (filter['type']) {
case 0:
if (elem.content.contains(filter['filter'])) {
return false;
}
break;
case 1:
if (RegExp(filter['filter'], caseSensitive: false)
.hasMatch(elem.content)) {
return false;
}
break;
case 2:
if (elem.idStr == filter['filter']) {
return false;
}
break;
}
}
return true;
return !(plPlayerController.dmUid.contains(elem.midHash) ||
plPlayerController.dmFilterString
.any((i) => elem.content.contains(i)) ||
plPlayerController.dmRegExp.any((i) => i.hasMatch(elem.content)));
}
}

View File

@@ -117,6 +117,9 @@ class _PlDanmakuState extends State<PlDanmaku> {
e.colorful == DmColorfulType.VipGradualColor
? true
: null,
count: widget.playerController.mergeDanmaku && e.hasAttr()
? e.attr
: null,
),
);
}

View File

@@ -6,6 +6,7 @@ import 'package:PiliPlus/utils/storage.dart';
import '../../http/danmaku_block.dart';
import '../../models/user/danmaku_block.dart';
import '../../plugin/pl_player/controller.dart';
import 'package:PiliPlus/common/widgets/spring_physics.dart';
class DanmakuBlockPage extends StatefulWidget {
const DanmakuBlockPage({super.key});
@@ -36,21 +37,38 @@ class _DanmakuBlockPageState extends State<DanmakuBlockPage> {
}
@override
void dispose() async {
void dispose() {
final regExp = RegExp(r'^/(.*)/$');
List<Map<String, dynamic>> simpleRuleList = _danmakuBlockController
.ruleTypes.values
.expand((element) => element)
.map<Map<String, dynamic>>((e) {
//当正则表达式前后都有"/"时去掉避免RegExp解析错误
if (e.type == 1 && e.filter.startsWith('/') && e.filter.endsWith('/')) {
e.filter = e.filter.substring(1, e.filter.length - 1);
if (e.type == 1) {
String? filter = regExp.firstMatch(e.filter)?.group(1);
if (filter != null) {
e.filter = filter;
}
}
return e.toMap();
}).toList();
// debugPrint("simpleRuleList:$simpleRuleList");
plPlayerController.danmakuFilterRule = simpleRuleList;
plPlayerController.filterCount = simpleRuleList.length;
for (var item in simpleRuleList) {
switch (item['type']) {
case 0:
plPlayerController.dmFilterString.add(item['filter']);
break;
case 1:
plPlayerController.dmRegExp
.add(RegExp(item['filter'], caseSensitive: false));
break;
case 2:
plPlayerController.dmUid.add(item['filter']);
break;
}
}
scrollController.dispose();
await GStorage.localCache.delete(LocalCacheKey.danmakuFilterRule);
GStorage.localCache.put(LocalCacheKey.danmakuFilterRule, simpleRuleList);
super.dispose();
}
@@ -114,17 +132,14 @@ class _DanmakuBlockPageState extends State<DanmakuBlockPage> {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: TabBar(
controller: _danmakuBlockController.tabController,
dividerColor: Colors.transparent,
tabs: [
for (var i = 0; i < ruleLabels.length; i++)
Obx(() => Tab(
text:
'${ruleLabels[i]}(${_danmakuBlockController.ruleTypes[i]!.length})')),
]),
title: TabBar(controller: _danmakuBlockController.tabController, tabs: [
for (var i = 0; i < ruleLabels.length; i++)
Obx(() => Tab(
text:
'${ruleLabels[i]}(${_danmakuBlockController.ruleTypes[i]!.length})')),
]),
),
body: TabBarView(
body: tabBarView(
controller: _danmakuBlockController.tabController,
children: [
for (var i = 0; i < ruleLabels.length; i++)

View File

@@ -34,7 +34,7 @@ class DynamicsController extends GetxController
int allFollowedUpsTotal = 0;
late int currentMid = -1;
late bool showLiveItems = false;
late bool showLiveItems = GStorage.expandDynLivePanel;
@override
void onInit() {
@@ -162,11 +162,9 @@ class DynamicsController extends GetxController
onRefresh() async {
queryFollowUp();
await Future.wait(<Future>[
Get.find<DynamicsTabController>(
tag: tabsConfig[tabController.index]['tag'])
.onRefresh()
]);
await Get.find<DynamicsTabController>(
tag: tabsConfig[tabController.index]['tag'])
.onRefresh();
}
// 返回顶部并刷新

Some files were not shown because too many files have changed in this diff Show More