Compare commits

..

1 Commits

Author SHA1 Message Date
c1008e0162 fix: 垂直布局视频卡片样式 2024-02-17 22:07:48 +08:00
154 changed files with 2636 additions and 6909 deletions

View File

@ -1,208 +0,0 @@
name: Pilipala Beta
on:
workflow_dispatch:
push:
branches:
- "x-main"
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.16.5
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 }}](${{ github.event.head_commit.url }})"

View File

@ -2,6 +2,8 @@
<img width="200" height="200" src="https://github.com/guozhigq/pilipala/blob/main/assets/images/logo/logo_android.png"> <img width="200" height="200" src="https://github.com/guozhigq/pilipala/blob/main/assets/images/logo/logo_android.png">
</div> </div>
<div align="center"> <div align="center">
<h1>PiliPala</h1> <h1>PiliPala</h1>
<div align="center"> <div align="center">
@ -9,9 +11,8 @@
![GitHub repo size](https://img.shields.io/github/repo-size/guozhigq/pilipala) ![GitHub repo size](https://img.shields.io/github/repo-size/guozhigq/pilipala)
![GitHub Repo stars](https://img.shields.io/github/stars/guozhigq/pilipala) ![GitHub Repo stars](https://img.shields.io/github/stars/guozhigq/pilipala)
![GitHub all releases](https://img.shields.io/github/downloads/guozhigq/pilipala/total) ![GitHub all releases](https://img.shields.io/github/downloads/guozhigq/pilipala/total)
</div> </div>
<p>使用 Flutter 开发的 BiliBili 第三方客户端</p> <p>使用Flutter开发的BiliBili第三方客户端</p>
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/screenshots/510shots_so.png" width="32%" alt="home" /> <img src="https://github.com/guozhigq/pilipala/blob/main/assets/screenshots/510shots_so.png" width="32%" alt="home" />
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/screenshots/174shots_so.png" width="32%" alt="home" /> <img src="https://github.com/guozhigq/pilipala/blob/main/assets/screenshots/174shots_so.png" width="32%" alt="home" />
@ -22,41 +23,46 @@
</div> </div>
## 开发环境 ## 开发环境
Xcode 13.4 不支持**auto_orientation**,请注释相关代码
Xcode 13.4 不支持 ```auto_orientation```,请注释相关代码
```bash ```bash
[✓] Flutter (Channel stable, 3.16.5, on macOS 14.1.2 23B92 darwin-arm64, locale [] Flutter (Channel stable, 3.16.4, on macOS 14.1.2 23B92 darwin-arm64, locale
zh-Hans-CN) zh-Hans-CN)
[] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[] Xcode - develop for iOS and macOS (Xcode 15.1) [] Xcode - develop for iOS and macOS (Xcode 15.1)
[] Chrome - develop for the web [] Chrome - develop for the web
[] Android Studio (version 2022.3) [] Android Studio (version 2022.3)
[✓] VS Code (version 1.87.2) [] VS Code (version 1.85.1)
[] Connected device (3 available) [] Connected device (3 available)
[] Network resources [] Network resources
``` ```
<br/>
## 技术交流 ## 技术交流
Telegram: https://t.me/+lm_oOVmF0RJiODk1 Telegram: https://t.me/+lm_oOVmF0RJiODk1
QQ频道: https://pd.qq.com/s/365esodk3
Telegram Beta 版本:@PiliPala_Beta
QQ 频道: https://pd.qq.com/s/365esodk3 <br/>
## 功能 ## 功能
目前着重移动端 (Android、iOS)暂时没有适配桌面端、Pad 端、手表端等 目前着重移动端(Android、iOS)暂时没有适配桌面端、Pad端、手表端等
<br/>
现有功能及[开发计划](https://github.com/users/guozhigq/projects/5) 现有功能及[开发计划](https://github.com/users/guozhigq/projects/5)
- [x] 推荐视频列表 (app 端)
- [x] 推荐视频列表(app端)
- [x] 最热视频列表 - [x] 最热视频列表
- [x] 热门直播 - [x] 热门直播
- [x] 番剧列表 - [x] 番剧列表
- [x] 屏蔽黑名单内用户视频 - [x] 屏蔽黑名单内用户视频
- [x] 排行榜
- [x] 用户相关 - [x] 用户相关
- [x] 粉丝、关注用户、拉黑用户查看 - [x] 粉丝、关注用户、拉黑用户查看
@ -66,13 +72,11 @@ QQ 频道: https://pd.qq.com/s/365esodk3
- [x] 稍后再看 - [x] 稍后再看
- [x] 观看记录 - [x] 观看记录
- [x] 我的收藏 - [x] 我的收藏
- [x] 黑名单管理
- [x] 动态相关 - [x] 动态相关
- [x] 全部、投稿、番剧分类查看 - [x] 全部、投稿、番剧分类查看
- [x] 动态评论查看 - [x] 动态评论查看
- [x] 动态评论回复功能 - [x] 动态评论回复功能
- [x] 动态未读标记
- [x] 视频播放相关 - [x] 视频播放相关
- [x] 双击快进/快退 - [x] 双击快进/快退
@ -81,13 +85,13 @@ QQ 频道: https://pd.qq.com/s/365esodk3
- [x] 垂直方向上滑全屏、下滑退出全屏 - [x] 垂直方向上滑全屏、下滑退出全屏
- [x] 水平方向手势快进/快退 - [x] 水平方向手势快进/快退
- [x] 全屏方向设置 - [x] 全屏方向设置
- [x] 倍速选择/长按 2 倍速 - [x] 倍速选择/长按2倍速
- [x] 硬件加速 (视机型而定) - [x] 硬件加速视机型而定
- [x] 画质选择 (高清画质未解锁) - [x] 画质选择高清画质未解锁
- [x] 音质选择 (视视频而定) - [x] 音质选择视视频而定
- [x] 解码格式选择 (视视频而定) - [x] 解码格式选择视视频而定
- [x] 弹幕 - [x] 弹幕
- [x] 字幕 - [ ] 字幕
- [x] 记忆播放 - [x] 记忆播放
- [x] 视频比例:高度/宽度适应、填充、包含等 - [x] 视频比例:高度/宽度适应、填充、包含等
@ -99,12 +103,12 @@ QQ 频道: https://pd.qq.com/s/365esodk3
- [x] 视频搜索排序、按时长筛选 - [x] 视频搜索排序、按时长筛选
- [x] 视频详情页相关 - [x] 视频详情页相关
- [x] 视频选集 (分 p) 切换 - [x] 视频选集(分p)切换
- [x] 点赞、投币、收藏/取消收藏 - [x] 点赞、投币、收藏/取消收藏
- [x] 相关视频查看 - [x] 相关视频查看
- [x] 评论用户身份标识 - [x] 评论用户身份标识
- [x] 评论 (排序) 查看、二楼评论查看 - [x] 评论(排序)查看、二楼评论查看
- [x] 主楼、二楼评论/表情回复功能 - [x] 主楼、二楼评论回复功能
- [x] 评论点赞 - [x] 评论点赞
- [x] 评论笔记图片查看、保存 - [x] 评论笔记图片查看、保存
@ -112,30 +116,29 @@ QQ 频道: https://pd.qq.com/s/365esodk3
- [x] 画质、音质、解码方式预设 - [x] 画质、音质、解码方式预设
- [x] 图片质量设定 - [x] 图片质量设定
- [x] 主题模式:亮色/暗色/跟随系统 - [x] 主题模式:亮色/暗色/跟随系统
- [x] 震动反馈 (可选) - [x] 震动反馈(可选)
- [x] 高帧率 - [x] 高帧率
- [x] 自动全屏 - [x] 自动全屏
- [ ] 等等 - [ ] 等等
<br/>
## 下载 ## 下载
可以通过右侧 Releases 进行下载或拉取代码到本地进行编译 可以通过右侧release进行下载或拉取代码到本地进行编译
### 从 F-Droid 安装 <br/>
<a href="https://f-droid.org/packages/com.guozhigq.pilipala">
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on-zh-cn.png"
alt="Get it on F-Droid"
height="80">
</a>
## 声明 ## 声明
此项目 (PiliPala) 是个人为了兴趣而开发, 仅用于学习和测试。 此项目PiliPala是个人为了兴趣而开发, 仅用于学习和测试。
所用 API 皆从官方网站收集, 不提供任何破解内容。 所用API皆从官方网站收集, 不提供任何破解内容。
感谢使用 感谢使用
<br/>
## 致谢 ## 致谢
- [bilibili-API-collect](https://github.com/SocialSisterYi/bilibili-API-collect) - [bilibili-API-collect](https://github.com/SocialSisterYi/bilibili-API-collect)
@ -143,3 +146,7 @@ QQ 频道: https://pd.qq.com/s/365esodk3
- [media-kit](https://github.com/media-kit/media-kit) - [media-kit](https://github.com/media-kit/media-kit)
- [dio](https://pub.dev/packages/dio) - [dio](https://pub.dev/packages/dio)
- 等等 - 等等
<br/>
<br/>
<br/>

View File

@ -223,10 +223,6 @@
android:pathPattern="/mobile/video/.*" /> android:pathPattern="/mobile/video/.*" />
<data android:scheme="https" android:host="www.bilibili.com" <data android:scheme="https" android:host="www.bilibili.com"
android:pathPattern="/mobile/video/.*" /> 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> </intent-filter>
</activity> </activity>

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

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

View File

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

View File

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

View File

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

View File

@ -1,25 +0,0 @@
## 1.0.24
该版本代码未开源,酌情使用。
### 功能
+ 私信功能
+ 回复我的、收到的赞查看
+ 新的登录方式
+ 全屏选集
+ 一键三连
+ 按分区搜索
### 优化
+ 页面跳转动画
+ 评论区跳转
### 修复
+ 音画不同步问题
+ 分集字幕未同步
+ 多语言字幕
+ 弹幕设置未生效
更多更新日志可在Github上查看
问题反馈、功能建议请查看「关于」页面。

View File

@ -9,7 +9,6 @@ PODS:
- Flutter - Flutter
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
- Flutter - Flutter
- FlutterMacOS
- ReachabilitySwift - ReachabilitySwift
- device_info_plus (0.0.1): - device_info_plus (0.0.1):
- Flutter - Flutter
@ -39,7 +38,7 @@ PODS:
- path_provider_foundation (0.0.1): - path_provider_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- permission_handler_apple (9.3.0): - permission_handler_apple (9.1.1):
- Flutter - Flutter
- ReachabilitySwift (5.0.0) - ReachabilitySwift (5.0.0)
- saver_gallery (0.0.1): - saver_gallery (0.0.1):
@ -72,7 +71,7 @@ DEPENDENCIES:
- audio_service (from `.symlinks/plugins/audio_service/ios`) - audio_service (from `.symlinks/plugins/audio_service/ios`)
- audio_session (from `.symlinks/plugins/audio_session/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`)
- auto_orientation (from `.symlinks/plugins/auto_orientation/ios`) - auto_orientation (from `.symlinks/plugins/auto_orientation/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`) - flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`)
@ -114,7 +113,7 @@ EXTERNAL SOURCES:
auto_orientation: auto_orientation:
:path: ".symlinks/plugins/auto_orientation/ios" :path: ".symlinks/plugins/auto_orientation/ios"
connectivity_plus: connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/darwin" :path: ".symlinks/plugins/connectivity_plus/ios"
device_info_plus: device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios" :path: ".symlinks/plugins/device_info_plus/ios"
Flutter: Flutter:
@ -167,7 +166,7 @@ SPEC CHECKSUMS:
audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_service: f509d65da41b9521a61f1c404dd58651f265a567
audio_session: 4f3e461722055d21515cf3261b64c973c062f345 audio_session: 4f3e461722055d21515cf3261b64c973c062f345
auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d
connectivity_plus: e2dad488011aeb593e219360e804c43cc1af5770 connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
@ -181,7 +180,7 @@ SPEC CHECKSUMS:
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
@ -194,7 +193,7 @@ SPEC CHECKSUMS:
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7 webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7
webview_flutter_wkwebview: 4f3e50f7273d31e5500066ed267e3ae4309c5ae4 webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
PODFILE CHECKSUM: 637cd290bed23275b5f5ffcc7eb1e73d0a5fb2be PODFILE CHECKSUM: 637cd290bed23275b5f5ffcc7eb1e73d0a5fb2be

View File

@ -2,7 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/utils/extension.dart'; import 'package:pilipala/utils/extension.dart';
import 'package:pilipala/utils/global_data.dart';
import '../../utils/storage.dart'; import '../../utils/storage.dart';
import '../constants.dart'; import '../constants.dart';
@ -33,10 +32,8 @@ class NetworkImgLayer extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final int defaultImgQuality = GlobalData().imgQuality;
final String imageUrl = final String imageUrl =
'${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp'; '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? 100}q.webp';
print(imageUrl);
int? memCacheWidth, memCacheHeight; int? memCacheWidth, memCacheHeight;
double aspectRatio = (width / height).toDouble(); double aspectRatio = (width / height).toDouble();
@ -84,7 +81,7 @@ class NetworkImgLayer extends StatelessWidget {
fadeOutDuration ?? const Duration(milliseconds: 120), fadeOutDuration ?? const Duration(milliseconds: 120),
fadeInDuration: fadeInDuration:
fadeInDuration ?? const Duration(milliseconds: 120), fadeInDuration ?? const Duration(milliseconds: 120),
filterQuality: FilterQuality.low, filterQuality: FilterQuality.high,
errorWidget: (BuildContext context, String url, Object error) => errorWidget: (BuildContext context, String url, Object error) =>
placeholder(context), placeholder(context),
placeholder: (BuildContext context, String url) => placeholder: (BuildContext context, String url) =>
@ -107,9 +104,7 @@ class NetworkImgLayer extends StatelessWidget {
? 0 ? 0
: StyleString.imgRadius.x), : StyleString.imgRadius.x),
), ),
child: type == 'bg' child: Center(
? const SizedBox()
: Center(
child: Image.asset( child: Image.asset(
type == 'avatar' type == 'avatar'
? 'assets/images/noface.jpeg' ? 'assets/images/noface.jpeg'

View File

@ -38,10 +38,6 @@ class VideoCardH extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final int aid = videoItem.aid; final int aid = videoItem.aid;
final String bvid = videoItem.bvid; final String bvid = videoItem.bvid;
String type = 'video';
try {
type = videoItem.type;
} catch (_) {}
final String heroTag = Utils.makeHeroTag(aid); final String heroTag = Utils.makeHeroTag(aid);
return GestureDetector( return GestureDetector(
onLongPress: () { onLongPress: () {
@ -57,10 +53,6 @@ class VideoCardH extends StatelessWidget {
child: InkWell( child: InkWell(
onTap: () async { onTap: () async {
try { try {
if (type == 'ketang') {
SmartDialog.showToast('课堂视频暂不支持播放');
return;
}
final int cid = final int cid =
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid); videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
Get.toNamed('/video?bvid=$bvid&cid=$cid', Get.toNamed('/video?bvid=$bvid&cid=$cid',
@ -103,20 +95,12 @@ class VideoCardH extends StatelessWidget {
height: maxHeight, height: maxHeight,
), ),
), ),
if (videoItem.duration != 0)
PBadge( PBadge(
text: Utils.timeFormat(videoItem.duration!), text: Utils.timeFormat(videoItem.duration!),
right: 6.0, right: 6.0,
bottom: 6.0, bottom: 6.0,
type: 'gray', type: 'gray',
), ),
if (type != 'video')
PBadge(
text: type,
left: 6.0,
bottom: 6.0,
type: 'primary',
),
// if (videoItem.rcmdReason != null && // if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '') // videoItem.rcmdReason.content != '')
// pBadge(videoItem.rcmdReason.content, context, // pBadge(videoItem.rcmdReason.content, context,

View File

@ -229,10 +229,7 @@ class VideoContent extends StatelessWidget {
), ),
if (crossAxisCount > 1) ...[ if (crossAxisCount > 1) ...[
const SizedBox(height: 2), const SizedBox(height: 2),
VideoStat( VideoStat(videoItem: videoItem, crossAxisCount: crossAxisCount),
videoItem: videoItem,
crossAxisCount: crossAxisCount,
),
], ],
if (crossAxisCount == 1) const SizedBox(height: 4), if (crossAxisCount == 1) const SizedBox(height: 4),
Row( Row(
@ -293,11 +290,14 @@ class VideoContent extends StatelessWidget {
color: Theme.of(context).colorScheme.outline, color: Theme.of(context).colorScheme.outline,
), ),
), ),
VideoStat( Expanded(
flex: 1,
child: VideoStat(
videoItem: videoItem, videoItem: videoItem,
crossAxisCount: crossAxisCount, crossAxisCount: crossAxisCount,
), ),
const Spacer(), ),
// const Spacer(),
], ],
if (videoItem.goto == 'av' && crossAxisCount != 1) ...[ if (videoItem.goto == 'av' && crossAxisCount != 1) ...[
VideoPopupMenu( VideoPopupMenu(

View File

@ -477,35 +477,4 @@ class Api {
/// 获取未读动态数 /// 获取未读动态数
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance'; 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';
/// 我的订阅详情
static const userSubFolderDetail = '/x/space/fav/season/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';
} }

View File

@ -1,9 +1,7 @@
// ignore_for_file: avoid_print // ignore_for_file: avoid_print
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'dart:math' show Random;
import 'package:cookie_jar/cookie_jar.dart'; import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio/io.dart'; import 'package:dio/io.dart';
@ -13,7 +11,6 @@ import 'package:hive/hive.dart';
import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/id_utils.dart';
import '../utils/storage.dart'; import '../utils/storage.dart';
import '../utils/utils.dart'; import '../utils/utils.dart';
import 'api.dart';
import 'constants.dart'; import 'constants.dart';
import 'interceptor.dart'; import 'interceptor.dart';
@ -27,7 +24,6 @@ class Request {
late bool enableSystemProxy; late bool enableSystemProxy;
late String systemProxyHost; late String systemProxyHost;
late String systemProxyPort; late String systemProxyPort;
static final RegExp spmPrefixExp = RegExp(r'<meta name="spm_prefix" content="([^"]+?)">');
/// 设置cookie /// 设置cookie
static setCookie() async { static setCookie() async {
@ -55,12 +51,13 @@ class Request {
} }
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null); setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
if (cookie.isEmpty) {
try { try {
await buvidActivate(); await Request().get(HttpString.baseUrl);
} catch (e) { } catch (e) {
log("setCookie, ${e.toString()}"); log("setCookie, ${e.toString()}");
} }
}
final String cookieString = cookie final String cookieString = cookie
.map((Cookie cookie) => '${cookie.name}=${cookie.value}') .map((Cookie cookie) => '${cookie.name}=${cookie.value}')
.join('; '); .join('; ');
@ -90,33 +87,6 @@ class Request {
dio.options.headers['referer'] = 'https://www.bilibili.com/'; 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 * config it and create
*/ */

View File

@ -45,13 +45,10 @@ class ApiInterceptor extends Interceptor {
void onError(DioException err, ErrorInterceptorHandler handler) async { void onError(DioException err, ErrorInterceptorHandler handler) async {
// 处理网络请求错误 // 处理网络请求错误
// handler.next(err); // handler.next(err);
String url = err.requestOptions.uri.toString();
if (!url.contains('heartBeat')) {
SmartDialog.showToast( SmartDialog.showToast(
await dioError(err), await dioError(err),
displayType: SmartToastType.onlyRefresh, displayType: SmartToastType.onlyRefresh,
); );
}
super.onError(err, handler); super.onError(err, handler);
} }
@ -78,23 +75,22 @@ class ApiInterceptor extends Interceptor {
} }
static Future<String> checkConnect() async { static Future<String> checkConnect() async {
final List<ConnectivityResult> connectivityResult = final ConnectivityResult connectivityResult =
await Connectivity().checkConnectivity(); await Connectivity().checkConnectivity();
if (connectivityResult.contains(ConnectivityResult.mobile)) { switch (connectivityResult) {
case ConnectivityResult.mobile:
return '正在使用移动流量'; return '正在使用移动流量';
} else if (connectivityResult.contains(ConnectivityResult.wifi)) { case ConnectivityResult.wifi:
return '正在使用wifi'; return '正在使用wifi';
} else if (connectivityResult.contains(ConnectivityResult.ethernet)) { case ConnectivityResult.ethernet:
return '正在使用局域网'; return '正在使用局域网';
} else if (connectivityResult.contains(ConnectivityResult.vpn)) { case ConnectivityResult.vpn:
return '正在使用代理网络'; return '正在使用代理网络';
} else if (connectivityResult.contains(ConnectivityResult.bluetooth)) { case ConnectivityResult.other:
return '正在使用蓝牙网络';
} else if (connectivityResult.contains(ConnectivityResult.other)) {
return '正在使用其他网络'; return '正在使用其他网络';
} else if (connectivityResult.contains(ConnectivityResult.none)) { case ConnectivityResult.none:
return '未连接到任何网络'; return '未连接到任何网络';
} else { default:
return ''; return '';
} }
} }

View File

@ -79,8 +79,6 @@ class MemberHttp {
String order = 'pubdate', String order = 'pubdate',
bool orderAvoided = true, bool orderAvoided = true,
}) async { }) async {
String dmImgStr = Utils.base64EncodeRandomString(16, 64);
String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128);
Map params = await WbiSign().makSign({ Map params = await WbiSign().makSign({
'mid': mid, 'mid': mid,
'ps': ps, 'ps': ps,
@ -90,11 +88,7 @@ class MemberHttp {
'order': order, 'order': order,
'platform': 'web', 'platform': 'web',
'web_location': 1550101, '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]}',
}); });
var res = await Request().get( var res = await Request().get(
Api.memberArchive, Api.memberArchive,
@ -107,13 +101,10 @@ class MemberHttp {
'data': MemberArchiveDataModel.fromJson(res.data['data']) 'data': MemberArchiveDataModel.fromJson(res.data['data'])
}; };
} else { } else {
Map errMap = {
-352: '风控校验失败,请检查登录状态',
};
return { return {
'status': false, 'status': false,
'data': [], 'data': [],
'msg': errMap[res.data['code']] ?? res.data['message'], 'msg': res.data['message'],
}; };
} }
} }
@ -132,13 +123,10 @@ class MemberHttp {
'data': DynamicsDataModel.fromJson(res.data['data']), 'data': DynamicsDataModel.fromJson(res.data['data']),
}; };
} else { } else {
Map errMap = {
-352: '风控校验失败,请检查登录状态',
};
return { return {
'status': false, 'status': false,
'data': [], 'data': [],
'msg': errMap[res.data['code']] ?? res.data['message'], 'msg': res.data['message'],
}; };
} }
} }

View File

@ -1,4 +1,3 @@
import 'dart:math';
import '../models/msg/account.dart'; import '../models/msg/account.dart';
import '../models/msg/session.dart'; import '../models/msg/session.dart';
import '../utils/wbi_sign.dart'; import '../utils/wbi_sign.dart';
@ -23,22 +22,14 @@ class MsgHttp {
Map signParams = await WbiSign().makSign(params); Map signParams = await WbiSign().makSign(params);
var res = await Request().get(Api.sessionList, data: signParams); var res = await Request().get(Api.sessionList, data: signParams);
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
try {
return { return {
'status': true, 'status': true,
'data': SessionDataModel.fromJson(res.data['data']), 'data': SessionDataModel.fromJson(res.data['data']),
}; };
} catch (err) {
return {
'status': false,
'data': [],
'msg': err.toString(),
};
}
} else { } else {
return { return {
'status': false, 'status': false,
'data': [], 'date': [],
'msg': res.data['message'], 'msg': res.data['message'],
}; };
} }
@ -51,16 +42,12 @@ class MsgHttp {
'mobi_app': 'web', 'mobi_app': 'web',
}); });
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
try {
return { return {
'status': true, 'status': true,
'data': res.data['data'] 'data': res.data['data']
.map<AccountListModel>((e) => AccountListModel.fromJson(e)) .map<AccountListModel>((e) => AccountListModel.fromJson(e))
.toList(), .toList(),
}; };
} catch (err) {
print('err🔟: $err');
}
} else { } else {
return { return {
'status': false, 'status': false,
@ -99,125 +86,4 @@ 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': "message: ${res.data['message']},"
" msg: ${res.data['msg']},"
" code: ${res.data['code']}",
};
}
}
// 发送私信
static Future sendMsg({
int? senderUid,
int? receiverId,
int? receiverType,
int? msgType,
dynamic content,
}) async {
String csrf = await Request.getCsrf();
Map<String, dynamic> params = await WbiSign().makSign({
'msg[sender_uid]': senderUid,
'msg[receiver_id]': receiverId,
'msg[receiver_type]': receiverType ?? 1,
'msg[msg_type]': msgType ?? 1,
'msg[msg_status]': 0,
'msg[dev_id]': getDevId(),
'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000,
'msg[new_face_version]': 0,
'msg[content]': content,
'from_firework': 0,
'build': 0,
'mobi_app': 'web',
'csrf_token': csrf,
'csrf': csrf,
});
var res =
await Request().post(Api.sendMsg, queryParameters: <String, dynamic>{
...params,
'csrf_token': csrf,
'csrf': csrf,
}, data: {
'w_sender_uid': params['msg[sender_uid]'],
'w_receiver_id': params['msg[receiver_id]'],
'w_dev_id': params['msg[dev_id]'],
'w_rid': params['w_rid'],
'wts': params['wts'],
'csrf_token': csrf,
'csrf': csrf,
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'date': [],
'msg': "message: ${res.data['message']},"
" msg: ${res.data['msg']},"
" code: ${res.data['code']}",
};
}
}
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();
}
} }

View File

@ -1,5 +1,4 @@
import '../models/video/reply/data.dart'; import '../models/video/reply/data.dart';
import '../models/video/reply/emote.dart';
import 'api.dart'; import 'api.dart';
import 'init.dart'; import 'init.dart';
@ -101,23 +100,4 @@ 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'],
};
}
}
} }

View File

@ -6,8 +6,6 @@ import '../models/user/fav_folder.dart';
import '../models/user/history.dart'; import '../models/user/history.dart';
import '../models/user/info.dart'; import '../models/user/info.dart';
import '../models/user/stat.dart'; import '../models/user/stat.dart';
import '../models/user/sub_detail.dart';
import '../models/user/sub_folder.dart';
import 'api.dart'; import 'api.dart';
import 'init.dart'; import 'init.dart';
@ -307,63 +305,4 @@ class UserHttp {
return {'status': false, 'msg': res.data['message']}; 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, 'msg': res.data['message']};
}
}
static Future userSubFolderDetail({
required int seasonId,
required int pn,
required int ps,
}) async {
var res = await Request().get(Api.userSubFolderDetail, 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 cancelSub({required int seasonId}) async {
var res = await Request().post(
Api.cancelSub,
queryParameters: {
'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']};
}
}
} }

View File

@ -8,11 +8,9 @@ import '../models/model_rec_video_item.dart';
import '../models/user/fav_folder.dart'; import '../models/user/fav_folder.dart';
import '../models/video/ai.dart'; import '../models/video/ai.dart';
import '../models/video/play/url.dart'; import '../models/video/play/url.dart';
import '../models/video/subTitile/result.dart';
import '../models/video_detail_res.dart'; import '../models/video_detail_res.dart';
import '../utils/recommend_filter.dart'; import '../utils/recommend_filter.dart';
import '../utils/storage.dart'; import '../utils/storage.dart';
import '../utils/subtitle.dart';
import '../utils/wbi_sign.dart'; import '../utils/wbi_sign.dart';
import 'api.dart'; import 'api.dart';
import 'init.dart'; import 'init.dart';
@ -477,54 +475,4 @@ class VideoHttp {
return {'status': false, 'data': []}; 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) {
print(err);
}
}
// 视频排行
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 = SubTitleUtils.convertToWebVTT(res.data['body']);
final List body = res.data['body'];
return {'content': content, 'body': body};
}
} }

View File

@ -16,7 +16,6 @@ import 'package:pilipala/pages/search/index.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/router/app_pages.dart'; import 'package:pilipala/router/app_pages.dart';
import 'package:pilipala/pages/main/view.dart'; import 'package:pilipala/pages/main/view.dart';
import 'package:pilipala/services/disable_battery_opt.dart';
import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/services/service_locator.dart';
import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/data.dart'; import 'package:pilipala/utils/data.dart';
@ -34,7 +33,6 @@ void main() async {
.then((_) async { .then((_) async {
await GStrorage.init(); await GStrorage.init();
await setupServiceLocator(); await setupServiceLocator();
clearLogs();
Request(); Request();
await Request.setCookie(); await Request.setCookie();
RecommendFilter(); RecommendFilter();
@ -73,7 +71,6 @@ void main() async {
)); ));
Data.init(); Data.init();
PiliSchame.init(); PiliSchame.init();
DisableBatteryOpt();
}); });
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,240 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.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,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 0),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '国创相关',
'type': RandType.creation,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 168),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '动画',
'type': RandType.animation,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 1),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '音乐',
'type': RandType.music,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 3),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '舞蹈',
'type': RandType.dance,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 129),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '游戏',
'type': RandType.game,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 4),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '知识',
'type': RandType.knowledge,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 36),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '科技',
'type': RandType.technology,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 188),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '运动',
'type': RandType.sport,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 234),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '汽车',
'type': RandType.car,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 223),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '生活',
'type': RandType.life,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 160),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '美食',
'type': RandType.food,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 211),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '动物圈',
'type': RandType.animal,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 217),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '鬼畜',
'type': RandType.madness,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 119),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '时尚',
'type': RandType.fashion,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 155),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '娱乐',
'type': RandType.entertainment,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 5),
},
{
'icon': const Icon(
Icons.live_tv_outlined,
size: 15,
),
'label': '影视',
'type': RandType.film,
'ctr': Get.put<ZoneController>,
'page': const ZonePage(rid: 181),
}
];

View File

@ -1,47 +0,0 @@
enum SubtitleType {
// 中文(中国)
zhCN,
// 中文(自动翻译)
aizh,
// 英语(自动生成)
aien,
}
extension SubtitleTypeExtension on SubtitleType {
String get description {
switch (this) {
case SubtitleType.zhCN:
return '中文(中国)';
case SubtitleType.aizh:
return '中文(自动翻译)';
case SubtitleType.aien:
return '英语(自动生成)';
}
}
}
extension SubtitleIdExtension on SubtitleType {
String get id {
switch (this) {
case SubtitleType.zhCN:
return 'zh-CN';
case SubtitleType.aizh:
return 'ai-zh';
case SubtitleType.aien:
return 'ai-en';
}
}
}
extension SubtitleCodeExtension on SubtitleType {
int get code {
switch (this) {
case SubtitleType.zhCN:
return 1;
case SubtitleType.aizh:
return 2;
case SubtitleType.aien:
return 3;
}
}
}

View File

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

@ -1,43 +0,0 @@
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

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

View File

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

View File

@ -8,7 +8,7 @@ class SessionDataModel {
this.hasMore, this.hasMore,
}); });
List<SessionList>? sessionList; List? sessionList;
int? hasMore; int? hasMore;
SessionDataModel.fromJson(Map<String, dynamic> json) { SessionDataModel.fromJson(Map<String, dynamic> json) {
@ -121,37 +121,35 @@ class LastMsg {
this.msgKey, this.msgKey,
this.msgStatus, this.msgStatus,
this.notifyCode, this.notifyCode,
// this.newFaceVersion, this.newFaceVersion,
}); });
int? senderIid; int? senderIid;
int? receiverType; int? receiverType;
int? receiverId; int? receiverId;
int? msgType; int? msgType;
dynamic content; Map? content;
int? msgSeqno; int? msgSeqno;
int? timestamp; int? timestamp;
String? atUids; String? atUids;
int? msgKey; int? msgKey;
int? msgStatus; int? msgStatus;
String? notifyCode; String? notifyCode;
// int? newFaceVersion; int? newFaceVersion;
LastMsg.fromJson(Map<String, dynamic> json) { LastMsg.fromJson(Map<String, dynamic> json) {
senderIid = json['sender_uid']; senderIid = json['sender_uid'];
receiverType = json['receiver_type']; receiverType = json['receiver_type'];
receiverId = json['receiver_id']; receiverId = json['receiver_id'];
msgType = json['msg_type']; msgType = json['msg_type'];
content = json['content'] != null && json['content'] != '' content = jsonDecode(json['content']);
? jsonDecode(json['content'])
: '';
msgSeqno = json['msg_seqno']; msgSeqno = json['msg_seqno'];
timestamp = json['timestamp']; timestamp = json['timestamp'];
atUids = json['at_uids']; atUids = json['at_uids'];
msgKey = json['msg_key']; msgKey = json['msg_key'];
msgStatus = json['msg_status']; msgStatus = json['msg_status'];
notifyCode = json['notify_code']; notifyCode = json['notify_code'];
// newFaceVersion = json['new_face_version']; newFaceVersion = json['new_face_version'];
} }
} }
@ -216,9 +214,7 @@ class MessageItem {
receiverId = json['receiver_id']; receiverId = json['receiver_id'];
// 1 文本 2 图片 18 系统提示 10 系统通知 5 撤回的消息 // 1 文本 2 图片 18 系统提示 10 系统通知 5 撤回的消息
msgType = json['msg_type']; msgType = json['msg_type'];
content = json['content'] != null && json['content'] != '' content = jsonDecode(json['content']);
? jsonDecode(json['content'])
: '';
msgSeqno = json['msg_seqno']; msgSeqno = json['msg_seqno'];
timestamp = json['timestamp']; timestamp = json['timestamp'];
atUids = json['at_uids']; atUids = json['at_uids'];

View File

@ -85,9 +85,7 @@ class SearchVideoItemModel {
// title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); // title = json['title'].replaceAll(RegExp(r'<.*?>'), '');
title = Em.regTitle(json['title']); title = Em.regTitle(json['title']);
description = json['description']; description = json['description'];
pic = json['pic'] != null && json['pic'].startsWith('//') pic = 'https:${json['pic']}';
? 'https:${json['pic']}'
: json['pic'] ?? '';
videoReview = json['video_review']; videoReview = json['video_review'];
pubdate = json['pubdate']; pubdate = json['pubdate'];
senddate = json['senddate']; senddate = json['senddate'];
@ -437,8 +435,7 @@ class SearchArticleItemModel {
pubTime = json['pub_time']; pubTime = json['pub_time'];
like = json['like']; like = json['like'];
title = Em.regTitle(json['title']); title = Em.regTitle(json['title']);
subTitle = subTitle = json['title'].replaceAll(RegExp(r'<[^>]*>'), '');
Em.decodeHtmlEntities(json['title'].replaceAll(RegExp(r'<[^>]*>'), ''));
rankOffset = json['rank_offset']; rankOffset = json['rank_offset'];
mid = json['mid']; mid = json['mid'];
imageUrls = json['image_urls']; imageUrls = json['image_urls'];

View File

@ -15,7 +15,7 @@ class FavFolderData {
? json['list'] ? json['list']
.map<FavFolderItemData>((e) => FavFolderItemData.fromJson(e)) .map<FavFolderItemData>((e) => FavFolderItemData.fromJson(e))
.toList() .toList()
: <FavFolderItemData>[]; : [FavFolderItemData()];
hasMore = json['has_more']; hasMore = json['has_more'];
} }
} }

View File

@ -1,123 +0,0 @@
class SubDetailModelData {
DetailInfo? info;
List<SubDetailMediaItem>? medias;
SubDetailModelData({this.info, this.medias});
SubDetailModelData.fromJson(Map<String, dynamic> json) {
info = DetailInfo.fromJson(json['info']);
if (json['medias'] != null) {
medias = <SubDetailMediaItem>[];
json['medias'].forEach((v) {
medias!.add(SubDetailMediaItem.fromJson(v));
});
}
}
}
class SubDetailMediaItem {
int? id;
String? title;
String? cover;
String? pic;
int? duration;
int? pubtime;
String? bvid;
Map? upper;
Map? cntInfo;
int? enableVt;
String? vtDisplay;
SubDetailMediaItem({
this.id,
this.title,
this.cover,
this.pic,
this.duration,
this.pubtime,
this.bvid,
this.upper,
this.cntInfo,
this.enableVt,
this.vtDisplay,
});
SubDetailMediaItem.fromJson(Map<String, dynamic> json) {
id = json['id'];
title = json['title'];
cover = json['cover'];
pic = json['cover'];
duration = json['duration'];
pubtime = json['pubtime'];
bvid = json['bvid'];
upper = json['upper'];
cntInfo = json['cnt_info'];
enableVt = json['enable_vt'];
vtDisplay = json['vt_display'];
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['id'] = id;
data['title'] = title;
data['cover'] = cover;
data['duration'] = duration;
data['pubtime'] = pubtime;
data['bvid'] = bvid;
data['upper'] = upper;
data['cnt_info'] = cntInfo;
data['enable_vt'] = enableVt;
data['vt_display'] = vtDisplay;
return data;
}
}
class DetailInfo {
int? id;
int? seasonType;
String? title;
String? cover;
Map? upper;
Map? cntInfo;
int? mediaCount;
String? intro;
int? enableVt;
DetailInfo({
this.id,
this.seasonType,
this.title,
this.cover,
this.upper,
this.cntInfo,
this.mediaCount,
this.intro,
this.enableVt,
});
DetailInfo.fromJson(Map<String, dynamic> json) {
id = json['id'];
seasonType = json['season_type'];
title = json['title'];
cover = json['cover'];
upper = json['upper'];
cntInfo = json['cnt_info'];
mediaCount = json['media_count'];
intro = json['intro'];
enableVt = json['enable_vt'];
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['id'] = id;
data['season_type'] = seasonType;
data['title'] = title;
data['cover'] = cover;
data['upper'] = upper;
data['cnt_info'] = cntInfo;
data['media_count'] = mediaCount;
data['intro'] = intro;
data['enable_vt'] = enableVt;
return data;
}
}

View File

@ -1,111 +0,0 @@
class SubFolderModelData {
final int? count;
final List<SubFolderItemData>? list;
SubFolderModelData({
this.count,
this.list,
});
factory SubFolderModelData.fromJson(Map<String, dynamic> json) {
return SubFolderModelData(
count: json['count'],
list: json['list'] != null
? (json['list'] as List)
.map<SubFolderItemData>((i) => SubFolderItemData.fromJson(i))
.toList()
: null,
);
}
}
class SubFolderItemData {
final int? id;
final int? fid;
final int? mid;
final int? attr;
final String? title;
final String? cover;
final Upper? upper;
final int? coverType;
final String? intro;
final int? ctime;
final int? mtime;
final int? state;
final int? favState;
final int? mediaCount;
final int? viewCount;
final int? vt;
final int? playSwitch;
final int? type;
final String? link;
final String? bvid;
SubFolderItemData({
this.id,
this.fid,
this.mid,
this.attr,
this.title,
this.cover,
this.upper,
this.coverType,
this.intro,
this.ctime,
this.mtime,
this.state,
this.favState,
this.mediaCount,
this.viewCount,
this.vt,
this.playSwitch,
this.type,
this.link,
this.bvid,
});
factory SubFolderItemData.fromJson(Map<String, dynamic> json) {
return SubFolderItemData(
id: json['id'],
fid: json['fid'],
mid: json['mid'],
attr: json['attr'],
title: json['title'],
cover: json['cover'],
upper: json['upper'] != null ? Upper.fromJson(json['upper']) : null,
coverType: json['cover_type'],
intro: json['intro'],
ctime: json['ctime'],
mtime: json['mtime'],
state: json['state'],
favState: json['fav_state'],
mediaCount: json['media_count'],
viewCount: json['view_count'],
vt: json['vt'],
playSwitch: json['play_switch'],
type: json['type'],
link: json['link'],
bvid: json['bvid'],
);
}
}
class Upper {
final int? mid;
final String? name;
final String? face;
Upper({
this.mid,
this.name,
this.face,
});
factory Upper.fromJson(Map<String, dynamic> json) {
return Upper(
mid: json['mid'],
name: json['name'],
face: json['face'],
);
}
}

View File

@ -34,7 +34,6 @@ class PlayUrlModel {
String? seekParam; String? seekParam;
String? seekType; String? seekType;
Dash? dash; Dash? dash;
List<Durl>? durl;
List<FormatItem>? supportFormats; List<FormatItem>? supportFormats;
// String? highFormat; // String? highFormat;
int? lastPlayTime; int? lastPlayTime;
@ -53,8 +52,7 @@ class PlayUrlModel {
videoCodecid = json['video_codecid']; videoCodecid = json['video_codecid'];
seekParam = json['seek_param']; seekParam = json['seek_param'];
seekType = json['seek_type']; seekType = json['seek_type'];
dash = json['dash'] != null ? Dash.fromJson(json['dash']) : null; dash = Dash.fromJson(json['dash']);
durl = json['durl']?.map<Durl>((e) => Durl.fromJson(e)).toList();
supportFormats = json['support_formats'] != null supportFormats = json['support_formats'] != null
? json['support_formats'] ? json['support_formats']
.map<FormatItem>((e) => FormatItem.fromJson(e)) .map<FormatItem>((e) => FormatItem.fromJson(e))
@ -252,30 +250,3 @@ class Flac {
audio = json['audio'] != null ? AudioItem.fromJson(json['audio']) : null; audio = json['audio'] != null ? AudioItem.fromJson(json['audio']) : null;
} }
} }
class Durl {
Durl({
this.order,
this.length,
this.size,
this.ahead,
this.vhead,
this.url,
});
int? order;
int? length;
int? size;
String? ahead;
String? vhead;
String? url;
Durl.fromJson(Map<String, dynamic> json) {
order = json['order'];
length = json['length'];
size = json['size'];
ahead = json['ahead'];
vhead = json['vhead'];
url = json['url'];
}
}

View File

@ -9,7 +9,6 @@ class ReplyContent {
this.vote, this.vote,
this.richText, this.richText,
this.isText, this.isText,
this.topicsMeta,
}); });
String? message; String? message;
@ -21,7 +20,6 @@ class ReplyContent {
Map? vote; Map? vote;
Map? richText; Map? richText;
bool? isText; bool? isText;
Map? topicsMeta;
ReplyContent.fromJson(Map<String, dynamic> json) { ReplyContent.fromJson(Map<String, dynamic> json) {
message = json['message'] message = json['message']
@ -41,7 +39,6 @@ class ReplyContent {
richText = json['rich_text'] ?? {}; richText = json['rich_text'] ?? {};
// 不包含@ 笔记 图片的时候,文字可折叠 // 不包含@ 笔记 图片的时候,文字可折叠
isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty; isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty;
topicsMeta = json['topics_meta'] ?? {};
} }
} }

View File

@ -1,120 +0,0 @@
class EmoteModelData {
final List<PackageItem>? packages;
EmoteModelData({
required this.packages,
});
factory EmoteModelData.fromJson(Map<String, dynamic> jsonRes) {
final List<PackageItem>? packages =
jsonRes['packages'] is List ? <PackageItem>[] : null;
if (packages != null) {
for (final dynamic item in jsonRes['packages']!) {
if (item != null) {
try {
packages.add(PackageItem.fromJson(item));
} catch (_) {}
}
}
}
return EmoteModelData(
packages: packages,
);
}
}
class PackageItem {
final int? id;
final String? text;
final String? url;
final int? mtime;
final int? type;
final int? attr;
final Meta? meta;
final List<Emote>? emote;
PackageItem({
required this.id,
required this.text,
required this.url,
required this.mtime,
required this.type,
required this.attr,
required this.meta,
required this.emote,
});
factory PackageItem.fromJson(Map<String, dynamic> jsonRes) {
final List<Emote>? emote = jsonRes['emote'] is List ? <Emote>[] : null;
if (emote != null) {
for (final dynamic item in jsonRes['emote']!) {
if (item != null) {
try {
emote.add(Emote.fromJson(item));
} catch (_) {}
}
}
}
return PackageItem(
id: jsonRes['id'],
text: jsonRes['text'],
url: jsonRes['url'],
mtime: jsonRes['mtime'],
type: jsonRes['type'],
attr: jsonRes['attr'],
meta: Meta.fromJson(jsonRes['meta']),
emote: emote,
);
}
}
class Meta {
final int? size;
final List<String>? suggest;
Meta({
required this.size,
required this.suggest,
});
factory Meta.fromJson(Map<String, dynamic> jsonRes) => Meta(
size: jsonRes['size'],
suggest: jsonRes['suggest'] is List ? <String>[] : null,
);
}
class Emote {
final int? id;
final int? packageId;
final String? text;
final String? url;
final int? mtime;
final int? type;
final int? attr;
final Meta? meta;
final dynamic activity;
Emote({
required this.id,
required this.packageId,
required this.text,
required this.url,
required this.mtime,
required this.type,
required this.attr,
required this.meta,
required this.activity,
});
factory Emote.fromJson(Map<String, dynamic> jsonRes) => Emote(
id: jsonRes['id'],
packageId: jsonRes['package_id'],
text: jsonRes['text'],
url: jsonRes['url'],
mtime: jsonRes['mtime'],
type: jsonRes['type'],
attr: jsonRes['attr'],
meta: Meta.fromJson(jsonRes['meta']),
activity: jsonRes['activity'],
);
}

View File

@ -1,20 +0,0 @@
class SubTitileContentModel {
double? from;
double? to;
int? location;
String? content;
SubTitileContentModel({
this.from,
this.to,
this.location,
this.content,
});
SubTitileContentModel.fromJson(Map<String, dynamic> json) {
from = json['from'];
to = json['to'];
location = json['location'];
content = json['content'];
}
}

View File

@ -1,89 +0,0 @@
import 'package:get/get.dart';
import '../../common/subtitle_type.dart';
class SubTitlteModel {
SubTitlteModel({
this.aid,
this.bvid,
this.cid,
this.loginMid,
this.loginMidHash,
this.isOwner,
this.name,
this.subtitles,
});
int? aid;
String? bvid;
int? cid;
int? loginMid;
String? loginMidHash;
bool? isOwner;
String? name;
List<SubTitlteItemModel>? subtitles;
factory SubTitlteModel.fromJson(Map<String, dynamic> json) => SubTitlteModel(
aid: json["aid"],
bvid: json["bvid"],
cid: json["cid"],
loginMid: json["login_mid"],
loginMidHash: json["login_mid_hash"],
isOwner: json["is_owner"],
name: json["name"],
subtitles: json["subtitle"] != null
? json["subtitle"]["subtitles"]
.map<SubTitlteItemModel>((x) => SubTitlteItemModel.fromJson(x))
.toList()
: [],
);
}
class SubTitlteItemModel {
SubTitlteItemModel({
this.id,
this.lan,
this.lanDoc,
this.isLock,
this.subtitleUrl,
this.type,
this.aiType,
this.aiStatus,
this.title,
this.code,
this.content,
this.body,
});
int? id;
String? lan;
String? lanDoc;
bool? isLock;
String? subtitleUrl;
int? type;
int? aiType;
int? aiStatus;
String? title;
int? code;
String? content;
List? body;
factory SubTitlteItemModel.fromJson(Map<String, dynamic> json) =>
SubTitlteItemModel(
id: json["id"],
lan: json["lan"].replaceAll('-', ''),
lanDoc: json["lan_doc"],
isLock: json["is_lock"],
subtitleUrl: json["subtitle_url"],
type: json["type"],
aiType: json["ai_type"],
aiStatus: json["ai_status"],
title: json["lan_doc"],
code: SubtitleType.values
.firstWhereOrNull(
(element) => element.id.toString() == json["lan"])
?.index ??
-1,
content: '',
body: [],
);
}

View File

@ -53,54 +53,29 @@ class _AboutPageState extends State<AboutPage> {
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.titleMedium,
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Text(
'使用Flutter开发的哔哩哔哩第三方客户端',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
const SizedBox(height: 20),
Obx( Obx(
() => Badge( () => ListTile(
isLabelVisible: _aboutController.isLoading.value title: const Text('当前版本'),
? false trailing: Text(_aboutController.currentVersion.value,
: _aboutController.isUpdate.value, style: subTitleStyle),
label: const Text('New'),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 30),
child: FilledButton.tonal(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
onTap: () => _aboutController.githubRelease(),
title: const Text('Github下载'),
),
ListTile(
onTap: () => _aboutController.panDownload(),
title: const Text('网盘下载'),
),
ListTile(
onTap: () => _aboutController.webSiteUrl(),
title: const Text('官网下载'),
),
ListTile(
onTap: () => _aboutController.qimiao(),
title: const Text('奇妙应用'),
),
SizedBox(
height:
MediaQuery.of(context).padding.bottom +
20)
],
);
},
);
},
child: Text(
'V${_aboutController.currentVersion.value}',
style: subTitleStyle.copyWith(
color: Theme.of(context).primaryColor,
),
), ),
), ),
Obx(
() => ListTile(
onTap: () => _aboutController.onUpdate(),
title: const Text('最新版本'),
trailing: Text(
_aboutController.isLoading.value
? '正在获取'
: _aboutController.isUpdate.value
? '有新版本 ❤️${_aboutController.remoteVersion.value}'
: '当前已是最新版',
style: subTitleStyle,
), ),
), ),
), ),
@ -112,9 +87,14 @@ class _AboutPageState extends State<AboutPage> {
// size: 16, // size: 16,
// ), // ),
// ), // ),
Divider(
thickness: 1,
height: 30,
color: Theme.of(context).colorScheme.outlineVariant,
),
ListTile( ListTile(
onTap: () => _aboutController.githubUrl(), onTap: () => _aboutController.githubUrl(),
title: const Text('开源地址'), title: const Text('Github'),
trailing: Text( trailing: Text(
'github.com/guozhigq/pilipala', 'github.com/guozhigq/pilipala',
style: subTitleStyle, style: subTitleStyle,
@ -148,44 +128,20 @@ class _AboutPageState extends State<AboutPage> {
color: outline, color: outline,
), ),
), ),
ListTile(
onTap: () {
showModalBottomSheet(
context: context,
builder: (context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile( ListTile(
onTap: () => _aboutController.qqChanel(), onTap: () => _aboutController.qqChanel(),
title: const Text('QQ群'), title: const Text('QQ群'),
trailing: Text(
'616150809',
style: subTitleStyle,
),
),
ListTile(
onTap: () => _aboutController.tgChanel(),
title: const Text('TG频道'),
trailing: Text(
'https://t.me/+lm_oOVmF0RJiODk1',
style: subTitleStyle,
),
),
SizedBox(
height: MediaQuery.of(context).padding.bottom + 20)
],
);
},
);
},
title: const Text('交流社区'),
trailing: Icon( trailing: Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
size: 16, size: 16,
color: outline, color: outline,
), ),
), ),
ListTile(
onTap: () => _aboutController.tgChanel(),
title: const Text('TG频道'),
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
),
ListTile( ListTile(
onTap: () => _aboutController.aPay(), onTap: () => _aboutController.aPay(),
title: const Text('赞助'), title: const Text('赞助'),
@ -205,8 +161,8 @@ class _AboutPageState extends State<AboutPage> {
}, },
title: const Text('清除缓存'), title: const Text('清除缓存'),
subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle), subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle),
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
), ),
SizedBox(height: MediaQuery.of(context).padding.bottom + 20)
], ],
), ),
), ),
@ -253,16 +209,12 @@ class AboutController extends GetxController {
// 获取远程版本 // 获取远程版本
Future getRemoteApp() async { Future getRemoteApp() async {
var result = await Request().get(Api.latestApp, extra: {'ua': 'pc'}); var result = await Request().get(Api.latestApp, extra: {'ua': 'pc'});
isLoading.value = false;
if (result.data == null || result.data.isEmpty) {
SmartDialog.showToast('获取远程版本失败,请检查网络');
return;
}
data = LatestDataModel.fromJson(result.data); data = LatestDataModel.fromJson(result.data);
remoteAppInfo = data; remoteAppInfo = data;
remoteVersion.value = data.tagName!; remoteVersion.value = data.tagName!;
isUpdate.value = isUpdate.value =
Utils.needUpdate(currentVersion.value, remoteVersion.value); Utils.needUpdate(currentVersion.value, remoteVersion.value);
isLoading.value = false;
} }
// 跳转下载/本地更新 // 跳转下载/本地更新
@ -278,26 +230,11 @@ class AboutController extends GetxController {
); );
} }
githubRelease() {
launchUrl(
Uri.parse('https://github.com/guozhigq/pilipala/releases'),
mode: LaunchMode.externalApplication,
);
}
// 从网盘下载 // 从网盘下载
panDownload() { panDownload() {
Clipboard.setData( launchUrl(
const ClipboardData(text: 'pili'),
);
SmartDialog.showToast(
'已复制提取码pili',
displayTime: const Duration(milliseconds: 500),
).then(
(value) => launchUrl(
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
),
); );
} }
@ -313,7 +250,7 @@ class AboutController extends GetxController {
// qq频道 // qq频道
qqChanel() { qqChanel() {
Clipboard.setData( Clipboard.setData(
const ClipboardData(text: '616150809'), const ClipboardData(text: '489981949'),
); );
SmartDialog.showToast('已复制QQ群号'); SmartDialog.showToast('已复制QQ群号');
} }
@ -354,13 +291,6 @@ class AboutController extends GetxController {
); );
} }
qimiao() {
launchUrl(
Uri.parse('https://www.magicalapk.com/home'),
mode: LaunchMode.externalApplication,
);
}
// 日志 // 日志
logs() { logs() {
Get.toNamed('/logs'); Get.toNamed('/logs');

View File

@ -7,8 +7,8 @@ import 'package:pilipala/utils/storage.dart';
class BangumiController extends GetxController { class BangumiController extends GetxController {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
RxList<BangumiListItemModel> bangumiList = <BangumiListItemModel>[].obs; RxList<BangumiListItemModel> bangumiList = [BangumiListItemModel()].obs;
RxList<BangumiListItemModel> bangumiFollowList = <BangumiListItemModel>[].obs; RxList<BangumiListItemModel> bangumiFollowList = [BangumiListItemModel()].obs;
int _currentPage = 1; int _currentPage = 1;
bool isLoadingMore = true; bool isLoadingMore = true;
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStrorage.userInfo;

View File

@ -25,6 +25,13 @@ class BangumiIntroController extends GetxController {
? int.tryParse(Get.parameters['epId']!) ? int.tryParse(Get.parameters['epId']!)
: null; : null;
// 是否预渲染 骨架屏
bool preRender = false;
// 视频详情 上个页面传入
Map? videoItem = {};
BangumiInfoModel? bangumiItem;
// 请求状态 // 请求状态
RxBool isLoading = false.obs; RxBool isLoading = false.obs;
@ -56,6 +63,27 @@ class BangumiIntroController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
if (Get.arguments.isNotEmpty as bool) {
if (Get.arguments.containsKey('bangumiItem') as bool) {
preRender = true;
bangumiItem = Get.arguments['bangumiItem'];
// bangumiItem!['pic'] = args.pic;
// if (args.title is String) {
// videoItem!['title'] = args.title;
// } else {
// String str = '';
// for (Map map in args.title) {
// str += map['text'];
// }
// videoItem!['title'] = str;
// }
// if (args.stat != null) {
// videoItem!['stat'] = args.stat;
// }
// videoItem!['pubdate'] = args.pubdate;
// videoItem!['owner'] = args.owner;
}
}
userInfo = userInfoCache.get('userInfoCache'); userInfo = userInfoCache.get('userInfoCache');
userLogin = userInfo != null; userLogin = userInfo != null;
} }
@ -168,8 +196,7 @@ class BangumiIntroController extends GetxController {
} }
Get.back(); Get.back();
}, },
child: const Text('确定'), child: const Text('确定'))
)
], ],
); );
}); });
@ -191,12 +218,14 @@ class BangumiIntroController extends GetxController {
addIds: addMediaIdsNew.join(','), addIds: addMediaIdsNew.join(','),
delIds: delMediaIdsNew.join(',')); delIds: delMediaIdsNew.join(','));
if (result['status']) { if (result['status']) {
if (result['data']['prompt']) {
addMediaIdsNew = []; addMediaIdsNew = [];
delMediaIdsNew = []; delMediaIdsNew = [];
Get.back();
// 重新获取收藏状态 // 重新获取收藏状态
queryHasFavVideo(); queryHasFavVideo();
SmartDialog.showToast('✅ 操作成功'); SmartDialog.showToast('✅ 操作成功');
Get.back(); }
} }
} }

View File

@ -12,10 +12,11 @@ import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/pages/bangumi/widgets/bangumi_panel.dart'; import 'package:pilipala/pages/bangumi/widgets/bangumi_panel.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/introduction/widgets/action_item.dart'; import 'package:pilipala/pages/video/detail/introduction/widgets/action_item.dart';
import 'package:pilipala/pages/video/detail/introduction/widgets/action_row_item.dart';
import 'package:pilipala/pages/video/detail/introduction/widgets/fav_panel.dart'; import 'package:pilipala/pages/video/detail/introduction/widgets/fav_panel.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import '../../../common/widgets/http_error.dart';
import 'controller.dart'; import 'controller.dart';
import 'widgets/intro_detail.dart'; import 'widgets/intro_detail.dart';
@ -50,6 +51,9 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
cid = widget.cid!; cid = widget.cid!;
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag); videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
bangumiIntroController.bangumiDetail.listen((BangumiInfoModel value) {
bangumiDetail = value;
});
_futureBuilderFuture = bangumiIntroController.queryBangumiIntro(); _futureBuilderFuture = bangumiIntroController.queryBangumiIntro();
videoDetailCtr.cid.listen((int p0) { videoDetailCtr.cid.listen((int p0) {
cid = p0; cid = p0;
@ -64,32 +68,27 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
future: _futureBuilderFuture, future: _futureBuilderFuture,
builder: (BuildContext context, AsyncSnapshot snapshot) { builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return const SliverToBoxAdapter(child: SizedBox());
}
if (snapshot.data['status']) { if (snapshot.data['status']) {
// 请求成功 // 请求成功
return Obx(
() => BangumiInfo( return BangumiInfo(
bangumiDetail: bangumiIntroController.bangumiDetail.value, loadingStatus: false,
bangumiDetail: bangumiDetail,
cid: cid, cid: cid,
),
); );
} else { } else {
// 请求错误 // 请求错误
return HttpError( // return HttpError(
errMsg: snapshot.data['msg'], // errMsg: snapshot.data['msg'],
fn: () => Get.back(), // fn: () => Get.back(),
); // );
return const SizedBox();
} }
} else { } else {
return const SliverToBoxAdapter( return BangumiInfo(
child: SizedBox( loadingStatus: true,
height: 100, bangumiDetail: bangumiDetail,
child: Center( cid: cid,
child: CircularProgressIndicator(),
),
),
); );
} }
}, },
@ -100,10 +99,12 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
class BangumiInfo extends StatefulWidget { class BangumiInfo extends StatefulWidget {
const BangumiInfo({ const BangumiInfo({
super.key, super.key,
this.loadingStatus = false,
this.bangumiDetail, this.bangumiDetail,
this.cid, this.cid,
}); });
final bool loadingStatus;
final BangumiInfoModel? bangumiDetail; final BangumiInfoModel? bangumiDetail;
final int? cid; final int? cid;
@ -116,6 +117,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
late final BangumiIntroController bangumiIntroController; late final BangumiIntroController bangumiIntroController;
late final VideoDetailController videoDetailCtr; late final VideoDetailController videoDetailCtr;
Box localCache = GStrorage.localCache; Box localCache = GStrorage.localCache;
late final BangumiInfoModel? bangumiItem;
late double sheetHeight; late double sheetHeight;
int? cid; int? cid;
bool isProcessing = false; bool isProcessing = false;
@ -134,10 +136,13 @@ class _BangumiInfoState extends State<BangumiInfo> {
super.initState(); super.initState();
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag); videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
bangumiItem = bangumiIntroController.bangumiItem;
sheetHeight = localCache.get('sheetHeight'); sheetHeight = localCache.get('sheetHeight');
cid = widget.cid!; cid = widget.cid!;
print('cid: $cid');
videoDetailCtr.cid.listen((p0) { videoDetailCtr.cid.listen((p0) {
cid = p0; cid = p0;
print('cid: $cid');
setState(() {}); setState(() {});
}); });
} }
@ -177,7 +182,8 @@ class _BangumiInfoState extends State<BangumiInfo> {
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 20), left: StyleString.safeSpace, right: StyleString.safeSpace, top: 20),
sliver: SliverToBoxAdapter( sliver: SliverToBoxAdapter(
child: Column( child: !widget.loadingStatus || bangumiItem != null
? Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
@ -188,10 +194,15 @@ class _BangumiInfoState extends State<BangumiInfo> {
NetworkImgLayer( NetworkImgLayer(
width: 105, width: 105,
height: 160, height: 160,
src: widget.bangumiDetail!.cover!, src: !widget.loadingStatus
? widget.bangumiDetail!.cover!
: bangumiItem!.cover!,
), ),
if (bangumiItem != null &&
bangumiItem!.rating != null)
PBadge( PBadge(
text: '评分 ${widget.bangumiDetail!.rating!['score']!}', text:
'评分 ${!widget.loadingStatus ? widget.bangumiDetail!.rating!['score']! : bangumiItem!.rating!['score']!}',
top: null, top: null,
right: 6, right: 6,
bottom: 6, bottom: 6,
@ -213,7 +224,9 @@ class _BangumiInfoState extends State<BangumiInfo> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
widget.bangumiDetail!.title!, !widget.loadingStatus
? widget.bangumiDetail!.title!
: bangumiItem!.title!,
style: const TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -233,7 +246,8 @@ class _BangumiInfoState extends State<BangumiInfo> {
backgroundColor: backgroundColor:
MaterialStateProperty.resolveWith( MaterialStateProperty.resolveWith(
(Set<MaterialState> states) { (Set<MaterialState> states) {
return t.colorScheme.primaryContainer return t
.colorScheme.primaryContainer
.withOpacity(0.7); .withOpacity(0.7);
}), }),
), ),
@ -252,13 +266,18 @@ class _BangumiInfoState extends State<BangumiInfo> {
children: [ children: [
StatView( StatView(
theme: 'gray', theme: 'gray',
view: widget.bangumiDetail!.stat!['views'], view: !widget.loadingStatus
? widget.bangumiDetail!.stat!['views']
: bangumiItem!.stat!['views'],
size: 'medium', size: 'medium',
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
StatDanMu( StatDanMu(
theme: 'gray', theme: 'gray',
danmu: widget.bangumiDetail!.stat!['danmakus'], danmu: !widget.loadingStatus
? widget
.bangumiDetail!.stat!['danmakus']
: bangumiItem!.stat!['danmakus'],
size: 'medium', size: 'medium',
), ),
], ],
@ -267,8 +286,15 @@ class _BangumiInfoState extends State<BangumiInfo> {
Row( Row(
children: [ children: [
Text( Text(
(widget.bangumiDetail!.areas!.isNotEmpty !widget.loadingStatus
? widget.bangumiDetail!.areas!.first['name'] ? (widget.bangumiDetail!.areas!
.isNotEmpty
? widget.bangumiDetail!.areas!
.first['name']
: '')
: (bangumiItem!.areas!.isNotEmpty
? bangumiItem!
.areas!.first['name']
: ''), : ''),
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
@ -277,7 +303,11 @@ class _BangumiInfoState extends State<BangumiInfo> {
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
widget.bangumiDetail!.publish!['pub_time_show'], !widget.loadingStatus
? widget.bangumiDetail!
.publish!['pub_time_show']
: bangumiItem!
.publish!['pub_time_show'],
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: t.colorScheme.outline, color: t.colorScheme.outline,
@ -285,16 +315,20 @@ class _BangumiInfoState extends State<BangumiInfo> {
), ),
], ],
), ),
// const SizedBox(height: 4),
Text( Text(
widget.bangumiDetail!.newEp!['desc'], !widget.loadingStatus
? widget.bangumiDetail!.newEp!['desc']
: bangumiItem!.newEp!['desc'],
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: t.colorScheme.outline, color: t.colorScheme.outline,
), ),
), ),
// const SizedBox(height: 10),
const Spacer(), const Spacer(),
Text( Text(
'简介:${widget.bangumiDetail!.evaluate!}', '简介:${!widget.loadingStatus ? widget.bangumiDetail!.evaluate! : bangumiItem!.evaluate!}',
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
@ -310,22 +344,45 @@ class _BangumiInfoState extends State<BangumiInfo> {
], ],
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
// 点赞收藏转发 布局样式1
/// 点赞收藏转发 // SingleChildScrollView(
// padding: const EdgeInsets.only(top: 7, bottom: 7),
// scrollDirection: Axis.horizontal,
// child: actionRow(
// context,
// bangumiIntroController,
// videoDetailCtr,
// ),
// ),
// 点赞收藏转发 布局样式2
actionGrid(context, bangumiIntroController), actionGrid(context, bangumiIntroController),
// 番剧分p // 番剧分p
if (widget.bangumiDetail!.episodes!.isNotEmpty) ...[ if ((!widget.loadingStatus &&
widget.bangumiDetail!.episodes!.isNotEmpty) ||
bangumiItem != null &&
bangumiItem!.episodes!.isNotEmpty) ...[
BangumiPanel( BangumiPanel(
pages: widget.bangumiDetail!.episodes!, pages: bangumiItem != null
cid: cid ?? widget.bangumiDetail!.episodes!.first.cid, ? bangumiItem!.episodes!
: widget.bangumiDetail!.episodes!,
cid: cid ??
(bangumiItem != null
? bangumiItem!.episodes!.first.cid
: widget.bangumiDetail!.episodes!.first.cid),
sheetHeight: sheetHeight, sheetHeight: sheetHeight,
changeFuc: (bvid, cid, aid) => changeFuc: (bvid, cid, aid) => bangumiIntroController
bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid), .changeSeasonOrbangu(bvid, cid, aid),
bangumiDetail: bangumiIntroController.bangumiDetail.value,
) )
], ],
], ],
)), )
: const SizedBox(
height: 100,
child: Center(
child: CircularProgressIndicator(),
),
),
),
); );
} }
@ -347,19 +404,25 @@ class _BangumiInfoState extends State<BangumiInfo> {
() => ActionItem( () => ActionItem(
icon: const Icon(FontAwesomeIcons.thumbsUp), icon: const Icon(FontAwesomeIcons.thumbsUp),
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
onTap: handleState(bangumiIntroController.actionLikeVideo), onTap:
handleState(bangumiIntroController.actionLikeVideo),
selectStatus: bangumiIntroController.hasLike.value, selectStatus: bangumiIntroController.hasLike.value,
text: widget.bangumiDetail!.stat!['likes']!.toString(), loadingStatus: false,
), text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['likes']!.toString()
: bangumiItem!.stat!['likes']!.toString()),
), ),
Obx( Obx(
() => ActionItem( () => ActionItem(
icon: const Icon(FontAwesomeIcons.b), icon: const Icon(FontAwesomeIcons.b),
selectIcon: const Icon(FontAwesomeIcons.b), selectIcon: const Icon(FontAwesomeIcons.b),
onTap: handleState(bangumiIntroController.actionCoinVideo), onTap:
handleState(bangumiIntroController.actionCoinVideo),
selectStatus: bangumiIntroController.hasCoin.value, selectStatus: bangumiIntroController.hasCoin.value,
text: widget.bangumiDetail!.stat!['coins']!.toString(), loadingStatus: false,
), text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['coins']!.toString()
: bangumiItem!.stat!['coins']!.toString()),
), ),
Obx( Obx(
() => ActionItem( () => ActionItem(
@ -367,22 +430,29 @@ class _BangumiInfoState extends State<BangumiInfo> {
selectIcon: const Icon(FontAwesomeIcons.solidStar), selectIcon: const Icon(FontAwesomeIcons.solidStar),
onTap: () => showFavBottomSheet(), onTap: () => showFavBottomSheet(),
selectStatus: bangumiIntroController.hasFav.value, selectStatus: bangumiIntroController.hasFav.value,
text: widget.bangumiDetail!.stat!['favorite']!.toString(), loadingStatus: false,
), text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['favorite']!.toString()
: bangumiItem!.stat!['favorite']!.toString()),
), ),
ActionItem( ActionItem(
icon: const Icon(FontAwesomeIcons.comment), icon: const Icon(FontAwesomeIcons.comment),
selectIcon: const Icon(FontAwesomeIcons.reply), selectIcon: const Icon(FontAwesomeIcons.reply),
onTap: () => videoDetailCtr.tabCtr.animateTo(1), onTap: () => videoDetailCtr.tabCtr.animateTo(1),
selectStatus: false, selectStatus: false,
text: widget.bangumiDetail!.stat!['reply']!.toString(), loadingStatus: false,
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['reply']!.toString()
: bangumiItem!.stat!['reply']!.toString(),
), ),
ActionItem( ActionItem(
icon: const Icon(FontAwesomeIcons.shareFromSquare), icon: const Icon(FontAwesomeIcons.shareFromSquare),
onTap: () => bangumiIntroController.actionShareVideo(), onTap: () => bangumiIntroController.actionShareVideo(),
selectStatus: false, selectStatus: false,
text: widget.bangumiDetail!.stat!['share']!.toString(), loadingStatus: false,
), text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['share']!.toString()
: bangumiItem!.stat!['share']!.toString()),
], ],
), ),
), ),
@ -390,4 +460,63 @@ class _BangumiInfoState extends State<BangumiInfo> {
); );
}); });
} }
Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) {
return Row(children: [
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.thumbsUp),
onTap: handleState(videoIntroController.actionLikeVideo),
selectStatus: videoIntroController.hasLike.value,
loadingStatus: widget.loadingStatus,
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['likes']!.toString()
: '-',
),
),
const SizedBox(width: 8),
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.b),
onTap: handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin.value,
loadingStatus: widget.loadingStatus,
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['coins']!.toString()
: '-',
),
),
const SizedBox(width: 8),
Obx(
() => ActionRowItem(
icon: const Icon(FontAwesomeIcons.heart),
onTap: () => showFavBottomSheet(),
selectStatus: videoIntroController.hasFav.value,
loadingStatus: widget.loadingStatus,
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['favorite']!.toString()
: '-',
),
),
const SizedBox(width: 8),
ActionRowItem(
icon: const Icon(FontAwesomeIcons.comment),
onTap: () {
videoDetailCtr.tabCtr.animateTo(1);
},
selectStatus: false,
loadingStatus: widget.loadingStatus,
text: !widget.loadingStatus
? widget.bangumiDetail!.stat!['reply']!.toString()
: '-',
),
const SizedBox(width: 8),
ActionRowItem(
icon: const Icon(FontAwesomeIcons.share),
onTap: () => videoIntroController.actionShareVideo(),
selectStatus: false,
loadingStatus: widget.loadingStatus,
text: '转发'),
]);
}
} }

View File

@ -14,14 +14,12 @@ class BangumiPanel extends StatefulWidget {
this.cid, this.cid,
this.sheetHeight, this.sheetHeight,
this.changeFuc, this.changeFuc,
this.bangumiDetail,
}); });
final List<EpisodeItem> pages; final List<EpisodeItem> pages;
final int? cid; final int? cid;
final double? sheetHeight; final double? sheetHeight;
final Function? changeFuc; final Function? changeFuc;
final BangumiInfoModel? bangumiDetail;
@override @override
State<BangumiPanel> createState() => _BangumiPanelState(); State<BangumiPanel> createState() => _BangumiPanelState();
@ -67,47 +65,6 @@ class _BangumiPanelState extends State<BangumiPanel> {
super.dispose(); super.dispose();
} }
Widget buildPageListItem(
EpisodeItem page,
int index,
bool isCurrentIndex,
) {
Color primary = Theme.of(context).colorScheme.primary;
return ListTile(
onTap: () {
Get.back();
setState(() {
changeFucCall(page, index);
});
},
dense: false,
leading: isCurrentIndex
? Image.asset(
'assets/images/live.gif',
color: primary,
height: 12,
)
: null,
title: Text(
'${page.title}${page.longTitle!}',
style: TextStyle(
fontSize: 14,
color: isCurrentIndex
? primary
: Theme.of(context).colorScheme.onSurface,
),
),
trailing: page.badge != null
? Text(
page.badge!,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
)
: const SizedBox(),
);
}
void showBangumiPanel() { void showBangumiPanel() {
showBottomSheet( showBottomSheet(
context: context, context: context,
@ -148,22 +105,38 @@ class _BangumiPanelState extends State<BangumiPanel> {
Expanded( Expanded(
child: Material( child: Material(
child: ScrollablePositionedList.builder( child: ScrollablePositionedList.builder(
itemCount: widget.pages.length + 1, itemCount: widget.pages.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) =>
bool isLastItem = index == widget.pages.length; ListTile(
bool isCurrentIndex = currentIndex == index; onTap: () {
return isLastItem setState(() {
? SizedBox( changeFucCall(widget.pages[index], index);
height: });
MediaQuery.of(context).padding.bottom +
20,
)
: buildPageListItem(
widget.pages[index],
index,
isCurrentIndex,
);
}, },
dense: false,
leading: index == currentIndex
? Image.asset(
'assets/images/live.gif',
color: Theme.of(context).colorScheme.primary,
height: 12,
)
: null,
title: Text(
'${index + 1}${widget.pages[index].longTitle!}',
style: TextStyle(
fontSize: 14,
color: index == currentIndex
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onSurface,
),
),
trailing: widget.pages[index].badge != null
? Image.asset(
'assets/images/big-vip.png',
height: 20,
)
: const SizedBox(),
),
itemScrollController: itemScrollController, itemScrollController: itemScrollController,
), ),
), ),
@ -205,11 +178,11 @@ class _BangumiPanelState extends State<BangumiPanel> {
return Column( return Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only(top: 10, bottom: 10), padding: const EdgeInsets.only(top: 10, bottom: 6),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text(''), const Text(''),
Expanded( Expanded(
child: Text( child: Text(
' 正在播放:${widget.pages[currentIndex].longTitle}', ' 正在播放:${widget.pages[currentIndex].longTitle}',
@ -229,7 +202,7 @@ class _BangumiPanelState extends State<BangumiPanel> {
), ),
onPressed: () => showBangumiPanel(), onPressed: () => showBangumiPanel(),
child: Text( child: Text(
'${widget.bangumiDetail!.newEp!['desc']}', '${widget.pages.length}',
style: const TextStyle(fontSize: 13), style: const TextStyle(fontSize: 13),
), ),
), ),
@ -282,15 +255,23 @@ class _BangumiPanelState extends State<BangumiPanel> {
), ),
const SizedBox(width: 2), const SizedBox(width: 2),
if (widget.pages[i].badge != null) ...[ if (widget.pages[i].badge != null) ...[
if (widget.pages[i].badge == '会员') ...[
Image.asset(
'assets/images/big-vip.png',
height: 16,
),
],
if (widget.pages[i].badge != '会员') ...[
const Spacer(), const Spacer(),
Text( Text(
widget.pages[i].badge!, widget.pages[i].badge!,
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 11,
color: color:
Theme.of(context).colorScheme.primary, Theme.of(context).colorScheme.primary,
), ),
), ),
],
] ]
], ],
), ),

View File

@ -139,7 +139,7 @@ class BlackListController extends GetxController {
int currentPage = 1; int currentPage = 1;
int pageSize = 50; int pageSize = 50;
RxInt total = 0.obs; RxInt total = 0.obs;
RxList<BlackListItem> blackList = <BlackListItem>[].obs; RxList<BlackListItem> blackList = [BlackListItem()].obs;
Future queryBlacklist({type = 'init'}) async { Future queryBlacklist({type = 'init'}) async {
if (type == 'init') { if (type == 'init') {

View File

@ -20,7 +20,7 @@ import 'package:pilipala/utils/utils.dart';
class DynamicsController extends GetxController { class DynamicsController extends GetxController {
int page = 1; int page = 1;
String? offset = ''; String? offset = '';
RxList<DynamicItemModel> dynamicsList = <DynamicItemModel>[].obs; RxList<DynamicItemModel> dynamicsList = [DynamicItemModel()].obs;
Rx<DynamicsType> dynamicsType = DynamicsType.values[0].obs; Rx<DynamicsType> dynamicsType = DynamicsType.values[0].obs;
RxString dynamicsTypeLabel = '全部'.obs; RxString dynamicsTypeLabel = '全部'.obs;
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
@ -105,7 +105,7 @@ class DynamicsController extends GetxController {
onSelectType(value) async { onSelectType(value) async {
dynamicsType.value = filterTypeList[value]['value']; dynamicsType.value = filterTypeList[value]['value'];
dynamicsList.value = <DynamicItemModel>[]; dynamicsList.value = [DynamicItemModel()];
page = 1; page = 1;
initialValue.value = value; initialValue.value = value;
await queryFollowDynamic(); await queryFollowDynamic();
@ -249,8 +249,8 @@ class DynamicsController extends GetxController {
return {'status': false, 'msg': '账号未登录'}; return {'status': false, 'msg': '账号未登录'};
} }
if (type == 'init') { if (type == 'init') {
upData.value.upList = <UpItem>[]; upData.value.upList = [];
upData.value.liveList = <LiveUserItem>[]; upData.value.liveUsers = LiveUsers();
} }
var res = await DynamicsHttp.followUp(); var res = await DynamicsHttp.followUp();
if (res['status']) { if (res['status']) {
@ -258,23 +258,20 @@ class DynamicsController extends GetxController {
if (upData.value.upList!.isEmpty) { if (upData.value.upList!.isEmpty) {
mid.value = -1; mid.value = -1;
} }
upData.value.upList!.insertAll(0, [
UpItem(face: '', uname: '全部动态', mid: -1),
UpItem(face: userInfo.face, uname: '', mid: userInfo.mid),
]);
} }
return res; return res;
} }
onSelectUp(mid) async { onSelectUp(mid) async {
dynamicsType.value = DynamicsType.values[0]; dynamicsType.value = DynamicsType.values[0];
dynamicsList.value = <DynamicItemModel>[]; dynamicsList.value = [DynamicItemModel()];
page = 1; page = 1;
queryFollowDynamic(); queryFollowDynamic();
} }
onRefresh() async { onRefresh() async {
page = 1; page = 1;
print('onRefresh');
await queryFollowUp(); await queryFollowUp();
await queryFollowDynamic(); await queryFollowDynamic();
} }
@ -296,7 +293,7 @@ class DynamicsController extends GetxController {
dynamicsType.value = DynamicsType.values[0]; dynamicsType.value = DynamicsType.values[0];
initialValue.value = 0; initialValue.value = 0;
SmartDialog.showToast('还原默认加载'); SmartDialog.showToast('还原默认加载');
dynamicsList.value = <DynamicItemModel>[]; dynamicsList.value = [DynamicItemModel()];
queryFollowDynamic(); queryFollowDynamic();
} }
} }

View File

@ -17,7 +17,7 @@ class DynamicDetailController extends GetxController {
int currentPage = 0; int currentPage = 0;
bool isLoadingMore = false; bool isLoadingMore = false;
RxString noMore = ''.obs; RxString noMore = ''.obs;
RxList<ReplyItemModel> replyList = <ReplyItemModel>[].obs; RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
RxInt acount = 0.obs; RxInt acount = 0.obs;
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();

View File

@ -14,7 +14,6 @@ import 'package:pilipala/pages/main/index.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import '../mine/controller.dart';
import 'controller.dart'; import 'controller.dart';
import 'widgets/dynamic_panel.dart'; import 'widgets/dynamic_panel.dart';
import 'widgets/up_panel.dart'; import 'widgets/up_panel.dart';
@ -29,7 +28,6 @@ class DynamicsPage extends StatefulWidget {
class _DynamicsPageState extends State<DynamicsPage> class _DynamicsPageState extends State<DynamicsPage>
with AutomaticKeepAliveClientMixin { with AutomaticKeepAliveClientMixin {
final DynamicsController _dynamicsController = Get.put(DynamicsController()); final DynamicsController _dynamicsController = Get.put(DynamicsController());
final MineController mineController = Get.put(MineController());
late Future _futureBuilderFuture; late Future _futureBuilderFuture;
late Future _futureBuilderFutureUp; late Future _futureBuilderFutureUp;
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStrorage.userInfo;
@ -258,14 +256,6 @@ class _DynamicsPageState extends State<DynamicsPage>
} }
}, },
); );
} else if (data['msg'] == "账号未登录") {
return HttpError(
errMsg: data['msg'],
btnText: "去登录",
fn: () {
mineController.onLogin();
},
);
} else { } else {
return HttpError( return HttpError(
errMsg: data['msg'], errMsg: data['msg'],

View File

@ -34,25 +34,25 @@ Widget articlePanel(item, context, {floor = 1}) {
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
], ],
// Text( Text(
// item.modules.moduleDynamic.major.opus.title, item.modules.moduleDynamic.major.opus.title,
// style: Theme.of(context) style: Theme.of(context)
// .textTheme .textTheme
// .titleMedium! .titleMedium!
// .copyWith(fontWeight: FontWeight.bold), .copyWith(fontWeight: FontWeight.bold),
// ), ),
// const SizedBox(height: 2), const SizedBox(height: 2),
// if (item.modules.moduleDynamic.major.opus.summary.text != if (item.modules.moduleDynamic.major.opus.summary.text !=
// 'undefined') ...[ 'undefined') ...[
// Text( Text(
// item.modules.moduleDynamic.major.opus.summary.richTextNodes.first item.modules.moduleDynamic.major.opus.summary.richTextNodes.first
// .text, .text,
// maxLines: 4, maxLines: 4,
// style: const TextStyle(height: 1.55), style: const TextStyle(height: 1.55),
// overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
// ), ),
// const SizedBox(height: 2), const SizedBox(height: 2),
// ], ],
picWidget(item, context) picWidget(item, context)
], ],
), ),

View File

@ -45,9 +45,7 @@ class _ContentState extends State<Content> {
if (len == 1) { if (len == 1) {
OpusPicsModel pictureItem = pics.first; OpusPicsModel pictureItem = pics.first;
picList.add(pictureItem.url!); picList.add(pictureItem.url!);
spanChilds.add(const TextSpan(text: '\n'));
/// 图片上方的空白间隔
// spanChilds.add(const TextSpan(text: '\n'));
spanChilds.add( spanChilds.add(
WidgetSpan( WidgetSpan(
child: LayoutBuilder( child: LayoutBuilder(

View File

@ -19,17 +19,6 @@ InlineSpan richNode(item, context) {
// 动态页面 richTextNodes 层级可能与主页动态层级不同 // 动态页面 richTextNodes 层级可能与主页动态层级不同
richTextNodes = richTextNodes =
item.modules.moduleDynamic.major.opus.summary.richTextNodes; item.modules.moduleDynamic.major.opus.summary.richTextNodes;
if (item.modules.moduleDynamic.major.opus.title != null) {
spanChilds.add(
TextSpan(
text: item.modules.moduleDynamic.major.opus.title + '\n',
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontWeight: FontWeight.bold),
),
);
}
} }
if (richTextNodes == null || richTextNodes.isEmpty) { if (richTextNodes == null || richTextNodes.isEmpty) {
return spacer; return spacer;

View File

@ -1,14 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/dynamics/up.dart'; import 'package:pilipala/models/dynamics/up.dart';
import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/pages/dynamics/controller.dart'; import 'package:pilipala/pages/dynamics/controller.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
class UpPanel extends StatefulWidget { class UpPanel extends StatefulWidget {
final FollowUpModel upData; final FollowUpModel? upData;
const UpPanel(this.upData, {Key? key}) : super(key: key); const UpPanel(this.upData, {Key? key}) : super(key: key);
@override @override
@ -22,22 +24,38 @@ class _UpPanelState extends State<UpPanel> {
List<UpItem> upList = []; List<UpItem> upList = [];
List<LiveUserItem> liveList = []; List<LiveUserItem> liveList = [];
static const itemPadding = EdgeInsets.symmetric(horizontal: 5, vertical: 0); static const itemPadding = EdgeInsets.symmetric(horizontal: 5, vertical: 0);
late MyInfo userInfo; Box userInfoCache = GStrorage.userInfo;
var userInfo;
void listFormat() { @override
userInfo = widget.upData.myInfo!; void initState() {
upList = widget.upData.upList!; super.initState();
liveList = widget.upData.liveList!; upList = widget.upData!.upList!;
if (widget.upData!.liveUsers != null) {
liveList = widget.upData!.liveUsers!.items!;
}
upList.insert(
0,
UpItem(face: '', uname: '全部动态', mid: -1),
);
userInfo = userInfoCache.get('userInfoCache');
upList.insert(
1,
UpItem(
face: userInfo.face,
uname: '',
mid: userInfo.mid,
),
);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
listFormat();
return SliverPersistentHeader( return SliverPersistentHeader(
floating: true, floating: true,
pinned: false, pinned: false,
delegate: _SliverHeaderDelegate( delegate: _SliverHeaderDelegate(
height: liveList.isNotEmpty || upList.isNotEmpty ? 126 : 0, height: 126,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -72,7 +90,7 @@ class _UpPanelState extends State<UpPanel> {
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
child: Row( child: Row(
children: [ children: [
Flexible( Expanded(
child: ListView( child: ListView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
controller: scrollController, controller: scrollController,

View File

@ -1,20 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../http/reply.dart';
import '../../models/video/reply/emote.dart';
class EmotePanelController extends GetxController
with GetTickerProviderStateMixin {
late List<PackageItem> emotePackage;
late TabController tabController;
Future getEmote() async {
var res = await ReplyHttp.getEmoteList(business: 'reply');
if (res['status']) {
emotePackage = res['data'].packages;
tabController = TabController(length: emotePackage.length, vsync: this);
}
return res;
}
}

View File

@ -1,4 +0,0 @@
library emote;
export './controller.dart';
export './view.dart';

View File

@ -1,116 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../models/video/reply/emote.dart';
import 'controller.dart';
class EmotePanel extends StatefulWidget {
final Function onChoose;
const EmotePanel({super.key, required this.onChoose});
@override
State<EmotePanel> createState() => _EmotePanelState();
}
class _EmotePanelState extends State<EmotePanel>
with AutomaticKeepAliveClientMixin {
final EmotePanelController _emotePanelController =
Get.put(EmotePanelController());
late Future _futureBuilderFuture;
@override
bool get wantKeepAlive => true;
@override
void initState() {
_futureBuilderFuture = _emotePanelController.getEmote();
super.initState();
}
@override
Widget build(BuildContext context) {
super.build(context);
return FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (data['status']) {
List<PackageItem> emotePackage =
_emotePanelController.emotePackage;
return Column(
children: [
Expanded(
child: TabBarView(
controller: _emotePanelController.tabController,
children: emotePackage.map(
(e) {
int size = e.emote!.first.meta!.size!;
int type = e.type!;
return Padding(
padding: const EdgeInsets.fromLTRB(12, 6, 12, 0),
child: GridView.builder(
gridDelegate:
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: size == 1 ? 40 : 60,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: e.emote!.length,
itemBuilder: (context, index) {
return Material(
color: Colors.transparent,
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
child: InkWell(
onTap: () {
widget.onChoose(e, e.emote![index]);
},
child: Padding(
padding: const EdgeInsets.all(3),
child: type == 4
? Text(
e.emote![index].text!,
overflow: TextOverflow.clip,
maxLines: 1,
)
: Image.network(
e.emote![index].url!,
width: size * 38,
height: size * 38,
),
),
),
);
},
),
);
},
).toList(),
)),
Divider(
height: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
TabBar(
controller: _emotePanelController.tabController,
dividerColor: Colors.transparent,
isScrollable: true,
tabs: _emotePanelController.emotePackage
.map((e) => Tab(text: e.text))
.toList(),
),
SizedBox(height: MediaQuery.of(context).padding.bottom + 20),
],
);
} else {
return Center(child: Text(data['msg']));
}
} else {
return const Center(child: Text('加载中...'));
}
});
}
}

View File

@ -10,7 +10,7 @@ class FansController extends GetxController {
int pn = 1; int pn = 1;
int ps = 20; int ps = 20;
int total = 0; int total = 0;
RxList<FansItemModel> fansList = <FansItemModel>[].obs; RxList<FansItemModel> fansList = [FansItemModel()].obs;
late int mid; late int mid;
late String name; late String name;
var userInfo; var userInfo;

View File

@ -24,7 +24,7 @@ class FavController extends GetxController {
if (!hasMore.value) { if (!hasMore.value) {
return; return;
} }
var res = await UserHttp.userfavFolder( var res = await await UserHttp.userfavFolder(
pn: currentPage, pn: currentPage,
ps: pageSize, ps: pageSize,
mid: userInfo!.mid!, mid: userInfo!.mid!,

View File

@ -16,7 +16,7 @@ class FavDetailController extends GetxController {
RxMap favInfo = {}.obs; RxMap favInfo = {}.obs;
RxList favList = [].obs; RxList favList = [].obs;
RxString loadingText = '加载中...'.obs; RxString loadingText = '加载中...'.obs;
RxInt mediaCount = 0.obs; int mediaCount = 0;
@override @override
void onInit() { void onInit() {
@ -29,12 +29,12 @@ class FavDetailController extends GetxController {
} }
Future<dynamic> queryUserFavFolderDetail({type = 'init'}) async { Future<dynamic> queryUserFavFolderDetail({type = 'init'}) async {
if (type == 'onLoad' && favList.length >= mediaCount.value) { if (type == 'onLoad' && favList.length >= mediaCount) {
loadingText.value = '没有更多了'; loadingText.value = '没有更多了';
return; return;
} }
isLoadingMore = true; isLoadingMore = true;
var res = await UserHttp.userFavFolderDetail( var res = await await UserHttp.userFavFolderDetail(
pn: currentPage, pn: currentPage,
ps: 20, ps: 20,
mediaId: mediaId!, mediaId: mediaId!,
@ -43,11 +43,11 @@ class FavDetailController extends GetxController {
favInfo.value = res['data'].info; favInfo.value = res['data'].info;
if (currentPage == 1 && type == 'init') { if (currentPage == 1 && type == 'init') {
favList.value = res['data'].medias; favList.value = res['data'].medias;
mediaCount.value = res['data'].info['media_count']; mediaCount = res['data'].info['media_count'];
} else if (type == 'onLoad') { } else if (type == 'onLoad') {
favList.addAll(res['data'].medias); favList.addAll(res['data'].medias);
} }
if (favList.length >= mediaCount.value) { if (favList.length >= mediaCount) {
loadingText.value = '没有更多了'; loadingText.value = '没有更多了';
} }
} }
@ -60,6 +60,7 @@ class FavDetailController extends GetxController {
var result = await VideoHttp.favVideo( var result = await VideoHttp.favVideo(
aid: id, addIds: '', delIds: mediaId.toString()); aid: id, addIds: '', delIds: mediaId.toString());
if (result['status']) { if (result['status']) {
if (result['data']['prompt']) {
List dataList = favList; List dataList = favList;
for (var i in dataList) { for (var i in dataList) {
if (i.id == id) { if (i.id == id) {
@ -70,6 +71,7 @@ class FavDetailController extends GetxController {
SmartDialog.showToast('取消收藏'); SmartDialog.showToast('取消收藏');
} }
} }
}
onLoad() { onLoad() {
queryUserFavFolderDetail(type: 'onLoad'); queryUserFavFolderDetail(type: 'onLoad');

View File

@ -29,8 +29,8 @@ class _FavDetailPageState extends State<FavDetailPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
mediaId = Get.parameters['mediaId']!;
_futureBuilderFuture = _favDetailController.queryUserFavFolderDetail(); _futureBuilderFuture = _favDetailController.queryUserFavFolderDetail();
mediaId = Get.parameters['mediaId']!;
titleStreamC = StreamController<bool>(); titleStreamC = StreamController<bool>();
_controller.addListener( _controller.addListener(
() { () {
@ -84,7 +84,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.titleMedium,
), ),
Text( Text(
'${_favDetailController.mediaCount}条视频', '${_favDetailController.item!.mediaCount!}条视频',
style: Theme.of(context).textTheme.labelMedium, style: Theme.of(context).textTheme.labelMedium,
) )
], ],
@ -175,7 +175,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
padding: const EdgeInsets.only(top: 15, bottom: 8, left: 14), padding: const EdgeInsets.only(top: 15, bottom: 8, left: 14),
child: Obx( child: Obx(
() => Text( () => Text(
'${_favDetailController.mediaCount}条视频', '${_favDetailController.favList.length}条视频',
style: TextStyle( style: TextStyle(
fontSize: fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize, Theme.of(context).textTheme.labelMedium!.fontSize,

View File

@ -80,6 +80,7 @@ class FavSearchController extends GetxController {
var result = await VideoHttp.favVideo( var result = await VideoHttp.favVideo(
aid: id, addIds: '', delIds: mediaId.toString()); aid: id, addIds: '', delIds: mediaId.toString());
if (result['status']) { if (result['status']) {
if (result['data']['prompt']) {
List dataList = favList; List dataList = favList;
for (var i in dataList) { for (var i in dataList) {
if (i.id == id) { if (i.id == id) {
@ -90,4 +91,5 @@ class FavSearchController extends GetxController {
SmartDialog.showToast('取消收藏'); SmartDialog.showToast('取消收藏');
} }
} }
}
} }

View File

@ -88,11 +88,9 @@ class HistoryController extends GetxController {
// 观看历史暂停状态 // 观看历史暂停状态
Future historyStatus() async { Future historyStatus() async {
var res = await UserHttp.historyStatus(); var res = await UserHttp.historyStatus();
if (res.data['code'] == 0) {
pauseStatus.value = res.data['data']; pauseStatus.value = res.data['data'];
localCache.put(LocalCacheKey.historyPause, res.data['data']); localCache.put(LocalCacheKey.historyPause, res.data['data']);
} }
}
// 清空观看历史 // 清空观看历史
Future onClearHistory() async { Future onClearHistory() async {

View File

@ -70,6 +70,10 @@ class _HistoryPageState extends State<HistoryPage> {
child1: AppBar( child1: AppBar(
titleSpacing: 0, titleSpacing: 0,
centerTitle: false, centerTitle: false,
leading: IconButton(
onPressed: () => Get.back(),
icon: const Icon(Icons.arrow_back_outlined),
),
title: Text( title: Text(
'观看记录', '观看记录',
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.titleMedium,

View File

@ -185,7 +185,7 @@ class HistoryItem extends StatelessWidget {
? '已看完' ? '已看完'
: '${Utils.timeFormat(videoItem.progress!)}/${Utils.timeFormat(videoItem.duration!)}', : '${Utils.timeFormat(videoItem.progress!)}/${Utils.timeFormat(videoItem.duration!)}',
right: 6.0, right: 6.0,
bottom: 8.0, bottom: 6.0,
type: 'gray', type: 'gray',
), ),
// 右上角 // 右上角
@ -258,27 +258,6 @@ class HistoryItem extends StatelessWidget {
), ),
), ),
), ),
videoItem.progress != 0
? Positioned(
left: 3,
right: 3,
bottom: 0,
child: ClipRRect(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(
StyleString.imgRadius.x),
bottomRight: Radius.circular(
StyleString.imgRadius.x),
),
child: LinearProgressIndicator(
value: videoItem.progress == -1
? 100
: videoItem.progress /
videoItem.duration,
),
),
)
: const SizedBox()
], ],
), ),
VideoContent(videoItem: videoItem, ctr: ctr) VideoContent(videoItem: videoItem, ctr: ctr)

View File

@ -26,7 +26,6 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
late List defaultTabs; late List defaultTabs;
late List<String> tabbarSort; late List<String> tabbarSort;
RxString defaultSearch = ''.obs; RxString defaultSearch = ''.obs;
late bool enableGradientBg;
@override @override
void onInit() { void onInit() {
@ -34,15 +33,13 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
userInfo = userInfoCache.get('userInfoCache'); userInfo = userInfoCache.get('userInfoCache');
userLogin.value = userInfo != null; userLogin.value = userInfo != null;
userFace.value = userInfo != null ? userInfo.face : ''; userFace.value = userInfo != null ? userInfo.face : '';
// 进行tabs配置
setTabConfig();
hideSearchBar = hideSearchBar =
setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); setting.get(SettingBoxKey.hideSearchBar, defaultValue: true);
if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) { if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) {
searchDefault(); searchDefault();
} }
enableGradientBg =
setting.get(SettingBoxKey.enableGradientBg, defaultValue: true);
// 进行tabs配置
setTabConfig();
} }
void onRefresh() { void onRefresh() {
@ -91,7 +88,6 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
vsync: this, vsync: this,
); );
// 监听 tabController 切换 // 监听 tabController 切换
if (enableGradientBg) {
tabController.animation!.addListener(() { tabController.animation!.addListener(() {
if (tabController.indexIsChanging) { if (tabController.indexIsChanging) {
if (initialIndex.value != tabController.index) { if (initialIndex.value != tabController.index) {
@ -106,7 +102,6 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
} }
}); });
} }
}
void searchDefault() async { void searchDefault() async {
var res = await Request().get(Api.searchDefault); var res = await Request().get(Api.searchDefault);

View File

@ -48,23 +48,17 @@ class _HomePageState extends State<HomePage>
super.build(context); super.build(context);
Brightness currentBrightness = MediaQuery.of(context).platformBrightness; Brightness currentBrightness = MediaQuery.of(context).platformBrightness;
// 设置状态栏图标的亮度 // 设置状态栏图标的亮度
if (_homeController.enableGradientBg) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarIconBrightness: currentBrightness == Brightness.light statusBarIconBrightness: currentBrightness == Brightness.light
? Brightness.dark ? Brightness.dark
: Brightness.light, : Brightness.light,
)); ));
}
return Scaffold( return Scaffold(
extendBody: true, extendBody: true,
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
appBar: _homeController.enableGradientBg
? null
: AppBar(toolbarHeight: 0, elevation: 0),
body: Stack( body: Stack(
children: [ children: [
// gradient background // gradient background
if (_homeController.enableGradientBg) ...[
Align( Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: Opacity( child: Opacity(
@ -75,14 +69,8 @@ class _HomePageState extends State<HomePage>
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
Theme.of(context) Theme.of(context).colorScheme.primary.withOpacity(0.9),
.colorScheme Theme.of(context).colorScheme.primary.withOpacity(0.5),
.primary
.withOpacity(0.9),
Theme.of(context)
.colorScheme
.primary
.withOpacity(0.5),
Theme.of(context).colorScheme.surface Theme.of(context).colorScheme.surface
], ],
begin: Alignment.topLeft, begin: Alignment.topLeft,
@ -92,7 +80,6 @@ class _HomePageState extends State<HomePage>
), ),
), ),
), ),
],
Column( Column(
children: [ children: [
CustomAppBar( CustomAppBar(
@ -103,37 +90,7 @@ class _HomePageState extends State<HomePage>
callback: showUserBottomSheet, callback: showUserBottomSheet,
), ),
if (_homeController.tabs.length > 1) ...[ if (_homeController.tabs.length > 1) ...[
if (_homeController.enableGradientBg) ...[
const CustomTabs(), const CustomTabs(),
] else ...[
const SizedBox(height: 4),
SizedBox(
width: double.infinity,
height: 42,
child: Align(
alignment: Alignment.center,
child: TabBar(
controller: _homeController.tabController,
tabs: [
for (var i in _homeController.tabs)
Tab(text: i['label'])
],
isScrollable: true,
dividerColor: Colors.transparent,
enableFeedback: true,
splashBorderRadius: BorderRadius.circular(10),
tabAlignment: TabAlignment.center,
onTap: (value) {
feedBack();
if (_homeController.initialIndex.value == value) {
_homeController.tabsCtrList[value]().animateToTop();
}
_homeController.initialIndex.value = value;
},
),
),
),
],
] else ...[ ] else ...[
const SizedBox(height: 6), const SizedBox(height: 6),
], ],
@ -415,16 +372,13 @@ class SearchBar extends StatelessWidget {
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Obx( Obx(
() => Expanded( () => Text(
child: Text(
ctr!.defaultSearch.value, ctr!.defaultSearch.value,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(color: colorScheme.outline), style: TextStyle(color: colorScheme.outline),
), ),
), ),
),
const SizedBox(width: 15),
], ],
), ),
), ),

View File

@ -7,7 +7,7 @@ class HotController extends GetxController {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
final int _count = 20; final int _count = 20;
int _currentPage = 1; int _currentPage = 1;
RxList<HotVideoItemModel> videoList = <HotVideoItemModel>[].obs; RxList<HotVideoItemModel> videoList = [HotVideoItemModel()].obs;
bool isLoadingMore = false; bool isLoadingMore = false;
bool flag = false; bool flag = false;
OverlayEntry? popupDialog; OverlayEntry? popupDialog;

View File

@ -1,13 +1,9 @@
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/live.dart'; import 'package:pilipala/http/live.dart';
import 'package:pilipala/models/live/quality.dart';
import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/models/live/room_info.dart';
import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart';
import '../../models/live/room_info_h5.dart'; import '../../models/live/room_info_h5.dart';
import '../../utils/storage.dart';
import '../../utils/video_utils.dart';
class LiveRoomController extends GetxController { class LiveRoomController extends GetxController {
String cover = ''; String cover = '';
@ -20,17 +16,10 @@ class LiveRoomController extends GetxController {
PlPlayerController plPlayerController = PlPlayerController plPlayerController =
PlPlayerController.getInstance(videoType: 'live'); PlPlayerController.getInstance(videoType: 'live');
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs; Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
late bool enableCDN;
late int currentQn;
int? tempCurrentQn;
late List<Map<String, dynamic>> acceptQnList;
RxString currentQnDesc = ''.obs;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
currentQn = setting.get(SettingBoxKey.defaultLiveQa,
defaultValue: LiveQuality.values.last.code);
roomId = int.parse(Get.parameters['roomid']!); roomId = int.parse(Get.parameters['roomid']!);
if (Get.arguments != null) { if (Get.arguments != null) {
liveItem = Get.arguments['liveItem']; liveItem = Get.arguments['liveItem'];
@ -42,8 +31,6 @@ class LiveRoomController extends GetxController {
cover = liveItem.cover; cover = liveItem.cover;
} }
} }
// CDN优化
enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true);
} }
playerInit(source) async { playerInit(source) async {
@ -65,31 +52,12 @@ class LiveRoomController extends GetxController {
} }
Future queryLiveInfo() async { Future queryLiveInfo() async {
var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn); var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: 10000);
if (res['status']) { if (res['status']) {
List<CodecItem> codec = List<CodecItem> codec =
res['data'].playurlInfo.playurl.stream.first.format.first.codec; res['data'].playurlInfo.playurl.stream.first.format.first.codec;
CodecItem item = codec.first; CodecItem item = codec.first;
// 以服务端返回的码率为准 String videoUrl = (item.urlInfo?.first.host)! +
currentQn = item.currentQn!;
if (tempCurrentQn != null && tempCurrentQn == currentQn) {
SmartDialog.showToast('画质切换失败,请检查登录状态');
}
List acceptQn = item.acceptQn!;
acceptQnList = acceptQn.map((e) {
return {
'code': e,
'desc': LiveQuality.values
.firstWhere((element) => element.code == e)
.description,
};
}).toList();
currentQnDesc.value = LiveQuality.values
.firstWhere((element) => element.code == currentQn)
.description;
String videoUrl = enableCDN
? VideoUtils.getCdnUrl(item)
: (item.urlInfo?.first.host)! +
item.baseUrl! + item.baseUrl! +
item.urlInfo!.first.extra!; item.urlInfo!.first.extra!;
await playerInit(videoUrl); await playerInit(videoUrl);
@ -115,17 +83,4 @@ class LiveRoomController extends GetxController {
} }
return res; return res;
} }
// 修改画质
void changeQn(int qn) async {
tempCurrentQn = currentQn;
if (currentQn == qn) {
return;
}
currentQn = qn;
currentQnDesc.value = LiveQuality.values
.firstWhere((element) => element.code == currentQn)
.description;
await queryLiveInfo();
}
} }

View File

@ -75,43 +75,39 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: Stack( body: Stack(
children: [ children: [
Positioned( // Obx(
left: 0, // () => Positioned.fill(
right: 0, // child: Opacity(
bottom: 0, // opacity: 0.8,
// child: _liveRoomController
// .roomInfoH5.value.roomInfo?.appBackground !=
// '' &&
// _liveRoomController
// .roomInfoH5.value.roomInfo?.appBackground !=
// null
// ? NetworkImgLayer(
// width: Get.width,
// height: Get.height,
// src: _liveRoomController
// .roomInfoH5.value.roomInfo?.appBackground ??
// '',
// )
// : Image.asset(
// 'assets/images/live/default_bg.webp',
// width: Get.width,
// height: Get.height,
// ),
// ),
// ),
// ),
Positioned.fill(
child: Opacity( child: Opacity(
opacity: 0.8, opacity: 0.8,
child: Image.asset( child: Image.asset(
'assets/images/live/default_bg.webp', 'assets/images/live/default_bg.webp',
fit: BoxFit.cover,
// width: Get.width,
// height: Get.height,
),
),
),
Obx(
() => Positioned(
left: 0,
right: 0,
bottom: 0,
child: _liveRoomController
.roomInfoH5.value.roomInfo?.appBackground !=
'' &&
_liveRoomController
.roomInfoH5.value.roomInfo?.appBackground !=
null
? Opacity(
opacity: 0.8,
child: NetworkImgLayer(
width: Get.width, width: Get.width,
height: Get.height, height: Get.height,
type: 'bg',
src: _liveRoomController
.roomInfoH5.value.roomInfo?.appBackground ??
'',
), ),
)
: const SizedBox(),
), ),
), ),
Column( Column(

View File

@ -3,7 +3,6 @@ import 'dart:io';
import 'package:floating/floating.dart'; import 'package:floating/floating.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/models/video/play/url.dart'; import 'package:pilipala/models/video/play/url.dart';
import 'package:pilipala/pages/live_room/index.dart'; import 'package:pilipala/pages/live_room/index.dart';
@ -30,6 +29,7 @@ class BottomControl extends StatefulWidget implements PreferredSizeWidget {
class _BottomControlState extends State<BottomControl> { class _BottomControlState extends State<BottomControl> {
late PlayUrlModel videoInfo; late PlayUrlModel videoInfo;
List<PlaySpeed> playSpeed = PlaySpeed.values;
TextStyle subTitleStyle = const TextStyle(fontSize: 12); TextStyle subTitleStyle = const TextStyle(fontSize: 12);
TextStyle titleStyle = const TextStyle(fontSize: 14); TextStyle titleStyle = const TextStyle(fontSize: 14);
Size get preferredSize => const Size(double.infinity, kToolbarHeight); Size get preferredSize => const Size(double.infinity, kToolbarHeight);
@ -84,30 +84,6 @@ class _BottomControlState extends State<BottomControl> {
// ), // ),
// ), // ),
// const SizedBox(width: 4), // const SizedBox(width: 4),
SizedBox(
width: 30,
child: PopupMenuButton<int>(
padding: EdgeInsets.zero,
onSelected: (value) {
widget.liveRoomCtr!.changeQn(value);
},
child: Obx(
() => Text(
widget.liveRoomCtr!.currentQnDesc.value,
style: const TextStyle(color: Colors.white, fontSize: 13),
),
),
itemBuilder: (BuildContext context) {
return widget.liveRoomCtr!.acceptQnList.map((e) {
return PopupMenuItem<int>(
value: e['code'],
child: Text(e['desc']),
);
}).toList();
},
),
),
const SizedBox(width: 10),
if (Platform.isAndroid) ...[ if (Platform.isAndroid) ...[
SizedBox( SizedBox(
width: 34, width: 34,
@ -135,7 +111,7 @@ class _BottomControlState extends State<BottomControl> {
), ),
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: 4),
], ],
ComBtn( ComBtn(
icon: const Icon( icon: const Icon(

View File

@ -9,20 +9,54 @@ import 'package:pilipala/http/common.dart';
import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/dynamics/index.dart';
import 'package:pilipala/pages/home/view.dart'; import 'package:pilipala/pages/home/view.dart';
import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/media/index.dart';
import 'package:pilipala/pages/rank/index.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import '../../models/common/dynamic_badge_mode.dart'; import '../../models/common/dynamic_badge_mode.dart';
import '../../models/common/nav_bar_config.dart';
class MainController extends GetxController { class MainController extends GetxController {
List<Widget> pages = <Widget>[ List<Widget> pages = <Widget>[
const HomePage(), const HomePage(),
const RankPage(),
const DynamicsPage(), const DynamicsPage(),
const MediaPage(), const MediaPage(),
]; ];
RxList navigationBars = defaultNavigationBars.obs; RxList navigationBars = [
{
'icon': const Icon(
Icons.home_outlined,
size: 21,
),
'selectIcon': const Icon(
Icons.home,
size: 21,
),
'label': "首页",
'count': 0,
},
{
'icon': const Icon(
Icons.motion_photos_on_outlined,
size: 21,
),
'selectIcon': const Icon(
Icons.motion_photos_on,
size: 21,
),
'label': "动态",
'count': 0,
},
{
'icon': const Icon(
Icons.video_collection_outlined,
size: 20,
),
'selectIcon': const Icon(
Icons.video_collection,
size: 21,
),
'label': "媒体库",
'count': 0,
}
].obs;
final StreamController<bool> bottomBarStream = final StreamController<bool> bottomBarStream =
StreamController<bool>.broadcast(); StreamController<bool>.broadcast();
Box setting = GStrorage.setting; Box setting = GStrorage.setting;
@ -41,10 +75,6 @@ class MainController extends GetxController {
Utils.checkUpdata(); Utils.checkUpdata();
} }
hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true); hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true);
int defaultHomePage =
setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0) as int;
selectedIndex = defaultNavigationBars
.indexWhere((item) => item['id'] == defaultHomePage);
var userInfo = userInfoCache.get('userInfoCache'); var userInfo = userInfoCache.get('userInfoCache');
userLogin.value = userInfo != null; userLogin.value = userInfo != null;
dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( dynamicBadgeType.value = DynamicBadgeMode.values[setting.get(

View File

@ -7,7 +7,6 @@ import 'package:pilipala/models/common/dynamic_badge_mode.dart';
import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/dynamics/index.dart';
import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/media/index.dart';
import 'package:pilipala/pages/rank/index.dart';
import 'package:pilipala/utils/event_bus.dart'; import 'package:pilipala/utils/event_bus.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
@ -23,7 +22,6 @@ class MainApp extends StatefulWidget {
class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin { class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
final MainController _mainController = Get.put(MainController()); final MainController _mainController = Get.put(MainController());
final HomeController _homeController = Get.put(HomeController()); final HomeController _homeController = Get.put(HomeController());
final RankController _rankController = Get.put(RankController());
final DynamicsController _dynamicController = Get.put(DynamicsController()); final DynamicsController _dynamicController = Get.put(DynamicsController());
final MediaController _mediaController = Get.put(MediaController()); final MediaController _mediaController = Get.put(MediaController());
@ -59,21 +57,6 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
_homeController.flag = false; _homeController.flag = false;
} }
if (currentPage is RankPage) {
if (_rankController.flag) {
// 单击返回顶部 双击并刷新
if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) {
_rankController.onRefresh();
} else {
_rankController.animateToTop();
}
_lastSelectTime = DateTime.now().millisecondsSinceEpoch;
}
_rankController.flag = true;
} else {
_rankController.flag = false;
}
if (currentPage is DynamicsPage) { if (currentPage is DynamicsPage) {
if (_dynamicController.flag) { if (_dynamicController.flag) {
// 单击返回顶部 双击并刷新 // 单击返回顶部 双击并刷新

View File

@ -28,11 +28,6 @@ class MediaController extends GetxController {
'title': '我的收藏', 'title': '我的收藏',
'onTap': () => Get.toNamed('/fav'), 'onTap': () => Get.toNamed('/fav'),
}, },
{
'icon': Icons.subscriptions_outlined,
'title': '我的订阅',
'onTap': () => Get.toNamed('/subscription'),
},
{ {
'icon': Icons.watch_later_outlined, 'icon': Icons.watch_later_outlined,
'title': '稍后再看', 'title': '稍后再看',

View File

@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:media_kit/media_kit.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/main/index.dart';
@ -103,11 +102,7 @@ class _MediaPageState extends State<MediaPage>
], ],
Obx(() => mediaController.userLogin.value Obx(() => mediaController.userLogin.value
? favFolder(mediaController, context) ? favFolder(mediaController, context)
: const SizedBox()), : const SizedBox())
SizedBox(
height: MediaQuery.of(context).padding.bottom +
kBottomNavigationBarHeight,
)
], ],
), ),
), ),

View File

@ -20,7 +20,7 @@ class MemberController extends GetxController {
Box userInfoCache = GStrorage.userInfo; Box userInfoCache = GStrorage.userInfo;
late int ownerMid; late int ownerMid;
// 投稿列表 // 投稿列表
RxList<VListItemModel>? archiveList = <VListItemModel>[].obs; RxList<VListItemModel>? archiveList = [VListItemModel()].obs;
dynamic userInfo; dynamic userInfo;
RxInt attribute = (-1).obs; RxInt attribute = (-1).obs;
RxString attributeText = '关注'.obs; RxString attributeText = '关注'.obs;

View File

@ -281,8 +281,8 @@ class _MemberPageState extends State<MemberPage>
future: _futureBuilderFuture, future: _futureBuilderFuture,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
Map? data = snapshot.data; Map data = snapshot.data!;
if (data != null && data['status']) { if (data['status']) {
return Obx( return Obx(
() => Stack( () => Stack(
alignment: AlignmentDirectional.center, alignment: AlignmentDirectional.center,
@ -302,14 +302,7 @@ class _MemberPageState extends State<MemberPage>
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.titleMedium! .titleMedium!
.copyWith( .copyWith(fontWeight: FontWeight.bold),
fontWeight: FontWeight.bold,
color: _memberController.memberInfo.value
.vip!.nicknameColor !=
null
? Color(_memberController.memberInfo
.value.vip!.nicknameColor!)
: null),
)), )),
const SizedBox(width: 2), const SizedBox(width: 2),
if (_memberController.memberInfo.value.sex == '') if (_memberController.memberInfo.value.sex == '')

View File

@ -18,32 +18,45 @@ class MemberSeasonsPanel extends StatelessWidget {
itemBuilder: (context, index) { itemBuilder: (context, index) {
MemberSeasonsList item = data!.seasonsList![index]; MemberSeasonsList item = data!.seasonsList![index];
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.only(bottom: 12, right: 4),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ListTile( Padding(
onTap: () => Get.toNamed( padding: const EdgeInsets.only(bottom: 12, left: 4),
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'), child: Row(
title: Text( children: [
Text(
item.meta!.name!, item.meta!.name!,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleSmall!, style: Theme.of(context).textTheme.titleSmall!,
), ),
dense: true, const SizedBox(width: 10),
leading: PBadge( PBadge(
stack: 'relative', stack: 'relative',
size: 'small', size: 'small',
text: item.meta!.total.toString(), text: item.meta!.total.toString(),
), ),
trailing: const Icon( const Spacer(),
SizedBox(
width: 35,
height: 35,
child: IconButton(
onPressed: () => Get.toNamed(
'/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'),
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
icon: const Icon(
Icons.arrow_forward, Icons.arrow_forward,
size: 20, size: 20,
), ),
), ),
const SizedBox(height: 10), )
],
),
),
LayoutBuilder( LayoutBuilder(
builder: (context, boxConstraints) { builder: (context, boxConstraints) {
return GridView.builder( return GridView.builder(

View File

@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/common/widgets/video_card_h.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import '../../common/widgets/http_error.dart';
import 'controller.dart'; import 'controller.dart';
class MemberArchivePage extends StatefulWidget { class MemberArchivePage extends StatefulWidget {
@ -87,16 +86,10 @@ class _MemberArchivePageState extends State<MemberArchivePage> {
: const SliverToBoxAdapter(), : const SliverToBoxAdapter(),
); );
} else { } else {
return HttpError( return const SliverToBoxAdapter();
errMsg: snapshot.data['msg'],
fn: () {},
);
} }
} else { } else {
return HttpError( return const SliverToBoxAdapter();
errMsg: snapshot.data['msg'],
fn: () {},
);
} }
} else { } else {
return const SliverToBoxAdapter(); return const SliverToBoxAdapter();

View File

@ -4,7 +4,6 @@ import 'package:get/get.dart';
import 'package:pilipala/pages/member_dynamics/index.dart'; import 'package:pilipala/pages/member_dynamics/index.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import '../../common/widgets/http_error.dart';
import '../dynamics/widgets/dynamic_panel.dart'; import '../dynamics/widgets/dynamic_panel.dart';
class MemberDynamicsPage extends StatefulWidget { class MemberDynamicsPage extends StatefulWidget {
@ -81,16 +80,10 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage> {
: const SliverToBoxAdapter(), : const SliverToBoxAdapter(),
); );
} else { } else {
return HttpError( return const SliverToBoxAdapter();
errMsg: snapshot.data['msg'],
fn: () {},
);
} }
} else { } else {
return HttpError( return const SliverToBoxAdapter();
errMsg: snapshot.data['msg'],
fn: () {},
);
} }
} else { } else {
return const SliverToBoxAdapter(); return const SliverToBoxAdapter();

View File

@ -135,14 +135,25 @@ class _ImagePreviewState extends State<ImagePreview>
), ),
body: Stack( body: Stack(
children: [ children: [
GestureDetector( DismissiblePage(
backgroundColor: Colors.transparent,
onDismissed: () {
Navigator.of(context).pop();
},
// Note that scrollable widget inside DismissiblePage might limit the functionality
// If scroll direction matches DismissiblePage direction
direction: DismissiblePageDismissDirection.down,
disabled: _dismissDisabled,
isFullScreen: true,
child: GestureDetector(
onLongPress: () => onOpenMenu(), onLongPress: () => onOpenMenu(),
child: ExtendedImageGesturePageView.builder( child: ExtendedImageGesturePageView.builder(
controller: ExtendedPageController( controller: ExtendedPageController(
initialPage: _previewController.initialPage.value, initialPage: _previewController.initialPage.value,
pageSpacing: 0, pageSpacing: 0,
), ),
onPageChanged: (int index) => _previewController.onChange(index), onPageChanged: (int index) =>
_previewController.onChange(index),
canScrollPage: (GestureDetails? gestureDetails) => canScrollPage: (GestureDetails? gestureDetails) =>
gestureDetails!.totalScale! <= 1.0, gestureDetails!.totalScale! <= 1.0,
itemCount: widget.imgList!.length, itemCount: widget.imgList!.length,
@ -234,14 +245,13 @@ class _ImagePreviewState extends State<ImagePreview>
}, },
), ),
), ),
),
Positioned( Positioned(
left: 0, left: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
child: Container( child: Container(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: 20,
right: 20,
bottom: MediaQuery.of(context).padding.bottom + 30), bottom: MediaQuery.of(context).padding.bottom + 30),
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
@ -254,34 +264,20 @@ class _ImagePreviewState extends State<ImagePreview>
tileMode: TileMode.mirror, tileMode: TileMode.mirror,
), ),
), ),
child: Row( child: Obx(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
widget.imgList!.length > 1
? Obx(
() => Text.rich( () => Text.rich(
textAlign: TextAlign.center, textAlign: TextAlign.center,
TextSpan( TextSpan(
style: const TextStyle( style: const TextStyle(color: Colors.white, fontSize: 15),
color: Colors.white, fontSize: 16),
children: [ children: [
TextSpan( TextSpan(
text: _previewController.currentPage text: _previewController.currentPage.toString()),
.toString()),
const TextSpan(text: ' / '), const TextSpan(text: ' / '),
TextSpan( TextSpan(text: widget.imgList!.length.toString()),
text:
widget.imgList!.length.toString()),
]), ]),
), ),
)
: const SizedBox(),
IconButton(
onPressed: () => Get.back(),
icon: const Icon(Icons.close, color: Colors.white),
), ),
], ),
)),
), ),
], ],
), ),

View File

@ -1,54 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/models/common/rank_type.dart';
import 'package:pilipala/utils/storage.dart';
class RankController extends GetxController with GetTickerProviderStateMixin {
bool flag = false;
late RxList tabs = [].obs;
RxInt initialIndex = 0.obs;
late TabController tabController;
late List tabsCtrList;
late List<Widget> tabsPageList;
Box setting = GStrorage.setting;
late final StreamController<bool> searchBarStream =
StreamController<bool>.broadcast();
late bool enableGradientBg;
@override
void onInit() {
super.onInit();
enableGradientBg =
setting.get(SettingBoxKey.enableGradientBg, defaultValue: true);
// 进行tabs配置
setTabConfig();
}
void onRefresh() {
int index = tabController.index;
var ctr = tabsCtrList[index];
ctr().onRefresh();
}
void animateToTop() {
int index = tabController.index;
var ctr = tabsCtrList[index];
ctr().animateToTop();
}
void setTabConfig() async {
tabs.value = tabsConfig;
initialIndex.value = 0;
tabsCtrList = tabs.map((e) => e['ctr']).toList();
tabsPageList = tabs.map<Widget>((e) => e['page']).toList();
tabController = TabController(
initialIndex: initialIndex.value,
length: tabs.length,
vsync: this,
);
}
}

View File

@ -1,4 +0,0 @@
library rank;
export './controller.dart';
export './view.dart';

View File

@ -1,149 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:pilipala/utils/feed_back.dart';
import './controller.dart';
class RankPage extends StatefulWidget {
const RankPage({Key? key}) : super(key: key);
@override
State<RankPage> createState() => _RankPageState();
}
class _RankPageState extends State<RankPage>
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
final RankController _rankController = Get.put(RankController());
List videoList = [];
late Stream<bool> stream;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
stream = _rankController.searchBarStream.stream;
}
@override
Widget build(BuildContext context) {
super.build(context);
Brightness currentBrightness = MediaQuery.of(context).platformBrightness;
// 设置状态栏图标的亮度
if (_rankController.enableGradientBg) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarIconBrightness: currentBrightness == Brightness.light
? Brightness.dark
: Brightness.light,
));
}
return Scaffold(
extendBody: true,
extendBodyBehindAppBar: false,
appBar: _rankController.enableGradientBg
? null
: AppBar(toolbarHeight: 0, elevation: 0),
body: Stack(
children: [
// gradient background
if (_rankController.enableGradientBg) ...[
Align(
alignment: Alignment.topLeft,
child: Opacity(
opacity: 0.6,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context)
.colorScheme
.primary
.withOpacity(0.9),
Theme.of(context)
.colorScheme
.primary
.withOpacity(0.5),
Theme.of(context).colorScheme.surface
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
stops: const [0, 0.0034, 0.34]),
),
),
),
),
],
Column(
children: [
const CustomAppBar(),
if (_rankController.tabs.length > 1) ...[
const SizedBox(height: 4),
SizedBox(
width: double.infinity,
height: 42,
child: Align(
alignment: Alignment.center,
child: TabBar(
controller: _rankController.tabController,
tabs: [
for (var i in _rankController.tabs)
Tab(text: i['label'])
],
isScrollable: true,
dividerColor: Colors.transparent,
enableFeedback: true,
splashBorderRadius: BorderRadius.circular(10),
tabAlignment: TabAlignment.center,
onTap: (value) {
feedBack();
if (_rankController.initialIndex.value == value) {
_rankController.tabsCtrList[value]().animateToTop();
}
_rankController.initialIndex.value = value;
},
),
),
),
] else ...[
const SizedBox(height: 6),
],
Expanded(
child: TabBarView(
controller: _rankController.tabController,
children: _rankController.tabsPageList,
),
),
],
),
],
),
);
}
}
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final double height;
const CustomAppBar({
super.key,
this.height = kToolbarHeight,
});
@override
Size get preferredSize => Size.fromHeight(height);
@override
Widget build(BuildContext context) {
final double top = MediaQuery.of(context).padding.top;
return Container(
width: MediaQuery.of(context).size.width,
height: top,
color: Colors.transparent,
);
}
}

View File

@ -1,53 +0,0 @@
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/model_hot_video_item.dart';
class ZoneController extends GetxController {
final ScrollController scrollController = ScrollController();
RxList<HotVideoItemModel> videoList = <HotVideoItemModel>[].obs;
bool isLoadingMore = false;
bool flag = false;
OverlayEntry? popupDialog;
int zoneID = 0;
// 获取推荐
Future queryRankFeed(type, rid) async {
zoneID = rid;
var res = await VideoHttp.getRankVideoList(zoneID);
if (res['status']) {
if (type == 'init') {
videoList.value = res['data'];
} else if (type == 'onRefresh') {
videoList.clear();
videoList.addAll(res['data']);
} else if (type == 'onLoad') {
videoList.clear();
videoList.addAll(res['data']);
}
}
isLoadingMore = false;
return res;
}
// 下拉刷新
Future onRefresh() async {
queryRankFeed('onRefresh', zoneID);
}
// 上拉加载
Future onLoad() async {
queryRankFeed('onLoad', zoneID);
}
// 返回顶部并刷新
void animateToTop() async {
if (scrollController.offset >=
MediaQuery.of(Get.context!).size.height * 5) {
scrollController.jumpTo(0);
} else {
await scrollController.animateTo(0,
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
}
}
}

View File

@ -1,4 +0,0 @@
library rank.zone;
export './controller.dart';
export './view.dart';

View File

@ -1,154 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/animated_dialog.dart';
import 'package:pilipala/common/widgets/overlay_pop.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/pages/main/index.dart';
import 'package:pilipala/pages/rank/zone/index.dart';
class ZonePage extends StatefulWidget {
const ZonePage({Key? key, required this.rid}) : super(key: key);
final int rid;
@override
State<ZonePage> createState() => _ZonePageState();
}
class _ZonePageState extends State<ZonePage>
with AutomaticKeepAliveClientMixin {
late ZoneController _zoneController;
List videoList = [];
Future? _futureBuilderFuture;
late ScrollController scrollController;
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
_zoneController = Get.put(ZoneController(), tag: widget.rid.toString());
_futureBuilderFuture = _zoneController.queryRankFeed('init', widget.rid);
scrollController = _zoneController.scrollController;
StreamController<bool> mainStream =
Get.find<MainController>().bottomBarStream;
StreamController<bool> searchBarStream =
Get.find<HomeController>().searchBarStream;
scrollController.addListener(
() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
if (!_zoneController.isLoadingMore) {
_zoneController.isLoadingMore = true;
_zoneController.onLoad();
}
}
final ScrollDirection direction =
scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
mainStream.add(true);
searchBarStream.add(true);
} else if (direction == ScrollDirection.reverse) {
mainStream.add(false);
searchBarStream.add(false);
}
},
);
}
@override
void dispose() {
scrollController.removeListener(() {});
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
return RefreshIndicator(
onRefresh: () async {
return await _zoneController.onRefresh();
},
child: CustomScrollView(
controller: _zoneController.scrollController,
slivers: [
SliverPadding(
// 单列布局 EdgeInsets.zero
padding:
const EdgeInsets.fromLTRB(0, StyleString.safeSpace - 5, 0, 0),
sliver: FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (data['status']) {
return Obx(
() => SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return VideoCardH(
videoItem: _zoneController.videoList[index],
showPubdate: true,
longPress: () {
_zoneController.popupDialog = _createPopupDialog(
_zoneController.videoList[index]);
Overlay.of(context)
.insert(_zoneController.popupDialog!);
},
longPressEnd: () {
_zoneController.popupDialog?.remove();
},
);
}, childCount: _zoneController.videoList.length),
),
);
} else {
return HttpError(
errMsg: data['msg'],
fn: () {
setState(() {
_futureBuilderFuture =
_zoneController.queryRankFeed('init', widget.rid);
});
},
);
}
} else {
// 骨架屏
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoCardHSkeleton();
}, childCount: 10),
);
}
},
),
),
SliverToBoxAdapter(
child: SizedBox(
height: MediaQuery.of(context).padding.bottom + 10,
),
)
],
),
);
}
OverlayEntry _createPopupDialog(videoItem) {
return OverlayEntry(
builder: (context) => AnimatedDialog(
closeFn: _zoneController.popupDialog?.remove,
child: OverlayPop(
videoItem: videoItem, closeFn: _zoneController.popupDialog?.remove),
),
);
}
}

View File

@ -15,7 +15,7 @@ class SSearchController extends GetxController {
Box histiryWord = GStrorage.historyword; Box histiryWord = GStrorage.historyword;
List historyCacheList = []; List historyCacheList = [];
RxList historyList = [].obs; RxList historyList = [].obs;
RxList<SearchSuggestItem> searchSuggestList = <SearchSuggestItem>[].obs; RxList<SearchSuggestItem> searchSuggestList = [SearchSuggestItem()].obs;
final _debouncer = final _debouncer =
Debouncer(delay: const Duration(milliseconds: 200)); // 设置延迟时间 Debouncer(delay: const Duration(milliseconds: 200)); // 设置延迟时间
String hintText = '搜索'; String hintText = '搜索';
@ -115,7 +115,7 @@ class SSearchController extends GetxController {
onLongSelect(word) { onLongSelect(word) {
int index = historyList.indexOf(word); int index = historyList.indexOf(word);
historyList.removeAt(index); historyList.value = historyList.removeAt(index);
historyList.refresh(); historyList.refresh();
histiryWord.put('cacheList', historyList); histiryWord.put('cacheList', historyList);
} }

View File

@ -8,7 +8,6 @@ import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/login.dart'; import 'package:pilipala/utils/login.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import '../../models/common/dynamic_badge_mode.dart'; import '../../models/common/dynamic_badge_mode.dart';
import '../../models/common/nav_bar_config.dart';
import '../main/index.dart'; import '../main/index.dart';
import 'widgets/select_dialog.dart'; import 'widgets/select_dialog.dart';
@ -24,7 +23,6 @@ class SettingController extends GetxController {
Rx<ThemeType> themeType = ThemeType.system.obs; Rx<ThemeType> themeType = ThemeType.system.obs;
var userInfo; var userInfo;
Rx<DynamicBadgeMode> dynamicBadgeType = DynamicBadgeMode.number.obs; Rx<DynamicBadgeMode> dynamicBadgeType = DynamicBadgeMode.number.obs;
RxInt defaultHomePage = 0.obs;
@override @override
void onInit() { void onInit() {
@ -42,8 +40,6 @@ class SettingController extends GetxController {
dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( dynamicBadgeType.value = DynamicBadgeMode.values[setting.get(
SettingBoxKey.dynamicBadgeMode, SettingBoxKey.dynamicBadgeMode,
defaultValue: DynamicBadgeMode.number.code)]; defaultValue: DynamicBadgeMode.number.code)];
defaultHomePage.value =
setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0);
} }
loginOut() async { loginOut() async {
@ -114,24 +110,4 @@ class SettingController extends GetxController {
SmartDialog.showToast('设置成功'); SmartDialog.showToast('设置成功');
} }
} }
// 设置默认启动页
seteDefaultHomePage(BuildContext context) async {
int? result = await showDialog(
context: context,
builder: (context) {
return SelectDialog<int>(
title: '首页启动页',
value: defaultHomePage.value,
values: defaultNavigationBars.map((e) {
return {'title': e['label'], 'value': e['id']};
}).toList());
},
);
if (result != null) {
defaultHomePage.value = result;
setting.put(SettingBoxKey.defaultHomePage, result);
SmartDialog.showToast('设置成功,重启生效');
}
}
} }

View File

@ -1,13 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/models/common/dynamics_type.dart'; import 'package:pilipala/models/common/dynamics_type.dart';
import 'package:pilipala/models/common/reply_sort_type.dart'; import 'package:pilipala/models/common/reply_sort_type.dart';
import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import '../home/index.dart';
import 'widgets/switch_item.dart'; import 'widgets/switch_item.dart';
class ExtraSetting extends StatefulWidget { class ExtraSetting extends StatefulWidget {
@ -140,20 +138,18 @@ class _ExtraSettingState extends State<ExtraSetting> {
), ),
body: ListView( body: ListView(
children: [ children: [
const SetSwitchItem( SetSwitchItem(
title: '大家都在搜', title: '大家都在搜',
subTitle: '是否展示「大家都在搜」', subTitle: '是否展示「大家都在搜」',
setKey: SettingBoxKey.enableHotKey, setKey: SettingBoxKey.enableHotKey,
defaultVal: true, defaultVal: true,
callFn: (val) => {SmartDialog.showToast('下次启动时生效')},
), ),
SetSwitchItem( const SetSwitchItem(
title: '搜索默认词', title: '搜索默认词',
subTitle: '是否展示搜索框默认词', subTitle: '是否展示搜索框默认词',
setKey: SettingBoxKey.enableSearchWord, setKey: SettingBoxKey.enableSearchWord,
defaultVal: true, defaultVal: true,
callFn: (val) {
Get.find<HomeController>().defaultSearch.value = '';
},
), ),
const SetSwitchItem( const SetSwitchItem(
title: '快速收藏', title: '快速收藏',
@ -173,12 +169,6 @@ class _ExtraSettingState extends State<ExtraSetting> {
setKey: SettingBoxKey.enableAi, setKey: SettingBoxKey.enableAi,
defaultVal: true, defaultVal: true,
), ),
const SetSwitchItem(
title: '相关视频推荐',
subTitle: '视频详情页推荐相关视频',
setKey: SettingBoxKey.enableRelatedVideo,
defaultVal: true,
),
ListTile( ListTile(
dense: false, dense: false,
title: Text('评论展示', style: titleStyle), title: Text('评论展示', style: titleStyle),

View File

@ -40,6 +40,10 @@ class _TabbarSetPageState extends State<TabbarSetPage> {
.where((i) => tabbarSort.contains((i['type'] as TabType).id)) .where((i) => tabbarSort.contains((i['type'] as TabType).id))
.map<String>((i) => (i['type'] as TabType).id) .map<String>((i) => (i['type'] as TabType).id)
.toList(); .toList();
if (sortedTabbar.isEmpty) {
SmartDialog.showToast('请至少设置一项!');
return;
}
settingStorage.put(SettingBoxKey.tabbarSort, sortedTabbar); settingStorage.put(SettingBoxKey.tabbarSort, sortedTabbar);
SmartDialog.showToast('保存成功,下次启动时生效'); SmartDialog.showToast('保存成功,下次启动时生效');
} }

View File

@ -1,88 +0,0 @@
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/utils/global_data.dart';
import '../../../models/common/gesture_mode.dart';
import '../../../utils/storage.dart';
import '../widgets/select_dialog.dart';
import '../widgets/switch_item.dart';
class PlayGesturePage extends StatefulWidget {
const PlayGesturePage({super.key});
@override
State<PlayGesturePage> createState() => _PlayGesturePageState();
}
class _PlayGesturePageState extends State<PlayGesturePage> {
Box setting = GStrorage.setting;
late int fullScreenGestureMode;
@override
void initState() {
super.initState();
fullScreenGestureMode = setting.get(SettingBoxKey.fullScreenGestureMode,
defaultValue: FullScreenGestureMode.values.last.index);
}
@override
Widget build(BuildContext context) {
TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!;
TextStyle subTitleStyle = Theme.of(context)
.textTheme
.labelMedium!
.copyWith(color: Theme.of(context).colorScheme.outline);
return Scaffold(
appBar: AppBar(
centerTitle: false,
titleSpacing: 0,
title: Text(
'手势设置',
style: Theme.of(context).textTheme.titleMedium,
),
),
body: ListView(
children: [
ListTile(
dense: false,
title: Text('全屏手势', style: titleStyle),
subtitle: Text(
'通过手势快速进入全屏',
style: subTitleStyle,
),
onTap: () async {
String? result = await showDialog(
context: context,
builder: (context) {
return SelectDialog<String>(
title: '全屏手势',
value: FullScreenGestureMode
.values[fullScreenGestureMode].values,
values: FullScreenGestureMode.values.map((e) {
return {'title': e.labels, 'value': e.values};
}).toList());
},
);
if (result != null) {
GlobalData().fullScreenGestureMode = FullScreenGestureMode
.values
.firstWhere((element) => element.values == result);
fullScreenGestureMode =
GlobalData().fullScreenGestureMode.index;
setting.put(
SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode);
setState(() {});
}
},
),
const SetSwitchItem(
title: '双击快退/快进',
subTitle: '左侧双击快退,右侧双击快进',
setKey: SettingBoxKey.enableQuickDouble,
defaultVal: true,
),
],
),
);
}
}

View File

@ -17,7 +17,6 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
Box videoStorage = GStrorage.video; Box videoStorage = GStrorage.video;
Box settingStorage = GStrorage.setting; Box settingStorage = GStrorage.setting;
late double playSpeedDefault; late double playSpeedDefault;
late List<double> playSpeedSystem;
late double longPressSpeedDefault; late double longPressSpeedDefault;
late List customSpeedsList; late List customSpeedsList;
late bool enableAutoLongPressSpeed; late bool enableAutoLongPressSpeed;
@ -54,9 +53,6 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// 系统预设倍速
playSpeedSystem =
videoStorage.get(VideoBoxKey.playSpeedSystem, defaultValue: playSpeed);
// 默认倍速 // 默认倍速
playSpeedDefault = playSpeedDefault =
videoStorage.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0); videoStorage.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0);
@ -68,7 +64,6 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
videoStorage.get(VideoBoxKey.customSpeedsList, defaultValue: []); videoStorage.get(VideoBoxKey.customSpeedsList, defaultValue: []);
enableAutoLongPressSpeed = settingStorage enableAutoLongPressSpeed = settingStorage
.get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false); .get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false);
// 开启动态长按倍速时不展示
if (enableAutoLongPressSpeed) { if (enableAutoLongPressSpeed) {
Map newItem = sheetMenu[1]; Map newItem = sheetMenu[1];
newItem['show'] = false; newItem['show'] = false;
@ -128,7 +123,7 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
} }
// 设定倍速弹窗 // 设定倍速弹窗
void showBottomSheet(String type, int i) { void showBottomSheet(type, i) {
showModalBottomSheet<void>( showModalBottomSheet<void>(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@ -164,11 +159,18 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
} }
// //
void menuAction(type, int index, id) async { void menuAction(type, index, id) async {
double chooseSpeed = 1.0; double chooseSpeed = 1.0;
if (type == 'system' && id == -1) {
SmartDialog.showToast('系统预设倍速不支持删除');
return;
}
// 获取当前选中的倍速值 // 获取当前选中的倍速值
chooseSpeed = if (type == 'system') {
type == 'system' ? playSpeedSystem[index] : customSpeedsList[index]; chooseSpeed = PlaySpeed.values[index].value;
} else {
chooseSpeed = customSpeedsList[index];
}
// 设置 // 设置
if (id == 1) { if (id == 1) {
// 设置默认倍速 // 设置默认倍速
@ -180,22 +182,17 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
videoStorage.put( videoStorage.put(
VideoBoxKey.longPressSpeedDefault, longPressSpeedDefault); VideoBoxKey.longPressSpeedDefault, longPressSpeedDefault);
} else if (id == -1) { } else if (id == -1) {
late List speedsList = if (customSpeedsList[index] == playSpeedDefault) {
type == 'system' ? playSpeedSystem : customSpeedsList; playSpeedDefault = 1.0;
if (speedsList[index] == playSpeedDefault) { videoStorage.put(VideoBoxKey.playSpeedDefault, playSpeedDefault);
SmartDialog.showToast('默认倍速不可删除');
} }
if (speedsList[index] == longPressSpeedDefault) { if (customSpeedsList[index] == longPressSpeedDefault) {
longPressSpeedDefault = 2.0; longPressSpeedDefault = 2.0;
videoStorage.put( videoStorage.put(
VideoBoxKey.longPressSpeedDefault, longPressSpeedDefault); VideoBoxKey.longPressSpeedDefault, longPressSpeedDefault);
} }
speedsList.removeAt(index); customSpeedsList.removeAt(index);
await videoStorage.put( await videoStorage.put(VideoBoxKey.customSpeedsList, customSpeedsList);
type == 'system'
? VideoBoxKey.playSpeedSystem
: VideoBoxKey.customSpeedsList,
speedsList);
} }
setState(() {}); setState(() {});
SmartDialog.showToast('操作成功'); SmartDialog.showToast('操作成功');
@ -252,7 +249,6 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
subtitle: Text(longPressSpeedDefault.toString()), subtitle: Text(longPressSpeedDefault.toString()),
) )
: const SizedBox(), : const SizedBox(),
if (playSpeedSystem.isNotEmpty) ...[
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 14, left: 14,
@ -276,16 +272,15 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
spacing: 8, spacing: 8,
runSpacing: 2, runSpacing: 2,
children: [ children: [
for (int i = 0; i < playSpeedSystem.length; i++) ...[ for (var i in PlaySpeed.values) ...[
FilledButton.tonal( FilledButton.tonal(
onPressed: () => showBottomSheet('system', i), onPressed: () => showBottomSheet('system', i.index),
child: Text(playSpeedSystem[i].toString()), child: Text(i.description),
), ),
] ]
], ],
), ),
) ),
],
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 14, left: 14,

View File

@ -7,10 +7,8 @@ import 'package:pilipala/models/video/play/quality.dart';
import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart';
import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart';
import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/services/service_locator.dart';
import 'package:pilipala/utils/global_data.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import '../../models/live/quality.dart';
import 'widgets/switch_item.dart'; import 'widgets/switch_item.dart';
class PlaySetting extends StatefulWidget { class PlaySetting extends StatefulWidget {
@ -23,7 +21,6 @@ class PlaySetting extends StatefulWidget {
class _PlaySettingState extends State<PlaySetting> { class _PlaySettingState extends State<PlaySetting> {
Box setting = GStrorage.setting; Box setting = GStrorage.setting;
late dynamic defaultVideoQa; late dynamic defaultVideoQa;
late dynamic defaultLiveQa;
late dynamic defaultAudioQa; late dynamic defaultAudioQa;
late dynamic defaultDecode; late dynamic defaultDecode;
late int defaultFullScreenMode; late int defaultFullScreenMode;
@ -34,8 +31,6 @@ class _PlaySettingState extends State<PlaySetting> {
super.initState(); super.initState();
defaultVideoQa = setting.get(SettingBoxKey.defaultVideoQa, defaultVideoQa = setting.get(SettingBoxKey.defaultVideoQa,
defaultValue: VideoQuality.values.last.code); defaultValue: VideoQuality.values.last.code);
defaultLiveQa = setting.get(SettingBoxKey.defaultLiveQa,
defaultValue: LiveQuality.values.last.code);
defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa,
defaultValue: AudioQuality.values.last.code); defaultValue: AudioQuality.values.last.code);
defaultDecode = setting.get(SettingBoxKey.defaultDecode, defaultDecode = setting.get(SettingBoxKey.defaultDecode,
@ -78,12 +73,6 @@ class _PlaySettingState extends State<PlaySetting> {
title: Text('倍速设置', style: titleStyle), title: Text('倍速设置', style: titleStyle),
subtitle: Text('设置视频播放速度', style: subTitleStyle), subtitle: Text('设置视频播放速度', style: subTitleStyle),
), ),
ListTile(
dense: false,
onTap: () => Get.toNamed('/playerGestureSet'),
title: Text('手势设置', style: titleStyle),
subtitle: Text('设置播放器手势', style: subTitleStyle),
),
const SetSwitchItem( const SetSwitchItem(
title: '开启1080P', title: '开启1080P',
subTitle: '免登录查看1080P视频', subTitle: '免登录查看1080P视频',
@ -145,25 +134,23 @@ class _PlaySettingState extends State<PlaySetting> {
setKey: SettingBoxKey.enableAutoBrightness, setKey: SettingBoxKey.enableAutoBrightness,
defaultVal: false, defaultVal: false,
), ),
const SetSwitchItem(
title: '双击快退/快进',
subTitle: '左侧双击快退,右侧双击快进',
setKey: SettingBoxKey.enableQuickDouble,
defaultVal: true,
),
const SetSwitchItem( const SetSwitchItem(
title: '弹幕开关', title: '弹幕开关',
subTitle: '展示弹幕', subTitle: '展示弹幕',
setKey: SettingBoxKey.enableShowDanmaku, setKey: SettingBoxKey.enableShowDanmaku,
defaultVal: false, defaultVal: false,
), ),
SetSwitchItem(
title: '控制栏动画',
subTitle: '播放器控制栏显示动画效果',
setKey: SettingBoxKey.enablePlayerControlAnimation,
defaultVal: true,
callFn: (bool val) {
GlobalData().enablePlayerControlAnimation = val;
}),
ListTile( ListTile(
dense: false, dense: false,
title: Text('默认视频画质', style: titleStyle), title: Text('默认画质', style: titleStyle),
subtitle: Text( subtitle: Text(
'当前默认画质${VideoQualityCode.fromCode(defaultVideoQa)!.description!}', '当前画质${VideoQualityCode.fromCode(defaultVideoQa)!.description!}',
style: subTitleStyle, style: subTitleStyle,
), ),
onTap: () async { onTap: () async {
@ -171,7 +158,7 @@ class _PlaySettingState extends State<PlaySetting> {
context: context, context: context,
builder: (context) { builder: (context) {
return SelectDialog<int>( return SelectDialog<int>(
title: '默认视频画质', title: '默认画质',
value: defaultVideoQa, value: defaultVideoQa,
values: VideoQuality.values.reversed.map((e) { values: VideoQuality.values.reversed.map((e) {
return {'title': e.description, 'value': e.code}; return {'title': e.description, 'value': e.code};
@ -185,32 +172,6 @@ class _PlaySettingState extends State<PlaySetting> {
} }
}, },
), ),
ListTile(
dense: false,
title: Text('默认直播画质', style: titleStyle),
subtitle: Text(
'当前默认画质${LiveQualityCode.fromCode(defaultLiveQa)!.description!}',
style: subTitleStyle,
),
onTap: () async {
int? result = await showDialog(
context: context,
builder: (context) {
return SelectDialog<int>(
title: '默认直播画质',
value: defaultLiveQa,
values: LiveQuality.values.reversed.map((e) {
return {'title': e.description, 'value': e.code};
}).toList());
},
);
if (result != null) {
defaultLiveQa = result;
setting.put(SettingBoxKey.defaultLiveQa, result);
setState(() {});
}
},
),
ListTile( ListTile(
dense: false, dense: false,
title: Text('默认音质', style: titleStyle), title: Text('默认音质', style: titleStyle),

View File

@ -8,11 +8,9 @@ import 'package:pilipala/models/common/theme_type.dart';
import 'package:pilipala/pages/setting/pages/color_select.dart'; import 'package:pilipala/pages/setting/pages/color_select.dart';
import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart';
import 'package:pilipala/pages/setting/widgets/slide_dialog.dart'; import 'package:pilipala/pages/setting/widgets/slide_dialog.dart';
import 'package:pilipala/utils/global_data.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import '../../models/common/dynamic_badge_mode.dart'; import '../../models/common/dynamic_badge_mode.dart';
import '../../models/common/nav_bar_config.dart';
import 'controller.dart'; import 'controller.dart';
import 'widgets/switch_item.dart'; import 'widgets/switch_item.dart';
@ -30,6 +28,7 @@ class _StyleSettingState extends State<StyleSetting> {
Box setting = GStrorage.setting; Box setting = GStrorage.setting;
late int picQuality; late int picQuality;
late double toastOpacity;
late ThemeType _tempThemeValue; late ThemeType _tempThemeValue;
late dynamic defaultCustomRows; late dynamic defaultCustomRows;
@ -37,6 +36,7 @@ class _StyleSettingState extends State<StyleSetting> {
void initState() { void initState() {
super.initState(); super.initState();
picQuality = setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); picQuality = setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10);
toastOpacity = setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0);
_tempThemeValue = settingController.themeType.value; _tempThemeValue = settingController.themeType.value;
defaultCustomRows = setting.get(SettingBoxKey.customRows, defaultValue: 2); defaultCustomRows = setting.get(SettingBoxKey.customRows, defaultValue: 2);
} }
@ -102,12 +102,6 @@ class _StyleSettingState extends State<StyleSetting> {
defaultVal: true, defaultVal: true,
needReboot: true, needReboot: true,
), ),
const SetSwitchItem(
title: '首页底栏背景渐变',
setKey: SettingBoxKey.enableGradientBg,
defaultVal: true,
needReboot: true,
),
ListTile( ListTile(
onTap: () async { onTap: () async {
int? result = await showDialog( int? result = await showDialog(
@ -176,8 +170,6 @@ class _StyleSettingState extends State<StyleSetting> {
SettingBoxKey.defaultPicQa, picQuality); SettingBoxKey.defaultPicQa, picQuality);
Get.back(); Get.back();
settingController.picQuality.value = picQuality; settingController.picQuality.value = picQuality;
GlobalData().imgQuality = picQuality;
SmartDialog.showToast('设置成功');
}, },
child: const Text('确定'), child: const Text('确定'),
) )
@ -266,14 +258,6 @@ class _StyleSettingState extends State<StyleSetting> {
'当前主题:${colorSelectController.type.value == 0 ? '动态取色' : '指定颜色'}', '当前主题:${colorSelectController.type.value == 0 ? '动态取色' : '指定颜色'}',
style: subTitleStyle)), style: subTitleStyle)),
), ),
ListTile(
dense: false,
onTap: () => settingController.seteDefaultHomePage(context),
title: Text('默认启动页', style: titleStyle),
subtitle: Obx(() => Text(
'当前启动页:${defaultNavigationBars.firstWhere((e) => e['id'] == settingController.defaultHomePage.value)['label']}',
style: subTitleStyle)),
),
ListTile( ListTile(
dense: false, dense: false,
onTap: () => Get.toNamed('/fontSizeSetting'), onTap: () => Get.toNamed('/fontSizeSetting'),

View File

@ -44,7 +44,6 @@ class _SelectDialogState<T> extends State<SelectDialog<T>> {
setState(() { setState(() {
_tempValue = value as T; _tempValue = value as T;
}); });
Navigator.pop(context, _tempValue);
}, },
), ),
] ]
@ -52,6 +51,19 @@ class _SelectDialogState<T> extends State<SelectDialog<T>> {
), ),
); );
}), }),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(
'取消',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () => Navigator.pop(context, _tempValue),
child: const Text('确定'),
)
],
); );
} }
} }

View File

@ -1,85 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/user/info.dart';
import 'package:pilipala/utils/storage.dart';
import '../../models/user/sub_folder.dart';
class SubController extends GetxController {
final ScrollController scrollController = ScrollController();
Rx<SubFolderModelData> subFolderData = SubFolderModelData().obs;
Box userInfoCache = GStrorage.userInfo;
UserInfoData? userInfo;
int currentPage = 1;
int pageSize = 20;
RxBool hasMore = true.obs;
Future<dynamic> querySubFolder({type = 'init'}) async {
userInfo = userInfoCache.get('userInfoCache');
if (userInfo == null) {
return {'status': false, 'msg': '账号未登录'};
}
var res = await UserHttp.userSubFolder(
pn: currentPage,
ps: pageSize,
mid: userInfo!.mid!,
);
if (res['status']) {
if (type == 'init') {
subFolderData.value = res['data'];
} else {
if (res['data'].list.isNotEmpty) {
subFolderData.value.list!.addAll(res['data'].list);
subFolderData.update((val) {});
}
}
currentPage++;
} else {
SmartDialog.showToast(res['msg']);
}
return res;
}
Future onLoad() async {
querySubFolder(type: 'onload');
}
// 取消订阅
Future<void> cancelSub(SubFolderItemData subFolderItem) async {
showDialog(
context: Get.context!,
builder: (context) => AlertDialog(
title: const Text('提示'),
content: const Text('确定取消订阅吗?'),
actions: [
TextButton(
onPressed: () {
Get.back();
},
child: Text(
'取消',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
var res = await UserHttp.cancelSub(seasonId: subFolderItem.id!);
if (res['status']) {
subFolderData.value.list!.remove(subFolderItem);
subFolderData.update((val) {});
SmartDialog.showToast('取消订阅成功');
} else {
SmartDialog.showToast(res['msg']);
}
Get.back();
},
child: const Text('确定'),
),
],
),
);
}
}

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