Compare commits

...

373 Commits

Author SHA1 Message Date
bggRGjQaUbCoE
34f63612a4 fix: #517
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-24 14:29:54 +08:00
bggRGjQaUbCoE
edfa9a8dd1 mod: live: only play audio
Closes #465

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-24 14:07:42 +08:00
bggRGjQaUbCoE
95fa19f121 opt: dm widget
Closes #160
Closes #515

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-24 13:44:54 +08:00
bggRGjQaUbCoE
79d0d314f5 fix: #516
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-24 13:12:36 +08:00
bggRGjQaUbCoE
ddbf168c87 chore: update dm dep
Closes #511

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-24 11:37:02 +08:00
bggRGjQaUbCoE
0eee8bbac2 mod: def hwdec
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-24 10:50:51 +08:00
bggRGjQaUbCoE
92f02b5943 fix: search
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-24 10:20:43 +08:00
bggRGjQaUbCoE
f110c2a55f fix: fav search
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-24 10:16:01 +08:00
bggRGjQaUbCoE
8ddf42fff1 opt: switch btn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-24 00:19:25 +08:00
My-Responsitories
d2c34d64c3 fix #510 2025-03-23 23:17:18 +08:00
bggRGjQaUbCoE
2341027972 mod: show switch btn when playall
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 22:19:13 +08:00
bggRGjQaUbCoE
006c9301d9 fix: follow staff
related #481

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 21:53:26 +08:00
bggRGjQaUbCoE
1f8955d0b3 opt: prev/next play
Closes #506

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 21:47:12 +08:00
bggRGjQaUbCoE
5a758ebb3a opt: play all
Closes #503

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 20:49:29 +08:00
bggRGjQaUbCoE
97bef56006 mod: live qa
Closes #464

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 20:35:16 +08:00
bggRGjQaUbCoE
96ba36ed67 fix: def subtitle stroke
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 20:34:59 +08:00
My-Responsitories
edf84fcc8f opt: blacklist (#501) 2025-03-23 11:13:07 +00:00
bggRGjQaUbCoE
a8428e52d2 opt: save subtitle
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 17:03:56 +08:00
bggRGjQaUbCoE
fd8559228e feat: save subtitle
Closes #495

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 16:27:38 +08:00
My-Responsitories
d6587cf3b6 feat: logout (#497)
* feat: logout

* update api type
2025-03-23 13:46:26 +08:00
My-Responsitories
7c3e3cb1f8 some fixes (#498)
* nologin reject headtbeat

* fix: change anonymity

* use account as key
2025-03-23 13:46:04 +08:00
bggRGjQaUbCoE
9d0ac30fad feat: follow staff
Closes #481

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 13:40:44 +08:00
bggRGjQaUbCoE
68d11d7638 feat: custom subtitle fontweight
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-23 12:19:25 +08:00
My-Responsitories
3881b3dc74 feat: retry before sending (#489)
* feat: retry before sending

* reduce idleTimeout
2025-03-23 12:09:11 +08:00
My-Responsitories
99b14d0f0e opt: danmaku filter (#486) 2025-03-23 12:07:57 +08:00
My-Responsitories
066f3d4132 some fix (#480)
* fix dislikeVideo

* fix profile update

* show login toast
2025-03-23 12:06:34 +08:00
My-Responsitories
b15fdfa2ff feat: account manager (#468)
* feat: account manager

* remove dep

* some fixes

* migrate accounts

* reimplement clearCookie
2025-03-19 13:19:32 +08:00
bggRGjQaUbCoE
94fa0652ac fix: #470
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-17 18:34:49 +08:00
bggRGjQaUbCoE
5c54e131ba opt: loadingwidget
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-17 14:00:11 +08:00
bggRGjQaUbCoE
1fc85fd618 mod: medialist: del btn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-17 13:59:47 +08:00
bggRGjQaUbCoE
e1c561b613 fix: #467
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-17 00:45:35 +08:00
bggRGjQaUbCoE
950620bf9e opt: dyn panel
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-16 21:26:37 +08:00
bggRGjQaUbCoE
ae7a1e2373 mod: dyn: show medialist type
Closes #462

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-16 20:50:04 +08:00
bggRGjQaUbCoE
bddeb72d9b fix: medialist: del btn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-16 18:44:32 +08:00
bggRGjQaUbCoE
b99cf4f629 opt: playall
Closes #450

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-16 18:19:22 +08:00
bggRGjQaUbCoE
b07cf62bdd mod: medialist: show del btn
Closes #451

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-16 16:36:43 +08:00
bggRGjQaUbCoE
57d2d3f5d9 fix: video page theme
mod: popscope

related #459

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-16 15:33:55 +08:00
My-Responsitories
7854c5e6b9 split report dialog (#460) 2025-03-16 13:34:04 +08:00
bggRGjQaUbCoE
0b8e95477c mod: handle show viewpoints btn
Closes #457

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-16 11:22:20 +08:00
dom
c2e9a7deb3 Update 功能请求.yml 2025-03-16 10:38:47 +08:00
bggRGjQaUbCoE
361a6a4c1d fix: get args
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-15 19:55:47 +08:00
bggRGjQaUbCoE
8eca9a6644 opt: live: msg bg
Closes #448

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-15 16:19:09 +08:00
bggRGjQaUbCoE
0770f325ab feat: subtitle stroke
Closes #446

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-15 15:46:07 +08:00
bggRGjQaUbCoE
98d52760b3 mod: pages
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-15 15:27:16 +08:00
bggRGjQaUbCoE
0ebe976b8a mod: custom enableSlideVolumeBrightness
Closes #439

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-14 19:47:22 +08:00
bggRGjQaUbCoE
21fe0ef288 mod: search from tag
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-14 12:19:30 +08:00
bggRGjQaUbCoE
28ef1890d1 fix: #438
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-13 22:08:06 +08:00
bggRGjQaUbCoE
d6f238c720 fix: get sortField
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-13 22:07:32 +08:00
bggRGjQaUbCoE
44bf9dd9e1 opt: dyn panel
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-13 13:49:39 +08:00
bggRGjQaUbCoE
e357da5162 opt: dyn panel
Closes #436

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-13 12:27:36 +08:00
bggRGjQaUbCoE
c296aa036a fix: img preview quality
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-13 11:54:22 +08:00
bggRGjQaUbCoE
12c46f938d mod: split dm/sub settings, add reset btn
Closes #223
Closes #361

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-12 18:27:08 +08:00
bggRGjQaUbCoE
b4412f5b37 opt: some widgets
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-12 16:45:21 +08:00
bggRGjQaUbCoE
5f2ac0d59b fix: ctr tag
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-12 09:37:10 +08:00
bggRGjQaUbCoE
2a73725455 opt: live room
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 22:25:13 +08:00
bggRGjQaUbCoE
a8725e64ee opt: live room
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 21:51:16 +08:00
bggRGjQaUbCoE
727ae8cd2b opt: live room
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 21:35:47 +08:00
bggRGjQaUbCoE
714f288170 opt: live room
Closes #427

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 21:10:37 +08:00
bggRGjQaUbCoE
3da64d2641 fix: share video
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 14:29:05 +08:00
My-Responsitories
2556290a6e fix #424 2025-03-11 14:11:13 +08:00
bggRGjQaUbCoE
66b547a904 fix: #424
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 13:59:58 +08:00
bggRGjQaUbCoE
c1ab273478 opt: video: long press
Closes #423

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 13:54:31 +08:00
bggRGjQaUbCoE
4aa3d5f273 opt: video sheet
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 13:44:24 +08:00
bggRGjQaUbCoE
a6a1de169b fix: video theme, view later
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 11:54:44 +08:00
bggRGjQaUbCoE
af6188be77 opt: live room bg
Closes #422

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-11 11:54:17 +08:00
bggRGjQaUbCoE
bd39de2109 opt: video dialog
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-10 23:56:51 +08:00
bggRGjQaUbCoE
33375aeb7d feat: dark video page
Closes #420

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-10 23:33:33 +08:00
bggRGjQaUbCoE
fafe6c1e91 opt: video page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-10 21:40:59 +08:00
bggRGjQaUbCoE
68b072bf44 mod: remove unused import
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-10 20:19:43 +08:00
My-Responsitories
99cdec62a1 view later (#419) 2025-03-10 18:46:07 +08:00
bggRGjQaUbCoE
59797a2f5f mod: jump from invalid fav item
Closes #416

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-10 16:52:54 +08:00
bggRGjQaUbCoE
5cc661e314 opt: video width
Closes #417

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-10 15:54:38 +08:00
bggRGjQaUbCoE
2ce79d21b5 opt: view invalid user space
Closes #414

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-10 15:14:36 +08:00
bggRGjQaUbCoE
b75fda3596 mod: common ctr
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-10 11:12:52 +08:00
bggRGjQaUbCoE
2efa6f4ace mod: jump from cvid
Closes #405

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-09 21:42:47 +08:00
bggRGjQaUbCoE
95e50e436b mod: option show dyn actionbar
Closes #412

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-09 21:22:19 +08:00
bggRGjQaUbCoE
35a53bc8ac mod: option shrink video
Closes #410

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-09 21:00:24 +08:00
bggRGjQaUbCoE
58c16ef52e chore: add episode info api
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-09 20:49:53 +08:00
bggRGjQaUbCoE
847ac80d5f opt: viewpoints page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-09 10:09:54 +08:00
bggRGjQaUbCoE
0408b27ca5 mod: check reply manually
Closes #407

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-09 09:50:06 +08:00
My-Responsitories
2949adbbfd fix: msg_feed insert type error (#411)
* fix: msg_feed insert type error

* jump to dyn secondary reply
2025-03-09 09:49:16 +08:00
dom
2f616ba237 Update and rename 功能请求.md to 功能请求.yml 2025-03-08 20:01:16 +08:00
dom
b50ead327c Update and rename bug-反馈.md to bug-反馈.yml 2025-03-08 19:56:06 +08:00
bggRGjQaUbCoE
2fe0f43cb6 fix: #404
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-08 08:33:03 +08:00
bggRGjQaUbCoE
b85413be9b opt: video bg
Closes #397

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-03-07 15:04:16 +08:00
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
bggRGjQaUbCoE
84cc65489f mod: scheme match
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-13 17:51:52 +08:00
bggRGjQaUbCoE
2b9cb54d91 opt: view from playlist
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-13 17:15:48 +08:00
dom
54c7fef217 opt: jump url (#246)
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-13 16:53:40 +08:00
bggRGjQaUbCoE
ba74cb8c01 opt: video bottom control
Closes #244

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-13 15:04:44 +08:00
bggRGjQaUbCoE
675932aa69 mod: try-catch some requests
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 22:17:01 +08:00
bggRGjQaUbCoE
d996e0a7dd fix: #240
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 21:52:27 +08:00
bggRGjQaUbCoE
b6279f702a fix: #239
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 20:48:14 +08:00
bggRGjQaUbCoE
695a89b91a opt: view pgc section
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 16:14:59 +08:00
bggRGjQaUbCoE
09753b6bbd fix: #226
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 14:20:16 +08:00
bggRGjQaUbCoE
6502b97388 mod: pgc coin
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 13:24:46 +08:00
bggRGjQaUbCoE
95d84647b7 opt: coin page checkbox
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 12:59:30 +08:00
bggRGjQaUbCoE
8f5065332e fix: intro up verify
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 12:59:23 +08:00
bggRGjQaUbCoE
71c8cbb8da fix: #232
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 12:39:31 +08:00
bggRGjQaUbCoE
3217731486 mod: coin with like
Closes #231

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 12:20:06 +08:00
bggRGjQaUbCoE
a4e63fe0e8 mod: video intro: show detailed owner info
Closes #229

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-12 12:27:29 +08:00
bggRGjQaUbCoE
cdb8f6845c mod: history card menu
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-11 21:50:59 +08:00
bggRGjQaUbCoE
0a7d286c47 mod: reply2reply header
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-11 21:14:35 +08:00
bggRGjQaUbCoE
e17fd0071d mod: forwarded live dyn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-11 21:14:35 +08:00
bggRGjQaUbCoE
a9ba30b9b9 mod: show dyn gif emote
mod: emote tabbar

opt: video progress indicator

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-11 18:30:01 +08:00
bggRGjQaUbCoE
4267a3b8e0 mod: member archive: show progress
Closes #225

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-11 17:21:06 +08:00
bggRGjQaUbCoE
50022ae635 fix: whisper: null check
related #217

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-11 15:20:52 +08:00
bggRGjQaUbCoE
0991621152 mod: dyn action
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-11 14:53:17 +08:00
bggRGjQaUbCoE
192f8924c8 fix: #217
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-11 14:07:43 +08:00
bggRGjQaUbCoE
51a12d7266 mod: minor tweaks
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-11 12:05:21 +08:00
bggRGjQaUbCoE
1417fcda6e fix: seek anim
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 21:33:57 +08:00
bggRGjQaUbCoE
6114e6f033 opt: restore scale btn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 19:58:44 +08:00
bggRGjQaUbCoE
bc2dbc59ce mod: video scale set
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 18:51:41 +08:00
bggRGjQaUbCoE
7c5075413e mod: add restore video scale button
related #222

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 18:31:22 +08:00
bggRGjQaUbCoE
52175b0b69 mod: show reply gif emote
Closes #212

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 16:22:48 +08:00
bggRGjQaUbCoE
f0a3515279 opt: search aid/bvid
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 13:46:41 +08:00
bggRGjQaUbCoE
3c2ccf7d40 mod: check av/bv search
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 13:31:50 +08:00
bggRGjQaUbCoE
abd01e1a27 fix: reply cv jump
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 13:14:40 +08:00
bggRGjQaUbCoE
0f63976a00 mod: reply jump
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 12:04:13 +08:00
bggRGjQaUbCoE
6817eb6e56 fix: reply jump url
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 11:13:55 +08:00
bggRGjQaUbCoE
a951d42623 mod: web down
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-10 11:13:54 +08:00
bggRGjQaUbCoE
8f5c2bf3ba mod: dyn article: show block type
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-09 22:47:31 +08:00
bggRGjQaUbCoE
7744217d17 mod: grpc reply: jump to vote
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-09 20:59:58 +08:00
bggRGjQaUbCoE
a84c153bdd fix: later request
log #214

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-09 18:35:01 +08:00
bggRGjQaUbCoE
31a0a90ba4 mod: reply2relpy header
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-09 15:43:57 +08:00
bggRGjQaUbCoE
383ce777e3 mod: webview: handle download request
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-09 10:44:46 +08:00
bggRGjQaUbCoE
e7ac88ffb1 opt: reply2reply header
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-09 09:36:26 +08:00
bggRGjQaUbCoE
9657c77999 mod: push article
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-09 09:36:26 +08:00
bggRGjQaUbCoE
afd508f28b opt: persistent header
Closes #211

ref pilipala

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-09 00:25:25 +08:00
bggRGjQaUbCoE
634612c1a2 fix: article
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-08 23:50:17 +08:00
bggRGjQaUbCoE
76545397d4 mod: video push
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-08 23:22:25 +08:00
bggRGjQaUbCoE
d2f586a7f1 fix: push bangumi
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-08 23:08:58 +08:00
bggRGjQaUbCoE
7cfebcb6ed opt: webview to video
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-08 22:56:43 +08:00
bggRGjQaUbCoE
9a3766e7b7 opt: webview to video
Closes #209

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-08 21:41:19 +08:00
bggRGjQaUbCoE
588a06bece opt: article content
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-07 17:40:45 +08:00
bggRGjQaUbCoE
e45a126862 fix: handleWebview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-07 16:39:33 +08:00
bggRGjQaUbCoE
a581945c9e feat: interactive video
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-07 15:38:33 +08:00
bggRGjQaUbCoE
331fd0d619 mod: intro panel
opt: pgc page

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-07 15:24:03 +08:00
bggRGjQaUbCoE
c6e229d571 fix: replay
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-06 12:09:29 +08:00
bggRGjQaUbCoE
b2c3b1ff95 fix: #199
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-06 12:09:08 +08:00
bggRGjQaUbCoE
3fc12fcc09 mod: widget
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-05 16:55:10 +08:00
bggRGjQaUbCoE
e098631553 mod: dyn square type
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-05 13:49:37 +08:00
bggRGjQaUbCoE
0fcd55755e mod: handleWebview
Closes #194

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-05 13:36:44 +08:00
bggRGjQaUbCoE
65e7c0c4f4 opt: pages
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-05 12:19:45 +08:00
bggRGjQaUbCoE
70aecd1e38 mod: view point
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-04 14:38:06 +08:00
bggRGjQaUbCoE
a40c773491 fix: interceptor
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-04 13:09:52 +08:00
bggRGjQaUbCoE
b4abb58a41 mod: seg bar, dyn decorate
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-04 11:33:23 +08:00
bggRGjQaUbCoE
e368436bc6 feat: reply: sync to dyn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-03 11:43:42 +08:00
bggRGjQaUbCoE
6c96b3a7f5 fix: check reply url
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-03 10:49:25 +08:00
bggRGjQaUbCoE
149f0c082d fix: reply2reply mode
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-03 10:20:30 +08:00
bggRGjQaUbCoE
994199b5a2 fix: check reply
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-02 23:44:47 +08:00
bggRGjQaUbCoE
8db3d80151 fix: onVideoDetailChange
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-02 22:29:25 +08:00
bggRGjQaUbCoE
93af1e7c44 opt: reply check
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-02 22:29:10 +08:00
dom
54e90bd986 feat: comment antifraud (#193)
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-02 21:24:07 +08:00
bggRGjQaUbCoE
ca16551917 mod: dm chart height
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-02 17:18:21 +08:00
bggRGjQaUbCoE
f4977d2855 mod: def hardwareDecoding
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-01 18:04:16 +08:00
bggRGjQaUbCoE
bd91fb7c6d mod: show volume when hiding sysui for ios
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-02-01 17:59:22 +08:00
bggRGjQaUbCoE
e1805896f4 Revert "opt: dm chart"
This reverts commit 31a639400e.
2025-01-31 20:40:18 +08:00
bggRGjQaUbCoE
31a639400e opt: dm chart
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-31 16:20:59 +08:00
bggRGjQaUbCoE
d6b24561fa fix: dm chart x
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-31 13:32:07 +08:00
dom
7ba9646d38 feat: danmaku chart (#192)
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-31 11:36:05 +08:00
bggRGjQaUbCoE
58a7cf1e75 fix: image preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-30 18:03:15 +08:00
bggRGjQaUbCoE
1a327198f7 opt: video: onreset
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-30 15:52:59 +08:00
bggRGjQaUbCoE
e4fe91ef92 Update README.md
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-30 14:22:20 +08:00
bggRGjQaUbCoE
afcf817c4f fix: video duration
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-30 13:57:21 +08:00
bggRGjQaUbCoE
21550815db fix: seek preview image
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-30 12:11:44 +08:00
bggRGjQaUbCoE
02af3a18ff opt: video seek preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-30 10:08:15 +08:00
bggRGjQaUbCoE
a5a13b45cf fix: seek preview index
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-29 23:45:10 +08:00
bggRGjQaUbCoE
0fd232ab3a feat: video seek preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-29 21:20:58 +08:00
bggRGjQaUbCoE
8d83143ca6 opt: fav
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-29 15:29:20 +08:00
bggRGjQaUbCoE
74452cd622 mod: save as livephoto for ios
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-29 12:07:00 +08:00
bggRGjQaUbCoE
cf2e8cec54 fix: horizontal preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-28 23:27:46 +08:00
bggRGjQaUbCoE
5231faf254 opt: pages
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-28 21:57:57 +08:00
bggRGjQaUbCoE
959d4de78a opt: image preview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-28 18:33:40 +08:00
bggRGjQaUbCoE
f5d7dc6b6a feat: live photo
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-28 15:40:19 +08:00
bggRGjQaUbCoE
b761c35d10 mod: show pendant/decorate
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-28 10:18:57 +08:00
bggRGjQaUbCoE
7f3f7f6bdd mod: dyn author panel
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 21:50:35 +08:00
bggRGjQaUbCoE
c5877b7c5e feat: custom show dyn decorate
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 21:21:28 +08:00
bggRGjQaUbCoE
9e4187ef17 mod: fetch only-fans dyn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 18:01:32 +08:00
bggRGjQaUbCoE
bf7ce3e5a2 mod: delay reloading fav
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 17:04:25 +08:00
bggRGjQaUbCoE
2c55314491 fix: dialog title
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 16:21:00 +08:00
bggRGjQaUbCoE
d28efef672 feat: copy/move toview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 15:17:11 +08:00
bggRGjQaUbCoE
49b631d560 feat: copy/move fav
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 14:59:00 +08:00
bggRGjQaUbCoE
896510f852 mod: fav sheet
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 13:39:33 +08:00
bggRGjQaUbCoE
1d8e469a46 feat: clean fav
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 13:26:31 +08:00
bggRGjQaUbCoE
caee40a5d9 mod: sr desc
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-27 11:52:45 +08:00
bggRGjQaUbCoE
7de051e6bb fix: skip listener
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-26 20:55:29 +08:00
bggRGjQaUbCoE
18cec3c752 mod: update android icon
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-26 15:22:31 +08:00
bggRGjQaUbCoE
3b46655051 fix: cdn test
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-26 13:06:45 +08:00
bggRGjQaUbCoE
f72ad572fb fix: cdn test
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-26 11:50:08 +08:00
bggRGjQaUbCoE
a57ea2adb6 mod: remove androidNotificationIcon tmply
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-01-25 22:00:26 +08:00
367 changed files with 23945 additions and 16241 deletions

View File

@@ -1,23 +0,0 @@
---
name: Bug 反馈
about: 描述你所遇到的bug
title: "[Bug] "
labels: bug
assignees: ''
---
### 问题描述
请提供一个清晰而简明的问题描述。
### 复现步骤
请提供复现该问题所需的具体步骤。
### 预期行为
请描述你期望的正确行为或结果。
### 错误日志
请提供设置->关于->错误日志中的内容粘贴在下方代码框中。如果没有请提供您的app版本号、系统版本、设备型号等相关信息。
### 相关信息
请补充截图、录屏、BV号等其他有助于解决问题的信息。

51
.github/ISSUE_TEMPLATE/bug-反馈.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Bug 反馈
description: 描述你所遇到的bug
labels: [ "bug" ]
title: "[Bug] "
body:
- type: checkboxes
id: checklist
attributes:
label: 检查清单
options:
- label: 之前没有人提交过类似或相同的 bug report。
required: true
- label: 正在使用最新版本。
required: true
- type: textarea
id: bug
attributes:
label: 问题描述
description: 请提供一个清晰而简明的问题描述。
validations:
required: true
- type: textarea
id: steps
attributes:
label: 复现步骤
description: 请提供复现该问题所需的具体步骤。
validations:
required: true
- type: textarea
id: expected
attributes:
label: 预期行为
description: 请描述你期望的正确行为或结果。
validations:
required: true
- type: textarea
id: log
attributes:
label: 错误日志
description: 请提供设置->关于->错误日志中的内容粘贴在下方代码框中。如果没有请提供您的app版本号、系统版本、设备型号等相关信息。
- type: textarea
id: info
attributes:
label: 相关信息
description: 请补充截图、录屏、BV号等其他有助于解决问题的信息。

View File

@@ -1,20 +0,0 @@
---
name: 功能请求
about: 对于功能的一些建议
title: "[FR] "
labels: enhancement
assignees: ''
---
### 功能描述
请提供对所请求功能的清晰描述。
### 目标
请描述你希望通过这个功能实现的目标。
### 解决方案
如果你有任何关于如何实现这个功能的想法或建议,请在这里提供。
### 其他
请提供已实现该功能或类似功能的应用

43
.github/ISSUE_TEMPLATE/功能请求.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: 功能请求
description: 对于功能的一些建议
labels: [ "enhancement" ]
title: "[FR] "
body:
- type: checkboxes
id: checklist
attributes:
label: 检查清单
options:
- label: 之前没有人提交过类似或相同的功能请求。
required: true
- label: 正在使用最新版本。
required: true
- type: textarea
id: desc
attributes:
label: 功能描述
description: 请提供对所请求功能的清晰描述。
validations:
required: true
- type: textarea
id: propose
attributes:
label: 目标
description: 请描述你希望通过这个功能实现的目标。
validations:
required: true
- type: textarea
id: solution
attributes:
label: 解决方案
description: 如果你有任何关于如何实现这个功能的想法或建议,请在这里提供。
- type: textarea
id: addition
attributes:
label: 其他
description: 请提供已实现该功能或类似功能的应用

View File

@@ -47,6 +47,18 @@
## feat
- [x] 屏蔽带货动态/评论
- [x] 互动视频
- [x] 发评/动态反诈
- [x] 高能进度条
- [x] 滑动跳转预览视频缩略图
- [x] Live Photo
- [x] 复制/移动收藏夹/稍后再看视频
- [x] 超分辨率
- [x] 合并弹幕
- [x] 会员彩色弹幕
- [x] 播放全部/继续播放/倒序播放
- [x] Cookie登录
- [x] 显示视频分段信息
- [x] 调节字幕大小
- [x] 调节全屏弹幕大小
@@ -73,8 +85,8 @@
- [x] 筛选搜索
- [x] 转发动态
- [x] 合集图片
- [x] 删除/置顶私信
- [x] 举报用户/评论/视频
- [x] 删除/置顶/撤回私信
- [x] 举报用户/评论/视频/动态
- [x] 删除/发布文本/图片动态
- [x] 其他

View File

@@ -1,132 +0,0 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.piliplus">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission
android:name="android.permission.INTERNET"
/>
<application
android:label="PiliPlus Debug"
tools:replace="android:label">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
android:supportsPictureInPicture="true"
android:resizeableActivity="true"
>
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter android:label="PiliPlus Debug">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:host="*.bilibili.com"/>
<data android:host="*.bilibili.cn"/>
<data android:host="*.bilibili.tv"/>
<data android:host="bilibili.com"/>
<data android:host="bilibili.cn"/>
<data android:host="bilibili.tv"/>
<data android:host="b23.tv" />
<!--<data android:host="live.bilibili.com"/>-->
<!--<data android:host="www.bilibili.com"/>-->
<!--<data android:host="www.bilibili.tv"/>-->
<!--<data android:host="www.bilibili.cn"/>-->
<!--<data android:host="m.bilibili.cn"/>-->
<!--<data android:host="m.bilibili.com"/>-->
<!--<data android:host="bilibili.cn"/>-->
<!--<data android:host="bilibili.com"/>-->
<!--<data android:host="bangumi.bilibili.com"/>-->
<!--<data android:host="space.bilibili.com"/>-->
</intent-filter>
<intent-filter android:label="PiliPlus Debug">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="bilibili"/>
<data android:host="forward" />
<data android:host="comment"
android:pathPattern="/detail/.*/.*/.*" />
<data android:host="uper" />
<data android:host="article"
android:pathPattern="/readlist" />
<data android:host="advertise" android:path="/home" />
<data android:host="clip" />
<data android:host="search" />
<data android:host="stardust-search" />
<data android:host="music" />
<data android:host="bangumi"
android:pathPattern="/season.*" />
<data android:host="bangumi" android:pathPattern="/.*" />
<data android:host="pictureshow"
android:pathPrefix="/creative_center" />
<data android:host="cliparea" />
<data android:host="im" />
<data android:host="im" android:path="/notifications" />
<data android:host="following" />
<data android:host="following"
android:pathPattern="/detail/.*" />
<data android:host="following"
android:path="/publishInfo/" />
<data android:host="laser" android:pathPattern="/.*" />
<data android:host="livearea" />
<data android:host="live" />
<data android:host="catalog" />
<data android:host="browser" />
<data android:host="user_center" />
<data android:host="login" />
<data android:host="space" />
<data android:host="author" />
<data android:host="tag" />
<data android:host="rank" />
<data android:host="external" />
<data android:host="blank" />
<data android:host="home" />
<data android:host="root" />
<data android:host="video" />
<data android:host="story" />
<data android:host="podcast" />
<data android:host="search" />
<data android:host="main" android:path="/favorite" />
<data android:host="pgc" android:path="/theater/match" />
<data android:host="pgc" android:path="/theater/square" />
<data android:host="m.bilibili.com"
android:path="/topic-detail" />
<data android:host="article" />
<data android:host="pegasus"
android:pathPattern="/channel/v2/.*" />
<data android:host="feed" android:pathPattern="/channel" />
<data android:host="vip" />
<data android:host="user_center" android:path="/vip" />
<data android:host="history" />
<data android:host="charge" android:path="/rank" />
<data android:host="assistant" />
<data android:host="assistant" />
<data android:host="feedback" />
<data android:host="auth" android:path="/launch" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="512dp"
android:width="512dp"
android:viewportWidth="512.0"
android:viewportHeight="512.0">
<path
android:fillColor="#FF5CB67B"
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
android:strokeWidth="0.783784"
android:fillType="evenOdd" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
android:fillType="evenOdd" />
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FF5CB67B</color>
</resources>

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -1,16 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="512dp"
android:width="512dp"
android:viewportWidth="512.0"
android:viewportHeight="512.0">
android:height="108dp"
android:width="108dp"
android:viewportWidth="108.0"
android:viewportHeight="108.0">
<path
android:fillColor="#FF5CB67B"
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
android:strokeWidth="0.783784"
android:fillColor="@color/ic_launcher_foreground"
android:pathData="M56,54L39.78,54l2.22,-10.94h14c3.02,0 5.47,2.45 5.47,5.47 0,3.02 -2.45,5.47 -5.47,5.47zM56,35.77h-9.62l-7.13,36.45h7.51L48.92,61.29h7.08c7.05,0 12.76,-5.71 12.76,-12.76 0,-7.05 -5.71,-12.76 -12.76,-12.76z"
android:fillType="evenOdd" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
android:fillType="evenOdd" />
</vector>
</vector>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="512dp"
android:width="512dp"
android:viewportWidth="512.0"
android:viewportHeight="512.0">
<path
android:fillColor="#FF5CB67B"
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
android:strokeWidth="0.783784"
android:fillType="evenOdd" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
android:fillType="evenOdd" />
</vector>

View File

@@ -2,15 +2,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="512.0"
android:viewportHeight="512.0">
android:viewportWidth="108.0"
android:viewportHeight="108.0">
<path
android:fillColor="#FF5CB67B"
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
android:strokeWidth="0.783784"
android:fillType="evenOdd" />
<path
android:fillColor="#FFFFFFFF"
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
android:pathData="M57.54,54L28.82,54l3.93,-19.36h24.78c5.35,0 9.68,4.33 9.68,9.68 0,5.35 -4.33,9.68 -9.68,9.68zM57.54,21.73L40.5,21.73L27.88,86.27h13.3l3.83,-19.36h12.54c12.48,0 22.59,-10.11 22.59,-22.59 0,-12.48 -10.11,-22.59 -22.59,-22.59z"
android:strokeWidth="0.252073"
android:fillType="evenOdd" />
</vector>

View File

@@ -1,14 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground>
<inset
android:drawable="@drawable/ic_launcher_foreground"
android:inset="16%" />
</foreground>
<monochrome>
<inset
android:drawable="@drawable/ic_launcher_monochrome"
android:inset="16%" />
</monochrome>
</adaptive-icon>
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

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

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_foreground">@android:color/system_accent1_100</color>
<color name="ic_launcher_background">@android:color/system_neutral1_800</color>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_foreground">@android:color/system_neutral2_700</color>
<color name="ic_launcher_background">@android:color/system_accent1_100</color>
</resources>

View File

@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FF5CB67B</color>
<color name="ic_launcher_foreground">#FF5CB67B</color>
<color name="ic_launcher_background">#FFFFFFFF</color>
</resources>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

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

@@ -39,51 +39,55 @@ class VideoCardHSkeleton extends StatelessWidget {
),
// VideoContent(videoItem: videoItem)
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
width: 200,
height: 11,
margin: const EdgeInsets.only(bottom: 5),
),
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
width: 150,
height: 13,
),
const Spacer(),
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
width: 100,
height: 13,
margin: const EdgeInsets.only(bottom: 5),
),
Row(
children: [
Container(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
width: 40,
height: 13,
margin: const EdgeInsets.only(right: 8),
),
Container(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
width: 40,
height: 13,
),
],
)
],
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color:
Theme.of(context).colorScheme.onInverseSurface,
width: 200,
height: 11,
margin: const EdgeInsets.only(bottom: 5),
),
Container(
color:
Theme.of(context).colorScheme.onInverseSurface,
width: 150,
height: 13,
),
const Spacer(),
Container(
color:
Theme.of(context).colorScheme.onInverseSurface,
width: 100,
height: 13,
margin: const EdgeInsets.only(bottom: 5),
),
Row(
children: [
Container(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
width: 40,
height: 13,
margin: const EdgeInsets.only(right: 8),
),
Container(
color: Theme.of(context)
.colorScheme
.onInverseSurface,
width: 40,
height: 13,
),
],
)
],
),
),
)),
),
],
),
);

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

@@ -1,3 +1,5 @@
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
show SourceModel;
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/models/dynamics/article_content_model.dart';
import 'package:PiliPlus/utils/extension.dart';
@@ -9,7 +11,9 @@ Widget articleContent({
required BuildContext context,
required List<ArticleContentModel> list,
Function(List<String>, int)? callback,
required double maxWidth,
}) {
debugPrint('articleContent');
List<String>? imgList = list
.where((item) => item.pic != null)
.toList()
@@ -55,30 +59,28 @@ Widget articleContent({
),
);
} else if (item.pic != null) {
return LayoutBuilder(
builder: (context, constraints) => Hero(
tag: item.pic!.pics!.first.url!,
child: GestureDetector(
onTap: () {
if (callback != null) {
callback(
imgList,
imgList.indexOf(item.pic!.pics!.first.url!),
);
} else {
context.imageView(
initialPage: imgList.indexOf(item.pic!.pics!.first.url!),
imgList: imgList,
);
}
},
child: NetworkImgLayer(
width: constraints.maxWidth,
height: constraints.maxWidth *
item.pic!.pics!.first.height! /
item.pic!.pics!.first.width!,
src: item.pic!.pics!.first.url,
),
return Hero(
tag: item.pic!.pics!.first.url!,
child: GestureDetector(
onTap: () {
if (callback != null) {
callback(
imgList,
imgList.indexOf(item.pic!.pics!.first.url!),
);
} else {
context.imageView(
initialPage: imgList.indexOf(item.pic!.pics!.first.url!),
imgList: imgList.map((url) => SourceModel(url: url)).toList(),
);
}
},
child: NetworkImgLayer(
width: maxWidth,
height: maxWidth *
item.pic!.pics!.first.height! /
item.pic!.pics!.first.width!,
src: item.pic!.pics!.first.url,
),
),
);

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

@@ -12,6 +12,7 @@ class PBadge extends StatelessWidget {
final double? fs;
final String? semanticsLabel;
final bool bold;
final double? textScaleFactor;
const PBadge({
super.key,
@@ -26,6 +27,7 @@ class PBadge extends StatelessWidget {
this.fs = 11,
this.semanticsLabel,
this.bold = true,
this.textScaleFactor,
});
@override
@@ -71,6 +73,9 @@ class PBadge extends StatelessWidget {
),
child: Text(
text ?? "",
textScaler: textScaleFactor != null
? TextScaler.linear(textScaleFactor!)
: null,
style: TextStyle(
height: 1,
fontSize: fs ?? fontSize,

View File

@@ -19,8 +19,16 @@ class CustomSliverPersistentHeaderDelegate
//创建child子组件
//shrinkOffsetchild偏移值minExtent~maxExtent
//overlapsContentSliverPersistentHeader覆盖其他子组件返回true否则返回false
return ColoredBox(
color: bgColor,
return DecoratedBox(
decoration: BoxDecoration(
color: bgColor,
boxShadow: [
BoxShadow(
color: bgColor,
offset: const Offset(0, -2),
),
],
),
child: child,
);
}

View File

@@ -31,3 +31,37 @@ class CustomToast extends StatelessWidget {
);
}
}
class LoadingWidget extends StatelessWidget {
const LoadingWidget({super.key, required this.msg});
///loading msg
final String msg;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
decoration: BoxDecoration(
color: Theme.of(context).dialogBackgroundColor,
borderRadius: BorderRadius.circular(15),
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
//loading animation
CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation(
Theme.of(context).colorScheme.onSurfaceVariant),
),
//msg
Container(
margin: const EdgeInsets.only(top: 20),
child: Text(msg,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant)),
),
]),
);
}
}

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void showConfirmDialog({
required BuildContext context,
required String title,
String? content,
required VoidCallback onConfirm,
}) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(title),
content: content == null ? null : Text(content),
actions: [
TextButton(
onPressed: Get.back,
child: Text(
'取消',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () {
Get.back();
onConfirm();
},
child: Text('确认'),
),
],
);
},
);
}

View File

@@ -1,3 +1,5 @@
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
show SourceModel;
import 'package:PiliPlus/utils/extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
@@ -11,6 +13,7 @@ Widget htmlRender({
required double constrainedWidth,
Function(List<String>, int)? callback,
}) {
debugPrint('htmlRender');
return SelectionArea(
child: Html(
data: htmlContent,
@@ -25,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/');
@@ -54,7 +51,7 @@ Widget htmlRender({
callback([imgUrl], 0);
} else {
context.imageView(
imgList: [imgUrl],
imgList: [SourceModel(url: imgUrl)],
);
}
},

View File

@@ -15,7 +15,7 @@ void imageSaveDialog({
final double imgWidth = min(Get.width, Get.height) - 8 * 2;
SmartDialog.show(
animationType: SmartAnimationType.centerScale_otherSlide,
builder: (context) => Container(
builder: (_) => Container(
width: imgWidth,
margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
decoration: BoxDecoration(
@@ -84,7 +84,11 @@ void imageSaveDialog({
SmartDialog.dismiss();
}
},
icon: const Icon(Icons.download, size: 20),
icon: Icon(
Icons.download,
size: 20,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
)
],
),

View File

@@ -2,9 +2,12 @@ import 'dart:math';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
show SourceModel, SourceType;
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/nine_grid_view.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/material.dart';
class ImageModel {
@@ -12,16 +15,20 @@ class ImageModel {
required this.width,
required this.height,
required this.url,
this.liveUrl,
});
dynamic width;
dynamic height;
String url;
String? liveUrl;
bool? _isLongPic;
bool? _isLivePhoto;
dynamic get safeWidth => width ?? 1;
dynamic get safeHeight => height ?? 1;
bool get isLongPic => _isLongPic ??= (safeHeight / safeWidth) > (22 / 9);
bool get isLivePhoto => _isLivePhoto ??= liveUrl?.isNotEmpty == true;
}
Widget imageview(
@@ -83,6 +90,17 @@ Widget imageview(
);
}
late final enableLivePhoto = GStorage.enableLivePhoto;
int parseSize(size) {
return switch (size) {
int() => size,
double() => size.round(),
String() => int.tryParse(size) ?? 1,
_ => 1,
};
}
return NineGridView(
type: NineGridType.weiBo,
margin: const EdgeInsets.only(top: 6),
@@ -102,7 +120,19 @@ Widget imageview(
onViewImage?.call();
context.imageView(
initialPage: index,
imgList: picArr.map((item) => item.url).toList(),
imgList: picArr.map(
(item) {
bool isLive = item.isLivePhoto && enableLivePhoto;
return SourceModel(
sourceType:
isLive ? SourceType.livePhoto : SourceType.networkImage,
url: item.url,
liveUrl: isLive ? item.liveUrl : null,
width: isLive ? parseSize(item.width) : null,
height: isLive ? parseSize(item.height) : null,
);
},
).toList(),
onDismissed: onDismissed,
);
}
@@ -143,7 +173,14 @@ Widget imageview(
},
),
),
if (picArr[index].isLongPic)
if (picArr[index].isLivePhoto)
const PBadge(
text: 'Live',
right: 8,
bottom: 8,
type: 'gray',
)
else if (picArr[index].isLongPic)
const PBadge(
text: '长图',
right: 8,

View File

@@ -6,13 +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;
@@ -33,6 +36,24 @@ typedef IndexedFocusedWidgetBuilder = Widget Function(
typedef IndexedTagStringBuilder = String Function(int index);
enum SourceType { fileImage, networkImage, livePhoto }
class SourceModel {
final SourceType sourceType;
final String url;
final String? liveUrl;
final int? width;
final int? height;
const SourceModel({
this.sourceType = SourceType.networkImage,
required this.url,
this.liveUrl,
this.width,
this.height,
});
}
class InteractiveviewerGallery<T> extends StatefulWidget {
const InteractiveviewerGallery({
super.key,
@@ -45,17 +66,14 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
this.onDismissed,
this.setStatusBar,
this.onClose,
this.isFile,
});
final bool? isFile;
final VoidCallback? onClose;
final ValueChanged? onClose;
final bool? setStatusBar;
/// The sources to show.
final List<String> sources;
final List<SourceModel> sources;
/// The index of the first source in [sources] to show.
final int initIndex;
@@ -92,17 +110,14 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
late Offset _doubleTapLocalPosition;
int? currentIndex;
late final RxInt currentIndex = widget.initIndex.obs;
late List<bool> _thumbList;
late final int _quality = GStorage.previewQ;
@override
void initState() {
super.initState();
_thumbList = List.generate(widget.sources.length, (_) => true);
_pageController = PageController(initialPage: widget.initIndex);
_transformationController = custom.TransformationController();
@@ -110,38 +125,51 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
)..addListener(() {
_transformationController!.value =
_animation?.value ?? Matrix4.identity();
});
)..addListener(listener);
currentIndex = widget.initIndex;
if (widget.setStatusBar != false) {
setStatusBar();
}
if (widget.sources[currentIndex.value].sourceType == SourceType.livePhoto) {
_onPlay(currentIndex.value);
}
}
void listener() {
_transformationController!.value = _animation?.value ?? Matrix4.identity();
}
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,
);
}
}
if (widget.isFile != true) {
for (int index = 0; index < widget.sources.length; index++) {
for (int index = 0; index < widget.sources.length; index++) {
if (widget.sources[index].sourceType == SourceType.networkImage) {
CachedNetworkImageProvider(_getActualUrl(index)).evict();
}
}
@@ -201,14 +229,22 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
}
}
void _onPlay(int index) {
_player ??= Player();
_videoController ??= VideoController(_player!);
_player!.open(Media(widget.sources[index].liveUrl!));
}
/// When the page view changed its page, the source will animate back into the
/// original scale if it was scaled up.
///
/// Additionally the swipe up / down to dismiss gets enabled.
void _onPageChanged(int page) {
setState(() {
currentIndex = page;
});
_player?.pause();
currentIndex.value = page;
if (widget.sources[page].sourceType == SourceType.livePhoto) {
_onPlay(page);
}
widget.onPageChanged?.call(page);
if (_transformationController!.value != Matrix4.identity()) {
// animate the reset for the transformation of the interactive viewer
@@ -224,19 +260,24 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
}
}
String _getActualUrl(int index) => _thumbList[index] && _quality != 100
? '${widget.sources[index]}@${_quality}q.webp'.http2https
: widget.sources[index].http2https;
String _getActualUrl(int index) {
return _quality != 100
? Utils.thumbnailImgUrl(widget.sources[index].url, _quality)
: widget.sources[index].url.http2https;
}
void onClose() {
if (widget.onClose != null) {
widget.onClose!();
widget.onClose!(false);
} else {
Get.back();
widget.onDismissed?.call(_pageController!.page!.floor());
}
}
Player? _player;
VideoController? _videoController;
@override
Widget build(BuildContext context) {
return Stack(
@@ -272,12 +313,15 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
_doubleTapLocalPosition = details.localPosition;
},
onDoubleTap: onDoubleTap,
onLongPress: widget.isFile == true ? null : onLongPress,
onLongPress:
widget.sources[index].sourceType == SourceType.fileImage
? null
: onLongPress,
child: widget.itemBuilder != null
? widget.itemBuilder!(
context,
index,
index == currentIndex,
index == currentIndex.value,
_enablePageView,
)
: _itemBuilder(index),
@@ -321,51 +365,70 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
if (widget.sources.length > 1)
Align(
alignment: Alignment.center,
child: Text(
"${currentIndex! + 1}/${widget.sources.length}",
style: const TextStyle(color: Colors.white),
child: Obx(
() => Text(
"${currentIndex.value + 1}/${widget.sources.length}",
style: const TextStyle(color: Colors.white),
),
),
),
if (widget.isFile != true)
if (widget.sources[currentIndex.value].sourceType !=
SourceType.fileImage)
Align(
alignment: Alignment.centerRight,
child: PopupMenuButton(
itemBuilder: (context) {
return [
PopupMenuItem(
value: 0,
onTap: () =>
onShareImg(widget.sources[currentIndex!]),
onTap: () => onShareImg(
widget.sources[currentIndex.value].url),
child: const Text("分享图片"),
),
PopupMenuItem(
value: 1,
onTap: () {
Utils.copyText(widget.sources[currentIndex!]);
Utils.copyText(
widget.sources[currentIndex.value].url);
},
child: const Text("复制链接"),
),
PopupMenuItem(
value: 2,
onTap: () {
DownloadUtils.downloadImg(
context,
[widget.sources[currentIndex!]],
[widget.sources[currentIndex.value].url],
);
},
child: const Text("保存图片"),
),
if (widget.sources.length > 1)
PopupMenuItem(
value: 3,
onTap: () {
DownloadUtils.downloadImg(
context,
widget.sources,
widget.sources
.map((item) => item.url)
.toList(),
);
},
child: const Text("保存全部图片"),
),
if (widget.sources[currentIndex.value].sourceType ==
SourceType.livePhoto)
PopupMenuItem(
onTap: () {
DownloadUtils.downloadLivePhoto(
context: context,
url: widget.sources[currentIndex.value].url,
liveUrl: widget
.sources[currentIndex.value].liveUrl!,
width:
widget.sources[currentIndex.value].width!,
height: widget
.sources[currentIndex.value].height!,
);
},
child: const Text("保存 Live Photo"),
),
];
},
child: const Icon(Icons.more_horiz, color: Colors.white),
@@ -381,49 +444,72 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
// 图片分享
void onShareImg(String imgUrl) async {
SmartDialog.showLoading();
var response = await Request()
.get(imgUrl, options: Options(responseType: ResponseType.bytes));
final temp = await getTemporaryDirectory();
SmartDialog.dismiss();
String imgName =
"plpl_pic_${DateTime.now().toString().split('-').join()}.jpg";
var path = '${temp.path}/$imgName';
File(path).writeAsBytesSync(response.data);
Share.shareXFiles([XFile(path)], subject: imgUrl);
try {
SmartDialog.showLoading();
var response = await Request()
.get(imgUrl, options: Options(responseType: ResponseType.bytes));
final temp = await getTemporaryDirectory();
SmartDialog.dismiss();
String imgName =
"plpl_pic_${DateTime.now().toString().split('-').join()}.jpg";
var path = '${temp.path}/$imgName';
File(path).writeAsBytesSync(response.data);
Rect? sharePositionOrigin;
if (Platform.isIOS && (await Utils.isIpad())) {
sharePositionOrigin = Rect.fromLTWH(0, 0, Get.width, Get.height / 2);
}
Share.shareXFiles(
[XFile(path)],
subject: imgUrl,
sharePositionOrigin: sharePositionOrigin,
);
} catch (e) {
SmartDialog.showToast(e.toString());
}
}
Widget _itemBuilder(index) {
return Center(
child: Hero(
tag: widget.sources[index],
child: widget.isFile == true
? Image(
filterQuality: FilterQuality.low,
image: FileImage(File(widget.sources[index])),
)
: CachedNetworkImage(
fadeInDuration: const Duration(milliseconds: 0),
fadeOutDuration: const Duration(milliseconds: 0),
imageUrl: _getActualUrl(index),
// fit: BoxFit.contain,
progressIndicatorBuilder: (context, url, progress) {
return Center(
child: SizedBox(
width: 150.0,
child: LinearProgressIndicator(
value: progress.progress ?? 0),
),
);
},
// errorListener: (value) {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// setState(() {
// _thumbList[index] = false;
// });
// });
// },
),
tag: widget.sources[index].url,
child: switch (widget.sources[index].sourceType) {
SourceType.fileImage => Image(
filterQuality: FilterQuality.low,
image: FileImage(File(widget.sources[index].url)),
),
SourceType.networkImage => CachedNetworkImage(
fadeInDuration: const Duration(milliseconds: 0),
fadeOutDuration: const Duration(milliseconds: 0),
imageUrl: _getActualUrl(index),
// fit: BoxFit.contain,
progressIndicatorBuilder: (context, url, progress) {
return Center(
child: SizedBox(
width: 150.0,
child:
LinearProgressIndicator(value: progress.progress ?? 0),
),
);
},
// errorListener: (value) {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// setState(() {
// _thumbList[index] = false;
// });
// });
// },
),
SourceType.livePhoto => Obx(() => currentIndex.value == index
? IgnorePointer(
child: Video(
controller: _videoController!,
fill: Colors.transparent,
),
)
: const SizedBox.shrink()),
},
),
);
}
@@ -487,7 +573,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
children: [
ListTile(
onTap: () {
onShareImg(widget.sources[currentIndex!]);
onShareImg(widget.sources[currentIndex.value].url);
Get.back();
},
dense: true,
@@ -496,7 +582,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
ListTile(
onTap: () {
Get.back();
Utils.copyText(widget.sources[currentIndex!]);
Utils.copyText(widget.sources[currentIndex.value].url);
},
dense: true,
title: const Text('复制链接', style: TextStyle(fontSize: 14)),
@@ -506,7 +592,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
Get.back();
DownloadUtils.downloadImg(
context,
[widget.sources[currentIndex!]],
[widget.sources[currentIndex.value].url],
);
},
dense: true,
@@ -518,12 +604,31 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
Get.back();
DownloadUtils.downloadImg(
context,
widget.sources,
widget.sources.map((item) => item.url).toList(),
);
},
dense: true,
title: const Text('保存全部图片', style: TextStyle(fontSize: 14)),
),
if (widget.sources[currentIndex.value].sourceType ==
SourceType.livePhoto)
ListTile(
onTap: () {
Get.back();
DownloadUtils.downloadLivePhoto(
context: context,
url: widget.sources[currentIndex.value].url,
liveUrl: widget.sources[currentIndex.value].liveUrl!,
width: widget.sources[currentIndex.value].width!,
height: widget.sources[currentIndex.value].height!,
);
},
dense: true,
title: const Text(
'保存 Live Photo',
style: TextStyle(fontSize: 14),
),
),
],
),
);

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 (Accounts.main.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 (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 (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

@@ -410,7 +410,7 @@ class _NineGridViewState extends State<NineGridView> {
@override
Widget build(BuildContext context) {
Widget? child = Container();
Widget? child;
double? realWidth = widget.width;
double? realHeight = widget.height;
switch (widget.type) {

View File

@@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
class RadioWidget<T> extends StatelessWidget {
final T value;
final T? groupValue;
final ValueChanged<T?> onChanged;
final String title;
final EdgeInsetsGeometry? padding;
const RadioWidget({
super.key,
required this.value,
this.groupValue,
required this.onChanged,
required this.title,
this.padding,
});
Widget _child() => Row(
children: [
Radio<T>(
value: value,
groupValue: groupValue,
onChanged: onChanged,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
Text(title),
],
);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => onChanged(value),
child: padding != null
? Padding(
padding: padding!,
child: _child(),
)
: _child(),
);
}
}
class WrapRadioOptionsGroup<T> extends StatelessWidget {
final String groupTitle;
final Map<T, String> options;
final T? selectedValue;
final ValueChanged<T?> onChanged;
final EdgeInsetsGeometry? itemPadding;
const WrapRadioOptionsGroup({
super.key,
required this.groupTitle,
required this.options,
required this.selectedValue,
required this.onChanged,
this.itemPadding,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (groupTitle.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 22),
child: Text(
groupTitle,
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Wrap(
children: options.entries.map((entry) {
return IntrinsicWidth(
child: RadioWidget<T>(
value: entry.key,
groupValue: selectedValue,
onChanged: onChanged,
title: entry.value,
padding: itemPadding ?? const EdgeInsets.only(right: 10),
),
);
}).toList(),
),
),
],
);
}
}

View File

@@ -0,0 +1,225 @@
import 'package:PiliPlus/common/widgets/radio_widget.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';
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) => StatefulBuilder(
builder: (context, setState) {
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: Form(
key: key,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: SingleChildScrollView(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.only(
left: 22,
right: 22,
bottom: 5,
),
child: Text('请选择举报的理由:'),
),
...options.entries.map(
(entry) => WrapRadioOptionsGroup<int>(
groupTitle: entry.key,
options: entry.value,
selectedValue: reasonType,
onChanged: (value) =>
setState(() => reasonType = value),
),
),
if (reasonType == 0)
ReasonField(
onChanged: (value) => reasonDesc = value),
],
),
),
),
),
BanUserCheckbox(onChanged: (value) => banUid = value),
],
),
),
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 ReasonField extends StatefulWidget {
final ValueChanged<String> onChanged;
String? _validator(String? value) => value.isNullOrEmpty ? '理由不能为空' : null;
const ReasonField({super.key, required this.onChanged});
@override
State<ReasonField> createState() => _ReasonFieldState();
}
class _ReasonFieldState extends State<ReasonField> {
final _controller = TextEditingController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 22, top: 5, right: 22),
child: TextFormField(
controller: _controller,
autofocus: true,
minLines: 4,
maxLines: 4,
decoration: const InputDecoration(
labelText: '为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息',
border: OutlineInputBorder(),
contentPadding: EdgeInsets.all(10),
),
onChanged: (value) {
widget.onChanged(value);
},
validator: widget._validator,
),
);
}
}
class BanUserCheckbox extends StatefulWidget {
final ValueChanged<bool> onChanged;
const BanUserCheckbox({super.key, required this.onChanged});
@override
State<BanUserCheckbox> createState() => _BanUserCheckboxState();
}
class _BanUserCheckboxState extends State<BanUserCheckbox> {
bool _banUid = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() => _banUid = !_banUid);
widget.onChanged(_banUid);
},
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,
),
),
],
),
),
);
}
}
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

@@ -22,7 +22,7 @@ class Segment {
class SegmentProgressBar extends CustomPainter {
final List<Segment> segmentColors;
late double _defHeight;
double? _defHeight;
SegmentProgressBar({
required this.segmentColors,
@@ -42,6 +42,18 @@ class SegmentProgressBar extends CustomPainter {
if (segmentColors[i].title != null) {
double fontSize = 10;
_defHeight ??= (TextPainter(
text: TextSpan(
text: segmentColors[i].title,
style: TextStyle(
fontSize: fontSize,
),
),
textDirection: TextDirection.ltr,
)..layout())
.height +
2;
TextPainter getTextPainter() => TextPainter(
text: TextSpan(
text: segmentColors[i].title,
@@ -51,14 +63,12 @@ class SegmentProgressBar extends CustomPainter {
height: 1,
),
),
strutStyle: StrutStyle(height: 1, leading: 0),
strutStyle:
StrutStyle(leading: 0, height: 1, fontSize: fontSize),
textDirection: TextDirection.ltr,
)..layout();
TextPainter textPainter = getTextPainter();
if (i == 0) {
_defHeight = textPainter.height;
}
late double prevStart;
if (i != 0) {
@@ -75,7 +85,7 @@ class SegmentProgressBar extends CustomPainter {
canvas.drawRect(
Rect.fromLTRB(
0,
-_defHeight - 2,
-_defHeight!,
size.width,
0,
),
@@ -86,9 +96,9 @@ class SegmentProgressBar extends CustomPainter {
canvas.drawRect(
Rect.fromLTWH(
segmentStart,
-_defHeight - 2,
-_defHeight!,
segmentEnd == segmentStart ? 2 : segmentEnd - segmentStart,
size.height + _defHeight + 2,
size.height + _defHeight!,
),
paint,
);
@@ -98,7 +108,7 @@ class SegmentProgressBar extends CustomPainter {
: (segmentStart - prevStart - textPainter.width) / 2 +
prevStart +
1;
double textY = (-_defHeight - textPainter.height) / 2 - 1;
double textY = (-_defHeight! - textPainter.height) / 2;
textPainter.paint(canvas, Offset(textX, textY));
} else {
canvas.drawRect(

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';
@@ -24,6 +23,7 @@ class VideoCardH extends StatelessWidget {
this.showPubdate = false,
this.onTap,
this.onLongPress,
this.onViewLater,
});
final dynamic videoItem;
final String source;
@@ -33,6 +33,7 @@ class VideoCardH extends StatelessWidget {
final bool showPubdate;
final VoidCallback? onTap;
final VoidCallback? onLongPress;
final ValueChanged<int>? onViewLater;
@override
Widget build(BuildContext context) {
@@ -42,132 +43,142 @@ 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!();
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);
if (source == 'later') {
onViewLater!(cid);
} else {
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,
),
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) {
@@ -176,107 +187,102 @@ class VideoCardH extends StatelessWidget {
: '';
if (pubdate != '') pubdate += ' ';
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (videoItem.title is String)
Expanded(
child: Text(
videoItem.title as String,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (videoItem.title is String)
Expanded(
child: Text(
videoItem.title,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
)
else
Expanded(
child: RichText(
overflow: TextOverflow.ellipsis,
maxLines: 2,
textScaler: MediaQuery.textScalerOf(context),
text: TextSpan(
children: [
for (final i in videoItem.title) ...[
TextSpan(
text: i['text'] as String,
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.bodyMedium!
.fontSize,
letterSpacing: 0.3,
color: i['type'] == 'em'
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
)
else
Expanded(
child: Text.rich(
overflow: TextOverflow.ellipsis,
maxLines: 2,
TextSpan(
children: [
for (final i in videoItem.title) ...[
TextSpan(
text: i['text'] as String,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
color: i['type'] == 'em'
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
]
],
),
),
]
],
),
),
// const Spacer(),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// Container(
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(4),
// border: Border.all(
// color: Theme.of(context).colorScheme.surfaceTint),
// ),
// child: Text(
// videoItem.rcmdReason.content,
// style: TextStyle(
// fontSize: 9,
// color: Theme.of(context).colorScheme.surfaceTint),
// ),
// ),
// const SizedBox(height: 4),
if (showOwner || showPubdate)
Expanded(
flex: 0,
child: Text(
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
),
const SizedBox(height: 3),
Row(
children: [
if (showView) ...[
statView(
context: context,
theme: 'gray',
view: videoItem.stat.view as int,
),
const SizedBox(width: 8),
],
if (showDanmaku)
statDanMu(
context: context,
theme: 'gray',
danmu: videoItem.stat.danmu as int,
),
const Spacer(),
if (source == 'normal') const SizedBox(width: 24),
],
),
],
),
// const Spacer(),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// Container(
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(4),
// border: Border.all(
// color: Theme.of(context).colorScheme.surfaceTint),
// ),
// child: Text(
// videoItem.rcmdReason.content,
// style: TextStyle(
// fontSize: 9,
// color: Theme.of(context).colorScheme.surfaceTint),
// ),
// ),
// const SizedBox(height: 4),
if (showOwner || showPubdate)
Expanded(
flex: 0,
child: Text(
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
),
const SizedBox(height: 3),
Row(
children: [
if (showView) ...[
statView(
context: context,
theme: 'gray',
view: videoItem.stat.view as int,
),
const SizedBox(width: 8),
],
if (showDanmaku)
statDanMu(
context: context,
theme: 'gray',
danmu: videoItem.stat.danmu as int,
),
const Spacer(),
if (source == 'normal') const SizedBox(width: 24),
],
),
],
),
);
}

View File

@@ -58,7 +58,7 @@ class VideoCardHGrpc extends StatelessWidget {
return;
}
try {
PiliScheme.routePush(Uri.parse(videoItem.smallCoverV5.base.uri));
PiliScheme.routePushFromUrl(videoItem.smallCoverV5.base.uri);
} catch (err) {
SmartDialog.showToast(err.toString());
}
@@ -111,7 +111,8 @@ class VideoCardHGrpc extends StatelessWidget {
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
);
},
@@ -133,58 +134,43 @@ class VideoCardHGrpc extends StatelessWidget {
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...[
Expanded(
child: Text(
videoItem.smallCoverV5.base.title,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
videoItem.smallCoverV5.base.title,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
],
// const Spacer(),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// Container(
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(4),
// border: Border.all(
// color: Theme.of(context).colorScheme.surfaceTint),
// ),
// child: Text(
// videoItem.rcmdReason.content,
// style: TextStyle(
// fontSize: 9,
// color: Theme.of(context).colorScheme.surfaceTint),
// ),
// ),
// const SizedBox(height: 4),
if (showOwner || showPubdate)
Text(
videoItem.smallCoverV5.rightDesc1,
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
const SizedBox(height: 3),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
// const Spacer(),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// Container(
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(4),
// border: Border.all(
// color: Theme.of(context).colorScheme.surfaceTint),
// ),
// child: Text(
// videoItem.rcmdReason.content,
// style: TextStyle(
// fontSize: 9,
// color: Theme.of(context).colorScheme.surfaceTint),
// ),
// ),
// const SizedBox(height: 4),
if (showOwner || showPubdate)
Text(
videoItem.smallCoverV5.rightDesc2,
videoItem.smallCoverV5.rightDesc1,
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
@@ -193,26 +179,36 @@ class VideoCardHGrpc extends StatelessWidget {
overflow: TextOverflow.clip,
),
),
// Row(
// children: [
// if (showView) ...[
// StatView(
// theme: 'gray',
// view: videoItem.stat.view as int,
// ),
// const SizedBox(width: 8),
// ],
// if (showDanmaku)
// StatDanMu(
// theme: 'gray',
// danmu: videoItem.stat.danmu as int,
// ),
// const Spacer(),
// if (source == 'normal') const SizedBox(width: 24),
// ],
// ),
],
),
const SizedBox(height: 3),
Text(
videoItem.smallCoverV5.rightDesc2,
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
// Row(
// children: [
// if (showView) ...[
// StatView(
// theme: 'gray',
// view: videoItem.stat.view as int,
// ),
// const SizedBox(width: 8),
// ],
// if (showDanmaku)
// StatDanMu(
// theme: 'gray',
// danmu: videoItem.stat.danmu as int,
// ),
// const Spacer(),
// if (source == 'normal') const SizedBox(width: 24),
// ],
// ),
],
),
);
}

View File

@@ -2,10 +2,10 @@ import 'package:PiliPlus/common/widgets/image_save.dart';
import 'package:PiliPlus/common/widgets/stat/danmu.dart';
import 'package:PiliPlus/common/widgets/stat/view.dart';
import 'package:PiliPlus/common/widgets/video_popup_menu.dart';
import 'package:PiliPlus/common/widgets/video_progress_indicator.dart';
import 'package:PiliPlus/models/space_archive/item.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import '../../utils/utils.dart';
import '../constants.dart';
import 'badge.dart';
@@ -47,8 +47,8 @@ class VideoCardHMemberVideo extends StatelessWidget {
return;
}
try {
Get.toNamed(
'/video?bvid=${videoItem.bvid}&cid=${videoItem.firstCid}',
Utils.toViewPage(
'bvid=${videoItem.bvid}&cid=${videoItem.firstCid}',
arguments: {
'heroTag': Utils.makeHeroTag(videoItem.bvid),
},
@@ -98,12 +98,29 @@ class VideoCardHMemberVideo extends StatelessWidget {
bottom: 6.0,
type: 'gray',
),
if (videoItem.history != null)
Builder(builder: (context) {
try {
return Positioned(
left: 0,
right: 0,
bottom: 0,
child: videoProgressIndicator(
videoItem.history!['progress'] /
videoItem.history!['duration'],
),
);
} catch (_) {
return const SizedBox.shrink();
}
}),
],
);
},
),
),
videoContent(context)
const SizedBox(width: 10),
videoContent(context),
],
);
},
@@ -125,66 +142,61 @@ class VideoCardHMemberVideo extends StatelessWidget {
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...[
Expanded(
child: Text(
// videoItem.season?['title'] ?? videoItem.title ?? '',
videoItem.title ?? '',
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: videoItem.bvid != null && videoItem.bvid == bvid
? FontWeight.bold
: null,
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
color: videoItem.bvid != null && videoItem.bvid == bvid
? Theme.of(context).colorScheme.primary
: null,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
// videoItem.season?['title'] ?? videoItem.title ?? '',
videoItem.title ?? '',
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: videoItem.bvid != null && videoItem.bvid == bvid
? FontWeight.bold
: null,
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
color: videoItem.bvid != null && videoItem.bvid == bvid
? Theme.of(context).colorScheme.primary
: null,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
videoItem.season != null
? Utils.dateFormat(videoItem.season?['mtime'])
: videoItem.publishTimeText ?? '',
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
const SizedBox(height: 3),
Row(
children: [
statView(
context: context,
theme: 'gray',
// view: videoItem.season?['view_content'] ??
// videoItem.viewContent,
view: videoItem.viewContent,
),
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
danmu: videoItem.danmaku,
),
],
Text(
videoItem.season != null
? Utils.dateFormat(videoItem.season?['mtime'])
: videoItem.publishTimeText ?? '',
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
const SizedBox(height: 3),
Row(
children: [
statView(
context: context,
theme: 'gray',
// view: videoItem.season?['view_content'] ??
// videoItem.viewContent,
view: videoItem.viewContent,
),
const SizedBox(width: 8),
statDanMu(
context: context,
theme: 'gray',
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
danmu: videoItem.danmaku,
),
],
),
],
),
),
],
),
);
}

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,
@@ -314,10 +277,9 @@ class VideoCardV extends StatelessWidget {
const Spacer(),
Expanded(
flex: 0,
child: RichText(
child: Text.rich(
maxLines: 1,
textScaler: MediaQuery.textScalerOf(context),
text: TextSpan(
TextSpan(
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelSmall!.fontSize,
@@ -337,10 +299,9 @@ class VideoCardV extends StatelessWidget {
const Spacer(),
Expanded(
flex: 0,
child: RichText(
child: Text.rich(
maxLines: 1,
textScaler: MediaQuery.textScalerOf(context),
text: TextSpan(
TextSpan(
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelSmall!.fontSize,

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

@@ -68,8 +68,7 @@ class VideoCustomActions {
VideoCustomAction(
'不感兴趣', 'dislike', Icon(MdiIcons.thumbDownOutline, size: 16),
() async {
String? accessKey = GStorage.localCache
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
String? accessKey = Accounts.get(AccountType.recommend).accessKey;
if (accessKey == null || accessKey == "") {
SmartDialog.showToast("请退出账号后重新登录");
return;
@@ -263,9 +262,7 @@ class VideoCustomActions {
act: 5,
reSrc: 11,
);
List<int> blackMidsList = GStorage.blackMidsList;
blackMidsList.insert(0, videoItem.owner.mid);
GStorage.setBlackMidsList(blackMidsList);
GStorage.setBlackMid(videoItem.owner.mid);
Get.back();
SmartDialog.showToast(res['msg'] ?? '成功');
},

View File

@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
Widget videoProgressIndicator(double progress) => ClipRect(
clipper: ProgressClipper(),
child: ClipRRect(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
child: LinearProgressIndicator(
minHeight: 10,
value: progress,
),
),
);
class ProgressClipper extends CustomClipper<Rect> {
@override
Rect getClip(Size size) {
return Rect.fromLTWH(0, 6, size.width, size.height - 6);
}
@override
bool shouldReclip(CustomClipper<Rect> oldClipper) {
return false;
}
}

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,130 +1,182 @@
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 final bool _isLogin = GStorage.userInfo.get('userInfoCache') != null;
static final int? _mid = GStorage.userInfo.get('userInfoCache')?.mid;
static final String? _accessKey = GStorage.localCache
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
static const gzipEncoder = GZipEncoder();
static const gzipDecoder = GZipDecoder();
static final String? _accessKey = Accounts.main.accessKey;
static const _build = 1462100;
static const _biliChannel = 'bili';
static const _mobiApp = 'android_hd';
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 : '',
'x-bili-mid': _isLogin ? _mid.toString() : '0',
'x-bili-aurora-zone': '',
'x-bili-trace-id': _traceId,
if (_isLogin) 'authorization': 'identify_v1 $_accessKey',
if (_accessKey != null) 'authorization': 'identify_v1 $_accessKey',
'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 +184,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 +207,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 +226,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 +249,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

@@ -121,7 +121,15 @@ class Api {
// up_mid num 目标用户mid 必要
// type num 目标内容属性 非必要 默认为全部 0全部 2视频稿件
// rid num 目标 视频稿件avid
static const String videoInFolder = '/x/v3/fav/folder/created/list-all';
static const String favFolder = '/x/v3/fav/folder/created/list-all';
static const String copyFav = '/x/v3/fav/resource/copy';
static const String moveFav = '/x/v3/fav/resource/move';
static const String copyToview = '/x/v2/history/toview/copy';
static const String moveToview = '/x/v2/history/toview/move';
// 视频详情页 相关视频
static const String relatedList = '/x/web-interface/archive/related';
@@ -182,6 +190,8 @@ class Api {
static const String deleteFolder = '/x/v3/fav/folder/del';
static const String cleanFav = '/x/v3/fav/resource/clean';
/// 收藏夹 详情
/// media_id 当前收藏夹id 搜索全部时为默认收藏夹id
/// pn int 当前页
@@ -255,6 +265,9 @@ class Api {
// 番剧/剧集明细
static const String bangumiInfo = '/pgc/view/web/season';
// https://api.bilibili.com/pgc/season/episode/web/info?ep_id=12345678
static const String episodeInfo = '/pgc/season/episode/web/info';
// 全部关注的up
// vmid 用户id pn 页码 ps 每页个数最大50 order: desc
// order_type 排序规则 最近访问传空,最常访问传 attention
@@ -398,9 +411,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';
@@ -409,7 +419,7 @@ class Api {
// https://api.bilibili.com/x/player/online/total?aid=913663681&cid=1203559746&bvid=BV1MM4y1s7NZ&ts=56427838
static const String onlineTotal = '/x/player/online/total';
static const String webDanmaku = '/x/v2/dm/web/seg.so';
// static const String webDanmaku = '/x/v2/dm/web/seg.so';
// 发送视频弹幕
//https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/action.md
@@ -458,12 +468,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
@@ -609,12 +616,14 @@ 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 =
'${HttpString.passBaseUrl}/x/passport-tv-login/qrcode/poll';
static const logout = '${HttpString.passBaseUrl}/login/exit/v2';
/// 置顶视频
static const getTopVideoApi = '/x/space/top/arc';
@@ -652,7 +661,7 @@ class Api {
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
/// 用户动态主页
static const dynamicSpmPrefix = 'https://space.bilibili.com/1/dynamic';
static const dynamicSpmPrefix = '${HttpString.spaceBaseUrl}/1/dynamic';
/// 激活buvid3
static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi';
@@ -708,4 +717,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

@@ -39,7 +39,8 @@ class DanmakuFilterHttp {
}
}
static Future danmakuFilterAdd({required String filter, required int type}) async {
static Future danmakuFilterAdd(
{required String filter, required int type}) async {
var res = await Request().post(
Api.danmakuFilterAdd,
queryParameters: {
@@ -51,7 +52,7 @@ class DanmakuFilterHttp {
if (res.data['code'] == 0) {
return {
'status': true,
'data': Rule.fromJson(res.data['data']),
'data': SimpleRule.fromJson(res.data['data']),
};
} else {
return {

View File

@@ -1,4 +1,7 @@
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:dio/dio.dart';
import '../models/dynamics/result.dart';
import '../models/dynamics/up.dart';
@@ -14,7 +17,7 @@ class DynamicsHttp {
'type': type ?? 'all',
'timezone_offset': '-480',
'offset': offset,
'features': 'itemOpusStyle'
'features': 'itemOpusStyle,listOnlyfans'
};
if (mid != -1) {
data['host_mid'] = mid;
@@ -24,6 +27,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,13 +90,23 @@ class DynamicsHttp {
//
static Future dynamicDetail({
String? id,
dynamic id,
dynamic rid,
dynamic type,
bool clearCookie = false,
}) async {
var res = await Request().get(Api.dynamicDetail, queryParameters: {
'timezone_offset': -480,
'id': id,
'features': 'itemOpusStyle',
});
var res = await Request().get(
Api.dynamicDetail,
queryParameters: {
'timezone_offset': -480,
if (id != null) 'id': id,
if (rid != null) 'rid': rid,
if (type != null) 'type': type,
'features': 'itemOpusStyle',
},
options:
clearCookie ? Options(extra: {'account': AnonymousAccount()}) : 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;
@@ -13,12 +14,21 @@ class HtmlHttp {
var response = await Request().get(
"https://www.bilibili.com/opus/$id",
extra: {'ua': 'pc'},
options: Options(
followRedirects: false,
validateStatus: (status) => true,
),
);
if (response.data is! String && response.data is! List<int>) {
return;
}
try {
if (response.data.contains('Redirecting to')) {
String? cvid = RegExp(r'cv\d+').firstMatch(response.data)?.group(0);
if (cvid != null) {
return await reqReadHtml(cvid, dynamicType, false);
}
RegExp regex = RegExp(r'//([\w\.]+)/(\w+)/(\w+)');
Match match = regex.firstMatch(response.data)!;
String matchedString = match.group(0)!;
@@ -48,10 +58,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 +75,7 @@ class HtmlHttp {
'avatar': avatar,
'uname': uname,
'updateTime': updateTime,
'content': test + opusContent,
'content': (test ?? '') + opusContent,
'commentId': int.parse(commentId)
};
} catch (err) {
@@ -73,7 +84,15 @@ class HtmlHttp {
}
// read
static Future reqReadHtml(id, dynamicType) async {
static Future reqReadHtml(id, dynamicType, [bool redirect = true]) async {
if (redirect) {
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

@@ -3,125 +3,88 @@ import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:math' show Random;
import 'package:cookie_jar/cookie_jar.dart';
import 'package:PiliPlus/build_config.dart';
import 'package:PiliPlus/http/retry_interceptor.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:PiliPlus/utils/accounts/account_manager/account_mgr.dart';
import 'package:archive/archive.dart';
import 'package:brotli/brotli.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';
import '../utils/utils.dart';
import 'api.dart';
import 'constants.dart';
import 'interceptor.dart';
import 'interceptor_anonymity.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web;
class Request {
static const gzipDecoder = GZipDecoder();
static const brotilDecoder = BrotliDecoder();
static final Request _instance = Request._internal();
static late CookieManager cookieManager;
static late AccountManager accountManager;
static late final Dio dio;
factory Request() => _instance;
late bool enableSystemProxy;
late String systemProxyHost;
late String systemProxyPort;
static final _rand = Random();
static final RegExp spmPrefixExp =
RegExp(r'<meta name="spm_prefix" content="([^"]+?)">');
/// 设置cookie
static setCookie() async {
final String cookiePath = await Utils.getCookiePath();
final PersistCookieJar cookieJar = PersistCookieJar(
ignoreExpires: true,
storage: FileStorage(cookiePath),
);
cookieManager = CookieManager(cookieJar);
dio.interceptors.add(cookieManager);
dio.interceptors.add(AnonymityInterceptor());
final List<Cookie> cookies = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.baseUrl));
for (Cookie item in cookies) {
await web.CookieManager().setCookie(
url: web.WebUri(item.domain ?? ''),
name: item.name,
value: item.value,
path: item.path ?? '',
domain: item.domain,
isSecure: item.secure,
isHttpOnly: item.httpOnly,
);
}
final userInfo = GStorage.userInfo.get('userInfoCache');
if (userInfo != null && userInfo.mid != null) {
final List<Cookie> cookie2 = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.tUrl));
if (cookie2.isEmpty) {
try {
await Request().get(HttpString.tUrl);
} catch (e) {
log("setCookie, ${e.toString()}");
}
}
}
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
try {
await buvidActivate();
} catch (e) {
log("setCookie, ${e.toString()}");
}
// final String cookieString = cookies
// .map((Cookie cookie) => '${cookie.name}=${cookie.value}')
// .join('; ');
// dio.options.headers['cookie'] = cookieString;
accountManager = AccountManager();
dio.interceptors.add(accountManager);
await Accounts.refresh();
final List<Cookie> cookies = Accounts.main.cookieJar.toList();
final webManager = web.CookieManager();
await Future.wait(cookies.map((item) => webManager.setCookie(
url: web.WebUri(item.domain ?? ''),
name: item.name,
value: item.value,
path: item.path ?? '',
domain: item.domain,
isSecure: item.secure,
isHttpOnly: item.httpOnly,
)));
}
// 从cookie中获取 csrf token
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 Accounts.main.csrf;
}
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);
static Future<void> buvidActive(Account account) async {
// 这样线程不安全, 但仍按预期进行
if (account.activited) return;
account.activited = true;
try {
final html = await Request().get(Api.dynamicSpmPrefix,
options: Options(extra: {'account': account}));
final String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!;
final String randPngEnd = base64.encode(
List<int>.generate(32, (_) => _rand.nextInt(256)) +
List<int>.filled(4, 0) +
[73, 69, 78, 68] +
List<int>.generate(4, (_) => _rand.nextInt(256)));
String jsonData = json.encode({
'3064': 1,
'39c8': '$spmPrefix.fp.risk',
'3c43': {
'adca': 'Linux',
'bfe9': randPngEnd.substring(randPngEnd.length - 50),
},
});
await Request().post(Api.activateBuvidApi,
data: {'payload': jsonData},
options: Options(contentType: Headers.jsonContentType));
} catch (e) {
log("setCookie, $e");
}
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 Future buvidActivate() async {
var html = await Request().get(Api.dynamicSpmPrefix);
String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!;
Random rand = Random();
String randPngEnd = base64.encode(
List<int>.generate(32, (_) => rand.nextInt(256)) +
List<int>.filled(4, 0) +
[73, 69, 78, 68] +
List<int>.generate(4, (_) => rand.nextInt(256)));
String jsonData = json.encode({
'3064': 1,
'39c8': '$spmPrefix.fp.risk',
'3c43': {
'adca': 'Linux',
'bfe9': randPngEnd.substring(randPngEnd.length - 50),
},
});
await Request().post(Api.activateBuvidApi,
data: {'payload': jsonData},
options: Options(contentType: 'application/json'));
}
/*
@@ -130,15 +93,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: 4000),
//响应流上前后两次接受到数据的间隔,单位为毫秒。
receiveTimeout: const Duration(milliseconds: 4000),
//Http请求头.
headers: {
'connection': 'keep-alive',
'accept-encoding': 'br,gzip',
'user-agent': 'Dart/3.6 (dart:io)', // Http2Adapter不会自动添加标头
'referer': HttpString.baseUrl,
'env': 'prod',
'app-key': 'android64',
'x-bili-aurora-zone': 'sh001',
},
responseDecoder: responseDecoder, // Http2Adapter没有自动解压
persistentConnection: true);
enableSystemProxy = GStorage.setting
.get(SettingBoxKey.enableSystemProxy, defaultValue: false) as bool;
@@ -147,48 +119,65 @@ class Request {
systemProxyPort =
GStorage.setting.get(SettingBoxKey.systemProxyPort, defaultValue: '');
dio = Dio(options);
final http11Adapter = IOHttpClientAdapter(createHttpClient: () {
final client = HttpClient()
..idleTimeout = const Duration(seconds: 15)
..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,
// ),
// );
/// 设置代理
late Uri proxy;
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;
},
);
proxy = Uri(
scheme: 'http',
host: systemProxyHost,
port: int.parse(systemProxyPort));
}
//添加拦截器
dio.interceptors.add(ApiInterceptor());
dio = Dio(options)
..httpClientAdapter =
GStorage.setting.get(SettingBoxKey.enableHttp2, defaultValue: false)
? Http2Adapter(
ConnectionManager(
idleTimeout: const Duration(seconds: 15),
onClientCreate: enableSystemProxy
? (_, config) {
config
..proxy = proxy
..onBadCertificate = (_) => true;
}
: GStorage.badCertificateCallback
? (_, config) {
config.onBadCertificate = (_) => true;
}
: null),
fallbackAdapter: http11Adapter)
: http11Adapter;
// 先于其他Interceptor
if (GStorage.retryCount > 0) {
dio.interceptors
.add(RetryInterceptor(GStorage.retryCount, GStorage.retryDelay));
}
// 日志拦截器 输出请求、响应内容
dio.interceptors.add(LogInterceptor(
request: false,
requestHeader: false,
responseHeader: false,
));
if (BuildConfig.isDebug) {
dio.interceptors.add(LogInterceptor(
request: false,
requestHeader: false,
responseHeader: false,
));
}
dio.transformer = BackgroundTransformer();
dio.options.validateStatus = (int? status) {
return status! >= 200 && status < 300 ||
HttpString.validateStatusCodes.contains(status);
return status! >= 200 && status < 300;
};
}
@@ -217,7 +206,7 @@ class Request {
} on DioException catch (e) {
Response errResponse = Response(
data: {
'message': await ApiInterceptor.dioError(e)
'message': await AccountManager.dioError(e)
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
statusCode: -1,
requestOptions: RequestOptions(),
@@ -246,7 +235,7 @@ class Request {
} on DioException catch (e) {
Response errResponse = Response(
data: {
'message': await ApiInterceptor.dioError(e)
'message': await AccountManager.dioError(e)
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
statusCode: -1,
requestOptions: RequestOptions(),
@@ -271,7 +260,7 @@ class Request {
return response.data;
} on DioException catch (e) {
debugPrint('downloadFile error: $e');
return Future.error(ApiInterceptor.dioError(e));
return Future.error(AccountManager.dioError(e));
}
}
@@ -285,20 +274,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

@@ -1,89 +0,0 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
class ApiInterceptor extends Interceptor {
// @override
// void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
// debugPrint("请求之前");
// // 在请求之前添加头部或认证信息
// options.headers['Authorization'] = 'Bearer token';
// options.headers['Content-Type'] = 'application/json';
// handler.next(options);
// }
// @override
// void onResponse(Response response, ResponseInterceptorHandler handler) {
// try {
// if (response.statusCode == 302) {
// final List<String> locations = response.headers['location']!;
// if (locations.isNotEmpty) {
// if (locations.first.startsWith('https://www.mcbbs.net')) {
// debugPrint('ApiInterceptor@@@@@: ${locations.first}');
// final Uri uri = Uri.parse(locations.first);
// final String? accessKey = uri.queryParameters['access_key'];
// final String? mid = uri.queryParameters['mid'];
// try {
// GStorage.localCache.put(LocalCacheKey.accessKey,
// <String, String?>{'mid': mid, 'value': accessKey});
// } catch (_) {}
// }
// }
// }
// } catch (err) {
// debugPrint('ApiInterceptor: $err');
// }
// handler.next(response);
// }
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
// 处理网络请求错误
// handler.next(err);
String url = err.requestOptions.uri.toString();
debugPrint('🌹🌹ApiInterceptor: $url');
if (url.contains('heartbeat') ||
url.contains('seg.so') ||
url.contains('online/total') ||
url.contains('github') ||
(url.contains('skipSegments') && err.requestOptions.method == 'GET')) {
// skip
} else {
SmartDialog.showToast(
await dioError(err) + url,
displayType: SmartToastType.onlyRefresh,
displayTime: const Duration(milliseconds: 1200),
);
}
super.onError(err, handler);
}
static Future<String> dioError(DioException error) async {
switch (error.type) {
case DioExceptionType.badCertificate:
return '证书有误!';
case DioExceptionType.badResponse:
return '服务器异常,请稍后重试!';
case DioExceptionType.cancel:
return '请求已被取消,请重新请求';
case DioExceptionType.connectionError:
return '连接错误,请检查网络设置';
case DioExceptionType.connectionTimeout:
return '网络连接超时,请检查网络设置';
case DioExceptionType.receiveTimeout:
return '响应超时,请稍后重试!';
case DioExceptionType.sendTimeout:
return '发送请求超时,请检查网络设置';
case DioExceptionType.unknown:
final String res =
(await Connectivity().checkConnectivity()).first.title;
return '$res网络异常 ${error.error}';
}
}
}
extension _ConnectivityResultExt on ConnectivityResult {
String get title => ['蓝牙', 'Wi-Fi', '局域', '流量', '', '代理', '其他'][index];
}

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