Compare commits

...

1062 Commits

Author SHA1 Message Date
caa066237d v1.0.25 更新日志 2024-10-10 23:04:59 +08:00
2e206f20cc Update yml 2024-10-10 23:02:16 +08:00
81491ce98a Merge branch 'design' 2024-10-10 22:27:20 +08:00
fadefa2ddc opt: bangumiIntroController初始化 2024-10-10 22:26:46 +08:00
90fa6a16b9 Merge branch 'fix' 2024-10-10 09:44:54 +08:00
212b4ee685 Merge branch 'design' 2024-10-10 09:43:31 +08:00
c634e602b9 fix: 字幕数据过大时页面卡死 2024-10-09 23:33:52 +08:00
a44c0ae51e opt: 评论信息布局 2024-10-09 22:41:12 +08:00
51654ac54e opt: 评论操作栏间隙 2024-10-09 22:03:58 +08:00
545be4b0da Merge branch 'design' 2024-10-09 00:01:32 +08:00
21f5aae3b9 Merge branch 'feature-watchLater' 2024-10-09 00:00:55 +08:00
3830380172 feat: 顺序播放 2024-10-08 23:46:40 +08:00
48d869a6c0 opt: 视频播放页状态栏前景色 2024-10-07 23:46:55 +08:00
81e7da1f16 Merge branch 'main' into design 2024-10-07 23:26:39 +08:00
9d965aae2f mod: 番剧封面比例 2024-10-07 23:15:17 +08:00
c92ef36504 Merge branch 'feature-opusRender' 2024-10-07 22:54:03 +08:00
ee1b3049cb Merge branch 'design' 2024-10-07 22:53:54 +08:00
24a854b663 Merge branch 'fix' 2024-10-07 22:53:49 +08:00
a366f5d3ce opt: 专栏内容可选 2024-10-07 22:51:02 +08:00
16847b8a92 mod: 评论跳转cv 2024-10-07 22:39:48 +08:00
e94f008168 opt: appScheme 补充 2024-10-07 21:46:00 +08:00
e34a75f0f3 Merge branch 'main' into fix 2024-10-07 17:56:28 +08:00
0332f24542 Merge branch 'design' 2024-10-07 17:56:04 +08:00
64c2bcc650 opt: 控制器初始化 2024-10-07 17:55:39 +08:00
d24ccd7b26 Merge branch 'feature-replyDel' 2024-10-07 14:38:06 +08:00
078941bd9c Merge branch 'fix' 2024-10-06 16:54:32 +08:00
fa53c1214c mod: 返回页面时stream重置 2024-10-06 16:53:06 +08:00
4118be70dc Merge branch 'main' into fix 2024-10-06 16:47:17 +08:00
47546c8e0d Merge branch 'feature-watchLater' 2024-10-06 16:46:43 +08:00
2dca375901 mod 2024-10-06 16:38:50 +08:00
00423be5a9 mod: 一键三连 2024-10-05 14:18:24 +08:00
722ba06787 feat: 专栏分类搜索 2024-10-04 23:07:34 +08:00
ef7a563caa Merge branch 'fix' 2024-10-04 11:47:11 +08:00
f1441ac97e fix: 收藏夹 2024-10-04 11:36:17 +08:00
8b82725a49 Merge branch 'design' 2024-10-03 21:39:18 +08:00
4cac02a4c0 opt: Request().post 2024-10-03 21:28:12 +08:00
e949dd60ce opt: 图片预览 2024-10-03 20:35:33 +08:00
e409b679c0 Merge branch 'main' into design 2024-10-03 16:37:35 +08:00
f3e0e3c2f6 Merge branch 'feature-opusRender' 2024-10-03 16:37:26 +08:00
566b2cbd7f Merge branch 'main' into design 2024-10-03 16:37:13 +08:00
2a436cfcfc feat: 原网页查看专栏 2024-10-03 16:30:47 +08:00
dd363e3cda Merge branch 'feature-readRender' into feature-opusRender 2024-10-03 16:02:37 +08:00
31f87bd24c fix: 登录页面 2024-10-02 22:27:20 +08:00
853627097d Merge branch 'fix' 2024-09-30 13:35:34 +08:00
877eeded37 fix: 黑名单展示 2024-09-30 13:09:40 +08:00
19866fe080 fix: 视频评论回顶 2024-09-30 12:00:30 +08:00
66d2ac9777 fix: 粉丝不可见时异常 2024-09-30 11:38:39 +08:00
ed9a75ecd3 fix: 视频总结 2024-09-30 11:23:40 +08:00
c3d23dfdba typo 2024-09-29 15:29:08 +08:00
6c644256d1 Merge branch 'main' into fix 2024-09-29 15:27:03 +08:00
cb657ad8bc Merge branch 'fix-liveHistory' 2024-09-29 14:11:02 +08:00
5a12889834 Merge branch 'feature-readRender' 2024-09-29 14:09:49 +08:00
ab6a55bc03 Merge branch 'feature-notifications' 2024-09-29 14:09:39 +08:00
d9285c92c7 Merge branch 'design' 2024-09-29 14:09:31 +08:00
31f07bff90 Merge branch 'fix' 2024-09-29 14:09:24 +08:00
b6390ea626 fix: read专栏图片浏览 2024-09-29 14:07:21 +08:00
11aa465a8f feat: 私信图片查看 2024-09-29 14:01:26 +08:00
400b87395a mod: 标题最大行数 2024-09-29 12:00:38 +08:00
79c148adeb fix: 链接兜底 2024-09-29 01:12:43 +08:00
ec5cc6ff91 feat: 直播间历史记录 2024-09-29 01:01:45 +08:00
f95bcb2e6c mod: 评论删除逻辑 2024-09-28 23:52:50 +08:00
166dceb1bd typo 2024-09-28 23:21:17 +08:00
0b50b1bafe Merge branch 'feature-notifications' 2024-09-28 23:16:41 +08:00
b34fc2ff1f mod: 系统消息 2024-09-28 23:16:00 +08:00
afe5fe0a5e Merge branch 'main' into feature-notifications 2024-09-28 21:44:55 +08:00
ba73933041 Merge branch 'feature-readRender' 2024-09-28 18:30:02 +08:00
26a5733675 feat: cv专栏记录 2024-09-28 18:22:42 +08:00
d3f2d1fb3c Merge branch 'feature-opusRender' 2024-09-28 17:24:54 +08:00
aa6b5af05d fix: 重定向cv 2024-09-28 17:24:23 +08:00
3db50621b7 Merge branch 'fix' 2024-09-28 16:28:40 +08:00
a54f11d458 Merge branch 'feature-upArticle' 2024-09-28 16:28:10 +08:00
0a6eb931b8 Merge branch 'feature-opusRender' 2024-09-28 16:26:23 +08:00
fcbe423375 Merge branch 'feature-readRender' 2024-09-28 16:26:13 +08:00
43750142ae faat: 用户专栏 2024-09-28 16:17:23 +08:00
2ac3307e4d fix: 浏览历史专栏跳转 2024-09-27 16:57:18 +08:00
9541d7a4ea opt: 专栏代码块渲染 2024-09-27 16:38:47 +08:00
c7f044e441 fix: Stream has already been listened to 2024-09-27 00:39:18 +08:00
feaec8fc30 fix: Stream has already been listened to 2024-09-27 00:36:13 +08:00
8c479915f0 fix: Stream has already been listened to 2024-09-27 00:34:32 +08:00
6b5743c5ab mod: 布局 2024-09-27 00:21:07 +08:00
30b609271f feat: read专栏内容渲染 2024-09-26 23:45:58 +08:00
3478c70176 Merge branch 'feature-opusRender' into feature-readRender 2024-09-26 22:12:27 +08:00
34537ab141 mod: arguments 2024-09-26 22:12:00 +08:00
6873eb3da7 opt: 专栏opus图片预览 2024-09-25 09:33:55 +08:00
38fbbeb5a8 feat: opus专栏内容渲染 2024-09-25 00:32:46 +08:00
509e48feef Merge branch 'main' into feature-readRender 2024-09-23 21:58:32 +08:00
150745bb2d Merge branch 'feature-watchLater' 2024-09-23 21:58:10 +08:00
d7e5ccaf3a mod 2024-09-23 19:43:56 +08:00
301679d37e Merge branch 'main' into design 2024-09-23 00:14:27 +08:00
f25467eeea Merge branch 'feature-liveRoomRender' 2024-09-22 23:42:48 +08:00
47985e5d17 mod: 布局 2024-09-22 23:42:25 +08:00
ce4a0814ee Merge branch 'feature-liveRoomRender' 2024-09-22 23:16:42 +08:00
249600ec27 opt: 竖屏直播布局 2024-09-22 23:16:19 +08:00
031358e72e Merge branch 'feature-followingLiveUser' into feature-liveRoomRender 2024-09-22 23:12:54 +08:00
420d2eeba2 Merge branch 'feature-followingLiveUser' 2024-09-22 23:10:33 +08:00
b24c96db7c mod: 请求异常布局 2024-09-22 23:10:05 +08:00
b0f01f0ff4 Merge branch 'main' into feature-liveRoomRender 2024-09-22 19:18:35 +08:00
ad243616e3 Merge branch 'feature-followingLiveUser' 2024-09-22 19:18:16 +08:00
1905ba2a98 mod: 直播用户过滤 2024-09-22 19:17:46 +08:00
e40573f8b5 Merge branch 'main' into feature-liveRoomRender 2024-09-22 16:15:48 +08:00
438b7ab5bd Merge branch 'feature-followingLiveUser' 2024-09-22 16:15:07 +08:00
913e9154f5 feat: 我的关注 正在直播 2024-09-22 16:11:52 +08:00
12aada6a3f Merge branch 'fix' 2024-09-21 23:17:46 +08:00
39aa967864 Merge branch 'design' 2024-09-21 16:16:11 +08:00
fbbe555787 mod: navBar首页可编辑 2024-09-21 16:15:05 +08:00
cf66d3be4c feat: 稍后再看&收藏夹播放全部 2024-09-21 15:14:38 +08:00
5f35f6cf5c Merge branch 'main' into feature-watchLater 2024-09-18 22:23:26 +08:00
dfc97d5e1f Merge branch 'opt-componentParams' 2024-09-18 22:18:18 +08:00
5f02c17a2d fix: 关注up主搜索ps 2024-09-18 00:29:43 +08:00
d2640f230c feat: 视频评论删除 2024-09-15 02:30:35 +08:00
fb010d6d4f Merge branch 'main' into feature-replyDel 2024-09-15 02:04:51 +08:00
be82d05d1c Merge branch 'opt-dataInt' 2024-09-15 02:04:37 +08:00
389ca286a7 mod: userInfo cache 2024-09-15 02:03:53 +08:00
8aebf463e5 opt: 播放器音量&亮度控制条 2024-09-15 01:16:30 +08:00
ad1428b28f Merge branch 'opt-componentParams' into design 2024-09-12 23:57:14 +08:00
6c23773fa2 merge main 2024-09-12 23:56:20 +08:00
0bec084bec opt: 收藏夹为空提示 2024-09-12 09:40:08 +08:00
95a452dea8 opt: stat组件传参 2024-09-11 20:25:40 +08:00
ba1704fa2a feat: 收藏夹新建/编辑 2024-09-11 00:03:14 +08:00
10f464aab1 mod: 动态视频标签展示 2024-09-09 00:47:16 +08:00
5651466881 fix: 暗黑模式下个人认证文本颜色 2024-09-09 00:12:38 +08:00
012b415233 fix: 点赞计数 2024-09-09 00:05:22 +08:00
1d87e66433 fix: 翻页错误 2024-09-08 17:46:23 +08:00
df18662863 fix: 用户合集参数 2024-09-08 17:46:08 +08:00
c3595a0dcd fix: 搜索条件筛选面板无法滚动 2024-09-08 15:53:56 +08:00
e4a820268a fix: iOS静音模式无声 2024-09-04 09:42:08 +08:00
5c7f477ad8 mod:收藏夹类型展示(公开/私密) 2024-08-31 14:26:03 +08:00
e525554152 Merge branch 'main' into fix 2024-08-31 14:01:55 +08:00
67aa95c027 fix: 部分接口忽略error response提示 2024-08-27 23:57:19 +08:00
01cae5e280 mod: GlobalDataCache init 2024-08-27 23:46:01 +08:00
478dd39b20 mod: bottomSheet样式 2024-08-26 23:48:12 +08:00
2e329553d9 opt: 数据初始化 2024-08-26 23:23:43 +08:00
f0cd9c5cf2 Merge branch 'main' into opt-dataInt 2024-08-26 23:19:58 +08:00
c60fd6a73e Merge branch 'feature-liveDanmaku' 2024-08-26 23:18:14 +08:00
f598b6adad opt: liveRoom getBuvid 2024-08-26 23:14:37 +08:00
8b93eaf13b Merge branch 'main' into fix 2024-08-26 19:45:16 +08:00
2216296ba2 Merge branch 'feature-liveDanmaku' 2024-08-26 00:16:37 +08:00
0c61394a9e Merge branch 'fix' 2024-08-26 00:13:29 +08:00
d9ddd2fd37 Merge branch 'design' 2024-08-26 00:13:13 +08:00
d85e9446e3 opt: fav follow bottomSheet 2024-08-26 00:12:35 +08:00
8fd3bfae5f fix: 系统消息标记已读 2024-08-25 22:07:42 +08:00
8810a74ebf fix: 未播放时返回当前页异常 2024-08-24 23:33:19 +08:00
b96f35bae2 mod: 移除up主页分割线 2024-08-24 00:28:07 +08:00
15e1cb5d47 feat: 私信可选 2024-08-24 00:07:08 +08:00
fc22834e4a mod: 直播弹幕开关 2024-08-23 23:47:15 +08:00
61e019f458 fix: 合集类型补充(直播回放) 2024-08-23 00:20:20 +08:00
844db213a5 mod: 输入框样式 2024-08-22 23:57:11 +08:00
44910b35d9 feat: 直播弹幕发送 2024-08-22 20:24:21 +08:00
69590c6092 Merge branch 'feature-liveDanmaku' of github.com:guozhigq/pilipala_app into feature-liveDanmaku 2024-08-21 00:14:54 +08:00
a074a360f2 feat: 直播弹幕2 2024-08-21 00:14:45 +08:00
869e49bec2 feat: 直播弹幕2 2024-08-21 00:09:26 +08:00
1012b5d009 feat: 直播弹幕 2024-08-18 23:30:51 +08:00
c0f7623e3c Merge branch 'feature-replySave' 2024-08-11 23:49:27 +08:00
914760b5a7 Merge branch 'design' 2024-08-11 23:49:13 +08:00
ae3c457f4e Merge branch 'fix' 2024-08-11 23:49:08 +08:00
ec30235421 opt: 一键三连动画 2024-08-11 23:16:33 +08:00
b188675faf opt 2024-08-11 18:43:21 +08:00
61337338bd fix: 图片预览Hero tag重复 2024-08-10 23:11:21 +08:00
0bb9a68cc5 mod: 保存封面时视频标题可选中 2024-08-09 18:39:16 +08:00
66edac428b mod: 补充合集类型 2024-08-04 00:35:42 +08:00
8e0fbb2a54 opt: 非pip状态切换至后台不关闭BottomSheet 2024-07-27 22:08:12 +08:00
a2cbcca909 fix: 首页异常 2024-07-27 11:02:42 +08:00
5e6f082ade feat: 评论保存为图片 2024-07-27 11:00:19 +08:00
7b07ec383b Merge branch 'main' into feature-replySave 2024-07-26 22:53:27 +08:00
c7ffcd91de Merge branch 'fix' 2024-07-26 22:52:00 +08:00
80d0b15ac8 fix: 动态评论查看详情 2024-07-26 22:47:14 +08:00
a0bc0314bf fix: 评论详情查看 2024-07-25 00:08:18 +08:00
933dca3698 Merge branch 'main' into fix 2024-07-23 23:30:54 +08:00
5b78810ba4 fix: 消息mid null 2024-07-23 23:29:53 +08:00
bc36493e60 fix: 首页推荐数据格式异常 2024-07-23 23:25:06 +08:00
1f63c7e6d4 Merge branch 'design' 2024-07-21 22:50:09 +08:00
bc3ce33f78 feat: 评论二楼跳转 2024-07-21 22:40:43 +08:00
9ac2752cbb Merge branch 'main' into design 2024-07-21 22:14:04 +08:00
9941e65a76 Merge branch 'opt-imagePreview' 2024-07-21 22:12:54 +08:00
dbc0246682 Merge branch 'feature-chargeArchives' 2024-07-21 22:10:03 +08:00
92b2f3ee92 Merge branch 'feature-upDynamic' 2024-07-21 22:09:29 +08:00
38b6b6b4e0 fix: 消息mid null 2024-07-21 18:50:36 +08:00
75859fc9cf mod: 取消投币限制&布局 2024-07-18 01:11:26 +08:00
9c00d3c4d3 feat: up投稿显示充电专属 2024-07-18 00:52:17 +08:00
5a4bf24b30 Merge branch 'fix' 2024-07-17 23:38:18 +08:00
9f676f8ef9 fix: 首页推荐数据异常 2024-07-17 23:37:55 +08:00
db708f9d8c Merge branch 'main' into fix 2024-07-17 23:29:43 +08:00
04bdf5c293 feat: 轮播查看up动态 2024-07-14 21:47:37 +08:00
b489a45481 fix: 收藏异常、收到的赞 2024-07-14 00:41:59 +08:00
a5b7ee5a96 Merge branch 'design' 2024-07-13 19:46:04 +08:00
cc2595fd7b opt: 图片预览 2024-07-13 16:47:44 +08:00
189abbc747 fix: 导航栏背景异常 2024-07-10 23:58:55 +08:00
7759add4c5 Merge branch 'design' 2024-07-08 23:45:19 +08:00
6f69212952 opt: 视频加载异常捕获 2024-07-08 23:44:39 +08:00
139405d4da Merge branch 'main' into design 2024-07-08 23:17:19 +08:00
7b9ff07b2c Merge branch 'design' 2024-07-07 23:17:27 +08:00
9c0ee95220 Merge branch 'fix' 2024-07-07 23:17:23 +08:00
aab5144371 fix: 字幕req导致headerControl初始化异常 2024-07-07 22:27:28 +08:00
637c82f536 Merge branch 'main' into design 2024-07-07 15:32:15 +08:00
0a0d375caa Merge branch 'main' into fix 2024-07-07 11:01:29 +08:00
c248d33109 mod 2024-07-07 11:01:09 +08:00
c28b11d05a Merge branch 'design' 2024-07-06 23:47:30 +08:00
db23f844f5 Merge branch 'fix' 2024-07-06 23:47:23 +08:00
1032b6d2ea Merge branch 'main' into design 2024-07-06 23:47:02 +08:00
dac0898729 opt: AppBar渐变背景 2024-07-06 23:43:50 +08:00
a23a18f3bc fix: 字幕导致headControl初始化失败 2024-07-06 19:31:28 +08:00
60e8975622 Merge branch 'feature-memberFav' 2024-07-06 18:47:20 +08:00
77ab68abf5 Merge branch 'design' 2024-07-06 18:45:03 +08:00
2e3703a8d6 Merge branch 'fix' 2024-07-06 18:44:55 +08:00
ba430eaa2e mod: up主合集补充 2024-07-06 15:56:25 +08:00
7ae637ed6a feat: 查看up主收藏订阅 2024-07-06 15:37:50 +08:00
b3f1400c04 fix: 评论区跳转笔记 2024-07-06 00:07:02 +08:00
8ac987bfe8 Merge branch 'main' into fix 2024-07-05 23:35:11 +08:00
739e1d9ada opt: AppBar渐变背景 2024-07-05 22:54:21 +08:00
a276aea3da Merge branch 'main' into design 2024-07-03 23:49:10 +08:00
bc842f4da5 Merge branch 'fix' 2024-07-03 23:47:40 +08:00
0d96327f34 fix: 字幕查询越界 2024-07-01 23:11:11 +08:00
2cc43fad57 Merge branch 'main' into fix 2024-07-01 22:39:21 +08:00
3b42cf5ba9 Merge branch 'design' 2024-06-30 22:02:37 +08:00
b1e283beea Merge branch 'fix' 2024-06-30 22:01:50 +08:00
605f0269a5 Merge branch 'feature-sendMsg' 2024-06-30 22:01:40 +08:00
c7ba9dc97b feat: 系统消息 2024-06-30 22:01:12 +08:00
a19129c596 opt: 字幕按钮显隐逻辑 2024-06-30 13:02:50 +08:00
9f1b74b7e2 mod 2024-06-30 00:28:26 +08:00
d004440252 Merge branch 'main' into design 2024-06-29 18:47:56 +08:00
de86dba39d feat: 港澳台模式 2024-06-29 18:47:04 +08:00
eaae622f95 opt: 字幕渲染逻辑 2024-06-29 17:12:06 +08:00
d8b7e99ad7 Merge branch 'main' into fix 2024-06-29 14:30:20 +08:00
0f1d71e9c4 Merge branch 'design' 2024-06-29 14:30:08 +08:00
ea7ae15384 mod: 合集 2024-06-29 14:29:43 +08:00
a63a9a28d7 feat: 最近点赞的视频 2024-06-29 13:53:43 +08:00
af28e8873b Merge branch 'fix' 2024-06-29 10:39:32 +08:00
317046cf41 Merge branch 'design' 2024-06-29 10:39:27 +08:00
6a28ccbf64 fix: 评论内容解析 2024-06-28 23:44:48 +08:00
7301673783 mod: 视频操作区图标 2024-06-28 23:24:42 +08:00
e31ae07848 Merge branch 'main' into design 2024-06-28 00:10:47 +08:00
dbfc31a1df v1.0.24 更新日志 2024-06-26 22:29:11 +08:00
936730d355 Merge branch 'feature-sendMsg' 2024-06-26 22:19:14 +08:00
0031b22902 mod: 未开发功能关闭入口 2024-06-26 22:18:49 +08:00
1c02617005 Merge branch 'design' 2024-06-25 23:51:53 +08:00
7aa02be251 mod 2024-06-25 23:51:32 +08:00
73005c3066 Merge branch 'main' into design 2024-06-25 23:36:06 +08:00
a38696beb5 Merge branch 'feature-sendMsg' 2024-06-25 00:12:18 +08:00
2156e084e6 Merge branch 'fix' 2024-06-25 00:12:03 +08:00
07ebe8f432 Merge branch 'design' 2024-06-25 00:11:59 +08:00
cc32224daf feat: 音频输出方式设置 2024-06-24 23:43:37 +08:00
abfb7a1439 fix: 评论区 /? 链接跳转异常 2024-06-24 23:04:32 +08:00
4fa9bdc380 mod: chatItem布局 2024-06-23 23:23:05 +08:00
c5247b27c7 mod: 消息计数清零 2024-06-23 23:12:04 +08:00
860818de08 Merge branch 'feature-appScheme' 2024-06-20 23:54:49 +08:00
2ebf1356a6 Merge branch 'fix' 2024-06-20 23:54:39 +08:00
6e48c7864a Merge branch 'design' 2024-06-20 23:54:35 +08:00
1db1d8f598 feat: 点击tab返回顶部 2024-06-20 23:38:49 +08:00
4de15ad6b3 opt: _routePush 逻辑分支 2024-06-20 00:06:45 +08:00
9f40b34020 Merge branch 'main' into feature-appScheme 2024-06-19 23:40:08 +08:00
56c0635389 mod: scheme 补充 2024-06-19 23:39:19 +08:00
54c38d8683 opt: 字幕类别补充 2024-06-19 23:17:36 +08:00
35d2d92480 fix: 分集切换字幕未更新 2024-06-18 23:35:38 +08:00
b61d2305a9 opt: 字幕类别补充 2024-06-17 23:51:01 +08:00
75525595c6 opt: 首页tab样式 2024-06-16 20:59:55 +08:00
2503d5cbb4 opt: 搜索结果为空 2024-06-16 19:09:05 +08:00
30f88f78a0 Merge branch 'fix' 2024-06-16 18:43:33 +08:00
4591593bb5 fix: 评论表情丢失 2024-06-16 18:34:32 +08:00
a2210f17f6 fix: 评论区链接重复跳转 2024-06-16 18:24:31 +08:00
06082359f3 Merge branch 'feature-sendMsg' 2024-06-16 18:03:00 +08:00
779f7e7666 feat: 收到的赞 2024-06-16 17:53:46 +08:00
569f7c23fd feat: 回复我的 2024-06-16 13:58:43 +08:00
bfdd996b08 feat: 未读消息计数 2024-06-15 20:34:28 +08:00
46cef5e55b feat: 会话移除 2024-06-15 17:03:52 +08:00
5e63f0da7b feat: 私信 2024-06-15 16:03:45 +08:00
c4441dccb2 Merge branch 'main' into feature-sendMsg 2024-06-14 21:50:55 +08:00
7159c582cd Merge branch 'design' 2024-06-14 21:50:32 +08:00
7d706aa012 Merge branch 'fix' 2024-06-14 21:50:29 +08:00
8038400678 Merge branch 'main' into feature-sendMsg 2024-06-13 23:14:50 +08:00
2873754317 feat: 播放器全屏回调 2024-06-13 23:13:47 +08:00
5afb50cac9 fix: 快进快退触发异常 2024-06-13 22:55:54 +08:00
593f7f1367 Merge branch 'main' into design 2024-06-12 23:38:18 +08:00
ca6f6dd531 Merge branch 'main' into fix 2024-06-12 23:38:00 +08:00
5c3d3cc957 undo: flutter version 3.22.1 -> 3.19.1 2024-06-12 23:37:29 +08:00
42a377fcab Merge branch 'fix' 2024-06-11 22:59:29 +08:00
7af1ebd3d5 fix: 弹幕显示&弹幕设置缓存 2024-06-11 22:59:07 +08:00
9067d4895f Merge branch 'feature-login' 2024-06-11 00:07:09 +08:00
beb5945135 fix: 网络异常时登录状态重置 2024-06-11 00:06:49 +08:00
cf597f1bcf Merge branch 'fix' 2024-06-10 23:46:22 +08:00
857d63ac96 Merge branch 'feature-updateVideoDetailStructure' 2024-06-10 23:46:03 +08:00
11b4b1b0b2 Merge branch 'design' 2024-06-10 23:45:56 +08:00
bc1c38c950 Merge branch 'main' into feature-sendMsg 2024-06-10 16:32:47 +08:00
d632d1c576 Merge branch 'main' into design 2024-06-10 14:55:02 +08:00
e47816d0c8 opt: 一键三连样式 2024-06-10 14:18:59 +08:00
128c9201bd fix: 评论框安全距离 2024-06-10 13:36:23 +08:00
05fea2b941 Merge branch 'main' into fix 2024-06-10 13:17:54 +08:00
c9cc57d14b Merge branch 'feature-updateVideoDetailStructure' 2024-06-10 13:13:08 +08:00
51be62cd26 opt: 一键三连 2024-06-10 12:50:15 +08:00
6e15264d91 Merge branch 'feature-login' 2024-06-10 01:10:13 +08:00
67012e88c2 Merge branch 'feature-updateVideoDetailStructure' 2024-06-10 01:10:07 +08:00
61c29d3082 fix: 跳转登录页 2024-06-10 01:09:28 +08:00
d94ddca905 opt: 三连进度条样式&登录状态判断 2024-06-10 00:58:04 +08:00
9675dcd510 Merge branch 'design' 2024-06-10 00:20:29 +08:00
5b0eeefc09 Merge branch 'feature-updateVideoDetailStructure' 2024-06-09 14:52:31 +08:00
006ba8bc59 feat: 一键三连 2024-06-09 14:51:59 +08:00
3a95b36e43 Merge branch 'main' into feature-updateVideoDetailStructure 2024-06-08 17:59:43 +08:00
be3bffa33c opt: 我的订阅页面布局 2024-06-08 17:54:40 +08:00
b37232931b mod: 默认不调用电池优化 2024-06-08 17:45:27 +08:00
0ada8163b7 Merge branch 'feature-search' 2024-06-08 17:41:45 +08:00
a03d159a86 mod: 内容分区字段 2024-06-08 17:36:55 +08:00
2b05f29c35 Merge branch 'main' into feature-search 2024-06-08 17:09:14 +08:00
d11b32cff7 Merge branch 'fix' 2024-06-08 15:36:06 +08:00
897551bf23 fix: 私信渲染类型错误 2024-06-08 15:35:45 +08:00
345c26d83a Merge branch 'main' into fix 2024-06-08 15:23:46 +08:00
f4b7f77e44 upgrade: new version syntax 2024-06-08 13:29:36 +08:00
57b8ebfa6f Merge branch 'main' into design 2024-06-08 13:16:13 +08:00
6f41683c5d Merge branch 'design' 2024-06-08 04:09:57 +08:00
24e62ca43b Merge branch 'feature-login' 2024-06-08 04:09:42 +08:00
aee52b0247 opt: 消息页面增加骨架屏 2024-06-08 04:09:00 +08:00
3a94281310 feat: web端扫码登录 2024-06-08 03:14:37 +08:00
4912506fb2 Merge branch 'design' 2024-06-08 00:43:45 +08:00
6f020df967 Merge branch 'fix' 2024-06-08 00:43:39 +08:00
0378dde36b Merge branch 'feature-login' 2024-06-08 00:43:22 +08:00
4fe8366c2f upgrade: gt3 plugin 2024-06-08 00:42:45 +08:00
9888aba62e feat: web端密码登录 2024-06-08 00:26:08 +08:00
f8897f74bf feat: web端验证码登录 2024-06-07 23:48:48 +08:00
709e4b4412 Merge branch 'main' into feature-login 2024-06-06 23:36:53 +08:00
7fc4f3fd41 fix: 首页app推荐关注状态判断 2024-06-06 00:28:46 +08:00
e0e1c6b08a upgrade: color 2024-06-04 23:59:23 +08:00
dbbf810dbd Merge branch 'opt-loadingAni' 2024-06-03 23:44:06 +08:00
a0e8918935 Merge branch 'feature-appScheme' 2024-06-03 23:43:46 +08:00
8d6d8932d5 Merge branch 'fix' 2024-06-03 23:43:27 +08:00
d5881452d3 Merge branch 'design' 2024-06-03 23:43:14 +08:00
3b1ad133db mod: logs replace# 2024-06-02 22:32:00 +08:00
accaaa1142 Merge branch 'main' into feature-appScheme 2024-06-02 16:08:11 +08:00
59304760f6 Merge branch 'main' into fix 2024-06-02 01:15:47 +08:00
251f0ed55d Merge branch 'design' 2024-06-02 01:06:35 +08:00
8f26884164 upgrade: 依赖升级 2024-06-02 01:04:51 +08:00
bde44af9a0 opt: main.daat code 2024-06-01 22:43:27 +08:00
2e57e73138 Merge branch 'main' into design 2024-06-01 12:56:06 +08:00
dfc3a1f8c3 Merge branch 'fix' 2024-06-01 12:55:59 +08:00
ac0dc59054 Merge branch 'main' into design 2024-06-01 12:55:34 +08:00
2358ba42e1 opt: pip issues #752 2024-06-01 00:21:04 +08:00
e1572c0962 fix: 锁定状态下避免手势退出全屏 issues #753 2024-05-31 23:37:27 +08:00
b14291ac1c Merge branch 'main' into fix 2024-05-31 23:32:38 +08:00
eef9e3ae9e opt: loading Ani 2024-05-30 23:58:42 +08:00
1283879fb7 Merge branch 'main' of github.com:guozhigq/pilipala_app 2024-05-29 23:58:44 +08:00
67f635aa3c Merge branch 'design' 2024-05-29 23:58:25 +08:00
6b4d24f595 opt: 动态页up主点击 2024-05-28 22:58:51 +08:00
f5bd4266da Merge branch 'main' into design 2024-05-28 22:45:02 +08:00
1c6047a326 Update README.md 2024-05-27 23:54:33 +08:00
063ae01d0a Merge branch 'fix' 2024-05-26 23:19:00 +08:00
4aa47f4a7b fix: pip进入 评论区异常 2024-05-26 23:18:37 +08:00
63d5752d3e fix: 连续快进异常 2024-05-26 17:30:42 +08:00
ccb0265a5f Merge branch 'design' 2024-05-26 16:34:42 +08:00
4bfc6f835f Merge branch 'fix' 2024-05-26 16:34:36 +08:00
5c0edf873b fix: 外链跳转 pic null 2024-05-26 16:26:39 +08:00
d6e24096e0 opt: 视频详情页Hero动画 2024-05-26 15:48:28 +08:00
1cefdb68b7 opt: 骨架屏补充 2024-05-25 16:38:16 +08:00
0d53090b11 Merge branch 'design' 2024-05-25 16:17:03 +08:00
d88603f475 Merge branch 'fix' 2024-05-25 16:16:58 +08:00
8d729549fc Merge branch 'fix-historySearch' 2024-05-25 16:16:45 +08:00
95685c4fdf mod: 历史记录搜索逻辑 2024-05-25 16:16:23 +08:00
732beb38a7 opt: 评论区跳转 2024-05-25 15:31:00 +08:00
f718c7dad5 Merge branch 'main' into fix 2024-05-25 15:11:47 +08:00
c387cb83e7 mod: 收藏夹跳转 2024-05-24 23:54:25 +08:00
7fd5d8240b Merge branch 'design' 2024-05-23 23:23:48 +08:00
58ab9d9810 Merge branch 'main' of github.com:guozhigq/pilipala_app 2024-05-23 23:23:39 +08:00
1bcaea31d9 Merge branch 'design' of github.com:guozhigq/pilipala_app into design 2024-05-23 00:39:03 +08:00
2dfd2d4dc1 opt: 视频操作栏安全区域优化 2024-05-23 00:32:59 +08:00
2619635e2b Merge branch 'main' into design 2024-05-23 00:15:44 +08:00
eea07984f3 Merge pull request #1 from guozhigq/fix
Merge branch 'main' into fix
2024-05-22 23:54:24 +08:00
f8abde0edb Merge branch 'main' into fix 2024-05-22 23:35:05 +08:00
ea718bee33 Merge branch 'main' into feature-updateVideoDetailStructure 2024-05-22 00:02:30 +08:00
5d99d8ed5b Merge branch 'opt-blockUser' 2024-05-21 23:52:22 +08:00
d2be65b76f Merge branch 'design' 2024-05-21 23:52:08 +08:00
ed28e98bba opt: 推荐视频拉黑逻辑 2024-05-20 23:45:38 +08:00
cb7cd0a2f0 feat: videoDetail menu edit 2024-05-19 19:57:13 +08:00
6b165b392f Merge branch 'main' into feature-updateVideoDetailStructure 2024-05-18 19:40:01 +08:00
eae39c480c mod: playInit autoplay 逻辑 2024-05-18 19:29:57 +08:00
9c2364d7ed Merge branch 'main' into design 2024-05-18 19:25:01 +08:00
0ff889b028 Merge branch 'feature-media_kit' 2024-05-18 00:08:31 +08:00
126165ac14 Merge branch 'design' 2024-05-17 23:57:09 +08:00
f23a21f6dc feat: 动态类型补充 2024-05-16 23:04:47 +08:00
37118f96b6 Merge branch 'main' into design 2024-05-16 22:56:01 +08:00
44f2a168ce opt: 播放器单例 2024-05-15 23:55:04 +08:00
c14954cad4 Merge branch 'fix' 2024-05-14 23:52:22 +08:00
31e8ee8d34 fix: 番剧无评分导致的异常 2024-05-13 23:55:17 +08:00
728f279a98 Merge branch 'main' into fix 2024-05-13 23:47:18 +08:00
11e38141c4 Merge branch 'fix' 2024-05-13 23:40:25 +08:00
68d35a7ed8 feat: 搜索分区UI 2024-05-13 23:37:22 +08:00
79bde1b7ff mod: 历史记录长按圆角 2024-05-13 23:16:25 +08:00
44ceb5779a Merge branch 'main' into design 2024-05-12 22:51:49 +08:00
5a10dbaf4c Merge branch 'main' into feature-media_kit 2024-05-12 00:54:24 +08:00
14b7ac438f fix: 直播全屏按钮 2024-05-11 23:50:41 +08:00
b8e3764345 fix: login Box has already been closed 2024-05-10 23:45:51 +08:00
eda3e777fe Merge branch 'fix' 2024-05-09 00:01:56 +08:00
37654c5eab fix: 评论区链接解析 2024-05-09 00:00:59 +08:00
5807999296 mod: scheme BVAV匹配规则 2024-05-08 23:19:29 +08:00
6380c138e8 typo: 历史记录接口 2024-05-08 23:19:02 +08:00
250131d2b0 fix: 分p字幕 2024-05-08 22:42:18 +08:00
c2a176ad2f fix: issues #718 2024-05-07 23:53:31 +08:00
7ff6884342 fix: pathSegments越界 2024-05-07 23:10:27 +08:00
4f10e6a869 fix: 动态未读标记 2024-05-07 00:01:44 +08:00
2fefe32587 mod: 默认不展示升级标识 2024-05-06 23:24:19 +08:00
0162b9614e fix: 评论区链接解析 2024-05-06 23:14:12 +08:00
db824681fd fix: 退出全屏按钮无效 2024-05-05 21:00:12 +08:00
5fab28284a Merge branch 'main' into fix 2024-05-05 20:57:00 +08:00
e62db9d447 Merge branch 'design' 2024-05-05 17:54:13 +08:00
e65389040f feat: 搜索计数 2024-05-05 17:53:54 +08:00
5934015104 Merge branch 'main' into design 2024-05-05 17:07:00 +08:00
ceb1fc2397 Merge branch 'fix' 2024-05-05 17:00:56 +08:00
93d8e19a8c fix: 动态跳转详情异常 2024-05-05 17:00:35 +08:00
15b63b42a2 Merge branch 'fix' 2024-05-05 10:09:49 +08:00
bb4325768f fix: 视频播放器封面异常 2024-05-05 10:06:26 +08:00
06e943027f Merge branch 'main' into fix 2024-05-05 09:43:52 +08:00
15f7c4a802 v1.0.23 更新日志 2024-05-04 23:22:24 +08:00
c64b48b04c Merge branch 'design' 2024-05-03 23:06:29 +08:00
dafbb83863 opt: 媒体库页面登录跳转 2024-05-03 22:58:14 +08:00
21453bb2d8 Merge branch 'main' into design 2024-05-03 22:15:18 +08:00
46f9b18ac3 Merge branch 'fix' 2024-05-03 21:42:30 +08:00
a32be2c5c6 fix: 半屏时播放完成状态栏消失 2024-05-03 21:42:01 +08:00
e6f4702f40 mod: 底部导航栏sdk判断 2024-05-03 21:41:04 +08:00
164da3f28e Merge branch 'fix' 2024-05-03 21:05:29 +08:00
c2873a2138 Merge branch 'design' 2024-05-03 21:05:23 +08:00
81af23e870 mod: MainActivity 2024-05-03 21:04:03 +08:00
0145232f53 Merge branch 'main' into fix 2024-05-03 16:29:55 +08:00
b2eaec15b1 opt: 视频卡片功能拓展 2024-05-03 15:58:54 +08:00
d18e87ef3f Merge branch 'design' 2024-05-02 23:38:30 +08:00
b8efe249de opt: up主投稿跳转番剧 2024-05-02 23:33:58 +08:00
9af748b0fb Merge branch 'design' 2024-05-02 22:19:40 +08:00
61505c6127 opt: RoutePush bangumi 2024-05-02 22:19:20 +08:00
d7c85d17c4 Merge branch 'main' into design 2024-05-02 21:06:04 +08:00
ab525da35d Merge branch 'feature-appScheme' 2024-05-01 23:41:39 +08:00
b85067f7eb opt: 系统级跳转av、bangumi 2024-05-01 23:41:18 +08:00
ebff9987b3 Merge branch 'main' into feature-appScheme 2024-05-01 23:19:30 +08:00
4d432c12ab Merge branch 'fix' 2024-05-01 22:52:13 +08:00
8082550da6 fix: 全屏状态异常 2024-05-01 22:51:55 +08:00
1d8ca1c141 Merge branch 'main' into fix 2024-05-01 22:37:06 +08:00
7bc4a3c633 Merge branch 'fix' 2024-05-01 20:44:58 +08:00
291312bde7 fix: 评论区跳转视频页pic null 2024-05-01 20:44:38 +08:00
91ad0fa03d fix: 查看评论详情点击区域 2024-05-01 20:22:52 +08:00
c6f35e98ed Merge branch 'main' into fix 2024-05-01 20:04:38 +08:00
e7bccbfab2 Merge branch 'fix-coverSave' 2024-05-01 20:04:14 +08:00
af7917ce7a Merge branch 'main' into fix-coverSave 2024-05-01 20:03:10 +08:00
2382768962 Merge branch 'fix' 2024-05-01 20:02:54 +08:00
e361e09ddf fix: 订阅详情type区分 2024-05-01 20:02:34 +08:00
afb354387a Merge branch 'main' into fix 2024-05-01 19:46:46 +08:00
7dbd832a80 feat: 长按保存封面 2024-05-01 19:46:27 +08:00
389747d6f4 v1.0.22 更新日志 2024-04-30 23:30:28 +08:00
013372a825 Merge branch 'fix' 2024-04-30 22:47:19 +08:00
68c73fde56 fix: videoDetail info copy 2024-04-30 22:47:02 +08:00
c4b06358ae Merge branch 'fix' 2024-04-30 22:40:41 +08:00
ba1a5486bf opt: 初始化时记忆播放 2024-04-30 22:40:22 +08:00
ab88b70e27 Merge branch 'main' into feature-updateVideoDetailStructure 2024-04-30 15:52:01 +08:00
c3cf7f2274 Merge branch 'main' into fix 2024-04-30 15:51:50 +08:00
1dd012109a Merge branch 'main' into design 2024-04-30 15:51:37 +08:00
b011805a7e Merge branch 'feature-updateVideoDetailStructure' 2024-04-30 15:51:12 +08:00
bab0985e86 mod: 关注按钮 2024-04-30 15:49:00 +08:00
1b0359efa7 Merge branch 'feature-updateVideoDetailStructure' 2024-04-30 15:35:49 +08:00
124ad62ca7 Merge branch 'fix' 2024-04-29 13:41:49 +08:00
3c8a92209f revert: 评论详情优先展示二级评论 2024-04-29 13:41:25 +08:00
787fac4bdd Merge branch 'fix' 2024-04-29 13:16:47 +08:00
0bb26aa467 fix: videoDetail cover null 2024-04-29 13:16:25 +08:00
3e95479248 Merge branch 'main' into fix 2024-04-28 23:28:03 +08:00
811487bdd4 fix: 重回前台弹幕堆积 2024-04-28 23:03:33 +08:00
06d47dfcc1 Merge branch 'fix' 2024-04-28 22:27:39 +08:00
bc4fb0e0ae fix: videoDetail cover null 2024-04-28 22:27:14 +08:00
90dcb54997 Merge branch 'feature-editNavBar' 2024-04-28 22:03:40 +08:00
8a6b4f987f revert: NavigationBar Color 2024-04-28 22:03:19 +08:00
4b7bc5c7b9 Merge branch 'main' into feature-editNavBar 2024-04-28 08:03:26 +08:00
7c293fc60f Merge branch 'design' 2024-04-28 07:40:20 +08:00
750e9d2b58 revert: 评论详情优先展示二级评论 2024-04-28 07:39:55 +08:00
fd7e2d2a31 Merge branch 'design' 2024-04-28 00:07:18 +08:00
c653a60f05 feat: 活动类型动态渲染 2024-04-28 00:06:08 +08:00
b81dd2b91c Merge branch 'fix' 2024-04-27 22:18:27 +08:00
aff39c291d fix: 合集cover null 2024-04-27 22:17:10 +08:00
d61068cd3f Merge branch 'main' into fix 2024-04-27 22:12:04 +08:00
cbb7b90bf9 mod 2024-04-27 22:11:56 +08:00
d631b0e172 Merge branch 'main' into design 2024-04-27 21:46:04 +08:00
75c37e3de2 Merge branch 'feature-collections' 2024-04-27 20:41:23 +08:00
8cd9a538b9 Merge branch 'design' 2024-04-27 20:41:00 +08:00
257596cb9e mod: 默认关闭硬解 2024-04-27 20:40:23 +08:00
f24291134c feat: 合集封面展示、封面切换 2024-04-27 17:44:19 +08:00
13ca6f3aed Merge branch 'main' into feature-collections 2024-04-27 16:17:24 +08:00
adf969d35d Merge branch 'opt-DialogAction' 2024-04-27 16:08:44 +08:00
0e52e0288c opt: 优化投币Dialog逻辑 2024-04-27 16:08:10 +08:00
89ea456d6d Merge branch 'fix' 2024-04-27 15:32:31 +08:00
29bdbbfe18 mod: playCount calc 2024-04-27 15:32:05 +08:00
6490d31d3d Merge branch 'main' into fix 2024-04-27 12:43:50 +08:00
599afd94b2 Merge branch 'feature-together' 2024-04-27 12:36:42 +08:00
b2e5cc0443 Merge branch 'fix' 2024-04-27 12:36:20 +08:00
0312c8d01d feat: 显示联合投稿up 2024-04-27 12:35:43 +08:00
b752214af3 fix: durl格式视频播放 2024-04-27 01:04:11 +08:00
8273ff2e02 opt: 评论框优化 2024-04-26 23:46:54 +08:00
4e66a3f9e7 opt: 评论详情优先展示二级评论 2024-04-26 23:22:26 +08:00
c6f5bc561c feat: up投稿展示充电专属标签 2024-04-25 23:18:21 +08:00
1f426881f2 mod: 使用feed_version V3推荐 2024-04-25 22:42:42 +08:00
ca58ec1cb0 Merge branch 'main' into design 2024-04-25 22:34:42 +08:00
2b7a566094 Merge branch 'design' 2024-04-25 00:00:17 +08:00
ab45d2e6a6 opt: 首页推荐卡片 2024-04-24 23:56:07 +08:00
31125435b1 opt: 首页推荐栏布局 2024-04-23 23:40:40 +08:00
926c99001e mod: 视频简介增加aid显示 2024-04-22 23:58:36 +08:00
935073ce75 mod: 视频详情页布局调整 2024-04-22 23:51:31 +08:00
fb59ca5e95 Merge branch 'main' into feature-updateVideoDetailStructure 2024-04-22 23:06:20 +08:00
e3b67e1fc6 Merge branch 'main' into design 2024-04-21 22:41:51 +08:00
e4d84e21db Merge branch 'feature-appScheme' 2024-04-21 19:26:21 +08:00
b83ac38c60 mod: scheme 补充 2024-04-21 19:25:42 +08:00
7ad68c7bb3 Merge branch 'main' into fix 2024-04-21 17:38:39 +08:00
45f0a8e015 Merge branch 'feature-dynamicForward' 2024-04-21 17:38:22 +08:00
c42ce2f1f9 mod: 番剧传递aid 2024-04-21 17:38:07 +08:00
9dc7256a00 feat: 投稿番剧评论时转发到动态 2024-04-21 16:47:38 +08:00
a8585c4a76 Merge branch 'feature-dynamicForward' 2024-04-21 15:52:47 +08:00
22d8cf12a0 opt: forward panel 2024-04-21 15:52:16 +08:00
cf8f0fac26 merge favFolder delete 2024-04-21 11:52:38 +08:00
0f8139fac7 Merge branch 'fix' 2024-04-20 23:32:47 +08:00
bc29f69133 opt: streamController close 2024-04-20 23:32:28 +08:00
0edc1bde48 Merge branch 'feature-dynamicForward' 2024-04-20 22:41:35 +08:00
03ce69b140 feat: 动态转发 2024-04-20 22:40:54 +08:00
fcd7781789 feat: 收藏夹删除 2024-04-20 00:55:18 +08:00
254fac144e Merge branch 'fix' 2024-04-19 23:50:22 +08:00
5f0bb44bf1 mod: 获取收藏夹pageSize 2024-04-19 23:43:23 +08:00
e2d07906dd Merge branch 'main' into fix 2024-04-19 23:28:00 +08:00
4bc24be368 Merge branch 'design' 2024-04-19 00:02:31 +08:00
350e349023 feat: ai总结复制功能 2024-04-19 00:01:09 +08:00
11e4f9d3dc Merge branch 'main' into design 2024-04-18 23:44:01 +08:00
61d61a019d Merge branch 'fix-dynamicReply' 2024-04-18 23:35:22 +08:00
3e105804d7 fix: 动态回复页面键盘收起后无法返回 2024-04-18 23:33:55 +08:00
43f2d995a7 Merge branch 'main' into fix 2024-04-18 22:50:24 +08:00
6edd0aa702 Merge branch 'main' of github.com:guozhigq/pilipala_app 2024-04-18 00:02:16 +08:00
f8e8deab8c Merge branch 'feature-playerSubtitle' 2024-04-18 00:01:32 +08:00
7fc2765471 opt: 字幕类别补充 2024-04-18 00:00:19 +08:00
706c134627 Update beta_ci.yml 2024-04-17 23:53:00 +08:00
c0b4aacafe Merge branch 'main' into feature-playerSubtitle 2024-04-17 23:16:12 +08:00
328f96a809 Merge branch 'fix' 2024-04-16 23:48:10 +08:00
038801a3b9 opt: 视频初始化 2024-04-16 23:41:45 +08:00
a8cfe9fa1a fix: seekTo异常 2024-04-16 23:38:03 +08:00
ef7295399b Merge branch 'fix' 2024-04-16 00:11:51 +08:00
76f0d5fba2 fix: zoneController error 2024-04-16 00:11:36 +08:00
adba7b33d3 fix: 预设视频画质异常 2024-04-15 22:59:35 +08:00
2354853689 Merge branch 'main' into fix 2024-04-15 22:28:08 +08:00
65f08e2bd5 Merge branch 'fix' 2024-04-14 17:11:04 +08:00
d943e156d2 fix: 关闭推荐时切换合集失效 2024-04-14 17:10:47 +08:00
3d3f106576 Merge branch 'fix' 2024-04-14 17:00:50 +08:00
e7f46883ed fix: 未开启自动播放时弹幕开关异常 2024-04-14 17:00:29 +08:00
c3a3974b08 Merge branch 'fix' 2024-04-14 00:48:14 +08:00
cc5e370510 fix: 跟随系统主题模式失效 2024-04-14 00:47:40 +08:00
56c8a956a3 Merge branch 'main' into fix 2024-04-14 00:12:51 +08:00
cba33dc303 Merge branch 'feature-collections' 2024-04-13 23:35:07 +08:00
f1334b5505 feat: 番剧选集 2024-04-13 23:34:46 +08:00
2a986218a8 Merge branch 'main' into feature-collections 2024-04-13 23:02:11 +08:00
dee2892d8a Merge branch 'main' into design 2024-04-13 22:58:04 +08:00
e21396c728 Merge branch 'design' 2024-04-13 22:57:41 +08:00
894f4fa68d opt: index为0时合集跳动 2024-04-13 22:57:18 +08:00
d01a54c1cc Merge branch 'feature-updateVideoDetailStructure' 2024-04-13 22:39:47 +08:00
fa1fea0e0b Merge branch 'design' 2024-04-13 18:49:39 +08:00
3fa809484c Merge branch 'fix' 2024-04-13 18:49:33 +08:00
b1c801c8c8 fix: mediaPage stream error 2024-04-13 16:31:28 +08:00
177645db1a Merge branch 'main' into fix 2024-04-13 16:22:14 +08:00
ff5101d3d0 opt: 可拖动标识 2024-04-13 15:46:12 +08:00
83d121fd9c opt: sheetHeight 2024-04-13 15:04:33 +08:00
d03678fea6 Merge branch 'main' into feature-updateVideoDetailStructure 2024-04-13 13:31:49 +08:00
cf206959aa Merge branch 'main' into design 2024-04-13 10:59:23 +08:00
6f31cb15e6 merge design 2024-04-13 10:58:58 +08:00
ea18c926fd Merge branch 'feature-editNavBar' 2024-04-13 10:57:45 +08:00
504d9e2065 opt: stream listener 2024-04-13 10:55:14 +08:00
ef6070aa8b mod: systemNavBarColor 2024-04-13 10:35:06 +08:00
2aa5c5e4b3 Merge branch 'feature-editNavBar' 2024-04-13 01:41:54 +08:00
4f438603ca Merge branch 'fix' 2024-04-13 01:41:44 +08:00
297ad6a46d opt: navBar样式 2024-04-13 01:41:24 +08:00
1076c02a58 fix: android 12权限处理 2024-04-12 23:12:12 +08:00
10435bb7b1 mod: 最近投币视频标题左对齐 2024-04-12 00:01:32 +08:00
8d9a30646b Merge branch 'feature-editNavBar' 2024-04-11 23:30:43 +08:00
c4c0ca38e3 Merge branch 'design' 2024-04-11 23:30:21 +08:00
a74647eb61 opt: 默认不收起顶栏&底栏 2024-04-11 23:29:51 +08:00
4b3dd3ca59 opt: navigationBar展示 2024-04-11 23:26:06 +08:00
815a62d5c2 Merge branch 'main' into fix 2024-04-11 00:01:13 +08:00
ebc4362c2e Merge branch 'feature-updateVideoDetailStructure' 2024-04-10 23:53:44 +08:00
7fd2304431 fix: pip进入时appbarStream异常 2024-04-10 23:52:21 +08:00
f95835d46b Merge branch 'main' into fix 2024-04-10 23:42:49 +08:00
8a08df50e1 Merge branch 'fix' 2024-04-10 23:38:07 +08:00
3f16a36bac fix: 楼中楼评论请求异常 2024-04-10 23:34:59 +08:00
b80bf7728f merge main 2024-04-09 23:51:35 +08:00
87ca412d86 Merge branch 'feature-profile' 2024-04-09 23:42:40 +08:00
9f9471d7f9 fix: up主页专栏视频时长显示 2024-04-09 23:42:09 +08:00
bd1faa79c3 Merge branch 'feature-liveRoomRender' 2024-04-09 23:33:42 +08:00
04bfc29452 feat: 直播间刷新 2024-04-09 23:33:13 +08:00
81a5d62600 Merge branch 'feature-headerControl' 2024-04-09 23:23:23 +08:00
ca37d45eb9 fix: 全屏切换合集视频标题未更新 2024-04-09 23:22:57 +08:00
2fec56979d Merge branch 'feature-dlna' 2024-04-08 23:56:17 +08:00
84f83c260a feat: 简单实现投屏 2024-04-08 23:55:29 +08:00
b9e93dabe6 Update beta_ci.yml 2024-04-08 23:45:54 +08:00
98a181fcdb Merge branch 'main' of github.com:guozhigq/pilipala_app 2024-04-08 23:44:28 +08:00
04b9b7a35a Merge branch 'fix' 2024-04-08 23:43:34 +08:00
2bd97f800e Update beta_ci.yml 2024-04-08 23:43:25 +08:00
48f0b59701 mod: 相册、存储权限判断 2024-04-08 23:33:43 +08:00
42673bd434 Merge branch 'feature-subscription' 2024-04-07 23:51:27 +08:00
aeca7c614e Merge branch 'feature-editNavBar' 2024-04-07 23:51:20 +08:00
9ee0f6526c mod: 订阅视频类型补充 2024-04-07 23:48:27 +08:00
9c12c21796 mod: navigation Bar编辑 2024-04-07 23:13:51 +08:00
f90e40a490 Merge branch 'feature-collections' 2024-04-06 22:24:09 +08:00
46c975bbbb mod: 合集按钮展示逻辑 2024-04-06 21:57:57 +08:00
c2a4d80c79 feat: 全屏选择合集 2024-04-06 21:32:29 +08:00
d1e83e4d1c Merge branch 'main' into feature-collections 2024-04-06 15:33:41 +08:00
1730672b15 Merge branch 'feature-collections' 2024-04-06 15:32:44 +08:00
ce56072995 mod: 更新播放器底栏配置 2024-04-06 15:32:08 +08:00
d4212f88c5 feat: 投稿、番剧合集整理 2024-04-06 15:26:53 +08:00
a5494484ae mod: 更新播放器底栏配置 2024-04-06 11:30:53 +08:00
99645c7b4a fix: seekTo multiple trigger 2024-04-05 23:20:44 +08:00
bec619111b Merge branch 'fix' 2024-04-05 22:20:51 +08:00
0d0e0b9adb fix: 关注按钮状态异常 2024-04-05 22:19:45 +08:00
7aa7ca1e70 Merge branch 'fix' 2024-04-05 20:07:30 +08:00
ec7762644b mod: findClosestNumber 2024-04-05 00:02:31 +08:00
5500a58c32 mod: 优化selectDialog选择逻辑 2024-04-05 00:02:31 +08:00
247f6ee0a7 mod: findClosestNumber 2024-04-04 23:59:53 +08:00
b58df720fd mod: 优化selectDialog选择逻辑 2024-04-04 23:41:16 +08:00
2ba72e3792 Update README.md 2024-04-03 23:59:24 +08:00
25c416f9bf Merge branch 'fix' 2024-04-03 23:20:44 +08:00
9ec9fc2830 Merge branch 'feature-updateVideoDetailStructure' 2024-04-03 23:20:21 +08:00
74f6b0ad1e fix: 评论框切换action时高度跳动 2024-04-02 23:58:27 +08:00
3fa72f5340 Merge branch 'main' into fix 2024-04-02 23:31:45 +08:00
c0f3b4f3a2 fix: 全屏时展示弹幕操作 2024-04-02 23:13:15 +08:00
c6de1fa95a mod: 评论b23.tv链接匹配 issue 2024-04-02 00:09:44 +08:00
da9828a295 mod: 个人主页样式修改 2024-04-02 00:09:44 +08:00
e212a32763 feat: 启动时清除日志 issues #656 2024-04-02 00:09:44 +08:00
a20217bf39 opt: 向下查找可用视频清晰度 2024-04-02 00:09:44 +08:00
469a5ec691 mod: 个人主页样式修改 2024-04-01 23:03:31 +08:00
8897c4dd5b feat: 启动时清除日志 issues #656 2024-03-31 11:11:50 +08:00
336feb4fda fix: utils timeFormat error 2024-03-31 00:38:08 +08:00
53b103b853 fix: utils timeFormat error 2024-03-31 00:27:39 +08:00
fd54915399 fix: 历史记录进度条 2024-03-30 22:30:18 +08:00
157726c4c6 Merge branch 'feature-subscription' 2024-03-30 22:25:56 +08:00
25272d6d1b Merge branch 'feature-historyProgress' 2024-03-30 22:25:30 +08:00
af1163f6e0 fix: 历史记录进度条 2024-03-30 22:17:37 +08:00
b689db16b5 Merge branch 'main' into feature-media_kit 2024-03-30 17:13:09 +08:00
d003f864ce feat: 订阅取消 issues #658 2024-03-30 17:01:32 +08:00
dd7b226351 Merge branch 'main' into feature-subscription 2024-03-30 16:40:10 +08:00
6c2eab86e9 fix: 视频标题展开 2024-03-30 00:09:31 +08:00
d806de7d8f feat: 播放记录进度条展示 2024-03-30 00:09:31 +08:00
5cb3e578a8 Merge branch 'feature-updateVideoDetailStructure' 2024-03-29 00:02:36 +08:00
8f9fbf5d41 fix: 视频标题展开 2024-03-29 00:01:17 +08:00
53941469ce opt: 向下查找可用视频清晰度 2024-03-28 00:29:22 +08:00
eb73680296 Merge branch 'main' into design 2024-03-28 00:27:28 +08:00
9845f0383a Merge branch 'feature-historyProgress' 2024-03-28 00:00:46 +08:00
fb3be848b4 feat: 播放记录进度条展示 2024-03-28 00:00:27 +08:00
7d7df17317 Merge branch 'fix' 2024-03-27 23:47:18 +08:00
aae08d0688 fix: 最热/最新评论标识未刷新 2024-03-27 23:44:07 +08:00
9fe5b78cfa Merge branch 'fix' 2024-03-27 23:37:07 +08:00
6b028c36af mod: 搜索专栏副标题转义 2024-03-27 23:34:59 +08:00
92c385ff58 Merge branch 'fix' 2024-03-27 23:28:09 +08:00
463ee1d5b5 mod: 标题转义补充 2024-03-27 23:27:53 +08:00
0a416c95bc Merge branch 'main' into fix 2024-03-27 23:20:10 +08:00
ed8443ba02 feat: navigation Bar编辑 2024-03-27 22:26:47 +08:00
3d09d80007 Merge branch 'feature-rank' 2024-03-26 22:14:41 +08:00
fffa15faa3 merge 2024-03-26 22:14:36 +08:00
d6b972a8ab mod: 弹幕图标颜色&稍后再看 2024-03-26 22:13:01 +08:00
d6fd299395 fix: tabbar指示器抖动 2024-03-25 22:25:05 +08:00
1953653044 Merge branch 'main' into feature-rank 2024-03-25 22:19:16 +08:00
9faa625d52 Merge branch 'feature-playerSubtitle' 2024-03-24 23:27:45 +08:00
955d8f5401 feat: 简单实现字幕功能 2024-03-24 23:25:45 +08:00
1f75a7e781 fix: appbar滑动距离移除 2024-03-24 16:54:19 +08:00
2cd8ab7d27 mod: 视频详情页简介查看&操作栏 2024-03-24 16:54:19 +08:00
7e7bb1f43a fix: appbar滑动距离移除 2024-03-24 16:53:20 +08:00
a925ef63eb mod: 视频详情页简介查看&操作栏 2024-03-24 16:53:20 +08:00
f8326e7cb5 fix: appbar滑动距离移除 2024-03-24 16:17:02 +08:00
02d2598d01 mod: 视频详情页简介查看&操作栏 2024-03-24 16:03:18 +08:00
862ccea879 Merge branch 'main' into feature-playerSubtitle 2024-03-24 13:48:15 +08:00
031d57e1fd opt: 排行榜切换tab数据缓存 2024-03-24 13:47:54 +08:00
77b509fd17 opt: 排行榜切换tab数据缓存 2024-03-24 13:42:40 +08:00
53b72bec25 mod: read schame补充 2024-03-24 13:16:13 +08:00
20745a4541 Merge branch 'main' into feature-appScheme 2024-03-24 11:44:02 +08:00
4db5a950f3 mod: 弹幕开关状态 2024-03-24 11:43:44 +08:00
c9327c97e5 fix: 评论投票message重复 2024-03-24 11:43:44 +08:00
8ff387d54a mod: 评论头部样式 2024-03-24 11:43:44 +08:00
11e907d74b mod: 弹幕开关状态 2024-03-24 10:48:45 +08:00
6298711528 Merge branch 'main' into feature-updateVideoDetailStructure 2024-03-24 10:43:24 +08:00
b1e80c298e Merge branch 'main' into design 2024-03-23 21:56:59 +08:00
72f1a82650 bump: sdk&dependencies 2024-03-23 19:44:37 +08:00
57f7628795 Merge branch 'main' into feature-customBottomControl 2024-03-23 18:13:47 +08:00
b5a46d1be0 feat: 接收自定义组件传入 2024-03-23 18:03:33 +08:00
7b15f19895 fix: 详情页TabBar布局异常 2024-03-23 18:00:52 +08:00
14338dc33d fix: 详情页TabBar布局异常 2024-03-23 17:23:51 +08:00
c216c9bd65 mod: tabbar样式&增加评论数 2024-03-21 23:51:43 +08:00
3701fdef97 Merge branch 'fix-scrollSetVolume' 2024-03-21 23:40:55 +08:00
59641f0216 Merge branch 'fix' 2024-03-21 23:40:30 +08:00
4dbcd2e0ec fix: 尝试修复音量调节抖动 issues #647 #498 #104 #198 2024-03-21 23:39:56 +08:00
6d276fce4c fix: 媒体库无法滑动 2024-03-21 00:03:20 +08:00
0f0546ae59 Merge branch 'main' into feature-rank 2024-03-20 23:30:04 +08:00
710361caea fix: 历史记录相关错误 2024-03-20 23:21:41 +08:00
00b81b194f Merge branch 'fix-issues#620' 2024-03-20 00:13:51 +08:00
734db176bf Merge branch 'main' into fix 2024-03-20 00:08:37 +08:00
c23fe9c2bf Merge branch 'main' into feature-updateVideoDetailStructure 2024-03-20 00:07:35 +08:00
79fcd017ab mod: tabbar样式&增加评论数 2024-03-20 00:07:00 +08:00
76784ee664 mod: seekTo 2024-03-19 23:33:32 +08:00
2dbef3fee2 mod: media_kit引入 2024-03-19 23:23:14 +08:00
3a66c8c03d Merge branch 'main' into feature-media_kit 2024-03-19 23:15:06 +08:00
64292d523f fix: player fit rebuild 2024-03-18 22:13:15 +08:00
42ed67e03f merge main 2024-03-17 22:42:46 +08:00
af96d16062 feat: 电池优化 2024-03-17 14:45:40 +08:00
12c299685b Merge branch 'feature-removeLockIcon' 2024-03-17 14:35:14 +08:00
1182a58cb4 Merge branch 'fix' 2024-03-17 14:30:26 +08:00
e04a7e5702 fix: 图片保存命名、质量、权限问题 2024-03-17 14:29:39 +08:00
e9dc6f7fdb Merge branch 'main' into fix 2024-03-17 01:13:54 +08:00
25d1ccc87a Merge branch 'feature-liveRoomRender' 2024-03-17 00:38:19 +08:00
33d28f51d1 feat: 未登录状态切换直播画质提示 2024-03-17 00:36:00 +08:00
a37f3b8b5b Merge branch 'main' into feature-liveRoomRender 2024-03-16 23:35:18 +08:00
a57f5e8b2f Merge branch 'main' of github.com:guozhigq/pilipala 2024-03-16 23:12:52 +08:00
beb640ac83 Merge branch 'fix-bangumi' 2024-03-16 23:12:31 +08:00
1b54f07bc3 merge main 2024-03-16 23:11:47 +08:00
fca0588377 Merge pull request #628 from guozhigq/feature-customBottomControl
Feature custom bottom control
2024-03-16 23:08:22 +08:00
4865948609 Merge branch 'fix-whisperRenderError' 2024-03-16 23:07:01 +08:00
09eb180fc7 Merge branch 'main' of github.com:guozhigq/pilipala 2024-03-16 23:06:18 +08:00
3fab47780b Merge pull request #635 from guozhigq/feature-videoDetailCodeCleanUp
feat: 视频番剧详情页代码整理
2024-03-16 23:05:58 +08:00
453cbd7b1c Merge branch 'main' of github.com:guozhigq/pilipala 2024-03-16 23:03:09 +08:00
0bf2326c73 Merge branch 'feature-updateVideoDetailStructure' 2024-03-16 23:02:43 +08:00
2985c624ab mod: stream close 2024-03-16 23:02:21 +08:00
32cbc2759e Merge branch 'main' into feature-updateVideoDetailStructure 2024-03-16 22:52:15 +08:00
13c77957fe fix: 番剧badge 2024-03-16 22:48:47 +08:00
f382c8f377 Merge pull request #632 from kalac2232/main
feature: 动态页跳转登录
2024-03-16 21:56:18 +08:00
491bc87251 fix: 私信列表渲染异常 issues #295 2024-03-15 23:26:43 +08:00
b13d7b475b feat: 非全屏状态下隐藏锁定按钮 2024-03-14 23:49:36 +08:00
5e8d9b524b Merge branch 'fix-favoritesCalc' 2024-03-14 23:31:00 +08:00
606f1b5c64 fix: 收藏夹详情计数错误 issues #637 2024-03-14 23:30:18 +08:00
c4fec14517 Merge branch 'main' into fix-favoritesCalc 2024-03-14 23:23:41 +08:00
f368ef83ee Merge branch 'fix' 2024-03-14 23:23:27 +08:00
357133fa97 fix:去除无用的setState 2024-03-14 10:00:32 +08:00
337cdafef3 typo: vdCtr 2024-03-14 00:18:03 +08:00
bc74e32c10 Merge branch 'main' into fix 2024-03-14 00:17:09 +08:00
a5558de872 Merge branch 'feature-hiddenRelateVideo' 2024-03-14 00:15:24 +08:00
73693c5bbb Merge branch 'feature-updateVideoDetailStructure' 2024-03-14 00:11:34 +08:00
2ad9c3c993 merge main 2024-03-14 00:11:04 +08:00
cab74cff17 Merge branch 'main' of github.com:guozhigq/pilipala 2024-03-14 00:08:52 +08:00
a161df2c7b Merge pull request #627 from guozhigq/feature-updateVideoDetailStructure
Feature update video detail structure
2024-03-14 00:08:32 +08:00
4def3ffb80 Merge branch 'fix' 2024-03-14 00:06:20 +08:00
da2bbeedff mod: 默认直播画质设置 2024-03-13 23:12:34 +08:00
70317f92e2 mod: 视频详情页结构优化 2024-03-13 22:42:34 +08:00
bc9ea43cd2 feat: 视频番剧详情页代码整理 2024-03-12 23:45:22 +08:00
882957e2f8 feature:动态页的错误按钮在未登录状态下的按钮为直接跳转登录页 2024-03-12 14:34:32 +08:00
d3766ae31b typo: dynamic type 2024-03-11 23:35:07 +08:00
641cf4ebb3 Merge branch 'main' of github.com:guozhigq/pilipala 2024-03-11 23:32:39 +08:00
99e6abdad9 feat: 相关视频推荐开关 issues #585 2024-03-11 23:31:20 +08:00
ab10223eca feat: 接收自定义组件传入 2024-03-11 23:03:50 +08:00
1eb2d23fb9 merge main 2024-03-11 22:36:36 +08:00
98aaca286b feat: 直播画质切换 2024-03-11 00:02:11 +08:00
dc1edf7e73 Merge pull request #624 from yeqiling/feature-rank
feat:支持排行榜功能
2024-03-10 22:58:47 +08:00
31405750e6 mod: 视频详情页布局 2024-03-10 22:44:07 +08:00
6fdfcb888d feat: 播放器底部控制栏自定义 2024-03-10 19:41:23 +08:00
3ece2bb173 feat: 系统倍速可编辑 2024-03-10 17:57:24 +08:00
b7b75e956f fix: 删除单个搜索历史无效 2024-03-10 16:13:11 +08:00
bf37c33291 feat:支持排行榜功能 2024-03-09 19:39:21 +08:00
06fb3e8d2f fix: 请求github异常 2024-03-09 01:25:54 +08:00
504be6fbda fix: 搜索结果类型为课堂时渲染异常 2024-03-09 01:18:26 +08:00
df4539a035 fix: github链接 issues #618 2024-03-08 23:15:48 +08:00
a3e1fd4e91 fix: 清除缓存提示 issues #619 2024-03-08 23:09:52 +08:00
f41bb02bae fix: 合集最后一p不显示 issues #620 2024-03-08 23:05:09 +08:00
105a29f311 feat: disable Battery Optimization 2024-03-08 22:56:16 +08:00
3bf6136bc6 fix: 楼中楼评论请求重复 #284 2024-03-08 00:03:34 +08:00
ab24da5f55 fix: 媒体通知进度条未按预期停止 2024-03-07 23:35:39 +08:00
ed0b43eff1 v1.0.21 更新日志 2024-03-06 23:29:18 +08:00
ab9ae3a481 fix: setState() called after dispose() 导致全屏失效 2024-03-06 00:04:52 +08:00
d728b1fb6d mod: 评论区非正常地址判断 2024-03-05 23:39:05 +08:00
12e947ef84 fix: reply callback null error issues #615 2024-03-05 23:21:51 +08:00
3fad86e7e3 fix: 视频简介被遮挡 issues #613 2024-03-05 23:04:59 +08:00
fea70011cb fix: navBars unmodifiable issues #612 2024-03-05 23:01:23 +08:00
32cdb27f7c fix: enableGradientBg未定义 2024-03-05 22:37:52 +08:00
eb4435045b fix: 番剧全屏时title取值异常 2024-03-04 23:48:01 +08:00
f1b829cec1 fix: 首页tarbar指示器跳动 2024-03-04 08:29:01 +08:00
b248158e62 v1.0.20 更新日志 2024-03-03 19:48:10 +08:00
83b0ff02e4 fix: 图片预览放大、取消下滑关闭图片预览 2024-03-03 18:32:13 +08:00
8109314aaf opt: url scheme优化 issues #581 2024-03-03 15:20:59 +08:00
c4b3446956 Merge branch 'fix-replyPanelScroll' 2024-03-03 12:54:26 +08:00
d804d95d78 Merge branch 'fix-pip' 2024-03-03 12:53:48 +08:00
234dfe9d64 Merge branch 'fix' 2024-03-03 12:53:40 +08:00
c20df8fd81 fix: enable pip 2024-03-03 12:22:10 +08:00
19f0b1b28f fix: 动态专栏重复 2024-03-03 11:53:15 +08:00
481fa0d934 feat: 默认启动页设置 issues #483 2024-03-03 11:09:52 +08:00
caca16a957 fix: 动态页面upPanel不刷新 2024-03-03 09:57:41 +08:00
602d795909 Merge branch 'main' into fix 2024-03-03 09:37:28 +08:00
800f714f4a mod: 视频详情页appBar 2024-03-03 00:52:47 +08:00
75f569cb79 mod: 合集布局 2024-03-03 00:15:16 +08:00
0e888537e8 mod: yml rename 2024-03-02 22:37:56 +08:00
a3ce15bd9e mod: CI format 2024-03-02 22:27:02 +08:00
40f94e7ace Merge pull request #587 from VillagerTom/sending-beta-to-tg-channel
推送至main分支时编译为beta版本,发送到Telegram频道
2024-03-02 22:24:19 +08:00
370dcaf419 mod: 用户登录状态msg取值 2024-03-02 16:05:15 +08:00
f81f348a3e fix: 视频详情页评论下拉刷新 issues #486 2024-03-02 15:51:24 +08:00
4191cafe78 fix: 推荐卡片单列布局 2024-03-02 14:47:07 +08:00
ae33cbf7ca fix: 搜索框默认搜索词溢出 2024-03-02 13:02:43 +08:00
fca7c36203 mod: 动态页面upPanel 2024-03-02 12:56:16 +08:00
5fc783ebc2 Merge branch 'design' 2024-03-02 11:46:20 +08:00
98122aeaae fix: audioHandler null 2024-03-02 11:45:36 +08:00
f0d8e2a122 feat: 播放器控制栏动画开关 2024-03-02 11:19:18 +08:00
f815affff9 opt: 播放器控制栏动画 2024-03-02 00:40:53 +08:00
fce701090a Merge branch 'main' into design 2024-03-02 00:39:01 +08:00
d6da2a8a47 fix: headerControl bvid丢失 2024-03-01 23:55:19 +08:00
b3e162c8d3 Merge branch 'main' into fix 2024-03-01 23:45:12 +08:00
e5eae93a78 fix: 私信页面表情面板 issues #588 2024-03-01 23:01:10 +08:00
962dcca6d4 Merge branch 'fix' 2024-03-01 00:15:14 +08:00
be56fb721f fix: 私信页面表情面板 issues #588 2024-03-01 00:14:42 +08:00
ce1c80fd86 fix: 动态最新关注横行拉伸 issuse #580 2024-02-29 23:42:09 +08:00
33ef18ef1d fix: 评论jumpUrl正则转义 2024-02-29 00:30:55 +08:00
ba61e38c9b Merge branch 'fix-dynamicReplySeekTime' 2024-02-29 00:00:09 +08:00
0b0db1a2b1 mod: videoPage path判断 2024-02-28 23:59:47 +08:00
a9d73a9f1b fix: 动态标题未显示 2024-02-28 23:51:30 +08:00
0b5397ec00 fix: 动态评论区seek error 2024-02-28 23:29:02 +08:00
466214b26a fix: statusBarIcon color 2024-02-28 23:17:03 +08:00
699be4125b 将版本号中的alpha改为beta; 加回之前删去的“v” 2024-02-28 16:22:14 +08:00
45cc46d6d6 重命名:.github/workflows/alpha.yml -> .github/workflows/CI.yml 2024-02-28 15:38:17 +08:00
3f9fcabc2d Revert "将alpha.yml的workflow name改为alpha, 避免混淆"
This reverts commit 04186cdd5b.
2024-02-28 15:36:30 +08:00
65d2bfd844 升级至channel-post@v1.0.7, 支持传输大文件 2024-02-28 15:35:28 +08:00
4642c2a847 将git log pretty format中raw body替换为subject, 避免revert commit多行输出 2024-02-28 15:35:28 +08:00
04186cdd5b 将alpha.yml的workflow name改为alpha, 避免混淆 2024-02-28 15:35:28 +08:00
40cc4e0dd1 channel-post@v1.0.5重复发送文件,改为v1.0.4 2024-02-28 15:35:28 +08:00
95bc4a9f46 在Telegram消息中显示最后一次提交信息 2024-02-28 15:35:28 +08:00
3bf3fd9a46 使用参数fetch-depth: 0取得所有分支和tags, 末端提交改回HEAD 2024-02-28 15:35:28 +08:00
83ad11402f 😅注释符被识别为文件名的一部分 2024-02-28 15:35:28 +08:00
cfeb0588c1 取消发送其他架构APK, 减少发送文件大小 2024-02-28 15:35:28 +08:00
381e832f3c 修正架构名称拼写错误 2024-02-28 15:35:28 +08:00
6c20a434ed 列出文件 2024-02-28 15:35:28 +08:00
fc2da3ce57 使checkout action克隆指定分支; 统一代码缩进 2024-02-28 15:35:28 +08:00
a3abed0a03 新增alpha.yml, 用于编译推送至alpha分支的代码并发送至Telegram频道 2024-02-28 15:35:28 +08:00
db03cdd442 fix: List dataType 2024-02-28 00:45:13 +08:00
542975d0ec feat: 全屏手势设置 issues #517 2024-02-28 00:34:46 +08:00
ee368d348d feat: 字幕展示 2024-02-27 22:50:02 +08:00
835ea0a9ff Merge branch 'design' 2024-02-26 00:03:00 +08:00
89501d3daa Merge branch 'main' of github.com:guozhigq/pilipala 2024-02-26 00:02:06 +08:00
90c0256766 opt: 图片加载&设置 2024-02-26 00:00:14 +08:00
c2767486f5 Merge branch 'main' into design 2024-02-25 23:24:33 +08:00
e2489ef0e3 feat: 私信页面表情面板 2024-02-25 22:48:02 +08:00
b2a4c54565 merge main 2024-02-25 20:32:02 +08:00
bf071ea9e1 feat: 消息未读标记 2024-02-25 19:34:24 +08:00
f8a8c0967a feat: 评论增加表情 2024-02-25 19:09:12 +08:00
078e4716b4 feat: 我的订阅 2024-02-25 12:12:54 +08:00
4da6667b81 mod: 直播间背景图片 2024-02-24 17:37:14 +08:00
e2befb11ff feat: 横屏全屏时展示视频标题 2024-02-24 02:37:16 +08:00
cb0ff334b3 Merge pull request #569 from My-Responsitories/fix-dynamic-risk-challenge
fix: up主页未登录状态风控校验
2024-02-24 02:01:08 +08:00
e536f58ff4 Merge branch 'feature-replyJumpUrl' 2024-02-24 01:49:06 +08:00
d9992663d8 Merge branch 'fix-issues#568' 2024-02-24 01:47:51 +08:00
b1a05c5c27 mod: 修改关于页面 2024-02-24 01:38:27 +08:00
02cc164635 feat: 首页tabbar样式设置 issues #564 2024-02-23 22:44:10 +08:00
35dc94014c mod: 直播mcdn链接替换 issues #568 2024-02-23 00:30:26 +08:00
5746b85b27 fix: 视频全屏遮挡 issues #347 2024-02-22 00:17:38 +08:00
740d5f1ddd fix: 视频详情页点击主页按钮卡死 issues #562 2024-02-21 23:27:32 +08:00
a0f92df5b5 fix dynamic risk challenge 2024-02-21 13:16:38 +08:00
fce96d4976 feat: 评论话题匹配 2024-02-20 23:54:45 +08:00
dd6c537135 Merge branch 'feature-chargeVideo' 2024-02-19 23:24:12 +08:00
bcf94e287a mod: 修改收藏视频响应判断 2024-02-18 23:44:21 +08:00
841d0f25f5 fix: 评论区jumpUrl BV跳转 2024-02-18 08:20:48 +08:00
4811dc5ba5 fix: changeSeasonOrbangu aid null 2024-02-18 08:11:11 +08:00
41af6c799b Merge branch 'main' into fix 2024-02-18 08:10:20 +08:00
e8f63f6114 fix: up投稿动态页增加未登录风控提示 2024-02-17 17:32:13 +08:00
d1e8068e51 Merge pull request #514 from orz12/fix-audio-fucus-interrupt
fix: 修复焦点恢复时错误播放的问题
2024-02-17 16:47:13 +08:00
6de9b1977c Merge pull request #548 from orz12/mod-imagepreview-hide-statusbar-in-android
mod: 图片预览页,安卓也隐藏状态栏
2024-02-17 15:09:52 +08:00
3c0f54bfd7 fix: app端model bvid null issues #546 2024-02-16 21:46:48 +08:00
3d2c6a122a feat: 充电视频试看 2024-02-16 21:30:29 +08:00
8950658f08 mod: 图片预览页,安卓也隐藏状态栏 2024-02-16 20:20:37 +08:00
7a78729a44 fix: 合集切换推荐视频未刷新 2024-02-16 18:23:34 +08:00
03e5e22fef Merge pull request #458 from orz12/mod-add-time-in-rcmd-and-search
mod: 搜索和推荐页增加时间
2024-02-16 11:42:29 +08:00
aa93ce0b89 Merge branch 'main' into mod-add-time-in-rcmd-and-search 2024-02-16 11:42:01 +08:00
0c365ad049 Merge branch 'design' 2024-02-16 11:00:48 +08:00
3d5c578fef mod: 动态页面upPanel 2024-02-16 11:00:23 +08:00
0a22f0f543 Merge branch 'design' 2024-02-16 09:49:55 +08:00
5bf7b69d79 feat: 收藏搜索结果删除 2024-02-16 09:33:59 +08:00
d57f84a1d7 fix: 路由跳转传参丢失 2024-02-15 21:59:28 +08:00
32b2f0ceff Merge pull request #539 from orz12/fix-speed-dialog-cannot-dismiss
fix: 播放速度dialog无法关闭
2024-02-15 21:10:52 +08:00
bae871cfa1 Merge branch 'feature-replyItem' 2024-02-15 21:07:55 +08:00
d95fe9fe14 mod: MorePanel样式 2024-02-15 21:07:23 +08:00
eb006e4c55 Merge branch 'feature-replyItem' 2024-02-14 20:09:48 +08:00
cb88d0c9ae Merge branch 'feature-liveRoomRender' 2024-02-14 20:09:40 +08:00
3efad736ae fix: 直播闪退 issues #540 2024-02-14 19:38:55 +08:00
42ad959155 fix: 速度设置无法取消 2024-02-14 08:44:00 +08:00
cdf800c49f mod: 评论复制逻辑 issues #420 #331 #297 #152 2024-02-13 23:33:51 +08:00
569277572a Merge pull request #536 from KoolShow/fix_seekto_regexp
fix: 含有小时的时间无法跳转
2024-02-12 18:11:26 +08:00
19b84571c1 Merge branch 'main' into fix_seekto_regexp 2024-02-12 18:11:15 +08:00
0812b8339e Merge branch 'feature-replyItem' 2024-02-12 17:55:08 +08:00
b817a0c807 修正正则表达式以匹配含小时的时间 2024-02-12 17:20:18 +08:00
3da70d7e27 Merge branch 'fix-replyRepeat' 2024-02-12 16:55:56 +08:00
5e59db85be fix: 评论笔记跳转 issues #472 2024-02-12 16:51:05 +08:00
77477ff4dd mod: merge main 2024-02-12 10:30:18 +08:00
89026e671c mod: 收藏视频相关 issues #51 #534 2024-02-12 10:07:29 +08:00
1c8e7e53a5 Merge branch 'fix-replyRepeat' 2024-02-11 23:20:11 +08:00
b264427be6 fix: 切换合集评论不刷新 issues #326 #525 2024-02-11 23:07:44 +08:00
d5134f972d Merge branch 'feature-liveRoomRender' 2024-02-11 18:48:24 +08:00
e2fd01a6d5 fix: video Storage初始化 2024-02-11 18:47:51 +08:00
289cc99bc2 mod 2024-02-11 09:10:45 +08:00
3d5ebe7e99 fix: 视频详情页评论重复请求 2024-02-10 19:57:10 +08:00
d9964d37a4 Merge branch 'fix-favBangumiPushError' 2024-02-10 19:24:54 +08:00
5da39a9c52 Merge branch 'feature-cacheManage' 2024-02-10 19:22:49 +08:00
44a162762c fix: 评论页面路由跳转 issues #405 2024-02-09 23:24:26 +08:00
d0f036ec35 fix: 评论回复多张图片拉伸 2024-02-09 09:32:28 +08:00
10b928474b mod 2024-02-08 22:46:39 +08:00
94f3b7c1e4 fix: minePage 路由跳转 2024-02-08 21:33:02 +08:00
fb8b2de115 feat: up搜索 2024-02-08 21:27:22 +08:00
0d5d33a365 feat: up投稿排序 2024-02-08 10:29:26 +08:00
c39e91073b feat: 应用内缓存清理 2024-02-07 22:57:30 +08:00
d258474a5a mod: 直播页面内容更新 2024-02-07 22:23:29 +08:00
b0c56feef5 mod: 首页网络异常请求重试 2024-02-07 02:47:11 +08:00
191472d0c4 mod: 网络请求异常样式修改 2024-02-07 01:17:35 +08:00
40c666e3d1 mod: 网络异常组件样式修改 2024-02-07 00:52:25 +08:00
63d600070b fix: 收藏详情页跳转搜索mediaId取值异常 2024-02-06 15:27:39 +08:00
ebdeec6730 fix: up主页跳转搜索mid取值异常 2024-02-06 12:23:07 +08:00
ee2a273d8b Merge branch 'main' into fix 2024-02-06 11:15:09 +08:00
083739e562 mod: 收藏卡片内容修改 2024-02-06 11:13:33 +08:00
71ccb9c0e5 fix: 收藏国创跳转异常 2024-02-06 11:01:36 +08:00
4a5f4ca2ca fix: 限时免费无法播放 issues #457 2024-02-06 00:14:46 +08:00
78ade4a193 mod: 移除评论按【最多回复】排序 issues #298 2024-02-05 23:41:40 +08:00
ae14653e72 Merge pull request #434 from orz12/mod-not-login-recommend2
mod: 推荐功能增强,新增模拟未登录和过滤器
2024-02-05 00:35:46 +08:00
01ac2c13e1 Merge branch 'main' into mod-not-login-recommend2 2024-02-05 00:35:11 +08:00
9e471b83d9 mod: cancel Get.snackbar 2024-02-05 00:19:03 +08:00
a560d66567 mod: rcmd FutureBuilder 2024-02-04 23:03:24 +08:00
80b39daaff mod: jumpUrl增加icon显示 issues #471 2024-02-04 22:06:45 +08:00
fb32388536 fix: 尝试修复焦点恢复时错误播放的问题 2024-02-04 18:44:48 +08:00
5f92a0c293 mod: 用户投稿显示弹幕数 2024-02-04 00:32:01 +08:00
3de009ac43 Merge branch 'fix-audioAutoReplay' 2024-02-03 23:46:34 +08:00
b29256f598 Merge branch 'fix-floating' 2024-02-03 23:42:54 +08:00
e7cf472a0f Merge branch 'fix-videoIntroError' 2024-02-03 23:39:17 +08:00
03c59d23b8 Merge branch 'fix-githubModelError' 2024-02-03 23:38:53 +08:00
b6f805f0e4 Merge branch 'main' of github.com:guozhigq/pilipala 2024-02-03 23:38:21 +08:00
e23c2469ed Merge pull request #509 from orz12/feat-auto_reply_push-msgtype
feat: 私信支持显示自动推送回复
2024-02-03 20:04:05 +08:00
387c799de1 feat: 动态未读标记 issues #459 2024-02-03 16:59:54 +08:00
230dd81342 fix: List 越界 2024-02-03 01:13:36 +08:00
47bdfec8c2 fix: github assets null error 2024-02-03 01:07:12 +08:00
6a844da259 mod: 点赞接口登录拦截 2024-02-03 00:53:18 +08:00
18bb58d293 mod: 投币状态响应status 2024-02-03 00:43:38 +08:00
045186b3c8 mod: 视频详情页响应status 2024-02-03 00:33:29 +08:00
b531599893 mod: floating依赖 2024-02-03 00:29:47 +08:00
1da84508d8 feat: 自动推送回复私信显示支持 2024-02-03 00:23:32 +08:00
4c44fab217 Merge pull request #502 from orz12/fix-query-onlineTotal-status-false
fix: 查询在线人数错误时没有返回status
2024-02-02 23:39:09 +08:00
5c3d438a7e Merge pull request #508 from guozhigq/fix-minePanelPush
fix: 个人面板无法跳转设置页面
2024-02-02 23:30:57 +08:00
92a8efdee1 Merge pull request #470 from orz12/fix-reply-reply-parse2
fix: 评论区识别逻辑重构,修复含有关键词的评论重复出现的问题
2024-02-02 23:27:59 +08:00
eb1e2ca5f4 fix: 个人面板无法跳转设置页面 2024-02-02 23:24:36 +08:00
6f62837495 mod: media_kit依赖 2024-02-02 23:12:04 +08:00
5b1022628c fix: 九图部分位置无法点击 2024-02-02 02:34:59 +08:00
33f61ac0fa fix: 查询在线人数错误时没有返回status 2024-02-02 01:18:06 +08:00
0b349e102e fix:评论区HTML实体转义;逻辑错误短路 2024-02-02 00:56:41 +08:00
81371c5a31 fix: 只有时间的评论区不高亮 2024-02-02 00:56:41 +08:00
85a59e11b9 fix: 修复没有关键词时无法匹配时间、修复不显示关键词时不替换超链接、时间添加中文冒号匹配并提升分支判定严格程度 2024-02-02 00:56:41 +08:00
e24ccc16fa mod: av2bv方法修改 2024-02-01 00:32:52 +08:00
89a43b1285 v1.0.19 更新日志 2024-01-31 23:28:51 +08:00
ea8af28828 fix: 专栏封面图尺寸异常 2024-01-31 23:11:03 +08:00
8a2c023343 fix: magType value 2024-01-31 23:03:45 +08:00
a86fe76e59 Merge branch 'fix-replyReqError' 2024-01-31 22:44:14 +08:00
d703e38c3f fix: avbv转换 2024-01-31 22:43:40 +08:00
9e93b50860 mod: 还原aid 2024-01-31 22:33:04 +08:00
9907967a0a Merge pull request #454 from orz12/feat-whisper-detail-type-and-emoji
feat: 私信显示分享视频内容、富文本表情,补充信息类型枚举
2024-01-31 08:08:29 +08:00
331969cc8d Merge pull request #443 from orz12/opt-video-detail-page
fix: 播放页数个问题
2024-01-31 08:06:26 +08:00
7157f89245 Update main.yml 2024-01-30 23:23:12 +08:00
163bb3c8da v1.0.18 更新 2024-01-30 23:17:00 +08:00
33f71c62df Merge branch 'fix-replyReqError' 2024-01-30 23:15:22 +08:00
365c367cc7 Merge branch 'main' of github.com:guozhigq/pilipala 2024-01-30 23:14:49 +08:00
7ea95e550b Merge branch 'feature-ciActionIpa' 2024-01-30 23:14:24 +08:00
699361e04c fix: aid 2024-01-30 23:11:54 +08:00
e835821f3c Merge pull request #452 from orz12/mod-ai-conclusion-not-support-tips
mod: AI视频总结提示
2024-01-29 23:47:16 +08:00
76d5f6333e Merge branch 'feature-logger' 2024-01-29 23:28:07 +08:00
91899a9537 Merge branch 'fix-replyLoadError' 2024-01-29 23:02:30 +08:00
5591bb3dbf Merge branch 'opt-requestError' 2024-01-29 23:01:30 +08:00
1adbdf127f Merge branch 'main' of github.com:guozhigq/pilipala 2024-01-29 22:58:34 +08:00
8169f5739c fix: 首页tabbar排序无效 2024-01-28 23:44:41 +08:00
127c6734f8 feat: 自动打包ipa文件 2024-01-28 23:15:34 +08:00
a78ce4f6d4 feat: 错误日志记录 2024-01-28 15:52:30 +08:00
22a2628513 fix: 评论区加载超时导致视图崩溃 issues #389 2024-01-27 22:42:04 +08:00
e972d17f1c mod: 优化请求异常信息 2024-01-27 22:37:16 +08:00
e603942b5f fix: 评论区时间正则拼接顺序 2024-01-27 15:49:53 +08:00
0c4bad406e fix: 评论区识别逻辑重构,修复含有关键词的评论重复出现的问题 2024-01-27 14:30:04 +08:00
791047753d Merge pull request #445 from orz12/feat-danmaku-strokewidth
feat: 新增弹幕描边粗细设置,默认值降低
2024-01-27 13:25:28 +08:00
3784f1f87b Merge pull request #468 from guozhigq/feature-modLongImgLabel
mod: 长图标签显示判断
2024-01-27 13:06:40 +08:00
c0162892ef mod: 长图标签显示判断 2024-01-27 13:05:59 +08:00
10d2995429 mod: 对齐搜索栏调整 2024-01-27 12:06:45 +08:00
b0d8f5d0b6 Merge branch 'main' into pr/434 2024-01-27 10:28:55 +08:00
a64a49df22 Merge pull request #466 from guozhigq/feature-removeRcmdCache
mod: 移除首页推荐视频默认缓存
2024-01-27 10:13:02 +08:00
349de75dfd mod: 移除首页推荐视频默认缓存 2024-01-27 10:12:21 +08:00
1014c26d29 mod: make headers eid issues #435 2024-01-27 01:11:29 +08:00
d1bacf8950 fix: 退出搜索页面控制器未销毁 2024-01-27 00:24:54 +08:00
0595648f4c fix: 搜索页面时长筛选、快速回顶 2024-01-26 23:25:06 +08:00
23c8b34189 fix: app端推荐屏蔽时间显示,播放量与弹幕组件改为动态类型 2024-01-26 16:40:29 +08:00
932be48125 mod: 推荐、搜索页添加时间,修复视频搜索页无法筛选和回顶 2024-01-26 16:38:56 +08:00
791eed8a01 Merge pull request #446 from orz12/fix-typo-in-http
fix: typo
2024-01-26 00:16:45 +08:00
e791210039 v1.0.17 更新 2024-01-25 23:16:10 +08:00
01fa3c1cb3 mod: 关于页面增加官网链接 2024-01-25 23:13:16 +08:00
9663278916 feat: 私信显示分享视频内容、富文本表情,补充信息类型枚举 2024-01-25 21:22:39 +08:00
1b03f3f31f mod: AI视频总结未支持提醒 2024-01-25 21:15:22 +08:00
a73f2974e1 fix: typo 2024-01-25 20:56:59 +08:00
bf8ae0f317 feat: 新增弹幕描边粗细设置,默认值降低 2024-01-25 20:55:35 +08:00
545def36e6 mod: 自动播放按钮改为官方版 2024-01-25 20:51:01 +08:00
aaeecc9e53 fix: 重力旋转后划出下方的详情页 2024-01-25 20:51:01 +08:00
16895b5c32 fix: 点击视频评论区用户头像后返回详情页灰屏 2024-01-25 20:51:01 +08:00
a68c04001b fix: 竖屏全屏异常 2024-01-25 20:51:01 +08:00
1dd70f482f fix: 旋转横屏仍有状态栏 2024-01-25 20:51:01 +08:00
103423abf7 fix: 修复部分手机横屏两侧不等宽 2024-01-25 20:51:01 +08:00
569184a507 opt: 切换页面时销毁播放器组件提升性能 2024-01-25 20:51:01 +08:00
e052c6eafe feat: 首页tabbar 编辑排序 2024-01-21 23:49:32 +08:00
dad4a28eb8 feat: 未读动态计数 2024-01-21 20:30:51 +08:00
7428cde108 mod: flutter 3.16 特性迁移 2024-01-21 18:50:25 +08:00
9e40e162ac feat: 优先返回首页 2024-01-21 16:21:38 +08:00
27c954ec95 fix: 两次返回退出应用 issues #303 2024-01-21 16:09:08 +08:00
ec98e5c73c Merge pull request #433 from orz12/mod-video-detail-reply
mod: 视频评论输入框高度自适应
2024-01-21 14:54:44 +08:00
538b3d88aa mod: 代码整理 2024-01-21 14:01:33 +08:00
aa4e251295 fix: 视频详情页null 2024-01-21 11:37:19 +08:00
32f84dc703 fix: 首页状态栏图标色 2024-01-20 22:48:40 +08:00
c8e157b2d6 fix: 首页tabController index不及时 2024-01-20 22:31:56 +08:00
9122dd7f3a mod: 新增推荐过滤器,回退model转换修改,移除不必要的futureBuilder 2024-01-20 17:07:10 +08:00
41ddeab41a 新增模拟未登录推荐,独立推荐设置,新增accesskey风控警告,统一推荐逻辑 2024-01-20 15:14:52 +08:00
5ca841de4e mod: ignore文件 2024-01-20 14:10:02 +08:00
ca6091b90d mod: 视频评论输入框:可随键盘高度与文本行数移动和缩起;实现失去焦点功能;添加滚动条 2024-01-20 13:51:17 +08:00
5169bc360c merge main 2024-01-20 13:44:32 +08:00
4673f6dc5b Merge pull request #418 from orz12/fix-customspeed-dynamic-not-double
fix:自定义倍速后白屏
2024-01-20 13:41:04 +08:00
5da6f2b021 Merge pull request #430 from orz12/feat-add-blacklist-on-recommended&related-video-card
feat: 推荐、相关视频卡片添加拉黑Up功能
2024-01-20 13:38:47 +08:00
53ce81673b Merge pull request #421 from orz12/opt-videodetail-widget-flicker
opt: 【关注】按钮与自动播放界面跳变
2024-01-20 11:51:09 +08:00
e991f36853 Merge pull request #407 from orz12/feat-shutdown-timer-service
feat: 添加定时关闭功能
2024-01-20 11:46:52 +08:00
211b7812de Merge pull request #431 from orz12/fix-notlogin-1080p
fix: 免登录看1080p
2024-01-20 11:45:42 +08:00
70cf27789f fix: 免登录看1080p 2024-01-19 13:53:58 +08:00
931a513ac5 feat: 推荐、相关视频卡片添加拉黑Up功能 2024-01-18 14:00:27 +08:00
c4bf7d3a3b Merge pull request #408 from orz12/opt-deep-router-danmaku
尝试优化视频导航有多层时弹幕所占资源
2024-01-17 00:08:51 +08:00
b9cfcf9c9e Merge pull request #425 from orz12/mod-upgrade-web-rcmd
mod: 升级web端推荐至V8,对齐官网版本
2024-01-17 00:07:46 +08:00
a418f457f5 mod: 升级web端推荐至V8,对齐官网版本 2024-01-16 01:32:55 +08:00
83e68c8ee3 Merge pull request #422 from orz12/fix-snackBarTheme-without-light-theme
fix: up设置分组没有日间模式,以及颜色不统一
2024-01-15 23:33:19 +08:00
d26066ff84 Merge pull request #419 from orz12/fix-season-timeformat
fix: 专栏时间没有年份
2024-01-15 23:32:24 +08:00
4000e1b9dc fix: up设置分组没有日间模式,以及颜色不统一 2024-01-15 21:02:46 +08:00
02bdb46625 opt: 【关注】按钮与自动播放界面跳变
获得数据后再淡入显示,避免关注按钮跳变(如已关注状态下,会先经历默认的高亮未关注状态,再跳变为暗色已关注)
自动播放开启时取消显示封面(与官方一致),避免黑屏-亮屏(加载出封面)-黑屏(缓冲视频流)-亮屏(显示视频)的闪烁过程
2024-01-15 20:27:51 +08:00
96737ded5b fix: 专栏时间没有年份 2024-01-15 02:14:43 +08:00
6654094480 opt: 代码优化 2024-01-15 00:59:19 +08:00
0cc25203b1 fix: 设置自定义倍速后白屏
原因:List<double>并非List<dynamic>,赋值会产生错误
2024-01-15 00:58:52 +08:00
9294c8bcdf mod: 动态长图 2024-01-14 19:17:17 +08:00
8f661337f5 opt: 图片渲染内存 2024-01-14 18:06:56 +08:00
db6662c980 Merge branch 'fix' 2024-01-13 22:12:39 +08:00
2dc7cf28c9 fix: 评论区@跟jumpUrl共存时链接解析 issues #404 2024-01-10 23:58:14 +08:00
c9fd6304fd 尝试优化多层弹幕所占资源 2024-01-10 10:33:28 +08:00
4d1e4511a3 feat: 添加定时关闭功能 2024-01-10 02:25:21 +08:00
449aa69033 Merge branch 'main' of github.com:guozhigq/pilipala 2024-01-09 23:14:41 +08:00
5fa32f1e2b fix: 双击播放无声 2024-01-09 23:13:49 +08:00
71bb4b30d2 mod: 进度条防抖 issues #362 2024-01-09 08:23:55 +08:00
8b0cb4c909 Merge pull request #401 from orz12/fix-first-request-without-cookie
fix: 首屏请求无cookie
2024-01-09 00:22:55 +08:00
79729e4b30 fix: iOS代理 2024-01-08 23:41:12 +08:00
7cf9e66a5b Merge pull request #397 from orz12/fix-handleplay-without-playerListener
fix: 手动播放没有监听播放状态的问题
2024-01-08 22:24:42 +08:00
917cff6311 Merge branch 'fix' 2024-01-08 22:21:09 +08:00
7c120aa0cc fix: launcher启动 2024-01-08 22:20:31 +08:00
2c7ce60f42 fix: 首屏请求无cookie 2024-01-08 19:02:19 +08:00
e4ebe6e145 修复手动播放没有监听播放状态的问题 2024-01-08 11:35:43 +08:00
5d0ca3f84c Merge pull request #395 from orz12/opt-danmu-and-autoplay
opt: 封面点击播放
2024-01-08 08:28:40 +08:00
49dfb2605c Merge pull request #396 from Integral-Tech/fix-abiCodes
Fix abiCodes
2024-01-08 08:28:09 +08:00
cfddcd79bd Fix abiCodes 2024-01-07 23:51:15 +08:00
a07a1697c2 Update header_control.dart 2024-01-07 23:37:21 +08:00
21259e260d Merge branch 'main' into opt-danmu-and-autoplay 2024-01-07 23:33:50 +08:00
438b392cfc mod: 发送弹幕样式 2024-01-07 22:24:17 +08:00
ac69896f9d Merge pull request #384 from orz12/opt-hidden-repeat-progressbar
opt: 控制条与常驻进度条互斥
2024-01-07 21:09:37 +08:00
f5263b090a Merge pull request #378 from Integral-Tech/versionCode-added
Add versionCode
2024-01-07 21:09:01 +08:00
bde7e35623 Merge branch 'design' 2024-01-07 21:06:48 +08:00
a0488f2c75 Update Flutter version 2024-01-07 21:05:40 +08:00
e8f7995b32 mod: 搜索页跳转 2024-01-07 20:50:15 +08:00
042a0a848d mod: 首页样式 2024-01-07 20:15:39 +08:00
a98d8537c7 opt: 封面点击播放+弹幕发送标识 2024-01-07 17:04:20 +08:00
8ebb4cc70e Merge pull request #365 from GuMengYu/home_page
Improve: 首页样式
2024-01-07 16:21:58 +08:00
fa8fd42e9a mod: format code 2024-01-07 12:58:24 +08:00
7a71798055 mod: snackBarTheme darkTheme 2024-01-06 16:51:28 +08:00
7c82193f22 mod: up设置分组适配暗黑模式 2024-01-06 15:46:37 +08:00
f5d928e0f3 mod: 补充scheme 2024-01-06 15:42:30 +08:00
e0aeefa203 fix: 评论区内容匹配 issues #385 2024-01-06 12:59:20 +08:00
d75d560d32 Merge pull request #383 from orz12/fix-statusbar-color-and-icon
fix: 非全屏透明appbar无图标、横屏视频竖屏全屏播放时有白色状态栏
2024-01-06 10:52:17 +08:00
7d9edc5f40 Merge pull request #387 from orz12/fix-chat-owner-decider
fix: 私信误判发送方
2024-01-06 10:51:52 +08:00
5a337fb145 fix: 私信误判发送方 2024-01-06 09:04:27 +08:00
6983c40f5f opt:移除冗余设置 2024-01-06 08:06:12 +08:00
aa94bf27ff opt: 控制条与常驻进度条互斥 2024-01-05 14:02:32 +08:00
50a46aa89d fix: 非全屏透明appbar无图标、横屏视频竖屏全屏播放时有白色状态栏
之前修复图标不显示的思路不太对(appbar改成白底),现在改成了透明底但图标显色,更沉浸,顺便修复问题
2024-01-05 12:02:24 +08:00
e79cd2df25 fix: 评论区链接解析 issues #381 2024-01-04 22:44:33 +08:00
cd2b7c62ee Merge branch 'main' into fix 2024-01-04 22:32:16 +08:00
483f02c7d2 mod: 动态内容增加投稿跳转 2024-01-04 00:08:42 +08:00
ec58fbc3cc mod: web端推荐观看数单位转换 2024-01-03 23:53:44 +08:00
e910f5b7e7 fix: 搜索结果为null时页面异常 2024-01-03 23:50:38 +08:00
6591d35c74 fix: 连续跳转search页面controller未重建 2024-01-03 23:45:05 +08:00
a2524e0a60 Merge branch 'main' into versionCode-added 2024-01-03 10:45:42 +08:00
9a9644c3eb Add abiCodes 2024-01-02 19:10:06 +08:00
d0f8d55c9f Add versionCode 2024-01-02 15:13:58 +08:00
367d8b844a improve: 首页样式改动
- 渐变的背景
- tab 调整
2024-01-01 17:48:55 +08:00
391 changed files with 29052 additions and 11063 deletions

208
.github/workflows/beta_ci.yml vendored Normal file
View File

@ -0,0 +1,208 @@
name: Pilipala Beta
on:
workflow_dispatch:
push:
branches:
- "never"
paths-ignore:
- "**.md"
- "**.txt"
- ".github/**"
- ".idea/**"
- "!.github/workflows/**"
jobs:
update_version:
name: Read and update version
runs-on: ubuntu-latest
outputs:
# 定义输出变量 version以便在其他job中引用
new_version: ${{ steps.version.outputs.new_version }}
last_commit: ${{ steps.get-last-commit.outputs.last_commit }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
fetch-depth: 0
- name: 获取first parent commit次数
id: get-first-parent-commit-count
run: |
version=$(yq e .version pubspec.yaml | cut -d "+" -f 1)
recent_release_tag=$(git tag -l | grep $version | egrep -v "[-|+]" || true)
if [[ "x$recent_release_tag" == "x" ]]; then
echo "当前版本tag不存在请手动生成tag."
exit 1
fi
git log --oneline --first-parent $recent_release_tag..HEAD
first_parent_commit_count=$(git rev-list --first-parent --count $recent_release_tag..HEAD)
echo "count=$first_parent_commit_count" >> $GITHUB_OUTPUT
- name: 获取最后一次提交
id: get-last-commit
run: |
last_commit=$(git log -1 --pretty="%h %s" --first-parent)
echo "last_commit=$last_commit" >> $GITHUB_OUTPUT
- name: 更新版本号
id: version
run: |
# 读取版本号
VERSION=$(yq e .version pubspec.yaml | cut -d "+" -f 1)
# 获取GitHub Actions的run_number
#RUN_NUMBER=${{ github.run_number }}
# 构建新版本号
NEW_VERSION=$VERSION-beta.${{ steps.get-first-parent-commit-count.outputs.count }}
# 输出新版本号
echo "New version: $NEW_VERSION"
# 设置新版本号为输出变量
echo "new_version=$NEW_VERSION" >>$GITHUB_OUTPUT
android:
name: Build CI (Android)
needs: update_version
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: 构建Java环境
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17"
token: ${{secrets.GIT_TOKEN}}
- name: 检查缓存
uses: actions/cache@v2
id: cache-flutter
with:
path: /root/flutter-sdk
key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.lock') }}
- name: 安装Flutter
if: steps.cache-flutter.outputs.cache-hit != 'true'
uses: subosito/flutter-action@v2
with:
flutter-version: 3.19.6
channel: any
- name: 下载项目依赖
run: flutter pub get
- name: 解码生成 jks
run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
- name: 更新版本号
id: version
run: |
# 更新pubspec.yaml文件中的版本号
sed -i "s/version: .*+/version: ${{ needs.update_version.outputs.new_version }}+/g" pubspec.yaml
- name: flutter build apk
run: flutter build apk --release --split-per-abi
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}}
- name: 重命名应用
run: |
for file in build/app/outputs/flutter-apk/app-*.apk; do
if [[ $file =~ app-(.?*)release.apk ]]; then
new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}v${{ needs.update_version.outputs.new_version }}.apk"
mv "$file" "$new_file_name"
fi
done
- name: 上传
uses: actions/upload-artifact@v3
with:
name: Pilipala-Beta
path: |
build/app/outputs/flutter-apk/Pili-*.apk
iOS:
name: Build CI (iOS)
needs: update_version
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: 安装Flutter
if: steps.cache-flutter.outputs.cache-hit != 'true'
uses: subosito/flutter-action@v2.10.0
with:
cache: true
flutter-version: 3.16.5
- name: 更新版本号
id: version
run: |
# 更新pubspec.yaml文件中的版本号
sed -i "" "s/version: .*+/version: ${{ needs.update_version.outputs.new_version }}+/g" pubspec.yaml
- name: flutter build ipa
run: |
flutter build ios --release --no-codesign
ln -sf ./build/ios/iphoneos Payload
zip -r9 app.ipa Payload/runner.app
- name: 重命名应用
run: |
DATE=${{ steps.date.outputs.date }}
for file in app.ipa; do
new_file_name="build/Pili-v${{ needs.update_version.outputs.new_version }}.ipa"
mv "$file" "$new_file_name"
done
- name: 上传
uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: Pilipala-Beta
path: |
build/Pili-*.ipa
upload:
runs-on: ubuntu-latest
needs:
- update_version
- android
- iOS
steps:
- uses: actions/download-artifact@v3
with:
name: Pilipala-Beta
path: ./Pilipala-Beta
- name: 发送到Telegram频道
uses: xireiki/channel-post@v1.0.7
with:
bot_token: ${{ secrets.BOT_TOKEN }}
chat_id: ${{ secrets.CHAT_ID }}
large_file: true
api_id: ${{ secrets.TELEGRAM_API_ID }}
api_hash: ${{ secrets.TELEGRAM_API_HASH }}
method: sendFile
path: Pilipala-Beta/*
parse_mode: Markdown
context: "*Beta版本: v${{ needs.update_version.outputs.new_version }}*\n更新内容: [${{ needs.update_version.outputs.last_commit }}]"

View File

@ -1,84 +0,0 @@
name: build_apk
# action事件触发
on:
push:
# push tag时触发
tags:
- 'v*.*.*'
# 可以有多个jobs
jobs:
build_apk:
# 运行环境 ubuntu-latest window-latest mac-latest
runs-on: ubuntu-latest
# 每个jobs中可以有多个steps
steps:
- name: 代码迁出
uses: actions/checkout@v3
- name: 构建Java环境
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17"
token: ${{secrets.GIT_TOKEN}}
- name: 检查缓存
uses: actions/cache@v2
id: cache-flutter
with:
path: /root/flutter-sdk # Flutter SDK 的路径
key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.lock') }}
- name: 安装Flutter
if: steps.cache-flutter.outputs.cache-hit != 'true'
uses: subosito/flutter-action@v2
with:
flutter-version: 3.16.4
channel: any
- name: 下载项目依赖
run: flutter pub get
- name: 解码生成 jks
run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
- name: flutter build apk
# 对应 android/app/build.gradle signingConfigs中的配置项
run: flutter build apk --release --split-per-abi
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}}
- name: 获取版本号
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT
# - name: 获取当前日期
# id: date
# run: echo "date=$(date +'%m%d')" >>$GITHUB_OUTPUT
- name: 重命名应用 Pili-arm64-v8a-*.*.*.0101.apk
run: |
# DATE=${{ steps.date.outputs.date }}
for file in build/app/outputs/flutter-apk/app-*-release.apk; do
if [[ $file =~ app-(.*)-release.apk ]]; then
new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}-${{ steps.version.outputs.version }}.apk"
mv "$file" "$new_file_name"
fi
done
- name: 构建和发布release
uses: ncipollo/release-action@v1
with:
# release title
name: v${{ steps.version.outputs.version }}
artifacts: "build/app/outputs/flutter-apk/Pili-*.apk"
bodyFile: "change_log/${{steps.version.outputs.version}}.md"
token: ${{ secrets.GIT_TOKEN }}
allowUpdates: true

157
.github/workflows/release_ci.yml vendored Normal file
View File

@ -0,0 +1,157 @@
name: Pilipala Release
# action事件触发
on:
push:
# push tag时触发
tags:
- "v*.*.*"
# 可以有多个jobs
jobs:
android:
# 运行环境 ubuntu-latest window-latest mac-latest
runs-on: ubuntu-latest
# 每个jobs中可以有多个steps
steps:
- name: 代码迁出
uses: actions/checkout@v3
- name: 构建Java环境
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17"
token: ${{secrets.GIT_TOKEN}}
- name: 检查缓存
uses: actions/cache@v2
id: cache-flutter
with:
path: /root/flutter-sdk # Flutter SDK 的路径
key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.lock') }}
- name: 安装Flutter
if: steps.cache-flutter.outputs.cache-hit != 'true'
uses: subosito/flutter-action@v2
with:
flutter-version: 3.19.6
channel: any
- name: 下载项目依赖
run: flutter pub get
- name: 解码生成 jks
run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
- name: flutter build apk
run: flutter build apk --release --split-per-abi
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}}
- name: flutter build apk
run: flutter build apk --release
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}}
- name: 获取版本号
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT
# - name: 获取当前日期
# id: date
# run: echo "date=$(date +'%m%d')" >>$GITHUB_OUTPUT
- name: 重命名应用
run: |
# DATE=${{ steps.date.outputs.date }}
for file in build/app/outputs/flutter-apk/app-*.apk; do
if [[ $file =~ app-(.?*)release.apk ]]; then
new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}${{ steps.version.outputs.version }}.apk"
mv "$file" "$new_file_name"
fi
done
- name: 上传
uses: actions/upload-artifact@v3
with:
name: Pilipala-Release
path: |
build/app/outputs/flutter-apk/Pili-*.apk
iOS:
runs-on: macos-latest
steps:
- name: 代码迁出
uses: actions/checkout@v4
- name: 安装Flutter
if: steps.cache-flutter.outputs.cache-hit != 'true'
uses: subosito/flutter-action@v2.10.0
with:
cache: true
flutter-version: 3.19.6
- name: flutter build ipa
run: |
flutter build ios --release --no-codesign
ln -sf ./build/ios/iphoneos Payload
zip -r9 app.ipa Payload/runner.app
- name: 获取版本号
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT
- name: 重命名应用
run: |
DATE=${{ steps.date.outputs.date }}
for file in app.ipa; do
new_file_name="build/Pili-${{ steps.version.outputs.version }}.ipa"
mv "$file" "$new_file_name"
done
- name: 上传
uses: actions/upload-artifact@v3
with:
if-no-files-found: error
name: Pilipala-Release
path: |
build/Pili-*.ipa
upload:
runs-on: ubuntu-latest
needs:
- android
- iOS
steps:
- uses: actions/download-artifact@v3
with:
name: Pilipala-Release
path: ./Pilipala-Release
- name: Install dependencies
run: sudo apt-get install tree -y
- name: Get version
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT
- name: Upload Release
uses: ncipollo/release-action@v1
with:
name: v${{ steps.version.outputs.version }}
token: ${{ secrets.GIT_TOKEN }}
omitBodyDuringUpdate: true
omitNameDuringUpdate: true
omitPrereleaseDuringUpdate: true
allowUpdates: true
artifacts: Pilipala-Release/*

106
.gitignore vendored
View File

@ -21,6 +21,29 @@ migrate_working_dir/
# is commented out by default.
#.vscode/
# Flutter repo-specific
/bin/cache/
/bin/internal/bootstrap.bat
/bin/internal/bootstrap.sh
/bin/mingit/
/dev/benchmarks/mega_gallery/
/dev/bots/.recipe_deps
/dev/bots/android_tools/
/dev/devicelab/ABresults*.json
/dev/docs/doc/
/dev/docs/api_docs.zip
/dev/docs/flutter.docs.zip
/dev/docs/lib/
/dev/docs/pubspec.yaml
/dev/integration_tests/**/xcuserdata
/dev/integration_tests/**/Pods
/packages/flutter/coverage/
version
analysis_benchmark.json
# packages file containing multi-root paths
.packages.generated
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
@ -31,14 +54,83 @@ migrate_working_dir/
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
flutter_*.png
linked_*.ds
unlinked.ds
unlinked_spec.ds
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# Android related
**/android/**/gradle-wrapper.jar
.gradle/
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
**/android/key.properties
*.jks
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/ephemeral
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# macOS
**/Flutter/ephemeral/
**/Pods/
**/macos/Flutter/GeneratedPluginRegistrant.swift
**/macos/Flutter/ephemeral
**/xcuserdata/
# Windows
**/windows/flutter/generated_plugin_registrant.cc
**/windows/flutter/generated_plugin_registrant.h
**/windows/flutter/generated_plugins.cmake
# Linux
**/linux/flutter/generated_plugin_registrant.cc
**/linux/flutter/generated_plugin_registrant.h
**/linux/flutter/generated_plugins.cmake
# Coverage
coverage/
# Symbols
app.*.symbols
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
!.vscode/settings.json

View File

@ -26,13 +26,13 @@
Xcode 13.4 不支持**auto_orientation**,请注释相关代码
```bash
[] Flutter (Channel stable, 3.16.4, on macOS 14.1.2 23B92 darwin-arm64, locale
[] Flutter (Channel stable, 3.16.5, on macOS 14.1.2 23B92 darwin-arm64, locale
zh-Hans-CN)
[] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[] Xcode - develop for iOS and macOS (Xcode 15.1)
[] Chrome - develop for the web
[] Android Studio (version 2022.3)
[] VS Code (version 1.85.1)
[] VS Code (version 1.87.2)
[] Connected device (3 available)
[] Network resources
@ -44,6 +44,9 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码
## 技术交流
Telegram: https://t.me/+lm_oOVmF0RJiODk1
Tg Beta版本@PiliPala_Beta
QQ频道: https://pd.qq.com/s/365esodk3
@ -91,7 +94,7 @@ QQ频道: https://pd.qq.com/s/365esodk3
- [x] 音质选择(视视频而定)
- [x] 解码格式选择(视视频而定)
- [x] 弹幕
- [ ] 字幕
- [x] 字幕
- [x] 记忆播放
- [x] 视频比例:高度/宽度适应、填充、包含等

View File

@ -58,11 +58,10 @@ android {
applicationId "com.guozhigq.pilipala"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
// minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
minSdkVersion 19
minSdkVersion 21
multiDexEnabled true
}
@ -95,3 +94,14 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
import com.android.build.OutputFile
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
if (abiVersionCode != null) {
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
}
}
}

View File

@ -45,7 +45,7 @@
android:fullBackupContent="false"
tools:replace="android:allowBackup">
<activity
android:name="com.ryanheise.audioservice.AudioServiceActivity"
android:name="com.guozhigq.pilipala.MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
@ -67,9 +67,9 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<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" android:host="forward" />
@ -223,6 +223,10 @@
android:pathPattern="/mobile/video/.*" />
<data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/mobile/video/.*" />
<data android:scheme="https" android:host="b23.tv"
android:pathPattern="/*" />
<data android:scheme="https" android:host="space.bilibili.com"
android:pathPattern="/*" />
</intent-filter>
</activity>

View File

@ -1,6 +1,8 @@
package com.guozhigq.pilipala
import io.flutter.embedding.android.FlutterActivity
// import io.flutter.embedding.android.FlutterActivity
import com.ryanheise.audioservice.AudioServiceActivity;
class MainActivity: FlutterActivity() {
class MainActivity: AudioServiceActivity() {
}

View File

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.7.10'
ext.kotlin_version = '1.9.0'
repositories {
google()
mavenCentral()

BIN
assets/images/coin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" data-pointer="none" viewBox="0 0 24 24"><path fill-rule="evenodd" d="m8.085 4.891-.999-1.499a1.008 1.008 0 0 1 1.679-1.118l1.709 2.566c.54-.008 1.045-.012 1.515-.012h.13c.345 0 .707.003 1.088.007l1.862-2.59a1.008 1.008 0 0 1 1.637 1.177l-1.049 1.46c.788.02 1.631.046 2.53.078 1.958.069 3.468 1.6 3.74 3.507.088.613.13 2.158.16 3.276l.001.027c.01.333.017.63.025.856a.987.987 0 0 1-1.974.069c-.008-.23-.016-.539-.025-.881v-.002c-.028-1.103-.066-2.541-.142-3.065-.143-1.004-.895-1.78-1.854-1.813-2.444-.087-4.466-.13-6.064-.131-1.598 0-3.619.044-6.063.13a2.037 2.037 0 0 0-1.945 1.748c-.15 1.04-.225 2.341-.225 3.904 0 1.874.11 3.474.325 4.798.154.949.95 1.66 1.91 1.708a97.58 97.58 0 0 0 5.416.139.988.988 0 0 1 0 1.975c-2.196 0-3.61-.047-5.513-.141A4.012 4.012 0 0 1 2.197 17.7c-.236-1.446-.351-3.151-.351-5.116 0-1.64.08-3.035.245-4.184A4.013 4.013 0 0 1 5.92 4.96c.761-.027 1.483-.05 2.164-.069Zm4.436 4.707h-1.32v4.63h2.222v.848h-2.618v1.078h2.431a5.01 5.01 0 0 1 3.575-3.115V9.598h-1.276a8.59 8.59 0 0 0 .748-1.42l-1.089-.384a14.232 14.232 0 0 1-.814 1.804h-1.518l.693-.308a8.862 8.862 0 0 0-.814-1.408l-1.045.352c.297.396.572.847.825 1.364Zm-4.18 3.564.154-1.485h1.98V8.289h-3.2v.979h2.067v1.43H7.483l-.308 3.454h2.277c0 1.166-.044 1.925-.12 2.277-.078.352-.386.528-.936.528-.308 0-.616-.022-.902-.055l.297 1.067.062.004c.285.02.551.04.818.04 1.001-.066 1.562-.418 1.694-1.056.11-.638.176-1.903.176-3.795h-2.2Zm7.458.11v-.858h-1.254v.858H15.8Zm-2.376-.858v.858h-1.199v-.858h1.2Zm-1.199-.946h1.2v-.902h-1.2v.902Zm2.321 0v-.902H15.8v.902h-1.254Zm3.517 10.594a4 4 0 1 0 0-8 4 4 0 0 0 0 8Zm-.002-1.502a2.5 2.5 0 0 1-2.217-3.657l3.326 3.398a2.49 2.49 0 0 1-1.109.259Zm2.5-2.5c0 .42-.103.815-.286 1.162l-3.328-3.401a2.5 2.5 0 0 1 3.614 2.239Z" clip-rule="evenodd"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" data-pointer="none" viewBox="0 0 24 24" id="svgcontent" overflow="visible" width="24" height="24" x="597" y="286"><g class="layer" style="pointer-events:all"><title style="pointer-events:inherit">Layer 1</title><path fill-rule="evenodd" d="M11.99,4.83C11.52,4.83 11.01,4.83 10.47,4.84L8.76,2.27A1.01,1.01 0 0 0 7.09,3.39L8.08,4.89C7.4,4.91 6.68,4.93 5.92,4.96A4.01,4.01 0 0 0 2.09,8.4C1.93,9.55 1.85,10.94 1.85,12.58C1.85,14.55 1.96,16.25 2.2,17.7A4.01,4.01 0 0 0 5.96,21.06L6.86,21.11C8.07,21.17 8.67,21.2 10.47,21.2A0.99,0.99 0 0 0 10.47,19.23C8.71,19.23 8.13,19.2 6.97,19.14L6.06,19.09A2.04,2.04 0 0 1 4.15,17.38C3.93,16.06 3.82,14.46 3.82,12.58C3.82,11.02 3.9,9.72 4.05,8.68C4.19,7.7 5.01,6.97 5.99,6.93C8.43,6.85 10.46,6.8 12.05,6.8C13.65,6.8 15.67,6.85 18.12,6.93C19.08,6.97 19.83,7.74 19.97,8.75C20.05,9.27 20.09,10.71 20.11,11.81L20.11,11.81C20.12,12.16 20.13,12.46 20.14,12.69A0.99,0.99 0 1 0 22.11,12.63C22.1,12.4 22.1,12.1 22.09,11.77L22.09,11.74C22.06,10.62 22.01,9.08 21.93,8.47C21.65,6.56 20.14,5.03 18.19,4.96C17.29,4.93 16.44,4.9 15.66,4.88L16.71,3.42A1.01,1.01 0 0 0 15.07,2.24L13.21,4.83C12.83,4.83 12.46,4.83 12.12,4.83L11.99,4.83zM12.51,9.6L11.19,9.6L11.19,14.23L13.41,14.23L13.41,15.08L10.79,15.08L10.79,16.16L13.41,16.16L13.42,16.84C13.78,16.86 14.13,17 14.43,17.24L14.54,17.24L14.54,16.16L17.23,16.16L17.23,15.08L14.53,15.08L14.53,14.23L16.8,14.23L16.8,9.6L15.52,9.6A8.59,8.59 0 0 0 16.27,8.18L15.18,7.8A14.23,14.23 0 0 1 14.37,9.6L12.85,9.6L13.54,9.3A8.86,8.86 0 0 0 12.73,7.89L11.68,8.24C11.98,8.64 12.26,9.09 12.51,9.6zM8.33,13.17L8.48,11.68L10.46,11.68L10.46,8.29L7.26,8.29L7.26,9.27L9.33,9.27L9.33,10.7L7.47,10.7L7.16,14.16L9.44,14.16C9.44,15.32 9.4,16.08 9.32,16.43C9.24,16.79 8.94,16.96 8.39,16.96C8.08,16.96 7.77,16.94 7.48,16.91L7.78,17.97L7.84,17.98C8.13,18 8.39,18.02 8.66,18.02C9.66,17.95 10.22,17.6 10.35,16.96C10.46,16.32 10.53,15.06 10.53,13.17L8.33,13.17zM15.79,13.28L15.79,12.42L14.53,12.42L14.53,13.28L15.79,13.28zM13.41,12.42L13.41,13.28L12.21,13.28L12.21,12.42L13.41,12.42zM12.21,11.47L13.41,11.47L13.41,10.57L12.21,10.57L12.21,11.47zM14.53,11.47L14.53,10.57L15.79,10.57L15.79,11.47L14.53,11.47z" clip-rule="evenodd" id="svg_1"></path><path fill="#000000" fill-rule="evenodd" d="M22.85,14.63A1,1 0 0 0 21.43,14.7L16.34,20.41L14.13,18.13L14.03,18.04L14.02,18.04A1,1 0 0 0 12.7,19.53L15.66,22.57L15.76,22.66L15.76,22.66C16.17,22.98 16.76,22.93 17.12,22.54L22.93,16.03L23.01,15.93L23.01,15.92A1,1 0 0 0 22.85,14.63z" clip-rule="evenodd" id="svg_2"></path></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

1
assets/loading.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

39
change_log/1.0.17.0125.md Normal file
View File

@ -0,0 +1,39 @@
## 1.0.17
### 功能
+ 视频全屏时隐藏进度条
+ 动态内容增加投稿跳转
+ 未开启自动播放时点击封面播放
+ 弹幕发送标识
+ 定时关闭
+ 推荐视频卡片拉黑up功能
+ 首页tabbar编辑排序
### 修复
+ 连续跳转搜索页未刷新
+ 搜索结果为空时页面异常
+ 评论区链接解析
+ 视频全屏状态栏背景色
+ 私信对话气泡位置
+ 设置up关注分组样式
+ 每次推荐请求数据相同
+ iOS代理网络异常
+ 双击切换播放状态无声
+ 设置自定义倍速白屏
+ 免登录查看1080p
### 优化
+ 首页web端推荐观看数展示
+ 首页web端推荐接口更新
+ 首页样式
+ 搜索页跳转
+ 弹幕资源优化
+ 图片渲染占用内存优化(部分)
+ 两次返回退出应用
+ schame 补充
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

16
change_log/1.0.18.0130.md Normal file
View File

@ -0,0 +1,16 @@
## 1.0.18
### 功能
### 修复
### 优化
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

15
change_log/1.0.19.0131.md Normal file
View File

@ -0,0 +1,15 @@
## 1.0.19
### 修复
+ 视频404、评论加载错误
+ bvav转换
### 优化
+ 视频详情页内存占用
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

31
change_log/1.0.20.0303.md Normal file
View File

@ -0,0 +1,31 @@
## 1.0.20
### 功能
+ 评论区增加表情
+ 首页渐变背景开关
+ 媒体库显示「我的订阅」
+ 评论区链接解析
+ 默认启动页设置
### 修复
+ 评论区内容重复
+ pip相关问题
+ 播放多p视频评论不刷新
+ 视频评论翻页重复
### 优化
+ url scheme优化
+ 图片预览放大
+ 图片加载速度
+ 视频评论区复制
+ 全屏显示视频标题
+ 网络异常处理
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

View File

@ -0,0 +1,9 @@
## 1.0.21
### 修复
+ 推荐视频全屏问题
+ 番剧全屏播放时灰屏问题
+ 评论回调导致页面卡死问题
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

27
change_log/1.0.22.0430.md Normal file
View File

@ -0,0 +1,27 @@
## 1.0.22
### 功能
+ 字幕
+ 全屏时选集
+ 动态转发
+ 评论视频并转发
+ 收藏夹删除
+ 合集显示封面
+ 底部导航栏编辑、排序功能
+ 历史记录进度条展示
+ 直播画质切换
+ 排行榜功能
+ 视频详情页推荐视频开关
+ 显示联合投稿up
### 修复
+ 收藏夹个数错误
+ 封面保存权限问题
+ 合集最后1p未展示
+ up主页关注按钮触发灰屏
### 优化
+ 视频简介查看逻辑
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

14
change_log/1.0.23.0504.md Normal file
View File

@ -0,0 +1,14 @@
## 1.0.23
### 功能
+ 封面下载
### 修复
+ 全屏问题
+ 视频播放器灰屏问题
+ 评论区点击区域问题
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

23
change_log/1.0.24.0626.md Normal file
View File

@ -0,0 +1,23 @@
## 1.0.24
### 功能
+ 私信功能
+ 回复我的、收到的赞查看
+ 新的登录方式
+ 全屏选集
+ 一键三连
+ 按分区搜索
### 优化
+ 页面跳转动画
+ 评论区跳转
### 修复
+ 音画不同步问题
+ 分集字幕未同步
+ 多语言字幕
+ 弹幕设置未生效
+
问题反馈、功能建议请查看「关于」页面。

39
change_log/1.0.25.1010.md Normal file
View File

@ -0,0 +1,39 @@
## 1.0.25
### 功能
+ 直播弹幕
+ 稍后再看、收藏夹播放全部
+ 收藏夹新建、编辑
+ 评论删除
+ 评论保存为图片
+ 动态页滑动切换up
+ up投稿筛选充电视频
+ 直播tab展示关注up
+ up主页专栏展示
### 优化
+ 视频详情页一键三连
+ 动态页标识充电视频
+ 播放器亮度、音量调整百分比展示
+ 封面预览时视频标题可复制
+ 竖屏直播布局
+ 图片预览
+ 专栏渲染优化
+ 私信图片查看
### 修复
+ 收藏夹点击异常
+ 搜索up异常
+ 系统通知已读异常
+ [赞了我的]展示错误
+ 部分up合集无法打开
+ 切换合集视频投币个数未重置
+ 搜索条件筛选面板无法滚动
+ 部分机型导航条未沉浸
+ 专栏图片渲染问题
+ 专栏浏览历史记录
+ 直播间历史记录
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

View File

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>

View File

@ -9,16 +9,21 @@ PODS:
- Flutter
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
- FlutterMacOS
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_mailer (0.0.1):
- Flutter
- flutter_volume_controller (0.0.1):
- Flutter
- fluttertoast (0.0.2):
- Flutter
- Toast
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- gt3_flutter_plugin (0.0.8):
- gt3_flutter_plugin (0.0.9):
- Flutter
- GT3Captcha-iOS
- GT3Captcha-iOS (0.15.8.3)
@ -33,9 +38,8 @@ PODS:
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.1.1):
- permission_handler_apple (9.3.0):
- Flutter
- ReachabilitySwift (5.0.0)
- saver_gallery (0.0.1):
- Flutter
- screen_brightness_ios (0.1.0):
@ -49,6 +53,7 @@ PODS:
- Flutter
- system_proxy (0.0.1):
- Flutter
- Toast (4.1.0)
- url_launcher_ios (0.0.1):
- Flutter
- volume_controller (0.0.1):
@ -65,10 +70,12 @@ DEPENDENCIES:
- audio_service (from `.symlinks/plugins/audio_service/ios`)
- audio_session (from `.symlinks/plugins/audio_session/ios`)
- auto_orientation (from `.symlinks/plugins/auto_orientation/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
- flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`)
- flutter_volume_controller (from `.symlinks/plugins/flutter_volume_controller/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- gt3_flutter_plugin (from `.symlinks/plugins/gt3_flutter_plugin/ios`)
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
@ -92,7 +99,7 @@ SPEC REPOS:
trunk:
- FMDB
- GT3Captcha-iOS
- ReachabilitySwift
- Toast
EXTERNAL SOURCES:
appscheme:
@ -104,13 +111,17 @@ EXTERNAL SOURCES:
auto_orientation:
:path: ".symlinks/plugins/auto_orientation/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
:path: ".symlinks/plugins/connectivity_plus/darwin"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
Flutter:
:path: Flutter
flutter_mailer:
:path: ".symlinks/plugins/flutter_mailer/ios"
flutter_volume_controller:
:path: ".symlinks/plugins/flutter_volume_controller/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
gt3_flutter_plugin:
:path: ".symlinks/plugins/gt3_flutter_plugin/ios"
media_kit_libs_ios_video:
@ -153,31 +164,33 @@ SPEC CHECKSUMS:
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
gt3_flutter_plugin: bfa1f26e9a09dc00401514be5ed437f964cabf23
gt3_flutter_plugin: 5bd2c08d3c19cbb6ee3b08f4358439e54c8ab2ee
GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
status_bar_control: 7c84146799e6a076315cc1550f78ef53aae3e446
system_proxy: bec1a5c5af67dd3e3ebf43979400a8756c04cc44
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
Toast: ec33c32b8688982cecc6348adeae667c1b9938da
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36
PODFILE CHECKSUM: 637cd290bed23275b5f5ffcc7eb1e73d0a5fb2be

View File

@ -156,7 +156,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,5 +1,6 @@
import UIKit
import Flutter
import AVFoundation
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
@ -8,6 +9,14 @@ import Flutter
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
//
do {
try AVAudioSession.sharedInstance().setCategory(.playback, options: [.duckOthers])
} catch {
print("Failed to set audio session category: \(error)")
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
}

View File

@ -49,6 +49,8 @@
<true/>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>请允许APP保存图片到相册</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>请允许APP保存图片到相册</string>
<key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能访问相册</string>
<key>NSAppleMusicUsageDescription</key>

View File

@ -0,0 +1,172 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../models/common/video_episode_type.dart';
class EpisodeBottomSheet {
final List<dynamic> episodes;
final int currentCid;
final dynamic dataType;
final BuildContext context;
final Function changeFucCall;
final int? cid;
final double? sheetHeight;
bool isFullScreen = false;
EpisodeBottomSheet({
required this.episodes,
required this.currentCid,
required this.dataType,
required this.context,
required this.changeFucCall,
this.cid,
this.sheetHeight,
this.isFullScreen = false,
});
Widget buildEpisodeListItem(
dynamic episode,
int index,
bool isCurrentIndex,
) {
Color primary = Theme.of(context).colorScheme.primary;
Color onSurface = Theme.of(context).colorScheme.onSurface;
String title = '';
switch (dataType) {
case VideoEpidoesType.videoEpisode:
title = episode.title;
break;
case VideoEpidoesType.videoPart:
title = episode.pagePart;
break;
case VideoEpidoesType.bangumiEpisode:
title = '${episode.title}${episode.longTitle!}';
break;
}
return isFullScreen || episode?.cover == null || episode?.cover == ''
? ListTile(
onTap: () {
SmartDialog.showToast('切换至「$title');
changeFucCall.call(episode, index);
},
dense: false,
leading: isCurrentIndex
? Image.asset(
'assets/images/live.gif',
color: primary,
height: 12,
)
: null,
title: Text(title,
style: TextStyle(
fontSize: 14,
color: isCurrentIndex ? primary : onSurface,
)))
: InkWell(
onTap: () {
SmartDialog.showToast('切换至「$title');
changeFucCall.call(episode, index);
},
child: Padding(
padding:
const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8),
child: Row(
children: [
NetworkImgLayer(
width: 130, height: 75, src: episode?.cover ?? ''),
const SizedBox(width: 10),
Expanded(
child: Text(
title,
maxLines: 2,
style: TextStyle(
fontSize: 14,
color: isCurrentIndex ? primary : onSurface,
),
),
),
],
),
),
);
}
Widget buildTitle() {
return AppBar(
toolbarHeight: 45,
automaticallyImplyLeading: false,
centerTitle: false,
title: Text(
'合集(${episodes.length}',
style: Theme.of(context).textTheme.titleMedium,
),
actions: !isFullScreen
? [
IconButton(
icon: const Icon(Icons.close, size: 20),
onPressed: () => Navigator.pop(context),
),
const SizedBox(width: 14),
]
: null,
);
}
Widget buildShowContent(BuildContext context) {
final ItemScrollController itemScrollController = ItemScrollController();
int currentIndex = episodes.indexWhere((dynamic e) => e.cid == currentCid);
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
itemScrollController.jumpTo(index: currentIndex);
});
return Container(
height: sheetHeight,
color: Theme.of(context).colorScheme.surface,
child: Column(
children: [
buildTitle(),
Expanded(
child: Material(
child: PageStorage(
bucket: PageStorageBucket(),
child: ScrollablePositionedList.builder(
itemScrollController: itemScrollController,
itemCount: episodes.length + 1,
itemBuilder: (BuildContext context, int index) {
bool isLastItem = index == episodes.length;
bool isCurrentIndex = currentIndex == index;
return isLastItem
? SizedBox(
height:
MediaQuery.of(context).padding.bottom + 20,
)
: buildEpisodeListItem(
episodes[index],
index,
isCurrentIndex,
);
},
),
),
),
),
],
),
);
});
}
/// The [BuildContext] of the widget that calls the bottom sheet.
PersistentBottomSheetController show(BuildContext context) {
final PersistentBottomSheetController btmSheetCtr = showBottomSheet(
context: context,
builder: (BuildContext context) {
return buildShowContent(context);
},
);
return btmSheetCtr;
}
}

View File

@ -13,8 +13,8 @@ class Skeleton extends StatelessWidget {
var shimmerGradient = LinearGradient(
colors: [
Colors.transparent,
Theme.of(context).colorScheme.background.withAlpha(10),
Theme.of(context).colorScheme.background.withAlpha(10),
Theme.of(context).colorScheme.surface.withAlpha(10),
Theme.of(context).colorScheme.surface.withAlpha(10),
Colors.transparent,
],
stops: const [

View File

@ -66,7 +66,7 @@ class PBadge extends StatelessWidget {
border: Border.all(color: borderColor),
),
child: Text(
text!,
text ?? '',
style: TextStyle(fontSize: fs ?? fontSize, color: color),
),
);

View File

@ -20,7 +20,7 @@ class ContentContainer extends StatelessWidget {
builder: (BuildContext context, BoxConstraints constraints) {
return SingleChildScrollView(
clipBehavior: childClipBehavior ?? Clip.hardEdge,
physics: isScrollable ? null : NeverScrollableScrollPhysics(),
physics: isScrollable ? null : const NeverScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: constraints.copyWith(
minHeight: constraints.maxHeight,
@ -34,7 +34,7 @@ class ContentContainer extends StatelessWidget {
child: contentWidget!,
)
else
Spacer(),
const Spacer(),
if (bottomWidget != null) bottomWidget!,
],
),

View File

@ -2,16 +2,17 @@ import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/utils/storage.dart';
Box setting = GStrorage.setting;
Box<dynamic> setting = GStrorage.setting;
class CustomToast extends StatelessWidget {
const CustomToast({super.key, required this.msg});
final String msg;
const CustomToast({Key? key, required this.msg}) : super(key: key);
@override
Widget build(BuildContext context) {
double toastOpacity =
setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0);
final double toastOpacity =
setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0) as double;
return Container(
margin:
EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 30),

View File

@ -1,95 +1,143 @@
import 'package:get/get.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart';
import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart';
import 'package:pilipala/utils/highlight.dart';
// ignore: must_be_immutable
class HtmlRender extends StatelessWidget {
String? htmlContent;
final int? imgCount;
final List? imgList;
HtmlRender({
const HtmlRender({
this.htmlContent,
this.imgCount,
this.imgList,
super.key,
});
final String? htmlContent;
final int? imgCount;
final List<String>? imgList;
@override
Widget build(BuildContext context) {
return Html(
data: htmlContent,
onLinkTap: (url, buildContext, attributes) => {},
onLinkTap: (String? url, Map<String, String> buildContext, attributes) {},
extensions: [
TagExtension(
tagsToExtend: {"img"},
builder: (extensionContext) {
tagsToExtend: <String>{'pre'},
builder: (ExtensionContext extensionContext) {
final Map<String, dynamic> attributes = extensionContext.attributes;
final String lang = attributes['data-lang'] as String;
final String code = attributes['codecontent'] as String;
List<String> selectedLanguages = [lang.split('@').first];
TextSpan? result = highlightExistingText(code, selectedLanguages);
if (result == null) {
return const Center(child: Text('代码块渲染失败'));
}
return SelectableText.rich(result);
},
),
TagExtension(
tagsToExtend: <String>{'img'},
builder: (ExtensionContext extensionContext) {
try {
Map attributes = extensionContext.attributes;
List key = attributes.keys.toList();
String? imgUrl = key.contains('src')
? attributes['src']
: attributes['data-src'];
if (imgUrl!.startsWith('//')) {
final Map<String, dynamic> attributes =
extensionContext.attributes;
final List<dynamic> key = attributes.keys.toList();
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;
bool isEmote = imgUrl.contains('/emote/');
bool isMall = imgUrl.contains('/mall/');
final bool isEmote = imgUrl.contains('/emote/');
final bool isMall = imgUrl.contains('/mall/');
if (isMall) {
return const SizedBox();
}
// bool inTable =
// extensionContext.element!.previousElementSibling == null ||
// extensionContext.element!.nextElementSibling == null;
// imgUrl = Utils().imageUrl(imgUrl!);
// return Image.network(
// imgUrl,
// width: isEmote ? 22 : null,
// height: isEmote ? 22 : null,
// );
return NetworkImgLayer(
width: isEmote ? 22 : Get.size.width - 24,
height: isEmote ? 22 : 200,
src: imgUrl,
return InkWell(
onTap: () {
Navigator.of(context).push(
HeroDialogRoute<void>(
builder: (BuildContext context) =>
InteractiveviewerGallery(
sources: imgList ?? [imgUrl],
initIndex: imgList?.indexOf(imgUrl) ?? 0,
itemBuilder: (
BuildContext context,
int index,
bool isFocus,
bool enablePageView,
) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (enablePageView) {
Navigator.of(context).pop();
}
},
child: Center(
child: Hero(
tag: imgList?[index] ?? imgUrl,
child: CachedNetworkImage(
fadeInDuration:
const Duration(milliseconds: 0),
imageUrl: imgList?[index] ?? imgUrl,
fit: BoxFit.contain,
),
),
),
);
},
onPageChanged: (int pageIndex) {},
),
),
);
},
child: CachedNetworkImage(imageUrl: imgUrl),
);
// return NetworkImgLayer(
// width: isEmote ? 22 : Get.size.width - 24,
// height: isEmote ? 22 : 200,
// src: imgUrl,
// );
} catch (err) {
print(err);
return const SizedBox();
}
},
),
],
style: {
"html": Style(
fontSize: FontSize.medium,
'html': Style(
fontSize: FontSize.large,
lineHeight: LineHeight.percent(140),
),
"body": Style(margin: Margins.zero, padding: HtmlPaddings.zero),
"a": Style(
'body': Style(margin: Margins.zero, padding: HtmlPaddings.zero),
'a': Style(
color: Theme.of(context).colorScheme.primary,
textDecoration: TextDecoration.none,
),
"p": Style(
'p': Style(
margin: Margins.only(bottom: 10),
),
"span": Style(
fontSize: FontSize.medium,
'span': Style(
fontSize: FontSize.large,
height: Height(1.65),
),
"div": Style(height: Height.auto()),
"li > p": Style(
'div': Style(height: Height.auto()),
'li > p': Style(
display: Display.inline,
),
"li": Style(
'li': Style(
padding: HtmlPaddings.only(bottom: 4),
textAlign: TextAlign.justify,
),
"img": Style(margin: Margins.only(top: 4, bottom: 4)),
'img': Style(margin: Margins.only(top: 4, bottom: 4)),
},
);
}

View File

@ -2,12 +2,18 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class HttpError extends StatelessWidget {
const HttpError(
{required this.errMsg, required this.fn, this.btnText, super.key});
const HttpError({
required this.errMsg,
required this.fn,
this.btnText,
this.isShowBtn = true,
super.key,
});
final String? errMsg;
final Function()? fn;
final String? btnText;
final bool isShowBtn;
@override
Widget build(BuildContext context) {
@ -22,20 +28,29 @@ class HttpError extends StatelessWidget {
"assets/images/error.svg",
height: 200,
),
const SizedBox(height: 20),
const SizedBox(height: 30),
Text(
errMsg ?? '请求异常',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 30),
OutlinedButton.icon(
onPressed: () {
fn!();
},
icon: const Icon(Icons.arrow_forward_outlined, size: 20),
label: Text(btnText ?? '点击重试'),
)
const SizedBox(height: 20),
if (isShowBtn)
FilledButton.tonal(
onPressed: () {
fn!();
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith((states) {
return Theme.of(context).colorScheme.primary.withAlpha(20);
}),
),
child: Text(
btnText ?? '点击重试',
style:
TextStyle(color: Theme.of(context).colorScheme.primary),
),
),
],
),
),

View File

@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/utils.dart';
import '../../utils/utils.dart';
import '../constants.dart';
import 'network_img_layer.dart';
class LiveCard extends StatelessWidget {
// ignore: prefer_typing_uninitialized_variables
final liveItem;
final dynamic liveItem;
const LiveCard({
Key? key,
@ -14,7 +14,7 @@ class LiveCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(liveItem.roomid);
final String heroTag = Utils.makeHeroTag(liveItem.roomid);
return Card(
elevation: 0,
@ -23,7 +23,6 @@ class LiveCard extends StatelessWidget {
borderRadius: BorderRadius.circular(0),
side: BorderSide(
color: Theme.of(context).dividerColor.withOpacity(0.08),
width: 1,
),
),
margin: EdgeInsets.zero,
@ -33,15 +32,16 @@ class LiveCard extends StatelessWidget {
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
child: LayoutBuilder(builder:
(BuildContext context, BoxConstraints boxConstraints) {
final double maxWidth = boxConstraints.maxWidth;
final double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: liveItem.cover,
src: liveItem.cover as String,
type: 'emote',
width: maxWidth,
height: maxHeight,
@ -58,7 +58,7 @@ class LiveCard extends StatelessWidget {
// view: liveItem.stat.view,
// danmaku: liveItem.stat.danmaku,
// duration: liveItem.duration,
online: liveItem.online,
online: liveItem.online as int,
),
),
),
@ -90,7 +90,7 @@ class LiveContent extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
liveItem.title,
liveItem.title as String,
textAlign: TextAlign.start,
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
maxLines: 2,
@ -99,7 +99,7 @@ class LiveContent extends StatelessWidget {
SizedBox(
width: double.infinity,
child: Text(
liveItem.uname,
liveItem.uname as String,
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
@ -114,9 +114,9 @@ class LiveContent extends StatelessWidget {
}
class LiveStat extends StatelessWidget {
final int? online;
const LiveStat({super.key, required this.online});
const LiveStat({Key? key, required this.online}) : super(key: key);
final int? online;
@override
Widget build(BuildContext context) {
@ -136,7 +136,7 @@ class LiveStat extends StatelessWidget {
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
children: <Widget>[
// Row(
// children: [
// StatView(

View File

@ -1,78 +1,106 @@
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/extension.dart';
import 'package:pilipala/utils/global_data_cache.dart';
import '../../utils/storage.dart';
import '../constants.dart';
Box setting = GStrorage.setting;
Box<dynamic> setting = GStrorage.setting;
class NetworkImgLayer extends StatelessWidget {
final String? src;
final double? width;
final double? height;
final double? cacheW;
final double? cacheH;
final String? type;
final Duration? fadeOutDuration;
final Duration? fadeInDuration;
final int? quality;
const NetworkImgLayer({
Key? key,
super.key,
this.src,
required this.width,
required this.height,
this.cacheW,
this.cacheH,
this.type,
this.fadeOutDuration,
this.fadeInDuration,
// 图片质量 默认1%
this.quality,
}) : super(key: key);
this.origAspectRatio,
});
final String? src;
final double width;
final double height;
final String? type;
final Duration? fadeOutDuration;
final Duration? fadeInDuration;
final int? quality;
final double? origAspectRatio;
@override
Widget build(BuildContext context) {
double pr = MediaQuery.of(context).devicePixelRatio;
int picQuality = setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10);
final int defaultImgQuality = GlobalDataCache().imgQuality;
if (src == '' || src == null) {
return placeholder(context);
}
final String imageUrl =
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp';
int? memCacheWidth, memCacheHeight;
double aspectRatio = (width / height).toDouble();
// double pr = 2;
return src != ''
void setMemCacheSizes() {
if (aspectRatio > 1) {
memCacheHeight = height.cacheSize(context);
} else if (aspectRatio < 1) {
memCacheWidth = width.cacheSize(context);
} else {
if (origAspectRatio != null && origAspectRatio! > 1) {
memCacheWidth = width.cacheSize(context);
} else if (origAspectRatio != null && origAspectRatio! < 1) {
memCacheHeight = height.cacheSize(context);
} else {
memCacheWidth = width.cacheSize(context);
memCacheHeight = height.cacheSize(context);
}
}
}
setMemCacheSizes();
if (memCacheWidth == null && memCacheHeight == null) {
memCacheWidth = width.toInt();
}
return src != '' && src != null
? ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(type == 'avatar'
? 50
: type == 'emote'
? 0
: StyleString.imgRadius.x),
clipBehavior: Clip.antiAlias,
borderRadius: BorderRadius.circular(
type == 'avatar'
? 50
: type == 'emote'
? 0
: StyleString.imgRadius.x,
),
child: CachedNetworkImage(
imageUrl:
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? picQuality}q.webp',
width: width ?? double.infinity,
height: height ?? double.infinity,
alignment: Alignment.center,
maxWidthDiskCache: ((cacheW ?? width!) * pr).toInt(),
// maxHeightDiskCache: (cacheH ?? height!).toInt(),
memCacheWidth: ((cacheW ?? width!) * pr).toInt(),
// memCacheHeight: (cacheH ?? height!).toInt(),
imageUrl: imageUrl,
width: width,
height: height,
memCacheWidth: memCacheWidth,
memCacheHeight: memCacheHeight,
fit: BoxFit.cover,
fadeOutDuration:
fadeOutDuration ?? const Duration(milliseconds: 200),
fadeOutDuration ?? const Duration(milliseconds: 120),
fadeInDuration:
fadeInDuration ?? const Duration(milliseconds: 200),
// filterQuality: FilterQuality.high,
errorWidget: (context, url, error) => placeholder(context),
placeholder: (context, url) => placeholder(context),
fadeInDuration ?? const Duration(milliseconds: 120),
filterQuality: FilterQuality.low,
errorWidget: (BuildContext context, String url, Object error) =>
placeholder(context),
placeholder: (BuildContext context, String url) =>
placeholder(context),
),
)
: placeholder(context);
}
Widget placeholder(context) {
Widget placeholder(BuildContext context) {
return Container(
width: width ?? double.infinity,
height: height ?? double.infinity,
clipBehavior: Clip.hardEdge,
width: width,
height: height,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.4),
borderRadius: BorderRadius.circular(type == 'avatar'
@ -81,14 +109,19 @@ class NetworkImgLayer extends StatelessWidget {
? 0
: StyleString.imgRadius.x),
),
child: Center(
child: Image.asset(
type == 'avatar'
? 'assets/images/noface.jpeg'
: 'assets/images/loading.png',
width: 300,
height: 300,
)),
child: type == 'bg'
? const SizedBox()
: Center(
child: Image.asset(
type == 'avatar'
? 'assets/images/noface.jpeg'
: 'assets/images/loading.png',
width: width,
height: height,
cacheWidth: width.cacheSize(context),
cacheHeight: height.cacheSize(context),
),
),
);
}
}

View File

@ -1,131 +0,0 @@
// ignore_for_file: depend_on_referenced_packages
import 'dart:math';
import 'dart:ui' as ui show Image;
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart';
double get maxDragOffset => 100;
double hideHeight = maxDragOffset / 2.3;
double refreshHeight = maxDragOffset / 1.5;
class PullToRefreshHeader extends StatelessWidget {
const PullToRefreshHeader(
this.info,
this.lastRefreshTime, {
this.color,
Key? key,
}) : super(key: key);
final PullToRefreshScrollNotificationInfo? info;
final DateTime? lastRefreshTime;
final Color? color;
@override
Widget build(BuildContext context) {
final PullToRefreshScrollNotificationInfo? infos = info;
if (infos == null) {
return Container();
}
String text = '';
if (infos.mode == PullToRefreshIndicatorMode.armed) {
text = 'Release to refresh';
} else if (infos.mode == PullToRefreshIndicatorMode.refresh ||
infos.mode == PullToRefreshIndicatorMode.snap) {
text = 'Loading...';
} else if (infos.mode == PullToRefreshIndicatorMode.done) {
text = 'Refresh completed.';
} else if (infos.mode == PullToRefreshIndicatorMode.drag) {
text = 'Pull to refresh';
} else if (infos.mode == PullToRefreshIndicatorMode.canceled) {
text = 'Cancel refresh';
}
final TextStyle ts = const TextStyle(
color: Colors.grey,
).copyWith(fontSize: 14);
final double dragOffset = info?.dragOffset ?? 0.0;
final DateTime time = lastRefreshTime ?? DateTime.now();
final double top = -hideHeight + dragOffset;
return Container(
height: dragOffset,
color: color ?? Colors.transparent,
// padding: EdgeInsets.only(top: dragOffset / 3),
// padding: EdgeInsets.only(bottom: 5.0),
child: Stack(
children: <Widget>[
Positioned(
left: 0.0,
right: 0.0,
top: top,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
alignment: Alignment.centerRight,
margin: const EdgeInsets.only(right: 12.0),
child: RefreshImage(top, null),
),
),
Column(
children: <Widget>[
Text(text, style: ts),
Text(
'Last updated:${DateFormat('yyyy-MM-dd hh:mm').format(time)}',
style: ts.copyWith(fontSize: 14),
)
],
),
const Spacer(),
],
),
)
],
),
);
}
}
class RefreshImage extends StatelessWidget {
const RefreshImage(this.top, Key? key) : super(key: key);
final double top;
@override
Widget build(BuildContext context) {
const double imageSize = 30;
return ExtendedImage.asset(
'assets/flutterCandies_grey.png',
width: imageSize,
height: imageSize,
afterPaintImage: (Canvas canvas, Rect rect, ui.Image image, Paint paint) {
final double imageHeight = image.height.toDouble();
final double imageWidth = image.width.toDouble();
final Size size = rect.size;
final double y =
(1 - min(top / (refreshHeight - hideHeight), 1)) * imageHeight;
canvas.drawImageRect(
image,
Rect.fromLTWH(0.0, y, imageWidth, imageHeight - y),
Rect.fromLTWH(rect.left, rect.top + y / imageHeight * size.height,
size.width, (imageHeight - y) / imageHeight * size.height),
Paint()
..colorFilter =
const ColorFilter.mode(Color(0xFFea5504), BlendMode.srcIn)
..isAntiAlias = false
..filterQuality = FilterQuality.low,
);
//canvas.restore();
},
);
}
}

View File

@ -3,10 +3,10 @@ import 'package:pilipala/utils/utils.dart';
class StatDanMu extends StatelessWidget {
final String? theme;
final int? danmu;
final dynamic danmu;
final String? size;
const StatDanMu({Key? key, this.theme, this.danmu, this.size})
const StatDanMu({Key? key, this.theme = 'gray', this.danmu, this.size})
: super(key: key);
@override
@ -14,24 +14,49 @@ class StatDanMu extends StatelessWidget {
Map<String, Color> colorObject = {
'white': Colors.white,
'gray': Theme.of(context).colorScheme.outline,
'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
};
Color color = colorObject[theme]!;
return StatIconText(
icon: Icons.subtitles_outlined,
text: Utils.numFormat(danmu!),
color: color,
size: size,
);
}
}
class StatIconText extends StatelessWidget {
final IconData icon;
final String text;
final Color color;
final String? size;
const StatIconText({
Key? key,
required this.icon,
required this.text,
required this.color,
this.size,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [
Icon(
Icons.subtitles_outlined,
icon,
size: 14,
color: color,
),
const SizedBox(width: 2),
Text(
Utils.numFormat(danmu!),
text,
style: TextStyle(
fontSize: size == 'medium' ? 12 : 11,
color: color,
),
)
),
],
);
}

View File

@ -3,30 +3,59 @@ import 'package:pilipala/utils/utils.dart';
class StatView extends StatelessWidget {
final String? theme;
final int? view;
final dynamic view;
final String? size;
const StatView({Key? key, this.theme, this.view, this.size})
: super(key: key);
const StatView({
Key? key,
this.theme = 'gray',
this.view,
this.size,
}) : super(key: key);
@override
Widget build(BuildContext context) {
Map<String, Color> colorObject = {
'white': Colors.white,
'gray': Theme.of(context).colorScheme.outline,
'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
};
Color color = colorObject[theme]!;
return StatIconText(
icon: Icons.play_circle_outlined,
text: Utils.numFormat(view!),
color: color,
size: size,
);
}
}
class StatIconText extends StatelessWidget {
final IconData icon;
final String text;
final Color color;
final String? size;
const StatIconText({
Key? key,
required this.icon,
required this.text,
required this.color,
this.size,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [
Icon(
Icons.play_circle_outlined,
icon,
size: 13,
color: color,
),
const SizedBox(width: 2),
Text(
Utils.numFormat(view!),
text,
style: TextStyle(
fontSize: size == 'medium' ? 12 : 11,
color: color,

View File

@ -1,129 +1,165 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/badge.dart';
import 'package:pilipala/common/widgets/stat/danmu.dart';
import 'package:pilipala/common/widgets/stat/view.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/http/constants.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/image_save.dart';
import 'package:pilipala/utils/route_push.dart';
import 'package:pilipala/utils/url_utils.dart';
import '../../http/search.dart';
import '../../http/user.dart';
import '../../http/video.dart';
import '../../utils/utils.dart';
import '../constants.dart';
import 'badge.dart';
import 'network_img_layer.dart';
import 'stat/danmu.dart';
import 'stat/view.dart';
// 视频卡片 - 水平布局
class VideoCardH extends StatelessWidget {
// ignore: prefer_typing_uninitialized_variables
final videoItem;
final Function()? longPress;
final Function()? longPressEnd;
final String source;
final bool showOwner;
final bool showView;
final bool showDanmaku;
final bool showPubdate;
const VideoCardH({
Key? key,
super.key,
required this.videoItem,
this.longPress,
this.longPressEnd,
this.onPressedFn,
this.source = 'normal',
this.showOwner = true,
this.showView = true,
this.showDanmaku = true,
this.showPubdate = false,
}) : super(key: key);
this.showCharge = false,
});
// ignore: prefer_typing_uninitialized_variables
final videoItem;
final Function()? onPressedFn;
// normal 推荐, later 稍后再看, search 搜索
final String source;
final bool showOwner;
final bool showView;
final bool showDanmaku;
final bool showPubdate;
final bool showCharge;
@override
Widget build(BuildContext context) {
int aid = videoItem.aid;
String bvid = videoItem.bvid;
String heroTag = Utils.makeHeroTag(aid);
return GestureDetector(
onLongPress: () {
if (longPress != null) {
longPress!();
final int aid = videoItem.aid;
final String bvid = videoItem.bvid;
String type = 'video';
try {
type = videoItem.type;
} catch (_) {}
final String heroTag = Utils.makeHeroTag(aid);
return InkWell(
onTap: () async {
try {
if (type == 'ketang') {
SmartDialog.showToast('课堂视频暂不支持播放');
return;
}
if (showCharge && videoItem?.typeid == 33) {
final String redirectUrl = await UrlUtils.parseRedirectUrl(
'${HttpString.baseUrl}/video/$bvid/');
final String lastPathSegment = redirectUrl.split('/').last;
if (lastPathSegment.contains('ss')) {
RoutePush.bangumiPush(
Utils.matchNum(lastPathSegment).first, null);
}
if (lastPathSegment.contains('ep')) {
RoutePush.bangumiPush(
null, Utils.matchNum(lastPathSegment).first);
}
return;
}
final int cid =
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
Get.toNamed('/video?bvid=$bvid&cid=$cid',
arguments: {'videoItem': videoItem, 'heroTag': heroTag});
} catch (err) {
SmartDialog.showToast(err.toString());
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async {
try {
int cid =
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
Get.toNamed('/video?bvid=$bvid&cid=$cid',
arguments: {'videoItem': videoItem, 'heroTag': heroTag});
} catch (err) {
SmartDialog.showToast(err.toString());
}
},
child: Padding(
padding: const EdgeInsets.fromLTRB(
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
child: LayoutBuilder(
builder: (context, boxConstraints) {
double width = (boxConstraints.maxWidth -
StyleString.cardSpace *
6 /
MediaQuery.of(context).textScaleFactor) /
2;
return Container(
constraints: const BoxConstraints(minHeight: 88),
height: width / StyleString.aspectRatio,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(
builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic,
width: maxWidth,
height: maxHeight,
),
onLongPress: () => imageSaveDialog(
context,
videoItem,
SmartDialog.dismiss,
),
child: Padding(
padding: const EdgeInsets.fromLTRB(
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints boxConstraints) {
final double width = (boxConstraints.maxWidth -
StyleString.cardSpace *
6 /
MediaQuery.textScalerOf(context).scale(1.0)) /
2;
return Container(
constraints: const BoxConstraints(minHeight: 88),
height: width / StyleString.aspectRatio,
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: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic as String,
width: maxWidth,
height: maxHeight,
),
),
if (videoItem.duration != 0)
PBadge(
text: Utils.timeFormat(videoItem.duration!),
top: null,
right: 6.0,
bottom: 6.0,
left: null,
type: 'gray',
),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// pBadge(videoItem.rcmdReason.content, context,
// 6.0, 6.0, null, null),
],
);
},
),
if (type != 'video')
PBadge(
text: type,
left: 6.0,
bottom: 6.0,
type: 'primary',
),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// pBadge(videoItem.rcmdReason.content, context,
// 6.0, 6.0, null, null),
if (showCharge && videoItem?.isChargingSrc)
const PBadge(
text: '充电专属',
right: 6.0,
top: 6.0,
type: 'primary',
),
],
);
},
),
VideoContent(
videoItem: videoItem,
source: source,
showOwner: showOwner,
showView: showView,
showDanmaku: showDanmaku,
showPubdate: showPubdate,
)
],
),
);
},
),
),
VideoContent(
videoItem: videoItem,
source: source,
showOwner: showOwner,
showView: showView,
showDanmaku: showDanmaku,
showPubdate: showPubdate,
onPressedFn: onPressedFn,
)
],
),
);
},
),
),
);
@ -138,6 +174,7 @@ class VideoContent extends StatelessWidget {
final bool showView;
final bool showDanmaku;
final bool showPubdate;
final Function()? onPressedFn;
const VideoContent({
super.key,
@ -147,6 +184,7 @@ class VideoContent extends StatelessWidget {
this.showView = true,
this.showDanmaku = true,
this.showPubdate = false,
this.onPressedFn,
});
@override
@ -157,9 +195,9 @@ class VideoContent extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (videoItem.title is String) ...[
if (source == 'normal' || source == 'later') ...[
Text(
videoItem.title,
videoItem.title as String,
textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.w500,
@ -172,9 +210,9 @@ class VideoContent extends StatelessWidget {
maxLines: 2,
text: TextSpan(
children: [
for (var i in videoItem.title) ...[
for (final i in videoItem.titleList) ...[
TextSpan(
text: i['text'],
text: i['text'] as String,
style: TextStyle(
fontWeight: FontWeight.w500,
letterSpacing: 0.3,
@ -216,7 +254,7 @@ class VideoContent extends StatelessWidget {
Row(
children: [
Text(
videoItem.owner.name,
videoItem.owner.name as String,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
@ -228,75 +266,49 @@ class VideoContent extends StatelessWidget {
Row(
children: [
if (showView) ...[
StatView(
theme: 'gray',
view: videoItem.stat.view,
),
StatView(view: videoItem.stat.view as int),
const SizedBox(width: 8),
],
if (showDanmaku)
StatDanMu(
theme: 'gray',
danmu: videoItem.stat.danmaku,
),
StatDanMu(danmu: videoItem.stat.danmaku as int),
const Spacer(),
// SizedBox(
// width: 20,
// height: 20,
// child: IconButton(
// tooltip: '稍后再看',
// style: ButtonStyle(
// padding: MaterialStateProperty.all(EdgeInsets.zero),
// ),
// onPressed: () async {
// var res =
// await UserHttp.toViewLater(bvid: videoItem.bvid);
// SmartDialog.showToast(res['msg']);
// },
// icon: Icon(
// Icons.more_vert_outlined,
// color: Theme.of(context).colorScheme.outline,
// size: 14,
// ),
// ),
// ),
if (source == 'normal')
SizedBox(
width: 24,
height: 24,
child: PopupMenuButton<String>(
child: IconButton(
padding: EdgeInsets.zero,
tooltip: '稍后再看',
onPressed: () {
feedBack();
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return MorePanel(videoItem: videoItem);
},
);
},
icon: Icon(
Icons.more_vert_outlined,
color: Theme.of(context).colorScheme.outline,
size: 14,
),
position: PopupMenuPosition.under,
// constraints: const BoxConstraints(maxHeight: 35),
onSelected: (String type) {},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
PopupMenuItem<String>(
onTap: () async {
var res = await UserHttp.toViewLater(
bvid: videoItem.bvid);
SmartDialog.showToast(res['msg']);
},
value: 'pause',
height: 35,
child: const Row(
children: [
Icon(Icons.watch_later_outlined, size: 16),
SizedBox(width: 6),
Text('稍后再看', style: TextStyle(fontSize: 13))
],
),
),
],
),
),
if (source == 'later') ...[
IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
onPressed: () => onPressedFn?.call(),
icon: Icon(
Icons.clear_outlined,
color: Theme.of(context).colorScheme.outline,
size: 18,
),
)
],
],
),
],
@ -305,3 +317,110 @@ class VideoContent extends StatelessWidget {
);
}
}
class MorePanel extends StatelessWidget {
final dynamic videoItem;
const MorePanel({super.key, required this.videoItem});
Future<dynamic> menuActionHandler(String type) async {
switch (type) {
case 'block':
blockUser();
break;
case 'watchLater':
var res = await UserHttp.toViewLater(bvid: videoItem.bvid as String);
SmartDialog.showToast(res['msg']);
Get.back();
break;
default:
}
}
void blockUser() async {
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: Text('确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?'
'\n\n被拉黑的Up可以在隐私设置-黑名单管理中解除'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: Text(
'点错了',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
var res = await VideoHttp.relationMod(
mid: videoItem.owner.mid,
act: 5,
reSrc: 11,
);
SmartDialog.dismiss();
SmartDialog.showToast(res['msg'] ?? '成功');
},
child: const Text('确认'),
)
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () => Get.back(),
child: Container(
height: 35,
padding: const EdgeInsets.only(bottom: 2),
child: Center(
child: Container(
width: 32,
height: 3,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.outline,
borderRadius: const BorderRadius.all(Radius.circular(3))),
),
),
),
),
ListTile(
onTap: () async => await menuActionHandler('block'),
minLeadingWidth: 0,
leading: const Icon(Icons.block, size: 19),
title: Text(
'拉黑up主 「${videoItem.owner.name}',
style: Theme.of(context).textTheme.titleSmall,
),
),
ListTile(
onTap: () async => await menuActionHandler('watchLater'),
minLeadingWidth: 0,
leading: const Icon(Icons.watch_later_outlined, size: 19),
title:
Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall),
),
ListTile(
onTap: () =>
imageSaveDialog(context, videoItem, SmartDialog.dismiss),
minLeadingWidth: 0,
leading: const Icon(Icons.photo_outlined, size: 19),
title:
Text('查看视频封面', style: Theme.of(context).textTheme.titleSmall),
),
const SizedBox(height: 20),
],
),
);
}
}

View File

@ -1,31 +1,32 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/badge.dart';
import 'package:pilipala/common/widgets/stat/danmu.dart';
import 'package:pilipala/common/widgets/stat/view.dart';
import 'package:pilipala/http/dynamics.dart';
import 'package:pilipala/http/search.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/image_save.dart';
import 'package:pilipala/utils/route_push.dart';
import '../../models/model_rec_video_item.dart';
import 'stat/danmu.dart';
import 'stat/view.dart';
import '../../http/dynamics.dart';
import '../../http/user.dart';
import '../../http/video.dart';
import '../../utils/id_utils.dart';
import '../../utils/utils.dart';
import '../constants.dart';
import 'badge.dart';
import 'network_img_layer.dart';
// 视频卡片 - 垂直布局
class VideoCardV extends StatelessWidget {
final dynamic videoItem;
final int crossAxisCount;
final Function()? longPress;
final Function()? longPressEnd;
final Function? blockUserCb;
const VideoCardV({
Key? key,
required this.videoItem,
required this.crossAxisCount,
this.longPress,
this.longPressEnd,
this.blockUserCb,
}) : super(key: key);
bool isStringNumeric(String str) {
@ -42,23 +43,11 @@ class VideoCardV extends StatelessWidget {
return;
}
int epId = videoItem.param;
SmartDialog.showLoading(msg: '资源获取中');
var result = await SearchHttp.bangumiInfo(seasonId: null, epId: epId);
if (result['status']) {
var bangumiDetail = result['data'];
int cid = bangumiDetail.episodes!.first.cid;
String bvid = IdUtils.av2bv(bangumiDetail.episodes!.first.aid);
SmartDialog.dismiss().then(
(value) => Get.toNamed(
'/video?bvid=$bvid&cid=$cid&epId=$epId',
arguments: {
'pic': videoItem.pic,
'heroTag': heroTag,
'videoType': SearchType.media_bangumi,
},
),
);
}
RoutePush.bangumiPush(
null,
epId,
heroTag: heroTag,
);
break;
case 'av':
String bvid = videoItem.bvid ?? IdUtils.av2bv(videoItem.aid);
@ -71,17 +60,13 @@ class VideoCardV extends StatelessWidget {
// 动态
case 'picture':
try {
String dynamicType = 'picture';
String uri = videoItem.uri;
String id = '';
if (videoItem.uri.startsWith('bilibili://article/')) {
// https://www.bilibili.com/read/cv27063554
dynamicType = 'read';
RegExp regex = RegExp(r'\d+');
Match match = regex.firstMatch(videoItem.uri)!;
String matchedNumber = match.group(0)!;
videoItem.param = int.parse(matchedNumber);
id = 'cv${videoItem.param}';
}
if (uri.startsWith('http')) {
String path = Uri.parse(uri).path;
@ -99,11 +84,10 @@ class VideoCardV extends StatelessWidget {
return;
}
}
Get.toNamed('/htmlRender', parameters: {
'url': uri,
Get.toNamed('/read', parameters: {
'title': videoItem.title,
'id': id,
'dynamicType': dynamicType
'id': videoItem.param,
'articleType': 'read'
});
} catch (err) {
SmartDialog.showToast(err.toString());
@ -125,64 +109,57 @@ class VideoCardV extends StatelessWidget {
@override
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(videoItem.id);
return Card(
elevation: 0,
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: GestureDetector(
onLongPress: () {
if (longPress != null) {
longPress!();
}
},
// onLongPressEnd: (details) {
// if (longPressEnd != null) {
// longPressEnd!();
// }
// },
child: InkWell(
onTap: () async => onPushDetail(heroTag),
child: Column(
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic,
width: maxWidth,
height: maxHeight,
),
),
if (videoItem.duration != null)
if (crossAxisCount == 1) ...[
PBadge(
bottom: 10,
right: 10,
text: videoItem.duration,
)
] else ...[
PBadge(
bottom: 6,
right: 7,
size: 'small',
type: 'gray',
text: videoItem.duration,
)
],
return InkWell(
onTap: () async => onPushDetail(heroTag),
onLongPress: () => imageSaveDialog(
context,
videoItem,
SmartDialog.dismiss,
),
borderRadius: BorderRadius.circular(16),
child: Column(
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
Hero(
tag: heroTag,
child: NetworkImgLayer(
src: videoItem.pic,
width: maxWidth,
height: maxHeight,
),
),
if (videoItem.duration > 0)
if (crossAxisCount == 1) ...[
PBadge(
bottom: 10,
right: 10,
text: Utils.timeFormat(videoItem.duration),
)
] else ...[
PBadge(
bottom: 6,
right: 7,
size: 'small',
type: 'gray',
text: Utils.timeFormat(videoItem.duration),
)
],
);
}),
),
VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount)
],
],
);
}),
),
),
VideoContent(
videoItem: videoItem,
crossAxisCount: crossAxisCount,
blockUserCb: blockUserCb,
)
],
),
);
}
@ -191,133 +168,101 @@ class VideoCardV extends StatelessWidget {
class VideoContent extends StatelessWidget {
final dynamic videoItem;
final int crossAxisCount;
const VideoContent(
{Key? key, required this.videoItem, required this.crossAxisCount})
: super(key: key);
final Function? blockUserCb;
const VideoContent({
Key? key,
required this.videoItem,
required this.crossAxisCount,
this.blockUserCb,
}) : super(key: key);
Widget _buildBadge(String text, String type, [double fs = 12]) {
return PBadge(
text: text,
stack: 'normal',
size: 'small',
type: type,
fs: fs,
);
}
@override
Widget build(BuildContext context) {
return Expanded(
flex: crossAxisCount == 1 ? 0 : 1,
child: Padding(
padding: crossAxisCount == 1
? const EdgeInsets.fromLTRB(9, 9, 9, 4)
: const EdgeInsets.fromLTRB(5, 8, 5, 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Expanded(
child: Text(
videoItem.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
if (videoItem.goto == 'av' && crossAxisCount == 1) ...[
const SizedBox(width: 10),
WatchLater(
size: 32,
iconSize: 18,
callFn: () async {
int aid = videoItem.param;
var res =
await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid));
SmartDialog.showToast(res['msg']);
},
),
],
],
),
if (crossAxisCount > 1) ...[
const SizedBox(height: 2),
VideoStat(
videoItem: videoItem,
),
],
if (crossAxisCount == 1) const SizedBox(height: 4),
Row(
children: [
if (videoItem.goto == 'bangumi') ...[
PBadge(
text: videoItem.bangumiBadge,
stack: 'normal',
size: 'small',
type: 'line',
fs: 9,
)
],
if (videoItem.rcmdReason != null &&
videoItem.rcmdReason.content != '') ...[
PBadge(
text: videoItem.rcmdReason.content,
stack: 'normal',
size: 'small',
type: 'color',
)
],
if (videoItem.goto == 'picture') ...[
const PBadge(
text: '动态',
stack: 'normal',
size: 'small',
type: 'line',
fs: 9,
)
],
if (videoItem.isFollowed == 1) ...[
const PBadge(
text: '已关注',
stack: 'normal',
size: 'small',
type: 'color',
)
],
Expanded(
flex: crossAxisCount == 1 ? 0 : 1,
child: Text(
videoItem.owner.name,
maxLines: 1,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
),
if (crossAxisCount == 1) ...[
Text(
'',
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
VideoStat(
videoItem: videoItem,
),
const Spacer(),
],
if (videoItem.goto == 'av' && crossAxisCount != 1) ...[
WatchLater(
size: 24,
iconSize: 14,
callFn: () async {
int aid = videoItem.param;
var res =
await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid));
SmartDialog.showToast(res['msg']);
},
),
] else ...[
const SizedBox(height: 24)
]
],
),
return Padding(
padding: crossAxisCount == 1
? const EdgeInsets.fromLTRB(9, 9, 9, 4)
: const EdgeInsets.fromLTRB(5, 8, 5, 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
videoItem.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
if (crossAxisCount > 1) ...[
const SizedBox(height: 2),
VideoStat(videoItem: videoItem, crossAxisCount: crossAxisCount),
],
),
if (crossAxisCount == 1) const SizedBox(height: 4),
Row(
children: [
if (videoItem.goto == 'bangumi')
_buildBadge(videoItem.bangumiBadge, 'line', 9),
if (videoItem.rcmdReason != null)
_buildBadge(videoItem.rcmdReason, 'color'),
if (videoItem.goto == 'picture') _buildBadge('动态', 'line', 9),
if (videoItem.isFollowed == 1) _buildBadge('已关注', 'color'),
Expanded(
flex: crossAxisCount == 1 ? 0 : 1,
child: Text(
videoItem.owner.name,
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
),
if (crossAxisCount == 1) ...[
const SizedBox(width: 10),
VideoStat(
videoItem: videoItem,
crossAxisCount: crossAxisCount,
),
const Spacer(),
],
if (videoItem.goto == 'av')
SizedBox(
width: 24,
height: 24,
child: IconButton(
padding: EdgeInsets.zero,
onPressed: () {
feedBack();
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return MorePanel(
videoItem: videoItem,
blockUserCb: blockUserCb,
);
},
);
},
icon: Icon(
Icons.more_vert_outlined,
color: Theme.of(context).colorScheme.outline,
size: 14,
),
),
)
],
),
],
),
);
}
@ -325,73 +270,149 @@ class VideoContent extends StatelessWidget {
class VideoStat extends StatelessWidget {
final dynamic videoItem;
final int crossAxisCount;
const VideoStat({
Key? key,
required this.videoItem,
required this.crossAxisCount,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return RichText(
maxLines: 1,
text: TextSpan(
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
children: [
if (videoItem.stat.view != '-')
TextSpan(text: '${videoItem.stat.view}观看'),
if (videoItem.stat.danmu != '-')
TextSpan(text: '${videoItem.stat.danmu}弹幕'),
],
),
return Row(
children: [
StatView(view: videoItem.stat.view),
const SizedBox(width: 8),
StatDanMu(danmu: videoItem.stat.danmu),
if (videoItem is RecVideoItemModel) ...<Widget>[
crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8),
RichText(
maxLines: 1,
text: TextSpan(
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)),
),
const SizedBox(width: 4),
]
],
);
}
}
class WatchLater extends StatelessWidget {
final double? size;
final double? iconSize;
final Function? callFn;
class MorePanel extends StatelessWidget {
final dynamic videoItem;
final Function? blockUserCb;
const MorePanel({
super.key,
required this.videoItem,
this.blockUserCb,
});
const WatchLater({
Key? key,
required this.size,
required this.iconSize,
this.callFn,
}) : super(key: key);
Future<dynamic> menuActionHandler(String type) async {
switch (type) {
case 'block':
Get.back();
blockUser();
break;
case 'watchLater':
var res = await UserHttp.toViewLater(bvid: videoItem.bvid as String);
SmartDialog.showToast(res['msg']);
Get.back();
break;
default:
}
}
void blockUser() async {
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: Text('确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?'
'\n\n被拉黑的Up可以在隐私设置-黑名单管理中解除'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: Text(
'点错了',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
var res = await VideoHttp.relationMod(
mid: videoItem.owner.mid,
act: 5,
reSrc: 11,
);
SmartDialog.dismiss();
if (res['status']) {
blockUserCb?.call(videoItem.owner.mid);
}
SmartDialog.showToast(res['msg']);
},
child: const Text('确认'),
)
],
);
},
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: size,
height: size,
child: PopupMenuButton<String>(
padding: EdgeInsets.zero,
tooltip: '稍后再看',
icon: Icon(
Icons.more_vert_outlined,
color: Theme.of(context).colorScheme.outline,
size: iconSize,
),
position: PopupMenuPosition.under,
// constraints: const BoxConstraints(maxHeight: 35),
onSelected: (String type) {},
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
onTap: () => callFn!(),
value: 'pause',
height: 35,
child: const Row(
children: [
Icon(Icons.watch_later_outlined, size: 16),
SizedBox(width: 6),
Text('稍后再看', style: TextStyle(fontSize: 13))
],
return Container(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () => Get.back(),
child: Container(
height: 35,
padding: const EdgeInsets.only(bottom: 2),
child: Center(
child: Container(
width: 32,
height: 3,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.outline,
borderRadius: const BorderRadius.all(Radius.circular(3))),
),
),
),
),
ListTile(
onTap: () async => await menuActionHandler('block'),
minLeadingWidth: 0,
leading: const Icon(Icons.block, size: 19),
title: Text(
'拉黑up主 「${videoItem.owner.name}',
style: Theme.of(context).textTheme.titleSmall,
),
),
ListTile(
onTap: () async => await menuActionHandler('watchLater'),
minLeadingWidth: 0,
leading: const Icon(Icons.watch_later_outlined, size: 19),
title:
Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall),
),
ListTile(
onTap: () =>
imageSaveDialog(context, videoItem, SmartDialog.dismiss),
minLeadingWidth: 0,
leading: const Icon(Icons.photo_outlined, size: 19),
title:
Text('查看视频封面', style: Theme.of(context).textTheme.titleSmall),
),
const SizedBox(height: 20),
],
),
);

View File

@ -185,11 +185,11 @@ class Api {
static const String searchDefault = '/x/web-interface/wbi/search/default';
// 搜索关键词
static const String serachSuggest =
static const String searchSuggest =
'https://s.search.bilibili.com/main/suggest';
// 分类搜索
static const String searchByType = '/x/web-interface/search/type';
static const String searchByType = '/x/web-interface/wbi/search/type';
// 记录视频播放进度
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/report.md
@ -214,6 +214,9 @@ class Api {
// https://api.bilibili.com/x/relation/tags
static const String followingsClass = '/x/relation/tags';
// 搜索follow
static const followSearch = '/x/relation/followings/search';
// 粉丝
// vmid 用户id pn 页码 ps 每页个数最大50 order: desc
// order_type 排序规则 最近访问传空,最常访问传 attention
@ -230,6 +233,10 @@ class Api {
static const String liveRoomInfo =
'${HttpString.liveBaseUrl}/xlive/web-room/v2/index/getRoomPlayInfo';
// 直播间详情 H5
static const String liveRoomInfoH5 =
'${HttpString.liveBaseUrl}/xlive/web-room/v1/index/getH5InfoByRoom';
// 用户信息 需要Wbi签名
// https://api.bilibili.com/x/space/wbi/acc/info?mid=503427686&token=&platform=web&web_location=1550101&w_rid=d709892496ce93e3d94d6d37c95bde91&wts=1689301482
static const String memberInfo = '/x/space/wbi/acc/info';
@ -393,12 +400,24 @@ class Api {
'${HttpString.passBaseUrl}/x/passport-login/captcha?source=main_web';
// web端短信验证码
static const String smsCode =
static const String webSmsCode =
'${HttpString.passBaseUrl}/x/passport-login/web/sms/send';
// web端验证码登录
static const String webSmsLogin =
'${HttpString.passBaseUrl}/x/passport-login/web/login/sms';
// web端密码登录
static const String loginInByWebPwd =
'${HttpString.passBaseUrl}/x/passport-login/web/login';
// web端二维码
static const String qrCodeApi =
'${HttpString.passBaseUrl}/x/passport-login/web/qrcode/generate';
// 扫码登录
static const String loginInByQrcode =
'${HttpString.passBaseUrl}/x/passport-login/web/qrcode/poll';
// app端短信验证码
static const String appSmsCode =
@ -467,4 +486,113 @@ class Api {
/// page_size
static const getSeasonDetailApi =
'/x/polymer/web-space/seasons_archives_list';
static const getSeriesDetailApi = '/x/series/archives';
/// 获取未读动态数
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
/// 用户动态主页
static const dynamicSpmPrefix = 'https://space.bilibili.com/1/dynamic';
/// 激活buvid3
static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi';
/// 获取字幕配置
static const getSubtitleConfig = '/x/player/v2';
/// 我的订阅
static const userSubFolder = '/x/v3/fav/folder/collected/list';
/// 我的订阅详情 type 21
static const userSeasonList = '/x/space/fav/season/list';
/// 我的订阅详情 type 11
static const userResourceList = '/x/v3/fav/resource/list';
/// 表情
static const emojiList = '/x/emote/user/panel/web';
/// 已读标记
static const String ackSessionMsg =
'${HttpString.tUrl}/session_svr/v1/session_svr/update_ack';
/// 发送私信
static const String sendMsg = '${HttpString.tUrl}/web_im/v1/web_im/send_msg';
/// 排行榜
static const String getRankApi = "/x/web-interface/ranking/v2";
/// 取消订阅
static const String cancelSub = '/x/v3/fav/season/unfav';
/// 动态转发
static const String dynamicForwardUrl = '/x/dynamic/feed/create/submit_check';
/// 创建动态
static const String dynamicCreate = '/x/dynamic/feed/create/dyn';
/// 删除收藏夹
static const String delFavFolder = '/x/v3/fav/folder/del';
/// 搜索结果计数
static const String searchCount = '/x/web-interface/wbi/search/all/v2';
/// 关闭会话
static const String removeSession =
'${HttpString.tUrl}/session_svr/v1/session_svr/remove_session';
/// 消息未读数
static const String unread = '${HttpString.tUrl}/x/im/web/msgfeed/unread';
/// 回复我的
static const String messageReplyAPi = '/x/msgfeed/reply';
/// 收到的赞
static const String messageLikeAPi = '/x/msgfeed/like';
/// 系统通知
static const String messageSystemAPi =
'${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify';
/// 系统通知 个人
static const String userMessageSystemAPi =
'${HttpString.messageBaseUrl}/x/sys-msg/query_user_notify';
/// 系统通知标记已读
static const String systemMarkRead =
'${HttpString.messageBaseUrl}/x/sys-msg/update_cursor';
/// 编辑收藏夹
static const String editFavFolder = '/x/v3/fav/folder/edit';
/// 新建收藏夹
static const String addFavFolder = '/x/v3/fav/folder/add';
/// 直播间弹幕信息
static const String getDanmuInfo =
'${HttpString.liveBaseUrl}/xlive/web-room/v1/index/getDanmuInfo';
/// 直播间发送弹幕
static const String sendLiveMsg = '${HttpString.liveBaseUrl}/msg/send';
/// 我的关注 - 正在直播
static const String getFollowingLive =
'${HttpString.liveBaseUrl}/xlive/web-ucenter/user/following';
/// 稍后再看&收藏夹视频列表
static const String mediaList = '/x/v2/medialist/resource/list';
/// 用户专栏
static const String opusList = '/x/polymer/web-dynamic/v1/opus/feed/space';
///
static const String getViewInfo = '/x/article/viewinfo';
/// 直播间记录
static const String liveRoomEntry =
'${HttpString.liveBaseUrl}/xlive/web-room/v1/index/roomEntryAction';
/// 删除评论
static const String replyDel = '/x/v2/reply/del';
}

View File

@ -1,5 +1,5 @@
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/bangumi/list.dart';
import '../models/bangumi/list.dart';
import 'index.dart';
class BangumiHttp {
static Future bangumiList({int? page}) async {

View File

@ -1,5 +1,5 @@
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/user/black.dart';
import '../models/user/black.dart';
import 'index.dart';
class BlackHttp {
static Future blackList({required int pn, int? ps}) async {
@ -28,7 +28,7 @@ class BlackHttp {
static Future removeBlack({required int fid}) async {
var res = await Request().post(
Api.removeBlack,
queryParameters: {
data: {
'act': 6,
'csrf': await Request.getCsrf(),
'fid': fid,

17
lib/http/common.dart Normal file
View File

@ -0,0 +1,17 @@
import 'index.dart';
class CommonHttp {
static Future unReadDynamic() async {
var res = await Request().get(Api.getUnreadDynamic,
data: {'alltype_offset': 0, 'video_offset': '', 'article_offset': 0});
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']['dyn_basic_infos']};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
}

View File

@ -5,6 +5,8 @@ class HttpString {
static const String appBaseUrl = 'https://app.bilibili.com';
static const String liveBaseUrl = 'https://api.live.bilibili.com';
static const String passBaseUrl = 'https://passport.bilibili.com';
static const String messageBaseUrl = 'https://message.bilibili.com';
static const String bangumiBaseUrl = 'https://bili.meark.me';
static const List<int> validateStatusCodes = [
302,
304,

View File

@ -1,9 +1,6 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/danmaku/dm.pb.dart';
import 'constants.dart';
import '../models/danmaku/dm.pb.dart';
import 'index.dart';
class DanmakaHttp {
// 获取视频弹幕
@ -24,21 +21,23 @@ class DanmakaHttp {
);
return DmSegMobileReply.fromBuffer(response.data);
}
static Future shootDanmaku({
int type = 1,//弹幕类选择(1视频弹幕 2漫画弹幕)
required int oid,// 视频cid
required String msg,//弹幕文本(长度小于 100 字符)
int mode = 1,// 弹幕类型(1滚动弹幕 4底端弹幕 5顶端弹幕 6逆向弹幕(不能使用) 7高级弹幕 8代码弹幕不能使用 9BAS弹幕pool必须为2)
int type = 1, //弹幕类选择(1视频弹幕 2漫画弹幕)
required int oid, // 视频cid
required String msg, //弹幕文本(长度小于 100 字符)
int mode =
1, // 弹幕类型(1滚动弹幕 4底端弹幕 5顶端弹幕 6逆向弹幕(不能使用) 7高级弹幕 8代码弹幕不能使用 9BAS弹幕pool必须为2)
// String? aid,// 稿件avid
// String? bvid,// bvid与aid必须有一个
required String bvid,
int? progress,// 弹幕出现在视频内的时间单位为毫秒默认为0
int? color,// 弹幕颜色(默认白色16777215
int? fontsize,// 弹幕字号默认25
int? pool,// 弹幕池选择0普通池 1字幕池 2特殊池代码/BAS弹幕默认普通池0
int? progress, // 弹幕出现在视频内的时间单位为毫秒默认为0
int? color, // 弹幕颜色(默认白色16777215
int? fontsize, // 弹幕字号默认25
int? pool, // 弹幕池选择0普通池 1字幕池 2特殊池代码/BAS弹幕默认普通池0
//int? rnd,// 当前时间戳*1000000若无此项则发送弹幕冷却时间限制为90s若有此项则发送弹幕冷却时间限制为5s
int? colorful,//60001专属渐变彩色需要会员
int? checkbox_type,//是否带 UP 身份标识0普通4带有标识
int? colorful, //60001专属渐变彩色需要会员
int? checkbox_type, //是否带 UP 身份标识0普通4带有标识
// String? csrf,//CSRF Token位于 Cookie Cookie 方式必要
// String? access_key,// APP 登录 Token APP 方式必要
}) async {
@ -68,9 +67,6 @@ class DanmakaHttp {
var response = await Request().post(
Api.shootDanmaku,
data: params,
options: Options(
contentType: Headers.formUrlEncodedContentType,
),
);
if (response.statusCode != 200) {
return {

View File

@ -1,6 +1,8 @@
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/dynamics/result.dart';
import 'package:pilipala/models/dynamics/up.dart';
import 'dart:math';
import 'package:dio/dio.dart';
import '../models/dynamics/result.dart';
import '../models/dynamics/up.dart';
import 'index.dart';
class DynamicsHttp {
static Future followDynamic({
@ -40,6 +42,7 @@ class DynamicsHttp {
'status': false,
'data': [],
'msg': res.data['message'],
'code': res.data['code'],
};
}
}
@ -67,7 +70,7 @@ class DynamicsHttp {
}) async {
var res = await Request().post(
Api.likeDynamic,
queryParameters: {
data: {
'dynamic_id': dynamicId,
'up': up,
'csrf': await Request.getCsrf(),
@ -117,4 +120,99 @@ class DynamicsHttp {
};
}
}
static Future dynamicForward() async {
var res = await Request().post(
Api.dynamicForwardUrl,
queryParameters: {
'csrf': await Request.getCsrf(),
'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'},
'x-bili-web-req-json': {'spm_id': '333.999'},
},
data: {
'attach_card': null,
'scene': 4,
'content': {
'conetents': [
{'raw_text': "2", 'type': 1, 'biz_id': ""}
]
}
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
static Future dynamicCreate({
required int mid,
required int scene,
int? oid,
String? dynIdStr,
String? rawText,
}) async {
DateTime now = DateTime.now();
int timestamp = now.millisecondsSinceEpoch ~/ 1000;
Random random = Random();
int randomNumber = random.nextInt(9000) + 1000;
String uploadId = '${mid}_${timestamp}_$randomNumber';
Map<String, dynamic> webRepostSrc = {
'dyn_id_str': dynIdStr ?? '',
};
/// 投稿转发
if (scene == 5) {
webRepostSrc = {
'revs_id': {'dyn_type': 8, 'rid': oid}
};
}
var res = await Request().post(
Api.dynamicCreate,
queryParameters: {
'platform': 'web',
'csrf': await Request.getCsrf(),
'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'},
'x-bili-web-req-json': {'spm_id': '333.999'},
},
data: {
'dyn_req': {
'content': {
'contents': [
{'raw_text': rawText ?? '', 'type': 1, 'biz_id': ''}
]
},
'scene': scene,
'attach_card': null,
'upload_id': uploadId,
'meta': {
'app_meta': {'from': 'create.dynamic.web', 'mobi_app': 'web'}
}
},
'web_repost_src': webRepostSrc
},
options: Options(contentType: 'application/json'),
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
}

View File

@ -1,5 +1,5 @@
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/fans/result.dart';
import '../models/fans/result.dart';
import 'index.dart';
class FanHttp {
static Future fans({int? vmid, int? pn, int? ps, String? orderType}) async {

67
lib/http/fav.dart Normal file
View File

@ -0,0 +1,67 @@
import 'index.dart';
class FavHttp {
/// 编辑收藏夹
static Future editFolder({
required String title,
required String intro,
required String mediaId,
String? cover,
int? privacy,
}) async {
var res = await Request().post(
Api.editFavFolder,
data: {
'title': title,
'intro': intro,
'media_id': mediaId,
'cover': cover ?? '',
'privacy': privacy ?? 0,
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
/// 新建收藏夹
static Future addFolder({
required String title,
required String intro,
String? cover,
int? privacy,
}) async {
var res = await Request().post(
Api.addFavFolder,
data: {
'title': title,
'intro': intro,
'cover': cover ?? '',
'privacy': privacy ?? 0,
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
}

View File

@ -1,5 +1,5 @@
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/follow/result.dart';
import '../models/follow/result.dart';
import 'index.dart';
class FollowHttp {
static Future followings(

View File

@ -1,6 +1,6 @@
import 'package:html/dom.dart';
import 'package:html/parser.dart';
import 'package:pilipala/http/index.dart';
import 'index.dart';
class HtmlHttp {
// article
@ -15,7 +15,7 @@ class HtmlHttp {
Match match = regex.firstMatch(response.data)!;
String matchedString = match.group(0)!;
response = await Request().get(
'https:$matchedString' + '/',
'https:$matchedString/',
extra: {'ua': 'pc'},
);
}

View File

@ -1,17 +1,21 @@
// ignore_for_file: avoid_print
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:async';
import 'package:dio/dio.dart';
import 'dart:math' show Random;
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/interceptor.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
// import 'package:dio_http2_adapter/dio_http2_adapter.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/utils/id_utils.dart';
import '../utils/storage.dart';
import '../utils/utils.dart';
import 'api.dart';
import 'constants.dart';
import 'interceptor.dart';
class Request {
static final Request _instance = Request._internal();
@ -20,25 +24,29 @@ class Request {
factory Request() => _instance;
Box setting = GStrorage.setting;
static Box localCache = GStrorage.localCache;
late dynamic enableSystemProxy;
late bool enableSystemProxy;
late String systemProxyHost;
late String systemProxyPort;
static final RegExp spmPrefixExp =
RegExp(r'<meta name="spm_prefix" content="([^"]+?)">');
static String? buvid;
/// 设置cookie
static setCookie() async {
Box userInfoCache = GStrorage.userInfo;
var cookiePath = await Utils.getCookiePath();
var cookieJar = PersistCookieJar(
Box setting = GStrorage.setting;
final String cookiePath = await Utils.getCookiePath();
final PersistCookieJar cookieJar = PersistCookieJar(
ignoreExpires: true,
storage: FileStorage(cookiePath),
);
cookieManager = CookieManager(cookieJar);
dio.interceptors.add(cookieManager);
var cookie = await cookieManager.cookieJar
final List<Cookie> cookie = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.baseUrl));
var userInfo = userInfoCache.get('userInfoCache');
final userInfo = userInfoCache.get('userInfoCache');
if (userInfo != null && userInfo.mid != null) {
var cookie2 = await cookieManager.cookieJar
final List<Cookie> cookie2 = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.tUrl));
if (cookie2.isEmpty) {
try {
@ -49,22 +57,27 @@ class Request {
}
}
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
if (cookie.isEmpty) {
try {
await Request().get(HttpString.baseUrl);
} catch (e) {
log("setCookie, ${e.toString()}");
}
String baseUrlType = 'default';
if (setting.get(SettingBoxKey.enableGATMode, defaultValue: false)) {
baseUrlType = 'bangumi';
}
var cookieString =
cookie.map((cookie) => '${cookie.name}=${cookie.value}').join('; ');
setBaseUrl(type: baseUrlType);
try {
await buvidActivate();
} catch (e) {
log("setCookie, ${e.toString()}");
}
final String cookieString = cookie
.map((Cookie cookie) => '${cookie.name}=${cookie.value}')
.join('; ');
dio.options.headers['cookie'] = cookieString;
}
// 从cookie中获取 csrf token
static Future<String> getCsrf() async {
var cookies = await cookieManager.cookieJar
List<Cookie> cookies = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.apiBaseUrl));
String token = '';
if (cookies.where((e) => e.name == 'bili_jct').isNotEmpty) {
@ -73,17 +86,66 @@ class Request {
return token;
}
static setOptionsHeaders(userInfo, status) {
static Future<String> getBuvid() async {
if (buvid != null) {
return buvid!;
}
final List<Cookie> cookies = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.baseUrl));
buvid = cookies.firstWhere((cookie) => cookie.name == 'buvid3').value;
if (buvid == null) {
try {
var result = await Request().get(
"${HttpString.apiBaseUrl}/x/frontend/finger/spi",
);
buvid = result["data"]["b_3"].toString();
} catch (e) {
// 处理请求错误
buvid = '';
print("Error fetching buvid: $e");
}
}
return buvid!;
}
static setOptionsHeaders(userInfo, bool status) {
if (status) {
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
dio.options.headers['x-bili-aurora-eid'] =
IdUtils.genAuroraEid(userInfo.mid);
}
dio.options.headers['env'] = 'prod';
dio.options.headers['app-key'] = 'android64';
dio.options.headers['x-bili-aurora-eid'] = 'UlMFQVcABlAH';
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 rand_png_end = 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': rand_png_end.substring(rand_png_end.length - 50),
},
});
await Request().post(Api.activateBuvidApi,
data: {'payload': jsonData},
options: Options(contentType: 'application/json'));
}
/*
* config it and create
*/
@ -100,30 +162,31 @@ class Request {
headers: {},
);
enableSystemProxy =
setting.get(SettingBoxKey.enableSystemProxy, defaultValue: false);
enableSystemProxy = setting.get(SettingBoxKey.enableSystemProxy,
defaultValue: false) as bool;
systemProxyHost =
localCache.get(LocalCacheKey.systemProxyHost, defaultValue: '');
systemProxyPort =
localCache.get(LocalCacheKey.systemProxyPort, defaultValue: '');
dio = Dio(options)
dio = Dio(options);
/// fix 第三方登录 302重定向 跟iOS代理问题冲突
..httpClientAdapter = Http2Adapter(
ConnectionManager(
idleTimeout: const Duration(milliseconds: 10000),
onClientCreate: (_, config) => config.onBadCertificate = (_) => true,
),
);
/// fix 第三方登录 302重定向 跟iOS代理问题冲突
// ..httpClientAdapter = Http2Adapter(
// ConnectionManager(
// idleTimeout: const Duration(milliseconds: 10000),
// onClientCreate: (_, ClientSetting config) =>
// config.onBadCertificate = (_) => true,
// ),
// );
/// 设置代理
if (enableSystemProxy) {
dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
final HttpClient client = HttpClient();
// Config the client.
client.findProxy = (uri) {
client.findProxy = (Uri uri) {
// return 'PROXY host:port';
return 'PROXY $systemProxyHost:$systemProxyPort';
};
@ -145,7 +208,7 @@ class Request {
));
dio.transformer = BackgroundTransformer();
dio.options.validateStatus = (status) {
dio.options.validateStatus = (int? status) {
return status! >= 200 && status < 300 ||
HttpString.validateStatusCodes.contains(status);
};
@ -156,7 +219,7 @@ class Request {
*/
get(url, {data, options, cancelToken, extra}) async {
Response response;
Options options = Options();
final Options options = Options();
ResponseType resType = ResponseType.json;
if (extra != null) {
resType = extra!['resType'] ?? ResponseType.json;
@ -175,8 +238,14 @@ class Request {
);
return response;
} on DioException catch (e) {
print('get error: $e');
return Future.error(await ApiInterceptor.dioError(e));
Response errResponse = Response(
data: {
'message': await ApiInterceptor.dioError(e)
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
statusCode: 200,
requestOptions: RequestOptions(),
);
return errResponse;
}
}
@ -191,14 +260,21 @@ class Request {
url,
data: data,
queryParameters: queryParameters,
options: options,
options:
options ?? Options(contentType: Headers.formUrlEncodedContentType),
cancelToken: cancelToken,
);
// print('post success: ${response.data}');
return response;
} on DioException catch (e) {
print('post error: $e');
return Future.error(await ApiInterceptor.dioError(e));
Response errResponse = Response(
data: {
'message': await ApiInterceptor.dioError(e)
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
statusCode: 200,
requestOptions: RequestOptions(),
);
return errResponse;
}
}
@ -248,4 +324,17 @@ class Request {
}
return headerUa;
}
static setBaseUrl({String type = 'default'}) {
switch (type) {
case 'default':
dio.options.baseUrl = HttpString.apiBaseUrl;
break;
case 'bangumi':
dio.options.baseUrl = HttpString.bangumiBaseUrl;
break;
default:
dio.options.baseUrl = HttpString.apiBaseUrl;
}
}
}

View File

@ -1,11 +1,10 @@
// ignore_for_file: avoid_print
import 'package:dio/dio.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/utils/storage.dart';
// import 'package:get/get.dart' hide Response;
import '../utils/storage.dart';
class ApiInterceptor extends Interceptor {
@override
@ -21,16 +20,16 @@ class ApiInterceptor extends Interceptor {
void onResponse(Response response, ResponseInterceptorHandler handler) {
try {
if (response.statusCode == 302) {
List<String> locations = response.headers['location']!;
final List<String> locations = response.headers['location']!;
if (locations.isNotEmpty) {
if (locations.first.startsWith('https://www.mcbbs.net')) {
final uri = Uri.parse(locations.first);
final accessKey = uri.queryParameters['access_key'];
final mid = uri.queryParameters['mid'];
final Uri uri = Uri.parse(locations.first);
final String? accessKey = uri.queryParameters['access_key'];
final String? mid = uri.queryParameters['mid'];
try {
Box localCache = GStrorage.localCache;
localCache.put(
LocalCacheKey.accessKey, {'mid': mid, 'value': accessKey});
localCache.put(LocalCacheKey.accessKey,
<String, String?>{'mid': mid, 'value': accessKey});
} catch (_) {}
}
}
@ -46,54 +45,58 @@ class ApiInterceptor extends Interceptor {
void onError(DioException err, ErrorInterceptorHandler handler) async {
// 处理网络请求错误
// handler.next(err);
SmartDialog.showToast(
await dioError(err),
displayType: SmartToastType.onlyRefresh,
);
String url = err.requestOptions.uri.toString();
final excludedPatterns = RegExp(r'heartbeat|seg\.so|online/total');
if (!excludedPatterns.hasMatch(url)) {
SmartDialog.showToast(
await dioError(err),
displayType: SmartToastType.onlyRefresh,
);
}
super.onError(err, handler);
}
static Future dioError(DioException error) async {
static Future<String> dioError(DioException error) async {
switch (error.type) {
case DioExceptionType.badCertificate:
return '证书有误!';
case DioExceptionType.badResponse:
return '服务器异常,请稍后重试!';
case DioExceptionType.cancel:
return "请求已被取消,请重新请求";
return '请求已被取消,请重新请求';
case DioExceptionType.connectionError:
return '连接错误,请检查网络设置';
case DioExceptionType.connectionTimeout:
return "网络连接超时,请检查网络设置";
return '网络连接超时,请检查网络设置';
case DioExceptionType.receiveTimeout:
return "响应超时,请稍后重试!";
return '响应超时,请稍后重试!';
case DioExceptionType.sendTimeout:
return "发送请求超时,请检查网络设置";
return '发送请求超时,请检查网络设置';
case DioExceptionType.unknown:
var res = await checkConect();
return res + " \n 网络异常,请稍后重试!";
default:
return "Dio异常";
final String res = await checkConnect();
return '$res,网络异常!';
}
}
static Future<dynamic> checkConect() async {
final connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
return 'connected with mobile network';
} else if (connectivityResult == ConnectivityResult.wifi) {
return 'connected with wifi network';
} else if (connectivityResult == ConnectivityResult.ethernet) {
// I am connected to a ethernet network.
} else if (connectivityResult == ConnectivityResult.vpn) {
// I am connected to a vpn network.
// Note for iOS and macOS:
// There is no separate network interface type for [vpn].
// It returns [other] on any device (also simulator)
} else if (connectivityResult == ConnectivityResult.other) {
// I am connected to a network which is not in the above mentioned networks.
} else if (connectivityResult == ConnectivityResult.none) {
return 'not connected to any network';
static Future<String> checkConnect() async {
final List<ConnectivityResult> connectivityResult =
await Connectivity().checkConnectivity();
if (connectivityResult.contains(ConnectivityResult.mobile)) {
return '正在使用移动流量';
} else if (connectivityResult.contains(ConnectivityResult.wifi)) {
return '正在使用wifi';
} else if (connectivityResult.contains(ConnectivityResult.ethernet)) {
return '正在使用局域网';
} else if (connectivityResult.contains(ConnectivityResult.vpn)) {
return '正在使用代理网络';
} else if (connectivityResult.contains(ConnectivityResult.bluetooth)) {
return '正在使用蓝牙网络';
} else if (connectivityResult.contains(ConnectivityResult.other)) {
return '正在使用其他网络';
} else if (connectivityResult.contains(ConnectivityResult.none)) {
return '未连接到任何网络';
} else {
return '';
}
}
}

View File

@ -1,7 +1,10 @@
import 'package:pilipala/http/api.dart';
import 'package:pilipala/http/init.dart';
import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/models/live/room_info.dart';
import 'package:pilipala/models/live/follow.dart';
import '../models/live/item.dart';
import '../models/live/room_info.dart';
import '../models/live/room_info_h5.dart';
import 'api.dart';
import 'init.dart';
class LiveHttp {
static Future liveList(
@ -46,4 +49,114 @@ class LiveHttp {
};
}
}
static Future liveRoomInfoH5({roomId, qn}) async {
var res = await Request().get(Api.liveRoomInfoH5, data: {
'room_id': roomId,
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': RoomInfoH5Model.fromJson(res.data['data'])
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
// 获取弹幕信息
static Future liveDanmakuInfo({roomId}) async {
var res = await Request().get(Api.getDanmuInfo, data: {
'id': roomId,
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
// 发送弹幕
static Future sendDanmaku({roomId, msg}) async {
var res = await Request().post(
Api.sendLiveMsg,
data: {
'bubble': 0,
'msg': msg,
'color': 16777215, // 颜色
'mode': 1, // 模式
'room_type': 0,
'jumpfrom': 71001, // 直播间来源
'reply_mid': 0,
'reply_attr': 0,
'replay_dmid': '',
'statistics': {"appId": 100, "platform": 5},
'fontsize': 25, // 字体大小
'rnd': DateTime.now().millisecondsSinceEpoch ~/ 1000, // 时间戳
'roomid': roomId,
'csrf': await Request.getCsrf(),
'csrf_token': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
// 我的关注 正在直播
static Future liveFollowing({int? pn, int? ps}) async {
var res = await Request().get(Api.getFollowingLive, data: {
'page': pn,
'page_size': ps,
'platform': 'web',
'ignoreRecord': 1,
'hit_ab': true,
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': LiveFollowingModel.fromJson(res.data['data'])
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
// 直播历史记录
static Future liveRoomEntry({required int roomId}) async {
await Request().post(
Api.liveRoomEntry,
data: {
'room_id': roomId,
'platform': 'pc',
'csrf_token': await Request.getCsrf(),
'csrf': await Request.getCsrf(),
'visit_id': '',
},
);
}
}

View File

@ -1,13 +1,13 @@
import 'dart:convert';
import 'dart:math';
import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import 'package:encrypt/encrypt.dart';
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/login/index.dart';
import 'package:pilipala/utils/login.dart';
import 'package:pilipala/http/constants.dart';
import 'package:uuid/uuid.dart';
import '../models/login/index.dart';
import '../utils/login.dart';
import 'index.dart';
class LoginHttp {
static Future queryCaptcha() async {
@ -22,32 +22,32 @@ class LoginHttp {
}
}
static Future sendSmsCode({
int? cid,
required int tel,
required String token,
required String challenge,
required String validate,
required String seccode,
}) async {
var res = await Request().post(
Api.appSmsCode,
data: {
'cid': cid,
'tel': tel,
"source": "main_web",
'token': token,
'challenge': challenge,
'validate': validate,
'seccode': seccode,
},
options: Options(
contentType: Headers.formUrlEncodedContentType,
// headers: {'user-agent': ApiConstants.userAgent}
),
);
print(res);
}
// static Future sendSmsCode({
// int? cid,
// required int tel,
// required String token,
// required String challenge,
// required String validate,
// required String seccode,
// }) async {
// var res = await Request().post(
// Api.appSmsCode,
// data: {
// 'cid': cid,
// 'tel': tel,
// "source": "main_web",
// 'token': token,
// 'challenge': challenge,
// 'validate': validate,
// 'seccode': seccode,
// },
// options: Options(
// contentType: Headers.formUrlEncodedContentType,
// // headers: {'user-agent': ApiConstants.userAgent}
// ),
// );
// print(res);
// }
// web端验证码
static Future sendWebSmsCode({
@ -61,6 +61,7 @@ class LoginHttp {
Map data = {
'cid': cid,
'tel': tel,
"source": "main_web",
'token': token,
'challenge': challenge,
'validate': validate,
@ -68,17 +69,50 @@ class LoginHttp {
};
FormData formData = FormData.fromMap({...data});
var res = await Request().post(
Api.smsCode,
Api.webSmsCode,
data: formData,
options: Options(
contentType: Headers.formUrlEncodedContentType,
),
);
print(res);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {'status': false, 'data': [], 'msg': res.data['message']};
}
}
// web端验证码登录
static Future loginInByWebSmsCode() async {}
static Future loginInByWebSmsCode({
int? cid,
required int tel,
required int code,
required String captchaKey,
}) async {
// webSmsLogin
Map data = {
"cid": cid,
"tel": tel,
"code": code,
"source": "main_mini",
"keep": 0,
"captcha_key": captchaKey,
"go_url": HttpString.baseUrl
};
FormData formData = FormData.fromMap({...data});
var res = await Request().post(
Api.webSmsLogin,
data: formData,
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {'status': false, 'data': [], 'msg': res.data['message']};
}
}
// web端密码登录
static Future liginInByWebPwd() async {}
@ -115,9 +149,6 @@ class LoginHttp {
var res = await Request().post(
Api.appSmsCode,
data: data,
options: Options(
contentType: Headers.formUrlEncodedContentType,
),
);
print(res);
}
@ -168,10 +199,82 @@ class LoginHttp {
var res = await Request().post(
Api.loginInByPwdApi,
data: data,
options: Options(
contentType: Headers.formUrlEncodedContentType,
),
);
print(res);
}
// web端密码登录
static Future loginInByWebPwd({
required int username,
required String password,
required String token,
required String challenge,
required String validate,
required String seccode,
}) async {
Map data = {
'username': username,
'password': password,
'keep': 0,
'token': token,
'challenge': challenge,
'validate': validate,
'seccode': seccode,
'source': 'main-fe-header',
"go_url": HttpString.baseUrl
};
FormData formData = FormData.fromMap({...data});
var res = await Request().post(
Api.loginInByWebPwd,
data: formData,
);
if (res.data['code'] == 0) {
if (res.data['data']['status'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'code': 1,
'data': res.data['data'],
'msg': res.data['data']['message'],
};
}
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
// web端登录二维码
static Future getWebQrcode() async {
var res = await Request().get(Api.qrCodeApi);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {'status': false, 'data': [], 'msg': res.data['message']};
}
}
// web端二维码轮询登录状态
static Future queryWebQrcodeStatus(String qrcodeKey) async {
var res = await Request()
.get(Api.loginInByQrcode, data: {'qrcode_key': qrcodeKey});
if (res.data['data']['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {'status': false, 'data': [], 'msg': res.data['message']};
}
}
}

View File

@ -1,17 +1,21 @@
import 'dart:convert';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/dynamics/result.dart';
import 'package:pilipala/models/follow/result.dart';
import 'package:pilipala/models/member/archive.dart';
import 'package:pilipala/models/member/coin.dart';
import 'package:pilipala/models/member/info.dart';
import 'package:pilipala/models/member/seasons.dart';
import 'package:pilipala/models/member/tags.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/utils/wbi_sign.dart';
import 'package:html/parser.dart';
import 'package:pilipala/models/member/article.dart';
import 'package:pilipala/models/member/like.dart';
import '../common/constants.dart';
import '../models/dynamics/result.dart';
import '../models/follow/result.dart';
import '../models/member/archive.dart';
import '../models/member/coin.dart';
import '../models/member/info.dart';
import '../models/member/seasons.dart';
import '../models/member/tags.dart';
import '../utils/storage.dart';
import '../utils/utils.dart';
import '../utils/wbi_sign.dart';
import 'index.dart';
class MemberHttp {
static Future memberInfo({
@ -79,6 +83,8 @@ class MemberHttp {
String order = 'pubdate',
bool orderAvoided = true,
}) async {
String dmImgStr = Utils.base64EncodeRandomString(16, 64);
String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128);
Map params = await WbiSign().makSign({
'mid': mid,
'ps': ps,
@ -88,8 +94,19 @@ class MemberHttp {
'order': order,
'platform': 'web',
'web_location': 1550101,
'order_avoided': orderAvoided
'order_avoided': orderAvoided,
'dm_img_list': '[]',
'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2),
'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2),
'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}',
...order == 'charge'
? {
'order': 'pubdate',
'special_type': 'charging',
}
: {}
});
var res = await Request().get(
Api.memberArchive,
data: params,
@ -101,10 +118,13 @@ class MemberHttp {
'data': MemberArchiveDataModel.fromJson(res.data['data'])
};
} else {
Map errMap = {
-352: '风控校验失败,请检查登录状态',
};
return {
'status': false,
'data': [],
'msg': res.data['message'],
'msg': errMap[res.data['code']] ?? res.data['message'],
};
}
}
@ -123,10 +143,13 @@ class MemberHttp {
'data': DynamicsDataModel.fromJson(res.data['data']),
};
} else {
Map errMap = {
-352: '风控校验失败,请检查登录状态',
};
return {
'status': false,
'data': [],
'msg': res.data['message'],
'msg': errMap[res.data['code']] ?? res.data['message'],
};
}
}
@ -175,13 +198,15 @@ class MemberHttp {
// 设置分组
static Future addUsers(int? fids, String? tagids) async {
var res = await Request().post(Api.addUsers, queryParameters: {
'fids': fids,
'tagids': tagids ?? '0',
'csrf': await Request.getCsrf(),
}, data: {
'cross_domain': true
});
var res = await Request().post(
Api.addUsers,
data: {
'fids': fids,
'tagids': tagids ?? '0',
'csrf': await Request.getCsrf(),
},
queryParameters: {'cross_domain': true},
);
if (res.data['code'] == 0) {
return {'status': true, 'data': [], 'msg': '操作成功'};
} else {
@ -316,7 +341,9 @@ class MemberHttp {
if (res.data['code'] == 0) {
return {
'status': true,
'data': MemberSeasonsDataModel.fromJson(res.data['data']['items_lists'])
'data': res.data['data']['list']
.map<MemberLikeDataModel>((e) => MemberLikeDataModel.fromJson(e))
.toList(),
};
} else {
return {
@ -397,11 +424,14 @@ class MemberHttp {
static Future cookieToKey() async {
var authCodeRes = await getTVCode();
if (authCodeRes['status']) {
var res = await Request().post(Api.cookieToKey, queryParameters: {
'auth_code': authCodeRes['data'],
'build': 708200,
'csrf': await Request.getCsrf(),
});
var res = await Request().post(
Api.cookieToKey,
data: {
'auth_code': authCodeRes['data'],
'build': 708200,
'csrf': await Request.getCsrf(),
},
);
await Future.delayed(const Duration(milliseconds: 300));
await qrcodePoll(authCodeRes['data']);
if (res.data['code'] == 0) {
@ -461,4 +491,133 @@ class MemberHttp {
};
}
}
// 搜索follow
static Future getfollowSearch({
required int mid,
required int ps,
required int pn,
required String name,
}) async {
Map<String, dynamic> data = {
'vmid': mid,
'pn': pn,
'ps': ps,
'order': 'desc',
'order_type': 'attention',
'gaia_source': 'main_web',
'name': name,
'web_location': 333.999,
};
Map params = await WbiSign().makSign(data);
var res = await Request().get(Api.followSearch, data: {
...data,
'w_rid': params['w_rid'],
'wts': params['wts'],
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': FollowDataModel.fromJson(res.data['data'])
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
static Future getSeriesDetail({
required int mid,
required int currentMid,
required int seriesId,
required int pn,
}) async {
var res = await Request().get(
Api.getSeriesDetailApi,
data: {
'mid': mid,
'series_id': seriesId,
'only_normal': true,
'sort': 'desc',
'pn': pn,
'ps': 30,
'current_mid': currentMid,
},
);
if (res.data['code'] == 0) {
try {
return {
'status': true,
'data': MemberSeasonsDataModel.fromJson(res.data['data'])
};
} catch (err) {
print(err);
}
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
static Future getWWebid({required int mid}) async {
var res = await Request().get('https://space.bilibili.com/$mid/article');
String? headContent = parse(res.data).head?.outerHtml;
final regex = RegExp(
r'<script id="__RENDER_DATA__" type="application/json">(.*?)</script>');
if (headContent != null) {
final match = regex.firstMatch(headContent);
if (match != null && match.groupCount >= 1) {
final content = match.group(1);
String decodedString = Uri.decodeComponent(content!);
Map<String, dynamic> map = jsonDecode(decodedString);
return {'status': true, 'data': map['access_id']};
} else {
return {'status': false, 'data': '请检查登录状态'};
}
}
return {'status': false, 'data': '请检查登录状态'};
}
// 获取用户专栏
static Future getMemberArticle({
required int mid,
required int pn,
required String wWebid,
String? offset,
}) async {
Map params = await WbiSign().makSign({
'host_mid': mid,
'page': pn,
'offset': offset,
'web_location': 333.999,
'w_webid': wWebid,
});
var res = await Request().get(Api.opusList, data: {
'host_mid': mid,
'page': pn,
'offset': offset,
'web_location': 333.999,
'w_webid': wWebid,
'w_rid': params['w_rid'],
'wts': params['wts'],
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': MemberArticleDataModel.fromJson(res.data['data'])
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'] ?? '请求异常',
};
}
}
}

View File

@ -1,8 +1,13 @@
import 'package:pilipala/http/api.dart';
import 'package:pilipala/http/init.dart';
import 'package:pilipala/models/msg/account.dart';
import 'package:pilipala/models/msg/session.dart';
import 'package:pilipala/utils/wbi_sign.dart';
import 'dart:convert';
import 'dart:math';
import 'package:pilipala/models/msg/like.dart';
import 'package:pilipala/models/msg/reply.dart';
import 'package:pilipala/models/msg/system.dart';
import '../models/msg/account.dart';
import '../models/msg/session.dart';
import '../utils/wbi_sign.dart';
import 'api.dart';
import 'init.dart';
class MsgHttp {
// 会话列表
@ -22,14 +27,22 @@ class MsgHttp {
Map signParams = await WbiSign().makSign(params);
var res = await Request().get(Api.sessionList, data: signParams);
if (res.data['code'] == 0) {
return {
'status': true,
'data': SessionDataModel.fromJson(res.data['data']),
};
try {
return {
'status': true,
'data': SessionDataModel.fromJson(res.data['data']),
};
} catch (err) {
return {
'status': false,
'data': [],
'msg': err.toString(),
};
}
} else {
return {
'status': false,
'date': [],
'data': [],
'msg': res.data['message'],
};
}
@ -42,12 +55,16 @@ class MsgHttp {
'mobi_app': 'web',
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data']
.map<AccountListModel>((e) => AccountListModel.fromJson(e))
.toList(),
};
try {
return {
'status': true,
'data': res.data['data']
.map<AccountListModel>((e) => AccountListModel.fromJson(e))
.toList(),
};
} catch (err) {
print('err🔟: $err');
}
} else {
return {
'status': false,
@ -86,4 +103,250 @@ class MsgHttp {
};
}
}
// 消息标记已读
static Future ackSessionMsg({
int? talkerId,
int? ackSeqno,
}) async {
String csrf = await Request.getCsrf();
Map params = await WbiSign().makSign({
'talker_id': talkerId,
'session_type': 1,
'ack_seqno': ackSeqno,
'build': 0,
'mobi_app': 'web',
'csrf_token': csrf,
'csrf': csrf
});
var res = await Request().get(Api.ackSessionMsg, data: params);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {'status': false, 'date': [], 'msg': res.data['message']};
}
}
// 发送私信
static Future sendMsg({
required int senderUid,
required int receiverId,
int? receiverType,
int? msgType,
dynamic content,
}) async {
String csrf = await Request.getCsrf();
var res = await Request().post(
Api.sendMsg,
data: {
'msg[sender_uid]': senderUid,
'msg[receiver_id]': receiverId,
'msg[receiver_type]': 1,
'msg[msg_type]': 1,
'msg[msg_status]': 0,
'msg[content]': jsonEncode(content),
'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000,
'msg[new_face_version]': 1,
'msg[dev_id]': getDevId(),
'from_firework': 0,
'build': 0,
'mobi_app': 'web',
'csrf_token': csrf,
'csrf': csrf,
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {'status': false, 'date': [], 'msg': res.data['message']};
}
}
static String getDevId() {
final List<String> b = [
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'A',
'B',
'C',
'D',
'E',
'F'
];
final List<String> s = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".split('');
for (int i = 0; i < s.length; i++) {
if ('-' == s[i] || '4' == s[i]) {
continue;
}
final int randomInt = Random().nextInt(16);
if ('x' == s[i]) {
s[i] = b[randomInt];
} else {
s[i] = b[3 & randomInt | 8];
}
}
return s.join();
}
static Future removeSession({
int? talkerId,
}) async {
String csrf = await Request.getCsrf();
Map params = await WbiSign().makSign({
'talker_id': talkerId,
'session_type': 1,
'build': 0,
'mobi_app': 'web',
'csrf_token': csrf,
'csrf': csrf
});
var res = await Request().get(Api.removeSession, data: params);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {'status': false, 'date': [], 'msg': res.data['message']};
}
}
static Future unread() async {
var res = await Request().get(Api.unread);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {'status': false, 'date': [], 'msg': res.data['message']};
}
}
// 回复我的
static Future messageReply({
int? id,
int? replyTime,
}) async {
var params = {
if (id != null) 'id': id,
if (replyTime != null) 'reply_time': replyTime,
};
var res = await Request().get(Api.messageReplyAPi, data: params);
if (res.data['code'] == 0) {
try {
return {
'status': true,
'data': MessageReplyModel.fromJson(res.data['data']),
};
} catch (err) {
return {'status': false, 'date': [], 'msg': err.toString()};
}
} else {
return {'status': false, 'date': [], 'msg': res.data['message']};
}
}
// 收到的赞
static Future messageLike({
int? id,
int? likeTime,
}) async {
var params = {
if (id != null) 'id': id,
if (likeTime != null) 'like_time': likeTime,
};
var res = await Request().get(Api.messageLikeAPi, data: params);
if (res.data['code'] == 0) {
try {
return {
'status': true,
'data': MessageLikeModel.fromJson(res.data['data']),
};
} catch (err) {
return {'status': false, 'date': [], 'msg': err.toString()};
}
} else {
return {'status': false, 'date': [], 'msg': res.data['message']};
}
}
static Future messageSystem() async {
var res = await Request().get(Api.messageSystemAPi, data: {
'csrf': await Request.getCsrf(),
'page_size': 20,
'build': 0,
'mobi_app': 'web',
});
if (res.data['code'] == 0) {
try {
return {
'status': true,
'data': res.data['data']['system_notify_list']
.map<MessageSystemModel>((e) => MessageSystemModel.fromJson(e))
.toList(),
};
} catch (err) {
return {'status': false, 'date': [], 'msg': err.toString()};
}
} else {
return {'status': false, 'date': [], 'msg': res.data['message']};
}
}
// 系统消息标记已读
static Future systemMarkRead(int cursor) async {
String csrf = await Request.getCsrf();
var res = await Request().get(Api.systemMarkRead, data: {
'csrf': csrf,
'cursor': cursor,
});
if (res.data['code'] == 0) {
return {
'status': true,
};
} else {
return {
'status': false,
'msg': res.data['message'],
};
}
}
static Future messageSystemAccount() async {
var res = await Request().get(Api.userMessageSystemAPi, data: {
'csrf': await Request.getCsrf(),
'page_size': 20,
'build': 0,
'mobi_app': 'web',
});
if (res.data['code'] == 0) {
try {
return {
'status': true,
'data': res.data['data']['system_notify_list']
.map<MessageSystemModel>((e) => MessageSystemModel.fromJson(e))
.toList(),
};
} catch (err) {
return {'status': false, 'date': [], 'msg': err.toString()};
}
} else {
return {'status': false, 'date': [], 'msg': res.data['message']};
}
}
}

116
lib/http/read.dart Normal file
View File

@ -0,0 +1,116 @@
import 'dart:convert';
import 'package:html/parser.dart';
import 'package:pilipala/models/read/opus.dart';
import 'package:pilipala/models/read/read.dart';
import 'package:pilipala/utils/wbi_sign.dart';
import 'index.dart';
class ReadHttp {
static List<String> extractScriptContents(String htmlContent) {
RegExp scriptRegExp = RegExp(r'<script>([\s\S]*?)<\/script>');
Iterable<Match> matches = scriptRegExp.allMatches(htmlContent);
List<String> scriptContents = [];
for (Match match in matches) {
String scriptContent = match.group(1)!;
scriptContents.add(scriptContent);
}
return scriptContents;
}
// 解析专栏 opus格式
static Future parseArticleOpus({required String id}) async {
var res = await Request().get('https://www.bilibili.com/opus/$id', extra: {
'ua': 'pc',
});
String? headContent = parse(res.data).head?.outerHtml;
var document = parse(headContent);
var linkTags = document.getElementsByTagName('link');
bool isCv = false;
String cvId = '';
for (var linkTag in linkTags) {
var attributes = linkTag.attributes;
if (attributes.containsKey('rel') &&
attributes['rel'] == 'canonical' &&
attributes.containsKey('data-vue-meta') &&
attributes['data-vue-meta'] == 'true') {
final String cvHref = linkTag.attributes['href']!;
RegExp regex = RegExp(r'cv(\d+)');
RegExpMatch? match = regex.firstMatch(cvHref);
if (match != null) {
cvId = match.group(1)!;
} else {
print('No match found.');
}
isCv = true;
break;
}
}
String scriptContent =
extractScriptContents(parse(res.data).body!.outerHtml)[0];
int startIndex = scriptContent.indexOf('{');
int endIndex = scriptContent.lastIndexOf('};');
String jsonContent = scriptContent.substring(startIndex, endIndex + 1);
// 解析JSON字符串为Map
Map<String, dynamic> jsonData = json.decode(jsonContent);
return {
'status': true,
'data': OpusDataModel.fromJson(jsonData),
'isCv': isCv,
'cvId': cvId,
};
}
// 解析专栏 cv格式
static Future parseArticleCv({required String id}) async {
var res = await Request().get(
'https://www.bilibili.com/read/cv$id',
extra: {'ua': 'pc'},
);
String scriptContent =
extractScriptContents(parse(res.data).body!.outerHtml)[0];
int startIndex = scriptContent.indexOf('{');
int endIndex = scriptContent.lastIndexOf('};');
String jsonContent = scriptContent.substring(startIndex, endIndex + 1);
// 解析JSON字符串为Map
Map<String, dynamic> jsonData = json.decode(jsonContent);
return {
'status': true,
'data': ReadDataModel.fromJson(jsonData),
};
}
//
static Future getViewInfo({required String id}) async {
Map params = await WbiSign().makSign({
'id': id,
'mobi_app': 'pc',
'from': 'web',
'gaia_source': 'main_web',
'web_location': 333.976,
});
var res = await Request().get(
Api.getViewInfo,
data: {
'id': id,
'mobi_app': 'pc',
'from': 'web',
'gaia_source': 'main_web',
'web_location': 333.976,
'w_rid': params['w_rid'],
'wts': params['wts'],
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
}

View File

@ -1,6 +1,7 @@
import 'package:pilipala/http/api.dart';
import 'package:pilipala/http/init.dart';
import 'package:pilipala/models/video/reply/data.dart';
import '../models/video/reply/data.dart';
import '../models/video/reply/emote.dart';
import 'api.dart';
import 'init.dart';
class ReplyHttp {
static Future replyList({
@ -21,19 +22,14 @@ class ReplyHttp {
return {
'status': true,
'data': ReplyData.fromJson(res.data['data']),
'code': 200,
};
} else {
Map errMap = {
-400: '请求错误',
-404: '无此项',
12002: '当前页面评论功能已关闭',
12009: '评论主体的type不合法',
12061: 'UP主已关闭评论区',
};
return {
'status': false,
'date': [],
'msg': errMap[res.data['code']] ?? res.data['message'],
'code': res.data['code'],
'msg': res.data['message'],
};
}
}
@ -82,7 +78,7 @@ class ReplyHttp {
}) async {
var res = await Request().post(
Api.likeReply,
queryParameters: {
data: {
'type': type,
'oid': oid,
'rpid': rpid,
@ -100,4 +96,44 @@ class ReplyHttp {
};
}
}
static Future getEmoteList({String? business}) async {
var res = await Request().get(Api.emojiList, data: {
'business': business ?? 'reply',
'web_location': '333.1245',
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': EmoteModelData.fromJson(res.data['data']),
};
} else {
return {
'status': false,
'date': [],
'msg': res.data['message'],
};
}
}
static Future replyDel({
required int type, //replyType
required int oid,
required int rpid,
}) async {
var res = await Request().post(
Api.replyDel,
queryParameters: {
'type': type, //type.index
'oid': oid,
'rpid': rpid,
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true, 'msg': '删除成功'};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
}

View File

@ -1,13 +1,14 @@
import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/models/search/hot.dart';
import 'package:pilipala/models/search/result.dart';
import 'package:pilipala/models/search/suggest.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/models/search/all.dart';
import 'package:pilipala/utils/wbi_sign.dart';
import '../models/bangumi/info.dart';
import '../models/common/search_type.dart';
import '../models/search/hot.dart';
import '../models/search/result.dart';
import '../models/search/suggest.dart';
import '../utils/storage.dart';
import 'index.dart';
class SearchHttp {
static Box setting = GStrorage.setting;
@ -37,7 +38,7 @@ class SearchHttp {
// 获取搜索建议
static Future searchSuggest({required term}) async {
var res = await Request().get(Api.serachSuggest,
var res = await Request().get(Api.searchSuggest,
data: {'term': term, 'main_ver': 'v1', 'highlight': term});
if (res.data is String) {
Map<String, dynamic> resultMap = json.decode(res.data);
@ -74,6 +75,7 @@ class SearchHttp {
required page,
String? order,
int? duration,
int? tids,
}) async {
var reqData = {
'search_type': searchType.type,
@ -83,9 +85,14 @@ class SearchHttp {
'page': page,
if (order != null) 'order': order,
if (duration != null) 'duration': duration,
if (tids != null && tids != -1) 'tids': tids,
};
var res = await Request().get(Api.searchByType, data: reqData);
if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) {
if (res.data['code'] == 0) {
if (res.data['data']['numPages'] == 0) {
// 我想返回数据使得可以通过data.list 取值,结果为[]
return {'status': true, 'data': Data()};
}
Object data;
try {
switch (searchType) {
@ -122,32 +129,37 @@ class SearchHttp {
return {
'status': false,
'data': [],
'msg': res.data['data'] != null && res.data['data']['numPages'] == 0
? '没有相关数据'
: res.data['message'],
'msg': res.data['message'],
};
}
}
static Future ab2c({int? aid, String? bvid}) async {
static Future<int> ab2c({int? aid, String? bvid}) async {
Map<String, dynamic> data = {};
if (aid != null) {
data['aid'] = aid;
} else if (bvid != null) {
data['bvid'] = bvid;
}
var res = await Request().get(Api.ab2c, data: {...data});
return res.data['data'].first['cid'];
final dynamic res =
await Request().get(Api.ab2c, data: <String, dynamic>{...data});
if (res.data['code'] == 0) {
return res.data['data'].first['cid'];
} else {
return -1;
}
}
static Future bangumiInfo({int? seasonId, int? epId}) async {
Map<String, dynamic> data = {};
static Future<Map<String, dynamic>> bangumiInfo(
{int? seasonId, int? epId}) async {
final Map<String, dynamic> data = {};
if (seasonId != null) {
data['season_id'] = seasonId;
} else if (epId != null) {
data['ep_id'] = epId;
}
var res = await Request().get(Api.bangumiInfo, data: {...data});
final dynamic res =
await Request().get(Api.bangumiInfo, data: <String, dynamic>{...data});
if (res.data['code'] == 0) {
return {
'status': true,
@ -161,4 +173,48 @@ class SearchHttp {
};
}
}
static Future<Map<String, dynamic>> ab2cWithPic(
{int? aid, String? bvid}) async {
Map<String, dynamic> data = {};
if (aid != null) {
data['aid'] = aid;
} else if (bvid != null) {
data['bvid'] = bvid;
}
final dynamic res =
await Request().get(Api.ab2c, data: <String, dynamic>{...data});
return {
'cid': res.data['data'].first['cid'],
'pic': res.data['data'].first['first_frame'],
};
}
static Future<Map<String, dynamic>> searchCount(
{required String keyword}) async {
Map<String, dynamic> data = {
'keyword': keyword,
'web_location': 333.999,
};
Map params = await WbiSign().makSign(data);
final dynamic res = await Request().get(Api.searchCount, data: params);
if (res.data['code'] == 0) {
return {
'status': true,
'data': SearchAllModel.fromJson(res.data['data']),
};
} else {
return {
'status': false,
'data': [],
'msg': '请求错误 🙅',
};
}
}
}
class Data {
List<dynamic> list;
Data({this.list = const []});
}

View File

@ -1,14 +1,20 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/http/api.dart';
import 'package:pilipala/http/init.dart';
import 'package:pilipala/models/model_hot_video_item.dart';
import 'package:pilipala/models/user/fav_detail.dart';
import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/models/user/history.dart';
import 'package:pilipala/models/user/info.dart';
import 'package:pilipala/models/user/stat.dart';
import 'package:pilipala/utils/wbi_sign.dart';
import 'package:html/parser.dart';
import 'package:pilipala/models/video/later.dart';
import '../common/constants.dart';
import '../models/model_hot_video_item.dart';
import '../models/user/fav_detail.dart';
import '../models/user/fav_folder.dart';
import '../models/user/history.dart';
import '../models/user/info.dart';
import '../models/user/stat.dart';
import '../models/user/sub_detail.dart';
import '../models/user/sub_folder.dart';
import 'api.dart';
import 'init.dart';
class UserHttp {
static Future<dynamic> userStat({required int mid}) async {
@ -56,12 +62,15 @@ class UserHttp {
if (res.data['data'] != null) {
data = FavFolderData.fromJson(res.data['data']);
return {'status': true, 'data': data};
} else {
return {'status': false, 'msg': '收藏夹为空'};
}
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'] ?? '账号未登录'
'msg': res.data['message'],
'code': res.data['code'],
};
}
}
@ -110,7 +119,12 @@ class UserHttp {
'data': {'list': list, 'count': res.data['data']['count']}
};
} else {
return {'status': false, 'data': [], 'msg': res.data['message']};
return {
'status': false,
'data': [],
'msg': res.data['message'],
'code': res.data['code'],
};
}
}
@ -125,7 +139,12 @@ class UserHttp {
if (res.data['code'] == 0) {
return {'status': true, 'data': HistoryData.fromJson(res.data['data'])};
} else {
return {'status': false, 'data': [], 'msg': res.data['message']};
return {
'status': false,
'data': [],
'msg': res.data['message'],
'code': res.data['code'],
};
}
}
@ -134,7 +153,7 @@ class UserHttp {
// 暂停switchStatus传true 否则false
var res = await Request().post(
Api.pauseHistory,
queryParameters: {
data: {
'switch': switchStatus,
'jsonp': 'jsonp',
'csrf': await Request.getCsrf(),
@ -153,7 +172,7 @@ class UserHttp {
static Future clearHistory() async {
var res = await Request().post(
Api.clearHistory,
queryParameters: {
data: {
'jsonp': 'jsonp',
'csrf': await Request.getCsrf(),
},
@ -171,7 +190,7 @@ class UserHttp {
}
var res = await Request().post(
Api.toViewLater,
queryParameters: data,
data: data,
);
if (res.data['code'] == 0) {
return {'status': true, 'msg': 'yeah稍后再看'};
@ -190,7 +209,7 @@ class UserHttp {
params[aid != null ? 'aid' : 'viewed'] = aid ?? true;
var res = await Request().post(
Api.toViewDel,
queryParameters: params,
data: params,
);
if (res.data['code'] == 0) {
return {'status': true, 'msg': 'yeah成功移除'};
@ -222,7 +241,7 @@ class UserHttp {
static Future toViewClear() async {
var res = await Request().post(
Api.toViewClear,
queryParameters: {
data: {
'jsonp': 'jsonp',
'csrf': await Request.getCsrf(),
},
@ -238,7 +257,7 @@ class UserHttp {
static Future delHistory(kid) async {
var res = await Request().post(
Api.delHistory,
queryParameters: {
data: {
'kid': kid,
'jsonp': 'jsonp',
'csrf': await Request.getCsrf(),
@ -306,4 +325,216 @@ class UserHttp {
return {'status': false, 'msg': res.data['message']};
}
}
// 我的订阅
static Future userSubFolder({
required int mid,
required int pn,
required int ps,
}) async {
var res = await Request().get(Api.userSubFolder, data: {
'up_mid': mid,
'ps': ps,
'pn': pn,
'platform': 'web',
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': SubFolderModelData.fromJson(res.data['data'])
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
'code': res.data['code'],
};
}
}
static Future userSeasonList({
required int seasonId,
required int pn,
required int ps,
}) async {
var res = await Request().get(Api.userSeasonList, data: {
'season_id': seasonId,
'ps': ps,
'pn': pn,
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': SubDetailModelData.fromJson(res.data['data'])
};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
static Future userResourceList({
required int seasonId,
required int pn,
required int ps,
}) async {
var res = await Request().get(Api.userResourceList, data: {
'media_id': seasonId,
'ps': ps,
'pn': pn,
'keyword': '',
'order': 'mtime',
'type': 0,
'tid': 0,
'platform': 'web',
});
if (res.data['code'] == 0) {
try {
return {
'status': true,
'data': SubDetailModelData.fromJson(res.data['data'])
};
} catch (err) {
return {'status': false, 'msg': err};
}
} else {
return {'status': false, 'msg': res.data['message']};
}
}
// 取消订阅
static Future cancelSub({required int seasonId}) async {
var res = await Request().post(
Api.cancelSub,
data: {
'platform': 'web',
'season_id': seasonId,
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
// 删除文件夹
static Future delFavFolder({required int mediaIds}) async {
var res = await Request().post(
Api.delFavFolder,
data: {
'media_ids': mediaIds,
'platform': 'web',
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
// 稍后再看播放全部
// static Future toViewPlayAll({required int oid, required String bvid}) async {
// var res = await Request().get(
// Api.watchLaterHtml,
// data: {
// 'oid': oid,
// 'bvid': bvid,
// },
// );
// String scriptContent =
// extractScriptContents(parse(res.data).body!.outerHtml)[0];
// int startIndex = scriptContent.indexOf('{');
// int endIndex = scriptContent.lastIndexOf('};');
// String jsonContent = scriptContent.substring(startIndex, endIndex + 1);
// // 解析JSON字符串为Map
// Map<String, dynamic> jsonData = json.decode(jsonContent);
// // 输出解析后的数据
// return {
// 'status': true,
// 'data': jsonData['resourceList']
// .map((e) => MediaVideoItemModel.fromJson(e))
// .toList()
// };
// }
static List<String> extractScriptContents(String htmlContent) {
RegExp scriptRegExp = RegExp(r'<script>([\s\S]*?)<\/script>');
Iterable<Match> matches = scriptRegExp.allMatches(htmlContent);
List<String> scriptContents = [];
for (Match match in matches) {
String scriptContent = match.group(1)!;
scriptContents.add(scriptContent);
}
return scriptContents;
}
// 稍后再看列表
static Future getMediaList({
required int type,
required int bizId,
required int ps,
int? oid,
}) async {
var res = await Request().get(
Api.mediaList,
data: {
'mobi_app': 'web',
'type': type,
'biz_id': bizId,
'oid': oid ?? '',
'otype': 2,
'ps': ps,
'direction': false,
'desc': true,
'sort_field': 1,
'tid': 0,
'with_current': false,
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data']['media_list'] != null
? res.data['data']['media_list']
.map<MediaVideoItemModel>(
(e) => MediaVideoItemModel.fromJson(e))
.toList()
: []
};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
// 解析收藏夹视频
static Future parseFavVideo({
required int mediaId,
required int oid,
required String bvid,
}) async {
var res = await Request().get(
'https://www.bilibili.com/list/ml$mediaId',
data: {
'oid': mediaId,
'bvid': bvid,
},
);
String scriptContent =
extractScriptContents(parse(res.data).body!.outerHtml)[0];
int startIndex = scriptContent.indexOf('{');
int endIndex = scriptContent.lastIndexOf('};');
String jsonContent = scriptContent.substring(startIndex, endIndex + 1);
// 解析JSON字符串为Map
Map<String, dynamic> jsonData = json.decode(jsonContent);
return {
'status': true,
'data': jsonData['resourceList']
.map<MediaVideoItemModel>((e) => MediaVideoItemModel.fromJson(e))
.toList()
};
}
}

View File

@ -1,19 +1,21 @@
import 'dart:developer';
import 'package:hive/hive.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/http/api.dart';
import 'package:pilipala/http/init.dart';
import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/home/rcmd/result.dart';
import 'package:pilipala/models/model_hot_video_item.dart';
import 'package:pilipala/models/model_rec_video_item.dart';
import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/models/video/ai.dart';
import 'package:pilipala/models/video/play/url.dart';
import 'package:pilipala/models/video_detail_res.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/wbi_sign.dart';
import '../common/constants.dart';
import '../models/common/reply_type.dart';
import '../models/home/rcmd/result.dart';
import '../models/model_hot_video_item.dart';
import '../models/model_rec_video_item.dart';
import '../models/user/fav_folder.dart';
import '../models/video/ai.dart';
import '../models/video/play/url.dart';
import '../models/video/subTitile/result.dart';
import '../models/video_detail_res.dart';
import '../utils/recommend_filter.dart';
import '../utils/storage.dart';
import '../utils/subtitle.dart';
import '../utils/wbi_sign.dart';
import 'api.dart';
import 'init.dart';
/// res.data['code'] == 0 请求正常返回结果
/// res.data['data'] 为结果
@ -34,62 +36,77 @@ class VideoHttp {
data: {
'version': 1,
'feed_version': 'V3',
'homepage_ver': 1,
'ps': ps,
'fresh_idx': freshIdx,
'fresh_type': 999999
'brush': freshIdx,
'fresh_type': 4
},
);
if (res.data['code'] == 0) {
List<RecVideoItemModel> list = [];
List<int> blackMidsList =
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
for (var i in res.data['data']['item']) {
list.add(RecVideoItemModel.fromJson(i));
//过滤掉live与ad以及拉黑用户
if (i['goto'] == 'av' &&
(i['owner'] != null &&
!blackMidsList.contains(i['owner']['mid']))) {
RecVideoItemModel videoItem = RecVideoItemModel.fromJson(i);
if (!RecommendFilter.filter(videoItem)) {
list.add(videoItem);
}
}
}
return {'status': true, 'data': list};
} else {
return {'status': false, 'data': [], 'msg': ''};
return {'status': false, 'data': [], 'msg': res.data['message']};
}
} catch (err) {
return {'status': false, 'data': [], 'msg': err.toString()};
}
}
static Future rcmdVideoListApp({int? ps, required int freshIdx}) async {
try {
var res = await Request().get(
Api.recommendListApp,
data: {
'idx': freshIdx,
'flush': '5',
'column': '4',
'device': 'pad',
'device_type': 0,
'device_name': 'vivo',
'pull': freshIdx == 0 ? 'true' : 'false',
'appkey': Constants.appKey,
'access_key': localCache
.get(LocalCacheKey.accessKey, defaultValue: {})['value'] ??
''
},
);
if (res.data['code'] == 0) {
List<RecVideoItemAppModel> list = [];
List<int> blackMidsList =
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
for (var i in res.data['data']['items']) {
// 屏蔽推广和拉黑用户
if (i['card_goto'] != 'ad_av' &&
(!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) &&
(i['args'] != null &&
!blackMidsList.contains(i['args']['up_mid']))) {
list.add(RecVideoItemAppModel.fromJson(i));
// 添加额外的loginState变量模拟未登录状态
static Future rcmdVideoListApp(
{bool loginStatus = true, required int freshIdx}) async {
var res = await Request().get(
Api.recommendListApp,
data: {
'idx': freshIdx,
'flush': '5',
'column': '4',
'device': 'pad',
'device_type': 0,
'device_name': 'vivo',
'pull': freshIdx == 0 ? 'true' : 'false',
'appkey': Constants.appKey,
'access_key': loginStatus
? (localCache
.get(LocalCacheKey.accessKey, defaultValue: {})['value'] ??
'')
: ''
},
);
if (res.data['code'] == 0) {
List<RecVideoItemAppModel> list = [];
List<int> blackMidsList =
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
for (var i in res.data['data']['items']) {
// 屏蔽推广和拉黑用户
if (i['card_goto'] != 'ad_av' &&
(!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) &&
(i['args'] != null &&
!blackMidsList.contains(i['args']['up_mid']))) {
RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i);
if (!RecommendFilter.filter(videoItem)) {
list.add(videoItem);
}
}
return {'status': true, 'data': list};
} else {
return {'status': false, 'data': [], 'msg': ''};
}
} catch (err) {
return {'status': false, 'data': [], 'msg': err.toString()};
return {'status': true, 'data': list};
} else {
return {'status': false, 'data': [], 'msg': res.data['message']};
}
}
@ -111,7 +128,7 @@ class VideoHttp {
}
return {'status': true, 'data': list};
} else {
return {'status': false, 'data': []};
return {'status': false, 'data': [], 'msg': res.data['message']};
}
} catch (err) {
return {'status': false, 'data': [], 'msg': err};
@ -134,6 +151,12 @@ class VideoHttp {
data['bvid'] = bvid;
}
// 免登录查看1080p
if (userInfoCache.get('userInfoCache') == null &&
setting.get(SettingBoxKey.p1080, defaultValue: true)) {
data['try_look'] = 1;
}
Map params = await WbiSign().makSign({
...data,
'fourk': 1,
@ -142,11 +165,6 @@ class VideoHttp {
'web_location': 1550101,
});
// 免登录查看1080p
if (userInfoCache.get('userInfoCache') == null &&
setting.get(SettingBoxKey.p1080, defaultValue: true)) {
data['try_look'] = 1;
}
try {
var res = await Request().get(Api.videoUrl, data: params);
if (res.data['code'] == 0) {
@ -170,22 +188,15 @@ class VideoHttp {
// 视频信息 标题、简介
static Future videoIntro({required String bvid}) async {
var res = await Request().get(Api.videoIntro, data: {'bvid': bvid});
VideoDetailResponse result = VideoDetailResponse.fromJson(res.data);
if (result.code == 0) {
if (res.data['code'] == 0) {
VideoDetailResponse result = VideoDetailResponse.fromJson(res.data);
return {'status': true, 'data': result.data!};
} else {
Map errMap = {
-400: '请求错误',
-403: '权限不足',
-404: '视频资源失效',
62002: '稿件不可见',
62004: '稿件审核中',
};
return {
'status': false,
'data': null,
'code': result.code,
'msg': errMap[result.code] ?? '请求异常',
'code': res.data['code'],
'msg': res.data['message'],
};
}
}
@ -196,7 +207,10 @@ class VideoHttp {
if (res.data['code'] == 0) {
List<HotVideoItemModel> list = [];
for (var i in res.data['data']) {
list.add(HotVideoItemModel.fromJson(i));
HotVideoItemModel videoItem = HotVideoItemModel.fromJson(i);
if (!RecommendFilter.filter(videoItem, relatedVideos: true)) {
list.add(videoItem);
}
}
return {'status': true, 'data': list};
} else {
@ -217,10 +231,11 @@ class VideoHttp {
// 获取投币状态
static Future hasCoinVideo({required String bvid}) async {
var res = await Request().get(Api.hasCoinVideo, data: {'bvid': bvid});
print('res: $res');
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
return {'status': true, 'data': []};
return {'status': false, 'data': []};
}
}
@ -228,7 +243,7 @@ class VideoHttp {
static Future coinVideo({required String bvid, required int multiply}) async {
var res = await Request().post(
Api.coinVideo,
queryParameters: {
data: {
'bvid': bvid,
'multiply': multiply,
'select_like': 0,
@ -256,7 +271,7 @@ class VideoHttp {
static Future oneThree({required String bvid}) async {
var res = await Request().post(
Api.oneThree,
queryParameters: {
data: {
'bvid': bvid,
'csrf': await Request.getCsrf(),
},
@ -272,7 +287,7 @@ class VideoHttp {
static Future likeVideo({required String bvid, required bool type}) async {
var res = await Request().post(
Api.likeVideo,
queryParameters: {
data: {
'bvid': bvid,
'like': type ? 1 : 2,
'csrf': await Request.getCsrf(),
@ -288,17 +303,20 @@ class VideoHttp {
// (取消)收藏
static Future favVideo(
{required int aid, String? addIds, String? delIds}) async {
var res = await Request().post(Api.favVideo, queryParameters: {
'rid': aid,
'type': 2,
'add_media_ids': addIds ?? '',
'del_media_ids': delIds ?? '',
'csrf': await Request.getCsrf(),
});
var res = await Request().post(
Api.favVideo,
data: {
'rid': aid,
'type': 2,
'add_media_ids': addIds ?? '',
'del_media_ids': delIds ?? '',
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
return {'status': false, 'data': []};
return {'status': false, 'data': [], 'msg': res.data['message']};
}
}
@ -332,14 +350,17 @@ class VideoHttp {
if (message == '') {
return {'status': false, 'data': [], 'msg': '请输入评论内容'};
}
var res = await Request().post(Api.replyAdd, queryParameters: {
'type': type.index,
'oid': oid,
'root': root == null || root == 0 ? '' : root,
'parent': parent == null || parent == 0 ? '' : parent,
'message': message,
'csrf': await Request.getCsrf(),
});
var res = await Request().post(
Api.replyAdd,
data: {
'type': type.index,
'oid': oid,
'root': root == null || root == 0 ? '' : root,
'parent': parent == null || parent == 0 ? '' : parent,
'message': message,
'csrf': await Request.getCsrf(),
},
);
log(res.toString());
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
@ -354,49 +375,64 @@ class VideoHttp {
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
return {'status': true, 'data': []};
return {'status': false, 'data': []};
}
}
// 操作用户关系
static Future relationMod(
{required int mid, required int act, required int reSrc}) async {
var res = await Request().post(Api.relationMod, queryParameters: {
'fid': mid,
'act': act,
're_src': reSrc,
'csrf': await Request.getCsrf(),
});
var res = await Request().post(
Api.relationMod,
data: {
'fid': mid,
'act': act,
're_src': reSrc,
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
if (act == 5) {
List<int> blackMidsList =
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
blackMidsList.add(mid);
setting.put(SettingBoxKey.blackMidsList, blackMidsList);
}
return {'status': true, 'data': res.data['data'], 'msg': '成功'};
} else {
return {'status': true, 'data': []};
return {'status': false, 'data': [], 'msg': res.data['message']};
}
}
// 视频播放进度
static Future heartBeat({bvid, cid, progress, realtime}) async {
await Request().post(Api.heartBeat, queryParameters: {
// 'aid': aid,
'bvid': bvid,
'cid': cid,
// 'epid': '',
// 'sid': '',
// 'mid': '',
'played_time': progress,
// 'realtime': realtime,
// 'type': '',
// 'sub_type': '',
'csrf': await Request.getCsrf(),
});
await Request().post(
Api.heartBeat,
data: {
// 'aid': aid,
'bvid': bvid,
'cid': cid,
// 'epid': '',
// 'sid': '',
// 'mid': '',
'played_time': progress,
// 'realtime': realtime,
// 'type': '',
// 'sub_type': '',
'csrf': await Request.getCsrf(),
},
);
}
// 添加追番
static Future bangumiAdd({int? seasonId}) async {
var res = await Request().post(Api.bangumiAdd, queryParameters: {
'season_id': seasonId,
'csrf': await Request.getCsrf(),
});
var res = await Request().post(
Api.bangumiAdd,
data: {
'season_id': seasonId,
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true, 'msg': res.data['result']['toast']};
} else {
@ -406,10 +442,13 @@ class VideoHttp {
// 取消追番
static Future bangumiDel({int? seasonId}) async {
var res = await Request().post(Api.bangumiDel, queryParameters: {
'season_id': seasonId,
'csrf': await Request.getCsrf(),
});
var res = await Request().post(
Api.bangumiDel,
data: {
'season_id': seasonId,
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true, 'msg': res.data['result']['toast']};
} else {
@ -426,6 +465,8 @@ class VideoHttp {
});
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
return {'status': false, 'data': null, 'msg': res.data['message']};
}
}
@ -440,11 +481,64 @@ class VideoHttp {
'up_mid': upMid,
});
var res = await Request().get(Api.aiConclusion, data: params);
if (res.data['code'] == 0) {
if (res.data['code'] == 0 && res.data['data']['code'] == 0) {
return {
'status': true,
'data': AiConclusionModel.fromJson(res.data['data']),
};
} else {
return {'status': false, 'data': []};
}
}
static Future getSubtitle({int? cid, String? bvid}) async {
var res = await Request().get(Api.getSubtitleConfig, data: {
'cid': cid,
'bvid': bvid,
});
try {
if (res.data['code'] == 0) {
return {
'status': true,
'data': SubTitlteModel.fromJson(res.data['data']),
};
} else {
return {'status': false, 'data': [], 'msg': res.data['msg']};
}
} catch (err) {
return {'status': false, 'data': [], 'msg': res.data['msg']};
}
}
// 视频排行
static Future getRankVideoList(int rid) async {
try {
var rankApi = "${Api.getRankApi}?rid=$rid&type=all";
var res = await Request().get(rankApi);
if (res.data['code'] == 0) {
List<HotVideoItemModel> list = [];
List<int> blackMidsList =
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
for (var i in res.data['data']['list']) {
if (!blackMidsList.contains(i['owner']['mid'])) {
list.add(HotVideoItemModel.fromJson(i));
}
}
return {'status': true, 'data': list};
} else {
return {'status': false, 'data': [], 'msg': res.data['message']};
}
} catch (err) {
return {'status': false, 'data': [], 'msg': err};
}
}
// 获取字幕内容
static Future<Map<String, dynamic>> getSubtitleContent(url) async {
var res = await Request().get('https:$url');
final String content =
await SubTitleUtils.convertToWebVTT(res.data['body']);
final List body = res.data['body'];
return {'content': content, 'body': body};
}
}

View File

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/services.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
@ -19,30 +20,51 @@ import 'package:pilipala/pages/main/view.dart';
import 'package:pilipala/services/service_locator.dart';
import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/data.dart';
import 'package:pilipala/utils/global_data_cache.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc.
import 'package:media_kit/media_kit.dart';
import 'package:pilipala/utils/recommend_filter.dart';
import 'package:catcher_2/catcher_2.dart';
import './services/loggeer.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
.then((_) async {
await GStrorage.init();
await setupServiceLocator();
runApp(const MyApp());
// 小白条、导航栏沉浸
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
await GStrorage.init();
clearLogs();
Request();
await Request.setCookie();
// 异常捕获 logo记录
final Catcher2Options releaseConfig = Catcher2Options(
SilentReportMode(),
[FileHandler(await getLogsPath())],
);
Catcher2(
releaseConfig: releaseConfig,
runAppFunction: () {
runApp(const MyApp());
},
);
// 小白条、导航栏沉浸
if (Platform.isAndroid) {
final androidInfo = await DeviceInfoPlugin().androidInfo;
if (androidInfo.version.sdkInt >= 29) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
}
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
systemNavigationBarColor: Colors.transparent,
systemNavigationBarDividerColor: Colors.transparent,
statusBarColor: Colors.transparent,
));
await Request.setCookie();
Data.init();
GStrorage.lazyInit();
PiliSchame.init();
});
}
PiliSchame.init();
await GlobalDataCache().initialize();
}
class MyApp extends StatelessWidget {
@ -83,6 +105,39 @@ class MyApp extends StatelessWidget {
} catch (_) {}
}
if (Platform.isAndroid) {
return AndroidApp(
brandColor: brandColor,
isDynamicColor: isDynamicColor,
currentThemeValue: currentThemeValue,
textScale: textScale,
);
} else {
return OtherApp(
brandColor: brandColor,
currentThemeValue: currentThemeValue,
textScale: textScale,
);
}
}
}
class AndroidApp extends StatelessWidget {
const AndroidApp({
super.key,
required this.brandColor,
required this.isDynamicColor,
required this.currentThemeValue,
required this.textScale,
});
final Color brandColor;
final bool isDynamicColor;
final ThemeType currentThemeValue;
final double textScale;
@override
Widget build(BuildContext context) {
return DynamicColorBuilder(
builder: ((ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
ColorScheme? lightColorScheme;
@ -102,58 +157,120 @@ class MyApp extends StatelessWidget {
brightness: Brightness.dark,
);
}
// 图片缓存
// PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20;
return GetMaterialApp(
title: 'PiLiPaLa',
theme: ThemeData(
// fontFamily: 'HarmonyOS',
colorScheme: currentThemeValue == ThemeType.dark
? darkColorScheme
: lightColorScheme,
useMaterial3: true,
pageTransitionsTheme: const PageTransitionsTheme(
builders: <TargetPlatform, PageTransitionsBuilder>{
TargetPlatform.android: ZoomPageTransitionsBuilder(
allowEnterRouteSnapshotting: false,
),
},
),
),
darkTheme: ThemeData(
// fontFamily: 'HarmonyOS',
colorScheme: currentThemeValue == ThemeType.light
? lightColorScheme
: darkColorScheme,
useMaterial3: true,
),
localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
locale: const Locale("zh", "CN"),
supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")],
fallbackLocale: const Locale("zh", "CN"),
getPages: Routes.getPages,
home: const MainApp(),
builder: (BuildContext context, Widget? child) {
return FlutterSmartDialog(
toastBuilder: (String msg) => CustomToast(msg: msg),
child: MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor:
MediaQuery.of(context).textScaleFactor * textScale),
child: child!,
),
);
},
navigatorObservers: [
VideoDetailPage.routeObserver,
SearchPage.routeObserver,
],
return BuildMainApp(
lightColorScheme: lightColorScheme,
darkColorScheme: darkColorScheme,
currentThemeValue: currentThemeValue,
textScale: textScale,
);
}),
);
}
}
class OtherApp extends StatelessWidget {
const OtherApp({
super.key,
required this.brandColor,
required this.currentThemeValue,
required this.textScale,
});
final Color brandColor;
final ThemeType currentThemeValue;
final double textScale;
@override
Widget build(BuildContext context) {
return BuildMainApp(
lightColorScheme: ColorScheme.fromSeed(
seedColor: brandColor,
brightness: Brightness.light,
),
darkColorScheme: ColorScheme.fromSeed(
seedColor: brandColor,
brightness: Brightness.dark,
),
currentThemeValue: currentThemeValue,
textScale: textScale,
);
}
}
class BuildMainApp extends StatelessWidget {
const BuildMainApp({
super.key,
required this.lightColorScheme,
required this.darkColorScheme,
required this.currentThemeValue,
required this.textScale,
});
final ColorScheme lightColorScheme;
final ColorScheme darkColorScheme;
final ThemeType currentThemeValue;
final double textScale;
@override
Widget build(BuildContext context) {
final SnackBarThemeData snackBarTheme = SnackBarThemeData(
actionTextColor: lightColorScheme.primary,
backgroundColor: lightColorScheme.secondaryContainer,
closeIconColor: lightColorScheme.secondary,
contentTextStyle: TextStyle(color: lightColorScheme.secondary),
elevation: 20,
);
return GetMaterialApp(
title: 'PiliPala',
theme: ThemeData(
colorScheme: currentThemeValue == ThemeType.dark
? darkColorScheme
: lightColorScheme,
snackBarTheme: snackBarTheme,
pageTransitionsTheme: const PageTransitionsTheme(
builders: <TargetPlatform, PageTransitionsBuilder>{
TargetPlatform.android: ZoomPageTransitionsBuilder(
allowEnterRouteSnapshotting: false,
),
},
),
),
darkTheme: ThemeData(
colorScheme: currentThemeValue == ThemeType.light
? lightColorScheme
: darkColorScheme,
snackBarTheme: snackBarTheme,
),
localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
locale: const Locale("zh", "CN"),
supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")],
fallbackLocale: const Locale("zh", "CN"),
getPages: Routes.getPages,
home: const MainApp(),
builder: (BuildContext context, Widget? child) {
return FlutterSmartDialog(
toastBuilder: (String msg) => CustomToast(msg: msg),
child: MediaQuery(
data: MediaQuery.of(context)
.copyWith(textScaler: TextScaler.linear(textScale)),
child: child!,
),
);
},
navigatorObservers: [
VideoDetailPage.routeObserver,
SearchPage.routeObserver,
],
onReady: () async {
RecommendFilter();
Data.init();
setupServiceLocator();
},
);
}
}

View File

@ -30,6 +30,7 @@ class BangumiListItemModel {
BangumiListItemModel({
this.badge,
this.badgeType,
this.pic,
this.cover,
// this.firstEp,
this.indexShow,
@ -50,6 +51,7 @@ class BangumiListItemModel {
String? badge;
int? badgeType;
String? pic;
String? cover;
String? indexShow;
int? isFinish;
@ -70,6 +72,7 @@ class BangumiListItemModel {
BangumiListItemModel.fromJson(Map<String, dynamic> json) {
badge = json['badge'] == '' ? null : json['badge'];
badgeType = json['badge_type'];
pic = json['cover'];
cover = json['cover'];
indexShow = json['index_show'];
isFinish = json['is_finish'];

View File

@ -0,0 +1,93 @@
// 操作类型的枚举值:点赞 不喜欢 收藏 投币 稍后再看 下载封面 后台播放 听视频 分享 下载视频
import 'package:flutter/material.dart';
import 'package:get/get.dart';
enum ActionType {
like,
coin,
collect,
watchLater,
share,
dislike,
downloadCover,
copyLink,
// backgroundPlay,
// listenVideo,
// downloadVideo,
}
extension ActionTypeExtension on ActionType {
String get value => [
'like',
'coin',
'collect',
'watchLater',
'share',
'dislike',
'downloadCover',
'copyLink',
// 'backgroundPlay',
// 'listenVideo',
// 'downloadVideo',
][index];
String get label => [
'点赞视频',
'投币',
'收藏视频',
'稍后再看',
'视频分享',
'不喜欢',
'下载封面',
'复制链接',
// '后台播放',
// '听视频',
// '下载视频',
][index];
}
List<Map> actionMenuConfig = [
{
'icon': const Icon(Icons.thumb_up_alt_outlined),
'label': '点赞视频',
'value': ActionType.like,
},
{
'icon': Image.asset(
'assets/images/coin.png',
width: 26,
color: IconTheme.of(Get.context!).color!.withOpacity(0.65),
),
'label': '投币',
'value': ActionType.coin,
},
{
'icon': const Icon(Icons.star_border),
'label': '收藏视频',
'value': ActionType.collect,
},
{
'icon': const Icon(Icons.watch_later_outlined),
'label': '稍后再看',
'value': ActionType.watchLater,
},
{
'icon': const Icon(Icons.share),
'label': '视频分享',
'value': ActionType.share,
},
{
'icon': const Icon(Icons.thumb_down_alt_outlined),
'label': '不喜欢',
'value': ActionType.dislike,
},
{
'icon': const Icon(Icons.image_outlined),
'label': '下载封面',
'value': ActionType.downloadCover,
},
{
'icon': const Icon(Icons.link_outlined),
'label': '复制链接',
'value': ActionType.copyLink,
},
];

View File

@ -0,0 +1,9 @@
enum DynamicBadgeMode { hidden, point, number }
extension DynamicBadgeModeDesc on DynamicBadgeMode {
String get description => ['隐藏', '红点', '数字'][index];
}
extension DynamicBadgeModeCode on DynamicBadgeMode {
int get code => [0, 1, 2][index];
}

View File

@ -7,5 +7,5 @@ enum DynamicsType {
extension BusinessTypeExtension on DynamicsType {
String get values => ['all', 'video', 'pgc', 'article'][index];
String get labels => ['全部', '视频', '', '专栏'][index];
String get labels => ['全部', '投稿', '', '专栏'][index];
}

View File

@ -0,0 +1,12 @@
enum FullScreenGestureMode {
/// 从上滑到下
fromToptoBottom,
/// 从下滑到上
fromBottomtoTop,
}
extension FullScreenGestureModeExtension on FullScreenGestureMode {
String get values => ['fromToptoBottom', 'fromBottomtoTop'][index];
String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index];
}

View File

@ -0,0 +1,4 @@
library commonn_model;
export './business_type.dart';
export './gesture_mode.dart';

View File

@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import '../../pages/dynamics/index.dart';
import '../../pages/home/index.dart';
import '../../pages/media/index.dart';
import '../../pages/rank/index.dart';
List defaultNavigationBars = [
{
'id': 0,
'icon': const Icon(
Icons.home_outlined,
size: 21,
),
'selectIcon': const Icon(
Icons.home,
size: 21,
),
'label': "首页",
'count': 0,
'page': const HomePage(),
},
{
'id': 1,
'icon': const Icon(
Icons.trending_up,
size: 21,
),
'selectIcon': const Icon(
Icons.trending_up_outlined,
size: 21,
),
'label': "排行榜",
'count': 0,
'page': const RankPage(),
},
{
'id': 2,
'icon': const Icon(
Icons.motion_photos_on_outlined,
size: 21,
),
'selectIcon': const Icon(
Icons.motion_photos_on,
size: 21,
),
'label': "动态",
'count': 0,
'page': const DynamicsPage(),
},
{
'id': 3,
'icon': const Icon(
Icons.video_collection_outlined,
size: 20,
),
'selectIcon': const Icon(
Icons.video_collection,
size: 21,
),
'label': "媒体库",
'count': 0,
'page': const MediaPage(),
}
];

View File

@ -0,0 +1,222 @@
import 'package:flutter/material.dart';
import 'package:pilipala/pages/rank/zone/index.dart';
enum RandType {
all,
creation,
animation,
music,
dance,
game,
knowledge,
technology,
sport,
car,
life,
food,
animal,
madness,
fashion,
entertainment,
film,
origin,
rookie
}
extension RankTypeDesc on RandType {
String get description => [
'全站',
'国创相关',
'动画',
'音乐',
'舞蹈',
'游戏',
'知识',
'科技',
'运动',
'汽车',
'生活',
'美食',
'动物圈',
'鬼畜',
'时尚',
'娱乐',
'影视'
][index];
String get id => [
'all',
'creation',
'animation',
'music',
'dance',
'game',
'knowledge',
'technology',
'sport',
'car',
'life',
'food',
'animal',
'madness',
'fashion',
'entertainment',
'film'
][index];
}
List tabsConfig = [
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '全站',
'type': RandType.all,
'page': const ZonePage(rid: 0),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '国创相关',
'type': RandType.creation,
'page': const ZonePage(rid: 168),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '动画',
'type': RandType.animation,
'page': const ZonePage(rid: 1),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '音乐',
'type': RandType.music,
'page': const ZonePage(rid: 3),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '舞蹈',
'type': RandType.dance,
'page': const ZonePage(rid: 129),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '游戏',
'type': RandType.game,
'page': const ZonePage(rid: 4),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '知识',
'type': RandType.knowledge,
'page': const ZonePage(rid: 36),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '科技',
'type': RandType.technology,
'page': const ZonePage(rid: 188),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '运动',
'type': RandType.sport,
'page': const ZonePage(rid: 234),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '汽车',
'type': RandType.car,
'page': const ZonePage(rid: 223),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '生活',
'type': RandType.life,
'page': const ZonePage(rid: 160),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '美食',
'type': RandType.food,
'page': const ZonePage(rid: 211),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '动物圈',
'type': RandType.animal,
'page': const ZonePage(rid: 217),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '鬼畜',
'type': RandType.madness,
'page': const ZonePage(rid: 119),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '时尚',
'type': RandType.fashion,
'page': const ZonePage(rid: 155),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '娱乐',
'type': RandType.entertainment,
'page': const ZonePage(rid: 5),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '影视',
'type': RandType.film,
'page': const ZonePage(rid: 181),
}
];

View File

@ -1,7 +1,7 @@
// 首页推荐类型
enum RcmdType { web, app }
enum RcmdType { web, app, notLogin }
extension RcmdTypeExtension on RcmdType {
String get values => ['web', 'app'][index];
String get labels => ['web端', 'app端'][index];
String get values => ['web', 'app', 'notLogin'][index];
String get labels => ['web端', 'app端', '游客模式'][index];
}

View File

@ -1,6 +1,6 @@
enum ReplySortType { time, like, reply }
enum ReplySortType { time, like }
extension ReplySortTypeExtension on ReplySortType {
String get titles => ['最新评论', '最热评论', '回复最多'][index];
String get labels => ['最新', '最热', '最多回复'][index];
String get titles => ['最新评论', '最热评论'][index];
String get labels => ['最新', '最热'][index];
}

View File

@ -28,7 +28,7 @@ extension SearchTypeExtension on SearchType {
String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index];
}
// 搜索类型为视频、专栏及相簿
// 搜索类型为视频时
enum ArchiveFilterType {
totalrank,
click,
@ -44,3 +44,21 @@ extension ArchiveFilterTypeExtension on ArchiveFilterType {
String get description =>
['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index];
}
// 搜索类型为专栏时
enum ArticleFilterType {
// 综合排序
totalrank,
// 最新发布
pubdate,
// 最多点击
click,
// 最多喜欢
attention,
// 最多评论
scores,
}
extension ArticleFilterTypeExtension on ArticleFilterType {
String get description => ['综合排序', '最新发布', '最多点击', '最多喜欢', '最多评论'][index];
}

View File

@ -9,6 +9,7 @@ enum TabType { live, rcmd, hot, bangumi }
extension TabTypeDesc on TabType {
String get description => ['直播', '推荐', '热门', '番剧'][index];
String get id => ['live', 'rcmd', 'hot', 'bangumi'][index];
}
List tabsConfig = [

View File

@ -0,0 +1,5 @@
enum VideoEpidoesType {
videoEpisode,
videoPart,
bangumiEpisode,
}

View File

@ -414,6 +414,8 @@ class DynamicMajorModel {
this.none,
this.type,
this.courses,
this.common,
this.music,
});
DynamicArchiveModel? archive;
@ -429,6 +431,8 @@ class DynamicMajorModel {
// MAJOR_TYPE_OPUS 图文/文章
String? type;
Map? courses;
Map? common;
Map? music;
DynamicMajorModel.fromJson(Map<String, dynamic> json) {
archive = json['archive'] != null
@ -452,6 +456,8 @@ class DynamicMajorModel {
json['none'] != null ? DynamicNoneModel.fromJson(json['none']) : null;
type = json['type'];
courses = json['courses'] ?? {};
common = json['common'] ?? {};
music = json['music'] ?? {};
}
}

View File

@ -2,18 +2,28 @@ class FollowUpModel {
FollowUpModel({
this.liveUsers,
this.upList,
this.liveList,
this.myInfo,
});
LiveUsers? liveUsers;
List<UpItem>? upList;
List<LiveUserItem>? liveList;
MyInfo? myInfo;
FollowUpModel.fromJson(Map<String, dynamic> json) {
liveUsers = json['live_users'] != null
? LiveUsers.fromJson(json['live_users'])
: null;
liveList = json['live_users'] != null
? json['live_users']['items']
.map<LiveUserItem>((e) => LiveUserItem.fromJson(e))
.toList()
: [];
upList = json['up_list'] != null
? json['up_list'].map<UpItem>((e) => UpItem.fromJson(e)).toList()
: [];
myInfo = json['my_info'] != null ? MyInfo.fromJson(json['my_info']) : null;
}
}
@ -93,3 +103,21 @@ class UpItem {
uname = json['uname'];
}
}
class MyInfo {
MyInfo({
this.face,
this.mid,
this.name,
});
String? face;
int? mid;
String? name;
MyInfo.fromJson(Map<String, dynamic> json) {
face = json['face'];
mid = json['mid'];
name = json['name'];
}
}

View File

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

View File

@ -1,8 +1,5 @@
import 'package:hive/hive.dart';
import 'package:pilipala/utils/id_utils.dart';
part 'result.g.dart';
@HiveType(typeId: 0)
class RecVideoItemAppModel {
RecVideoItemAppModel({
this.id,
@ -27,47 +24,27 @@ class RecVideoItemAppModel {
this.adInfo,
});
@HiveField(0)
int? id;
@HiveField(1)
int? aid;
@HiveField(2)
String? bvid;
@HiveField(3)
int? cid;
@HiveField(4)
String? pic;
@HiveField(5)
RcmdStat? stat;
@HiveField(6)
String? duration;
@HiveField(7)
int? duration;
String? title;
@HiveField(8)
int? isFollowed;
@HiveField(9)
RcmdOwner? owner;
@HiveField(10)
RcmdReason? rcmdReason;
@HiveField(11)
String? rcmdReason;
String? goto;
@HiveField(12)
int? param;
@HiveField(13)
String? uri;
@HiveField(14)
String? talkBack;
// 番剧
@HiveField(15)
String? bangumiView;
@HiveField(16)
String? bangumiFollow;
@HiveField(17)
String? bangumiBadge;
@HiveField(18)
String? cardType;
@HiveField(19)
Map? adInfo;
RecVideoItemAppModel.fromJson(Map<String, dynamic> json) {
@ -75,19 +52,29 @@ class RecVideoItemAppModel {
? json['player_args']['aid']
: int.parse(json['param'] ?? '-1');
aid = json['player_args'] != null ? json['player_args']['aid'] : -1;
bvid = null;
bvid = json['player_args'] != null
? IdUtils.av2bv(json['player_args']['aid'])
: '';
cid = json['player_args'] != null ? json['player_args']['cid'] : -1;
pic = json['cover'];
stat = RcmdStat.fromJson(json);
duration = json['cover_right_text'];
title = json['title'];
isFollowed = 0;
// 改用player_args中的duration作为原始数据秒数
duration =
json['player_args'] != null ? json['player_args']['duration'] : -1;
//duration = json['cover_right_text'];
title = json['title'] ?? '获取标题失败';
owner = RcmdOwner.fromJson(json);
rcmdReason = json['rcmd_reason_style'] != null
? RcmdReason.fromJson(json['rcmd_reason_style'])
: null;
rcmdReason = json['bottom_rcmd_reason'] ?? json['top_rcmd_reason'];
// 由于app端api并不会直接返回与owner的关注状态
// 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态从而与web端接口等效
RegExp regex = RegExp(r'已关注|新关注');
isFollowed = regex.hasMatch(rcmdReason ?? '') ? 1 : 0;
// 如果是就无需再显示推荐原因交由view统一处理即可
if (isFollowed == 1) {
rcmdReason = null;
}
goto = json['goto'];
param = int.parse(json['param']);
param = int.parse(json['param'] ?? '-1');
uri = json['uri'];
talkBack = json['talk_back'];
@ -102,18 +89,14 @@ class RecVideoItemAppModel {
}
}
@HiveType(typeId: 1)
class RcmdStat {
RcmdStat({
this.view,
this.like,
this.danmu,
});
@HiveField(0)
String? view;
@HiveField(1)
String? like;
@HiveField(2)
String? danmu;
RcmdStat.fromJson(Map<String, dynamic> json) {
@ -122,13 +105,10 @@ class RcmdStat {
}
}
@HiveType(typeId: 2)
class RcmdOwner {
RcmdOwner({this.name, this.mid});
@HiveField(0)
String? name;
@HiveField(1)
int? mid;
RcmdOwner.fromJson(Map<String, dynamic> json) {
@ -141,13 +121,11 @@ class RcmdOwner {
}
}
@HiveType(typeId: 8)
class RcmdReason {
RcmdReason({
this.content,
});
@HiveField(0)
String? content;
RcmdReason.fromJson(Map<String, dynamic> json) {

View File

@ -1,209 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'result.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class RecVideoItemAppModelAdapter extends TypeAdapter<RecVideoItemAppModel> {
@override
final int typeId = 0;
@override
RecVideoItemAppModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return RecVideoItemAppModel(
id: fields[0] as int?,
aid: fields[1] as int?,
bvid: fields[2] as String?,
cid: fields[3] as int?,
pic: fields[4] as String?,
stat: fields[5] as RcmdStat?,
duration: fields[6] as String?,
title: fields[7] as String?,
isFollowed: fields[8] as int?,
owner: fields[9] as RcmdOwner?,
rcmdReason: fields[10] as RcmdReason?,
goto: fields[11] as String?,
param: fields[12] as int?,
uri: fields[13] as String?,
talkBack: fields[14] as String?,
bangumiView: fields[15] as String?,
bangumiFollow: fields[16] as String?,
bangumiBadge: fields[17] as String?,
cardType: fields[18] as String?,
adInfo: (fields[19] as Map?)?.cast<dynamic, dynamic>(),
);
}
@override
void write(BinaryWriter writer, RecVideoItemAppModel obj) {
writer
..writeByte(20)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.aid)
..writeByte(2)
..write(obj.bvid)
..writeByte(3)
..write(obj.cid)
..writeByte(4)
..write(obj.pic)
..writeByte(5)
..write(obj.stat)
..writeByte(6)
..write(obj.duration)
..writeByte(7)
..write(obj.title)
..writeByte(8)
..write(obj.isFollowed)
..writeByte(9)
..write(obj.owner)
..writeByte(10)
..write(obj.rcmdReason)
..writeByte(11)
..write(obj.goto)
..writeByte(12)
..write(obj.param)
..writeByte(13)
..write(obj.uri)
..writeByte(14)
..write(obj.talkBack)
..writeByte(15)
..write(obj.bangumiView)
..writeByte(16)
..write(obj.bangumiFollow)
..writeByte(17)
..write(obj.bangumiBadge)
..writeByte(18)
..write(obj.cardType)
..writeByte(19)
..write(obj.adInfo);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RecVideoItemAppModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class RcmdStatAdapter extends TypeAdapter<RcmdStat> {
@override
final int typeId = 1;
@override
RcmdStat read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return RcmdStat(
view: fields[0] as String?,
like: fields[1] as String?,
danmu: fields[2] as String?,
);
}
@override
void write(BinaryWriter writer, RcmdStat obj) {
writer
..writeByte(3)
..writeByte(0)
..write(obj.view)
..writeByte(1)
..write(obj.like)
..writeByte(2)
..write(obj.danmu);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RcmdStatAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class RcmdOwnerAdapter extends TypeAdapter<RcmdOwner> {
@override
final int typeId = 2;
@override
RcmdOwner read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return RcmdOwner(
name: fields[0] as String?,
mid: fields[1] as int?,
);
}
@override
void write(BinaryWriter writer, RcmdOwner obj) {
writer
..writeByte(2)
..writeByte(0)
..write(obj.name)
..writeByte(1)
..write(obj.mid);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RcmdOwnerAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class RcmdReasonAdapter extends TypeAdapter<RcmdReason> {
@override
final int typeId = 8;
@override
RcmdReason read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return RcmdReason(
content: fields[0] as String?,
);
}
@override
void write(BinaryWriter writer, RcmdReason obj) {
writer
..writeByte(1)
..writeByte(0)
..write(obj.content);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RcmdReasonAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

126
lib/models/live/follow.dart Normal file
View File

@ -0,0 +1,126 @@
class LiveFollowingModel {
int? count;
List<LiveFollowingItemModel>? list;
int? liveCount;
int? neverLivedCount;
List? neverLivedFaces;
int? pageSize;
String? title;
int? totalPage;
LiveFollowingModel({
this.count,
this.list,
this.liveCount,
this.neverLivedCount,
this.neverLivedFaces,
this.pageSize,
this.title,
this.totalPage,
});
LiveFollowingModel.fromJson(Map<String, dynamic> json) {
count = json['count'];
if (json['list'] != null) {
list = <LiveFollowingItemModel>[];
json['list'].forEach((v) {
list!.add(LiveFollowingItemModel.fromJson(v));
});
}
liveCount = json['live_count'];
neverLivedCount = json['never_lived_count'];
if (json['never_lived_faces'] != null) {
neverLivedFaces = <dynamic>[];
json['never_lived_faces'].forEach((v) {
neverLivedFaces!.add(v);
});
}
pageSize = json['pageSize'];
title = json['title'];
totalPage = json['totalPage'];
}
}
class LiveFollowingItemModel {
int? roomId;
int? uid;
String? uname;
String? title;
String? face;
int? liveStatus;
int? recordNum;
String? recentRecordId;
int? isAttention;
int? clipNum;
int? fansNum;
String? areaName;
String? areaValue;
String? tags;
String? recentRecordIdV2;
int? recordNumV2;
int? recordLiveTime;
String? areaNameV2;
String? roomNews;
String? watchIcon;
String? textSmall;
String? roomCover;
String? pic;
int? parentAreaId;
int? areaId;
LiveFollowingItemModel({
this.roomId,
this.uid,
this.uname,
this.title,
this.face,
this.liveStatus,
this.recordNum,
this.recentRecordId,
this.isAttention,
this.clipNum,
this.fansNum,
this.areaName,
this.areaValue,
this.tags,
this.recentRecordIdV2,
this.recordNumV2,
this.recordLiveTime,
this.areaNameV2,
this.roomNews,
this.watchIcon,
this.textSmall,
this.roomCover,
this.pic,
this.parentAreaId,
this.areaId,
});
LiveFollowingItemModel.fromJson(Map<String, dynamic> json) {
roomId = json['roomid'];
uid = json['uid'];
uname = json['uname'];
title = json['title'];
face = json['face'];
liveStatus = json['live_status'];
recordNum = json['record_num'];
recentRecordId = json['recent_record_id'];
isAttention = json['is_attention'];
clipNum = json['clipnum'];
fansNum = json['fans_num'];
areaName = json['area_name'];
areaValue = json['area_value'];
tags = json['tags'];
recentRecordIdV2 = json['recent_record_id_v2'];
recordNumV2 = json['record_num_v2'];
recordLiveTime = json['record_live_time'];
areaNameV2 = json['area_name_v2'];
roomNews = json['room_news'];
watchIcon = json['watch_icon'];
textSmall = json['text_small'];
roomCover = json['room_cover'];
pic = json['room_cover'];
parentAreaId = json['parent_area_id'];
areaId = json['area_id'];
}
}

View File

@ -0,0 +1,101 @@
class LiveMessageModel {
// 消息类型
final LiveMessageType type;
// 用户名
final String userName;
// 信息
final String? message;
// 数据
final dynamic data;
final String? face;
final int? uid;
final Map<String, dynamic>? emots;
// 颜色
final LiveMessageColor color;
LiveMessageModel({
required this.type,
required this.userName,
required this.message,
required this.color,
this.data,
this.face,
this.uid,
this.emots,
});
}
class LiveSuperChatMessage {
final String backgroundBottomColor;
final String backgroundColor;
final DateTime endTime;
final String face;
final String message;
final String price;
final DateTime startTime;
final String userName;
LiveSuperChatMessage({
required this.backgroundBottomColor,
required this.backgroundColor,
required this.endTime,
required this.face,
required this.message,
required this.price,
required this.startTime,
required this.userName,
});
}
enum LiveMessageType {
// 普通留言
chat,
// 醒目留言
superChat,
//
online,
// 加入
join,
// 关注
follow,
}
class LiveMessageColor {
final int r, g, b;
LiveMessageColor(this.r, this.g, this.b);
static LiveMessageColor get white => LiveMessageColor(255, 255, 255);
static LiveMessageColor numberToColor(int intColor) {
var obj = intColor.toRadixString(16);
LiveMessageColor color = LiveMessageColor.white;
if (obj.length == 4) {
obj = "00$obj";
}
if (obj.length == 6) {
var R = int.parse(obj.substring(0, 2), radix: 16);
var G = int.parse(obj.substring(2, 4), radix: 16);
var B = int.parse(obj.substring(4, 6), radix: 16);
color = LiveMessageColor(R, G, B);
}
if (obj.length == 8) {
var R = int.parse(obj.substring(2, 4), radix: 16);
var G = int.parse(obj.substring(4, 6), radix: 16);
var B = int.parse(obj.substring(6, 8), radix: 16);
//var A = int.parse(obj.substring(0, 2), radix: 16);
color = LiveMessageColor(R, G, B);
}
return color;
}
@override
String toString() {
return "#${r.toRadixString(16).padLeft(2, '0')}${g.toRadixString(16).padLeft(2, '0')}${b.toRadixString(16).padLeft(2, '0')}";
}
}

View File

@ -0,0 +1,43 @@
enum LiveQuality {
dolby,
super4K,
origin,
bluRay,
superHD,
smooth,
flunt,
}
extension LiveQualityCode on LiveQuality {
static final List<int> _codeList = [
30000,
20000,
10000,
400,
250,
150,
80,
];
int get code => _codeList[index];
static LiveQuality? fromCode(int code) {
final index = _codeList.indexOf(code);
if (index != -1) {
return LiveQuality.values[index];
}
return null;
}
}
extension VideoQualityDesc on LiveQuality {
static final List<String> _descList = [
'杜比',
'4K',
'原画',
'蓝光',
'超清',
'高清',
'流畅',
];
get description => _descList[index];
}

View File

@ -1,11 +1,13 @@
class RoomInfoModel {
RoomInfoModel({
this.roomId,
this.isPortrait,
this.liveStatus,
this.liveTime,
this.playurlInfo,
});
int? roomId;
bool? isPortrait;
int? liveStatus;
int? liveTime;
PlayurlInfo? playurlInfo;
@ -13,6 +15,7 @@ class RoomInfoModel {
RoomInfoModel.fromJson(Map<String, dynamic> json) {
roomId = json['room_id'];
liveStatus = json['live_status'];
isPortrait = json['is_portrait'];
liveTime = json['live_time'];
playurlInfo = PlayurlInfo.fromJson(json['playurl_info']);
}

View File

@ -0,0 +1,130 @@
class RoomInfoH5Model {
RoomInfoH5Model({
this.roomInfo,
this.anchorInfo,
this.isRoomFeed,
this.watchedShow,
this.likeInfoV3,
this.blockInfo,
});
RoomInfo? roomInfo;
AnchorInfo? anchorInfo;
int? isRoomFeed;
Map? watchedShow;
LikeInfoV3? likeInfoV3;
Map? blockInfo;
RoomInfoH5Model.fromJson(Map<String, dynamic> json) {
roomInfo = RoomInfo.fromJson(json['room_info']);
anchorInfo = AnchorInfo.fromJson(json['anchor_info']);
isRoomFeed = json['is_room_feed'];
watchedShow = json['watched_show'];
likeInfoV3 = LikeInfoV3.fromJson(json['like_info_v3']);
blockInfo = json['block_info'];
}
}
class RoomInfo {
RoomInfo({
this.uid,
this.roomId,
this.title,
this.cover,
this.description,
this.liveStatus,
this.liveStartTime,
this.areaId,
this.areaName,
this.parentAreaId,
this.parentAreaName,
this.online,
this.background,
this.appBackground,
this.liveId,
});
int? uid;
int? roomId;
String? title;
String? cover;
String? description;
int? liveStatus;
int? liveStartTime;
int? areaId;
String? areaName;
int? parentAreaId;
String? parentAreaName;
int? online;
String? background;
String? appBackground;
String? liveId;
RoomInfo.fromJson(Map<String, dynamic> json) {
uid = json['uid'];
roomId = json['room_id'];
title = json['title'];
cover = json['cover'];
description = json['description'];
liveStatus = json['liveS_satus'];
liveStartTime = json['live_start_time'];
areaId = json['area_id'];
areaName = json['area_name'];
parentAreaId = json['parent_area_id'];
parentAreaName = json['parent_area_name'];
online = json['online'];
background = json['background'];
appBackground = json['app_background'];
liveId = json['live_id'];
}
}
class AnchorInfo {
AnchorInfo({
this.baseInfo,
this.relationInfo,
});
BaseInfo? baseInfo;
RelationInfo? relationInfo;
AnchorInfo.fromJson(Map<String, dynamic> json) {
baseInfo = BaseInfo.fromJson(json['base_info']);
relationInfo = RelationInfo.fromJson(json['relation_info']);
}
}
class BaseInfo {
BaseInfo({
this.uname,
this.face,
});
String? uname;
String? face;
BaseInfo.fromJson(Map<String, dynamic> json) {
uname = json['uname'];
face = json['face'];
}
}
class RelationInfo {
RelationInfo({this.attention});
int? attention;
RelationInfo.fromJson(Map<String, dynamic> json) {
attention = json['attention'];
}
}
class LikeInfoV3 {
LikeInfoV3({this.totalLikes});
int? totalLikes;
LikeInfoV3.fromJson(Map<String, dynamic> json) {
totalLikes = json['total_likes'];
}
}

View File

@ -142,7 +142,7 @@ class Stat {
Stat.fromJson(Map<String, dynamic> json) {
view = json["play"];
danmaku = json['comment'];
danmaku = json['video_review'];
}
}

View File

@ -0,0 +1,46 @@
class MemberArticleDataModel {
MemberArticleDataModel({
this.hasMore,
this.items,
this.offset,
this.updateNum,
});
bool? hasMore;
List<MemberArticleItemModel>? items;
String? offset;
int? updateNum;
MemberArticleDataModel.fromJson(Map<String, dynamic> json) {
hasMore = json['has_more'];
items = json['items']
.map<MemberArticleItemModel>((e) => MemberArticleItemModel.fromJson(e))
.toList();
offset = json['offset'];
updateNum = json['update_num'];
}
}
class MemberArticleItemModel {
MemberArticleItemModel({
this.content,
this.cover,
this.jumpUrl,
this.opusId,
this.stat,
});
String? content;
Map? cover;
String? jumpUrl;
String? opusId;
Map? stat;
MemberArticleItemModel.fromJson(Map<String, dynamic> json) {
content = json['content'];
cover = json['cover'];
jumpUrl = json['jump_url'];
opusId = json['opus_id'];
stat = json['stat'];
}
}

View File

@ -47,18 +47,23 @@ class Vip {
this.status,
this.dueDate,
this.label,
this.nicknameColor,
});
int? type;
int? status;
int? dueDate;
Map? label;
int? nicknameColor;
Vip.fromJson(Map<String, dynamic> json) {
type = json['type'];
status = json['status'];
dueDate = json['due_date'];
label = json['label'];
nicknameColor = json['nickname_color'] == ''
? null
: int.parse("0xFF${json['nickname_color'].replaceAll('#', '')}");
}
}

210
lib/models/member/like.dart Normal file
View File

@ -0,0 +1,210 @@
class MemberLikeDataModel {
MemberLikeDataModel({
this.aid,
this.videos,
this.tid,
this.tname,
this.pic,
this.title,
this.pubdate,
this.ctime,
this.desc,
this.state,
this.duration,
this.redirectUrl,
this.rights,
this.owner,
this.stat,
this.dimension,
this.cover43,
this.bvid,
this.interVideo,
this.resourceType,
this.subtitle,
this.enableVt,
});
final int? aid;
final int? videos;
final int? tid;
final String? tname;
final String? pic;
final String? title;
final int? pubdate;
final int? ctime;
final String? desc;
final int? state;
final int? duration;
final String? redirectUrl;
final Rights? rights;
final Owner? owner;
final Stat? stat;
final Dimension? dimension;
final String? cover43;
final String? bvid;
final bool? interVideo;
final String? resourceType;
final String? subtitle;
final int? enableVt;
factory MemberLikeDataModel.fromJson(Map<String, dynamic> json) =>
MemberLikeDataModel(
aid: json["aid"],
videos: json["videos"],
tid: json["tid"],
tname: json["tname"],
pic: json["pic"],
title: json["title"],
pubdate: json["pubdate"],
ctime: json["ctime"],
desc: json["desc"],
state: json["state"],
duration: json["duration"],
redirectUrl: json["redirect_url"],
rights: Rights.fromJson(json["rights"]),
owner: Owner.fromJson(json["owner"]),
stat: Stat.fromJson(json["stat"]),
dimension: Dimension.fromJson(json["dimension"]),
cover43: json["cover43"],
bvid: json["bvid"],
interVideo: json["inter_video"],
resourceType: json["resource_type"],
subtitle: json["subtitle"],
enableVt: json["enable_vt"],
);
}
class Dimension {
Dimension({
required this.width,
required this.height,
required this.rotate,
});
final int width;
final int height;
final int rotate;
factory Dimension.fromJson(Map<String, dynamic> json) => Dimension(
width: json["width"],
height: json["height"],
rotate: json["rotate"],
);
}
class Owner {
Owner({
required this.mid,
required this.name,
required this.face,
});
final int mid;
final String name;
final String face;
factory Owner.fromJson(Map<String, dynamic> json) => Owner(
mid: json["mid"],
name: json["name"],
face: json["face"],
);
}
class Rights {
Rights({
required this.bp,
required this.elec,
required this.download,
required this.movie,
required this.pay,
required this.hd5,
required this.noReprint,
required this.autoplay,
required this.ugcPay,
required this.isCooperation,
required this.ugcPayPreview,
required this.noBackground,
required this.arcPay,
required this.payFreeWatch,
});
final int bp;
final int elec;
final int download;
final int movie;
final int pay;
final int hd5;
final int noReprint;
final int autoplay;
final int ugcPay;
final int isCooperation;
final int ugcPayPreview;
final int noBackground;
final int arcPay;
final int payFreeWatch;
factory Rights.fromJson(Map<String, dynamic> json) => Rights(
bp: json["bp"],
elec: json["elec"],
download: json["download"],
movie: json["movie"],
pay: json["pay"],
hd5: json["hd5"],
noReprint: json["no_reprint"],
autoplay: json["autoplay"],
ugcPay: json["ugc_pay"],
isCooperation: json["is_cooperation"],
ugcPayPreview: json["ugc_pay_preview"],
noBackground: json["no_background"],
arcPay: json["arc_pay"],
payFreeWatch: json["pay_free_watch"],
);
}
class Stat {
Stat({
required this.aid,
required this.view,
required this.danmaku,
required this.reply,
required this.favorite,
required this.coin,
required this.share,
required this.nowRank,
required this.hisRank,
required this.like,
required this.dislike,
required this.vt,
required this.vv,
});
final int aid;
final int view;
final int danmaku;
final int reply;
final int favorite;
final int coin;
final int share;
final int nowRank;
final int hisRank;
final int like;
final int dislike;
final int vt;
final int vv;
factory Stat.fromJson(Map<String, dynamic> json) => Stat(
aid: json["aid"],
view: json["view"],
danmaku: json["danmaku"],
reply: json["reply"],
favorite: json["favorite"],
coin: json["coin"],
share: json["share"],
nowRank: json["now_rank"],
hisRank: json["his_rank"],
like: json["like"],
dislike: json["dislike"],
vt: json["vt"],
vv: json["vv"],
);
}

View File

@ -2,18 +2,32 @@ class MemberSeasonsDataModel {
MemberSeasonsDataModel({
this.page,
this.seasonsList,
this.seriesList,
});
Map? page;
List<MemberSeasonsList>? seasonsList;
List<MemberArchiveItem>? seriesList;
MemberSeasonsDataModel.fromJson(Map<String, dynamic> json) {
page = json['page'];
seasonsList = json['seasons_list'] != null
var tempList1 = json['seasons_list'] != null
? json['seasons_list']
.map<MemberSeasonsList>((e) => MemberSeasonsList.fromJson(e))
.toList()
: [];
var tempList2 = json['series_list'] != null
? json['series_list']
.map<MemberSeasonsList>((e) => MemberSeasonsList.fromJson(e))
.toList()
: [];
seriesList = json['archives'] != null
? json['archives']
.map<MemberArchiveItem>((e) => MemberArchiveItem.fromJson(e))
.toList()
: [];
seasonsList = [...tempList1, ...tempList2];
}
}
@ -86,6 +100,8 @@ class MamberMeta {
this.ptime,
this.seasonId,
this.total,
this.seriesId,
this.category,
});
String? cover;
@ -95,6 +111,8 @@ class MamberMeta {
int? ptime;
int? seasonId;
int? total;
int? seriesId;
int? category;
MamberMeta.fromJson(Map<String, dynamic> json) {
cover = json['cover'];
@ -104,5 +122,7 @@ class MamberMeta {
ptime = json['ptime'];
seasonId = json['season_id'];
total = json['total'];
seriesId = json['series_id'];
category = json['category'];
}
}

View File

@ -23,6 +23,7 @@ class HotVideoItemModel {
this.dimension,
this.shortLinkV2,
this.firstFrame,
this.cover,
this.pubLocation,
this.seasontype,
this.isOgv,
@ -50,6 +51,7 @@ class HotVideoItemModel {
Dimension? dimension;
String? shortLinkV2;
String? firstFrame;
String? cover;
String? pubLocation;
int? seasontype;
bool? isOgv;
@ -77,6 +79,7 @@ class HotVideoItemModel {
dimension = Dimension.fromMap(json['dimension']);
shortLinkV2 = json["short_link_v2"];
firstFrame = json["first_frame"];
cover = json["first_frame"];
pubLocation = json["pub_location"];
seasontype = json["seasontype"];
isOgv = json["isOgv"];

View File

@ -1,19 +1,12 @@
import 'package:hive/hive.dart';
part 'model_owner.g.dart';
@HiveType(typeId: 3)
class Owner {
Owner({
this.mid,
this.name,
this.face,
});
@HiveField(0)
int? mid;
@HiveField(1)
String? name;
@HiveField(2)
String? face;
Owner.fromJson(Map<String, dynamic> json) {

View File

@ -1,47 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'model_owner.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class OwnerAdapter extends TypeAdapter<Owner> {
@override
final int typeId = 3;
@override
Owner read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return Owner(
mid: fields[0] as int?,
name: fields[1] as String?,
face: fields[2] as String?,
);
}
@override
void write(BinaryWriter writer, Owner obj) {
writer
..writeByte(3)
..writeByte(0)
..write(obj.mid)
..writeByte(1)
..write(obj.name)
..writeByte(2)
..write(obj.face);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is OwnerAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -1,11 +1,5 @@
import 'package:pilipala/utils/utils.dart';
import './model_owner.dart';
import 'package:hive/hive.dart';
part 'model_rec_video_item.g.dart';
@HiveType(typeId: 0)
class RecVideoItemModel {
RecVideoItemModel({
this.id,
@ -23,32 +17,19 @@ class RecVideoItemModel {
this.rcmdReason,
});
@HiveField(0)
int? id = -1;
@HiveField(1)
String? bvid = '';
@HiveField(2)
int? cid = -1;
@HiveField(3)
String? goto = '';
@HiveField(4)
String? uri = '';
@HiveField(5)
String? pic = '';
@HiveField(6)
String? title = '';
@HiveField(7)
String? duration = '';
@HiveField(8)
int? duration = -1;
int? pubdate = -1;
@HiveField(9)
Owner? owner;
@HiveField(10)
Stat? stat;
@HiveField(11)
int? isFollowed;
@HiveField(12)
RcmdReason? rcmdReason;
String? rcmdReason;
RecVideoItemModel.fromJson(Map<String, dynamic> json) {
id = json["id"];
@ -58,51 +39,29 @@ class RecVideoItemModel {
uri = json["uri"];
pic = json["pic"];
title = json["title"];
duration = Utils.tampToSeektime(json["duration"]);
duration = json["duration"];
pubdate = json["pubdate"];
owner = Owner.fromJson(json["owner"]);
stat = Stat.fromJson(json["stat"]);
isFollowed = json["is_followed"] ?? 0;
rcmdReason = json["rcmd_reason"] != null
? RcmdReason.fromJson(json["rcmd_reason"])
: RcmdReason(content: '');
rcmdReason = json["rcmd_reason"]?['content'];
}
}
@HiveType(typeId: 1)
class Stat {
Stat({
this.view,
this.like,
this.danmu,
});
@HiveField(0)
int? view;
@HiveField(1)
int? like;
@HiveField(2)
int? danmu;
int? view;
int? like;
int? danmu;
Stat.fromJson(Map<String, dynamic> json) {
// 无需在model中转换以保留原始数据在view层处理即可
view = json["view"];
like = json["like"];
danmu = json['danmaku'];
}
}
@HiveType(typeId: 2)
class RcmdReason {
RcmdReason({
this.reasonType,
this.content,
});
@HiveField(0)
int? reasonType;
@HiveField(1)
String? content = '';
RcmdReason.fromJson(Map<String, dynamic> json) {
reasonType = json["reason_type"];
content = json["content"] ?? '';
}
}

View File

@ -1,154 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'model_rec_video_item.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class RecVideoItemModelAdapter extends TypeAdapter<RecVideoItemModel> {
@override
final int typeId = 0;
@override
RecVideoItemModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return RecVideoItemModel(
id: fields[0] as int?,
bvid: fields[1] as String?,
cid: fields[2] as int?,
goto: fields[3] as String?,
uri: fields[4] as String?,
pic: fields[5] as String?,
title: fields[6] as String?,
duration: fields[7] as String?,
pubdate: fields[8] as int?,
owner: fields[9] as Owner?,
stat: fields[10] as Stat?,
isFollowed: fields[11] as int?,
rcmdReason: fields[12] as RcmdReason?,
);
}
@override
void write(BinaryWriter writer, RecVideoItemModel obj) {
writer
..writeByte(13)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.bvid)
..writeByte(2)
..write(obj.cid)
..writeByte(3)
..write(obj.goto)
..writeByte(4)
..write(obj.uri)
..writeByte(5)
..write(obj.pic)
..writeByte(6)
..write(obj.title)
..writeByte(7)
..write(obj.duration)
..writeByte(8)
..write(obj.pubdate)
..writeByte(9)
..write(obj.owner)
..writeByte(10)
..write(obj.stat)
..writeByte(11)
..write(obj.isFollowed)
..writeByte(12)
..write(obj.rcmdReason);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RecVideoItemModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class StatAdapter extends TypeAdapter<Stat> {
@override
final int typeId = 1;
@override
Stat read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return Stat(
view: fields[0] as int?,
like: fields[1] as int?,
danmu: fields[2] as int?,
);
}
@override
void write(BinaryWriter writer, Stat obj) {
writer
..writeByte(3)
..writeByte(0)
..write(obj.view)
..writeByte(1)
..write(obj.like)
..writeByte(2)
..write(obj.danmu);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is StatAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class RcmdReasonAdapter extends TypeAdapter<RcmdReason> {
@override
final int typeId = 2;
@override
RcmdReason read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return RcmdReason(
reasonType: fields[0] as int?,
content: fields[1] as String?,
);
}
@override
void write(BinaryWriter writer, RcmdReason obj) {
writer
..writeByte(2)
..writeByte(0)
..write(obj.reasonType)
..writeByte(1)
..write(obj.content);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RcmdReasonAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

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