Merge branch 'main' into feature-appScheme
This commit is contained in:
3
.github/workflows/beta_ci.yml
vendored
3
.github/workflows/beta_ci.yml
vendored
@ -12,7 +12,6 @@ on:
|
|||||||
- ".idea/**"
|
- ".idea/**"
|
||||||
- "!.github/workflows/**"
|
- "!.github/workflows/**"
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update_version:
|
update_version:
|
||||||
name: Read and update version
|
name: Read and update version
|
||||||
@ -96,7 +95,7 @@ jobs:
|
|||||||
if: steps.cache-flutter.outputs.cache-hit != 'true'
|
if: steps.cache-flutter.outputs.cache-hit != 'true'
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: 3.16.5
|
flutter-version: 3.19.6
|
||||||
channel: any
|
channel: any
|
||||||
|
|
||||||
- name: 下载项目依赖
|
- name: 下载项目依赖
|
||||||
|
|||||||
4
.github/workflows/release_ci.yml
vendored
4
.github/workflows/release_ci.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
|||||||
if: steps.cache-flutter.outputs.cache-hit != 'true'
|
if: steps.cache-flutter.outputs.cache-hit != 'true'
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: 3.16.5
|
flutter-version: 3.19.6
|
||||||
channel: any
|
channel: any
|
||||||
|
|
||||||
- name: 下载项目依赖
|
- name: 下载项目依赖
|
||||||
@ -98,7 +98,7 @@ jobs:
|
|||||||
uses: subosito/flutter-action@v2.10.0
|
uses: subosito/flutter-action@v2.10.0
|
||||||
with:
|
with:
|
||||||
cache: true
|
cache: true
|
||||||
flutter-version: 3.16.5
|
flutter-version: 3.19.6
|
||||||
|
|
||||||
- name: flutter build ipa
|
- name: flutter build ipa
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 1.7 KiB |
23
change_log/1.0.24.0626.md
Normal file
23
change_log/1.0.24.0626.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
## 1.0.24
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
+ 私信功能
|
||||||
|
+ 回复我的、收到的赞查看
|
||||||
|
+ 新的登录方式
|
||||||
|
+ 全屏选集
|
||||||
|
+ 一键三连
|
||||||
|
+ 按分区搜索
|
||||||
|
|
||||||
|
### 优化
|
||||||
|
+ 页面跳转动画
|
||||||
|
+ 评论区跳转
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
+ 音画不同步问题
|
||||||
|
+ 分集字幕未同步
|
||||||
|
+ 多语言字幕
|
||||||
|
+ 弹幕设置未生效
|
||||||
|
+
|
||||||
|
|
||||||
|
|
||||||
|
问题反馈、功能建议请查看「关于」页面。
|
||||||
39
change_log/1.0.25.1010.md
Normal file
39
change_log/1.0.25.1010.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
## 1.0.25
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
+ 直播弹幕
|
||||||
|
+ 稍后再看、收藏夹播放全部
|
||||||
|
+ 收藏夹新建、编辑
|
||||||
|
+ 评论删除
|
||||||
|
+ 评论保存为图片
|
||||||
|
+ 动态页滑动切换up
|
||||||
|
+ up投稿筛选充电视频
|
||||||
|
+ 直播tab展示关注up
|
||||||
|
+ up主页专栏展示
|
||||||
|
|
||||||
|
### 优化
|
||||||
|
+ 视频详情页一键三连
|
||||||
|
+ 动态页标识充电视频
|
||||||
|
+ 播放器亮度、音量调整百分比展示
|
||||||
|
+ 封面预览时视频标题可复制
|
||||||
|
+ 竖屏直播布局
|
||||||
|
+ 图片预览
|
||||||
|
+ 专栏渲染优化
|
||||||
|
+ 私信图片查看
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
+ 收藏夹点击异常
|
||||||
|
+ 搜索up异常
|
||||||
|
+ 系统通知已读异常
|
||||||
|
+ [赞了我的]展示错误
|
||||||
|
+ 部分up合集无法打开
|
||||||
|
+ 切换合集视频投币个数未重置
|
||||||
|
+ 搜索条件筛选面板无法滚动
|
||||||
|
+ 部分机型导航条未沉浸
|
||||||
|
+ 专栏图片渲染问题
|
||||||
|
+ 专栏浏览历史记录
|
||||||
|
+ 直播间历史记录
|
||||||
|
|
||||||
|
|
||||||
|
更多更新日志可在Github上查看
|
||||||
|
问题反馈、功能建议请查看「关于」页面。
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Flutter
|
import Flutter
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
@ -8,6 +9,14 @@ import Flutter
|
|||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
GeneratedPluginRegistrant.register(with: self)
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
|
|
||||||
|
// 设置音频会话类别,确保在静音模式下播放音频
|
||||||
|
do {
|
||||||
|
try AVAudioSession.sharedInstance().setCategory(.playback, options: [.duckOthers])
|
||||||
|
} catch {
|
||||||
|
print("Failed to set audio session category: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ class PBadge extends StatelessWidget {
|
|||||||
border: Border.all(color: borderColor),
|
border: Border.all(color: borderColor),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
text!,
|
text ?? '',
|
||||||
style: TextStyle(fontSize: fs ?? fontSize, color: color),
|
style: TextStyle(fontSize: fs ?? fontSize, color: color),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart';
|
||||||
import 'network_img_layer.dart';
|
import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart';
|
||||||
|
import 'package:pilipala/utils/highlight.dart';
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class HtmlRender extends StatelessWidget {
|
class HtmlRender extends StatelessWidget {
|
||||||
@ -22,6 +24,20 @@ class HtmlRender extends StatelessWidget {
|
|||||||
data: htmlContent,
|
data: htmlContent,
|
||||||
onLinkTap: (String? url, Map<String, String> buildContext, attributes) {},
|
onLinkTap: (String? url, Map<String, String> buildContext, attributes) {},
|
||||||
extensions: [
|
extensions: [
|
||||||
|
TagExtension(
|
||||||
|
tagsToExtend: <String>{'pre'},
|
||||||
|
builder: (ExtensionContext extensionContext) {
|
||||||
|
final Map<String, dynamic> attributes = extensionContext.attributes;
|
||||||
|
final String lang = attributes['data-lang'] as String;
|
||||||
|
final String code = attributes['codecontent'] as String;
|
||||||
|
List<String> selectedLanguages = [lang.split('@').first];
|
||||||
|
TextSpan? result = highlightExistingText(code, selectedLanguages);
|
||||||
|
if (result == null) {
|
||||||
|
return const Center(child: Text('代码块渲染失败'));
|
||||||
|
}
|
||||||
|
return SelectableText.rich(result);
|
||||||
|
},
|
||||||
|
),
|
||||||
TagExtension(
|
TagExtension(
|
||||||
tagsToExtend: <String>{'img'},
|
tagsToExtend: <String>{'img'},
|
||||||
builder: (ExtensionContext extensionContext) {
|
builder: (ExtensionContext extensionContext) {
|
||||||
@ -44,20 +60,52 @@ class HtmlRender extends StatelessWidget {
|
|||||||
if (isMall) {
|
if (isMall) {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
// bool inTable =
|
return InkWell(
|
||||||
// extensionContext.element!.previousElementSibling == null ||
|
onTap: () {
|
||||||
// extensionContext.element!.nextElementSibling == null;
|
Navigator.of(context).push(
|
||||||
// imgUrl = Utils().imageUrl(imgUrl!);
|
HeroDialogRoute<void>(
|
||||||
// return Image.network(
|
builder: (BuildContext context) =>
|
||||||
// imgUrl,
|
InteractiveviewerGallery(
|
||||||
// width: isEmote ? 22 : null,
|
sources: imgList ?? [imgUrl],
|
||||||
// height: isEmote ? 22 : null,
|
initIndex: imgList?.indexOf(imgUrl) ?? 0,
|
||||||
// );
|
itemBuilder: (
|
||||||
return NetworkImgLayer(
|
BuildContext context,
|
||||||
width: isEmote ? 22 : Get.size.width - 24,
|
int index,
|
||||||
height: isEmote ? 22 : 200,
|
bool isFocus,
|
||||||
src: imgUrl,
|
bool enablePageView,
|
||||||
|
) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
if (enablePageView) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Center(
|
||||||
|
child: Hero(
|
||||||
|
tag: imgList?[index] ?? imgUrl,
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
fadeInDuration:
|
||||||
|
const Duration(milliseconds: 0),
|
||||||
|
imageUrl: imgList?[index] ?? imgUrl,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
onPageChanged: (int pageIndex) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: CachedNetworkImage(imageUrl: imgUrl),
|
||||||
|
);
|
||||||
|
// return NetworkImgLayer(
|
||||||
|
// width: isEmote ? 22 : Get.size.width - 24,
|
||||||
|
// height: isEmote ? 22 : 200,
|
||||||
|
// src: imgUrl,
|
||||||
|
// );
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
@ -66,7 +114,7 @@ class HtmlRender extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
style: {
|
style: {
|
||||||
'html': Style(
|
'html': Style(
|
||||||
fontSize: FontSize.medium,
|
fontSize: FontSize.large,
|
||||||
lineHeight: LineHeight.percent(140),
|
lineHeight: LineHeight.percent(140),
|
||||||
),
|
),
|
||||||
'body': Style(margin: Margins.zero, padding: HtmlPaddings.zero),
|
'body': Style(margin: Margins.zero, padding: HtmlPaddings.zero),
|
||||||
@ -78,7 +126,7 @@ class HtmlRender extends StatelessWidget {
|
|||||||
margin: Margins.only(bottom: 10),
|
margin: Margins.only(bottom: 10),
|
||||||
),
|
),
|
||||||
'span': Style(
|
'span': Style(
|
||||||
fontSize: FontSize.medium,
|
fontSize: FontSize.large,
|
||||||
height: Height(1.65),
|
height: Height(1.65),
|
||||||
),
|
),
|
||||||
'div': Style(height: Height.auto()),
|
'div': Style(height: Height.auto()),
|
||||||
|
|||||||
@ -2,12 +2,18 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
class HttpError extends StatelessWidget {
|
class HttpError extends StatelessWidget {
|
||||||
const HttpError(
|
const HttpError({
|
||||||
{required this.errMsg, required this.fn, this.btnText, super.key});
|
required this.errMsg,
|
||||||
|
required this.fn,
|
||||||
|
this.btnText,
|
||||||
|
this.isShowBtn = true,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final String? errMsg;
|
final String? errMsg;
|
||||||
final Function()? fn;
|
final Function()? fn;
|
||||||
final String? btnText;
|
final String? btnText;
|
||||||
|
final bool isShowBtn;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -29,6 +35,7 @@ class HttpError extends StatelessWidget {
|
|||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
if (isShowBtn)
|
||||||
FilledButton.tonal(
|
FilledButton.tonal(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
fn!();
|
fn!();
|
||||||
@ -40,7 +47,8 @@ class HttpError extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
btnText ?? '点击重试',
|
btnText ?? '点击重试',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
style:
|
||||||
|
TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -2,7 +2,7 @@ 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 'package:pilipala/utils/global_data_cache.dart';
|
||||||
import '../../utils/storage.dart';
|
import '../../utils/storage.dart';
|
||||||
import '../constants.dart';
|
import '../constants.dart';
|
||||||
|
|
||||||
@ -33,7 +33,11 @@ class NetworkImgLayer extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final int defaultImgQuality = GlobalData().imgQuality;
|
int defaultImgQuality = 10;
|
||||||
|
try {
|
||||||
|
defaultImgQuality = GlobalDataCache().imgQuality;
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
if (src == '' || src == null) {
|
if (src == '' || src == null) {
|
||||||
return placeholder(context);
|
return placeholder(context);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,130 +0,0 @@
|
|||||||
// ignore_for_file: depend_on_referenced_packages
|
|
||||||
|
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:ui' as ui show Image;
|
|
||||||
|
|
||||||
import 'package:extended_image/extended_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart';
|
|
||||||
|
|
||||||
double get maxDragOffset => 100;
|
|
||||||
double hideHeight = maxDragOffset / 2.3;
|
|
||||||
double refreshHeight = maxDragOffset / 1.5;
|
|
||||||
|
|
||||||
class PullToRefreshHeader extends StatelessWidget {
|
|
||||||
const PullToRefreshHeader(
|
|
||||||
this.info,
|
|
||||||
this.lastRefreshTime, {
|
|
||||||
this.color,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final PullToRefreshScrollNotificationInfo? info;
|
|
||||||
final DateTime? lastRefreshTime;
|
|
||||||
final Color? color;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final PullToRefreshScrollNotificationInfo? infos = info;
|
|
||||||
if (infos == null) {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
String text = '';
|
|
||||||
if (infos.mode == PullToRefreshIndicatorMode.armed) {
|
|
||||||
text = 'Release to refresh';
|
|
||||||
} else if (infos.mode == PullToRefreshIndicatorMode.refresh ||
|
|
||||||
infos.mode == PullToRefreshIndicatorMode.snap) {
|
|
||||||
text = 'Loading...';
|
|
||||||
} else if (infos.mode == PullToRefreshIndicatorMode.done) {
|
|
||||||
text = 'Refresh completed.';
|
|
||||||
} else if (infos.mode == PullToRefreshIndicatorMode.drag) {
|
|
||||||
text = 'Pull to refresh';
|
|
||||||
} else if (infos.mode == PullToRefreshIndicatorMode.canceled) {
|
|
||||||
text = 'Cancel refresh';
|
|
||||||
}
|
|
||||||
|
|
||||||
final TextStyle ts = const TextStyle(
|
|
||||||
color: Colors.grey,
|
|
||||||
).copyWith(fontSize: 14);
|
|
||||||
|
|
||||||
final double dragOffset = info?.dragOffset ?? 0.0;
|
|
||||||
|
|
||||||
final DateTime time = lastRefreshTime ?? DateTime.now();
|
|
||||||
final double top = -hideHeight + dragOffset;
|
|
||||||
return Container(
|
|
||||||
height: dragOffset,
|
|
||||||
color: color ?? Colors.transparent,
|
|
||||||
// padding: EdgeInsets.only(top: dragOffset / 3),
|
|
||||||
// padding: EdgeInsets.only(bottom: 5.0),
|
|
||||||
child: Stack(
|
|
||||||
children: <Widget>[
|
|
||||||
Positioned(
|
|
||||||
left: 0.0,
|
|
||||||
right: 0.0,
|
|
||||||
top: top,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
margin: const EdgeInsets.only(right: 12.0),
|
|
||||||
child: RefreshImage(top, null),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
Text(text, style: ts),
|
|
||||||
Text(
|
|
||||||
'Last updated:${DateFormat('yyyy-MM-dd hh:mm').format(time)}',
|
|
||||||
style: ts.copyWith(fontSize: 14),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RefreshImage extends StatelessWidget {
|
|
||||||
const RefreshImage(this.top, Key? key) : super(key: key);
|
|
||||||
|
|
||||||
final double top;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
const double imageSize = 30;
|
|
||||||
return ExtendedImage.asset(
|
|
||||||
'assets/flutterCandies_grey.png',
|
|
||||||
width: imageSize,
|
|
||||||
height: imageSize,
|
|
||||||
afterPaintImage: (Canvas canvas, Rect rect, ui.Image image, Paint paint) {
|
|
||||||
final double imageHeight = image.height.toDouble();
|
|
||||||
final double imageWidth = image.width.toDouble();
|
|
||||||
final Size size = rect.size;
|
|
||||||
final double y =
|
|
||||||
(1 - min(top / (refreshHeight - hideHeight), 1)) * imageHeight;
|
|
||||||
|
|
||||||
canvas.drawImageRect(
|
|
||||||
image,
|
|
||||||
Rect.fromLTWH(0.0, y, imageWidth, imageHeight - y),
|
|
||||||
Rect.fromLTWH(rect.left, rect.top + y / imageHeight * size.height,
|
|
||||||
size.width, (imageHeight - y) / imageHeight * size.height),
|
|
||||||
Paint()
|
|
||||||
..colorFilter =
|
|
||||||
const ColorFilter.mode(Color(0xFFea5504), BlendMode.srcIn)
|
|
||||||
..isAntiAlias = false
|
|
||||||
..filterQuality = FilterQuality.low,
|
|
||||||
);
|
|
||||||
|
|
||||||
//canvas.restore();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,7 +6,7 @@ class StatDanMu extends StatelessWidget {
|
|||||||
final dynamic danmu;
|
final dynamic danmu;
|
||||||
final String? size;
|
final String? size;
|
||||||
|
|
||||||
const StatDanMu({Key? key, this.theme, this.danmu, this.size})
|
const StatDanMu({Key? key, this.theme = 'gray', this.danmu, this.size})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -17,21 +17,46 @@ class StatDanMu extends StatelessWidget {
|
|||||||
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
|
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
|
||||||
};
|
};
|
||||||
Color color = colorObject[theme]!;
|
Color color = colorObject[theme]!;
|
||||||
|
return StatIconText(
|
||||||
|
icon: Icons.subtitles_outlined,
|
||||||
|
text: Utils.numFormat(danmu!),
|
||||||
|
color: color,
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatIconText extends StatelessWidget {
|
||||||
|
final IconData icon;
|
||||||
|
final String text;
|
||||||
|
final Color color;
|
||||||
|
final String? size;
|
||||||
|
|
||||||
|
const StatIconText({
|
||||||
|
Key? key,
|
||||||
|
required this.icon,
|
||||||
|
required this.text,
|
||||||
|
required this.color,
|
||||||
|
this.size,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.subtitles_outlined,
|
icon,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
Text(
|
Text(
|
||||||
Utils.numFormat(danmu!),
|
text,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: size == 'medium' ? 12 : 11,
|
fontSize: size == 'medium' ? 12 : 11,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,12 @@ class StatView extends StatelessWidget {
|
|||||||
final dynamic view;
|
final dynamic view;
|
||||||
final String? size;
|
final String? size;
|
||||||
|
|
||||||
const StatView({Key? key, this.theme, this.view, this.size})
|
const StatView({
|
||||||
: super(key: key);
|
Key? key,
|
||||||
|
this.theme = 'gray',
|
||||||
|
this.view,
|
||||||
|
this.size,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -17,16 +21,41 @@ class StatView extends StatelessWidget {
|
|||||||
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
|
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
|
||||||
};
|
};
|
||||||
Color color = colorObject[theme]!;
|
Color color = colorObject[theme]!;
|
||||||
|
return StatIconText(
|
||||||
|
icon: Icons.play_circle_outlined,
|
||||||
|
text: Utils.numFormat(view!),
|
||||||
|
color: color,
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatIconText extends StatelessWidget {
|
||||||
|
final IconData icon;
|
||||||
|
final String text;
|
||||||
|
final Color color;
|
||||||
|
final String? size;
|
||||||
|
|
||||||
|
const StatIconText({
|
||||||
|
Key? key,
|
||||||
|
required this.icon,
|
||||||
|
required this.text,
|
||||||
|
required this.color,
|
||||||
|
this.size,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.play_circle_outlined,
|
icon,
|
||||||
size: 13,
|
size: 13,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
Text(
|
Text(
|
||||||
Utils.numFormat(view!),
|
text,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: size == 'medium' ? 12 : 11,
|
fontSize: size == 'medium' ? 12 : 11,
|
||||||
color: color,
|
color: color,
|
||||||
|
|||||||
@ -266,17 +266,11 @@ class VideoContent extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (showView) ...[
|
if (showView) ...[
|
||||||
StatView(
|
StatView(view: videoItem.stat.view as int),
|
||||||
theme: 'gray',
|
|
||||||
view: videoItem.stat.view as int,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
],
|
],
|
||||||
if (showDanmaku)
|
if (showDanmaku)
|
||||||
StatDanMu(
|
StatDanMu(danmu: videoItem.stat.danmaku as int),
|
||||||
theme: 'gray',
|
|
||||||
danmu: videoItem.stat.danmaku as int,
|
|
||||||
),
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (source == 'normal')
|
if (source == 'normal')
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|||||||
@ -60,17 +60,13 @@ class VideoCardV extends StatelessWidget {
|
|||||||
// 动态
|
// 动态
|
||||||
case 'picture':
|
case 'picture':
|
||||||
try {
|
try {
|
||||||
String dynamicType = 'picture';
|
|
||||||
String uri = videoItem.uri;
|
String uri = videoItem.uri;
|
||||||
String id = '';
|
|
||||||
if (videoItem.uri.startsWith('bilibili://article/')) {
|
if (videoItem.uri.startsWith('bilibili://article/')) {
|
||||||
// https://www.bilibili.com/read/cv27063554
|
// https://www.bilibili.com/read/cv27063554
|
||||||
dynamicType = 'read';
|
|
||||||
RegExp regex = RegExp(r'\d+');
|
RegExp regex = RegExp(r'\d+');
|
||||||
Match match = regex.firstMatch(videoItem.uri)!;
|
Match match = regex.firstMatch(videoItem.uri)!;
|
||||||
String matchedNumber = match.group(0)!;
|
String matchedNumber = match.group(0)!;
|
||||||
videoItem.param = int.parse(matchedNumber);
|
videoItem.param = int.parse(matchedNumber);
|
||||||
id = 'cv${videoItem.param}';
|
|
||||||
}
|
}
|
||||||
if (uri.startsWith('http')) {
|
if (uri.startsWith('http')) {
|
||||||
String path = Uri.parse(uri).path;
|
String path = Uri.parse(uri).path;
|
||||||
@ -88,11 +84,10 @@ class VideoCardV extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Get.toNamed('/htmlRender', parameters: {
|
Get.toNamed('/read', parameters: {
|
||||||
'url': uri,
|
|
||||||
'title': videoItem.title,
|
'title': videoItem.title,
|
||||||
'id': id,
|
'id': videoItem.param,
|
||||||
'dynamicType': dynamicType
|
'articleType': 'read'
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
SmartDialog.showToast(err.toString());
|
SmartDialog.showToast(err.toString());
|
||||||
@ -215,9 +210,8 @@ class VideoContent extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (videoItem.goto == 'bangumi')
|
if (videoItem.goto == 'bangumi')
|
||||||
_buildBadge(videoItem.bangumiBadge, 'line', 9),
|
_buildBadge(videoItem.bangumiBadge, 'line', 9),
|
||||||
if (videoItem.rcmdReason?.content != null &&
|
if (videoItem.rcmdReason != null)
|
||||||
videoItem.rcmdReason.content != '')
|
_buildBadge(videoItem.rcmdReason, 'color'),
|
||||||
_buildBadge(videoItem.rcmdReason.content, 'color'),
|
|
||||||
if (videoItem.goto == 'picture') _buildBadge('动态', 'line', 9),
|
if (videoItem.goto == 'picture') _buildBadge('动态', 'line', 9),
|
||||||
if (videoItem.isFollowed == 1) _buildBadge('已关注', 'color'),
|
if (videoItem.isFollowed == 1) _buildBadge('已关注', 'color'),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -288,9 +282,9 @@ class VideoStat extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(theme: 'gray', view: videoItem.stat.view),
|
StatView(view: videoItem.stat.view),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
StatDanMu(theme: 'gray', danmu: videoItem.stat.danmu),
|
StatDanMu(danmu: videoItem.stat.danmu),
|
||||||
if (videoItem is RecVideoItemModel) ...<Widget>[
|
if (videoItem is RecVideoItemModel) ...<Widget>[
|
||||||
crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8),
|
crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8),
|
||||||
RichText(
|
RichText(
|
||||||
|
|||||||
@ -487,6 +487,8 @@ class Api {
|
|||||||
static const getSeasonDetailApi =
|
static const getSeasonDetailApi =
|
||||||
'/x/polymer/web-space/seasons_archives_list';
|
'/x/polymer/web-space/seasons_archives_list';
|
||||||
|
|
||||||
|
static const getSeriesDetailApi = '/x/series/archives';
|
||||||
|
|
||||||
/// 获取未读动态数
|
/// 获取未读动态数
|
||||||
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
|
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
|
||||||
|
|
||||||
@ -548,4 +550,49 @@ class Api {
|
|||||||
|
|
||||||
/// 收到的赞
|
/// 收到的赞
|
||||||
static const String messageLikeAPi = '/x/msgfeed/like';
|
static const String messageLikeAPi = '/x/msgfeed/like';
|
||||||
|
|
||||||
|
/// 系统通知
|
||||||
|
static const String messageSystemAPi =
|
||||||
|
'${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify';
|
||||||
|
|
||||||
|
/// 系统通知 个人
|
||||||
|
static const String userMessageSystemAPi =
|
||||||
|
'${HttpString.messageBaseUrl}/x/sys-msg/query_user_notify';
|
||||||
|
|
||||||
|
/// 系统通知标记已读
|
||||||
|
static const String systemMarkRead =
|
||||||
|
'${HttpString.messageBaseUrl}/x/sys-msg/update_cursor';
|
||||||
|
|
||||||
|
/// 编辑收藏夹
|
||||||
|
static const String editFavFolder = '/x/v3/fav/folder/edit';
|
||||||
|
|
||||||
|
/// 新建收藏夹
|
||||||
|
static const String addFavFolder = '/x/v3/fav/folder/add';
|
||||||
|
|
||||||
|
/// 直播间弹幕信息
|
||||||
|
static const String getDanmuInfo =
|
||||||
|
'${HttpString.liveBaseUrl}/xlive/web-room/v1/index/getDanmuInfo';
|
||||||
|
|
||||||
|
/// 直播间发送弹幕
|
||||||
|
static const String sendLiveMsg = '${HttpString.liveBaseUrl}/msg/send';
|
||||||
|
|
||||||
|
/// 我的关注 - 正在直播
|
||||||
|
static const String getFollowingLive =
|
||||||
|
'${HttpString.liveBaseUrl}/xlive/web-ucenter/user/following';
|
||||||
|
|
||||||
|
/// 稍后再看&收藏夹视频列表
|
||||||
|
static const String mediaList = '/x/v2/medialist/resource/list';
|
||||||
|
|
||||||
|
/// 用户专栏
|
||||||
|
static const String opusList = '/x/polymer/web-dynamic/v1/opus/feed/space';
|
||||||
|
|
||||||
|
///
|
||||||
|
static const String getViewInfo = '/x/article/viewinfo';
|
||||||
|
|
||||||
|
/// 直播间记录
|
||||||
|
static const String liveRoomEntry =
|
||||||
|
'${HttpString.liveBaseUrl}/xlive/web-room/v1/index/roomEntryAction';
|
||||||
|
|
||||||
|
/// 删除评论
|
||||||
|
static const String replyDel = '/x/v2/reply/del';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ class BlackHttp {
|
|||||||
static Future removeBlack({required int fid}) async {
|
static Future removeBlack({required int fid}) async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.removeBlack,
|
Api.removeBlack,
|
||||||
queryParameters: {
|
data: {
|
||||||
'act': 6,
|
'act': 6,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
'fid': fid,
|
'fid': fid,
|
||||||
|
|||||||
@ -5,6 +5,8 @@ class HttpString {
|
|||||||
static const String appBaseUrl = 'https://app.bilibili.com';
|
static const String appBaseUrl = 'https://app.bilibili.com';
|
||||||
static const String liveBaseUrl = 'https://api.live.bilibili.com';
|
static const String liveBaseUrl = 'https://api.live.bilibili.com';
|
||||||
static const String passBaseUrl = 'https://passport.bilibili.com';
|
static const String passBaseUrl = 'https://passport.bilibili.com';
|
||||||
|
static const String messageBaseUrl = 'https://message.bilibili.com';
|
||||||
|
static const String bangumiBaseUrl = 'https://bili.meark.me';
|
||||||
static const List<int> validateStatusCodes = [
|
static const List<int> validateStatusCodes = [
|
||||||
302,
|
302,
|
||||||
304,
|
304,
|
||||||
|
|||||||
@ -67,9 +67,6 @@ class DanmakaHttp {
|
|||||||
var response = await Request().post(
|
var response = await Request().post(
|
||||||
Api.shootDanmaku,
|
Api.shootDanmaku,
|
||||||
data: params,
|
data: params,
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
import '../models/dynamics/result.dart';
|
import '../models/dynamics/result.dart';
|
||||||
import '../models/dynamics/up.dart';
|
import '../models/dynamics/up.dart';
|
||||||
import 'index.dart';
|
import 'index.dart';
|
||||||
@ -69,7 +70,7 @@ class DynamicsHttp {
|
|||||||
}) async {
|
}) async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.likeDynamic,
|
Api.likeDynamic,
|
||||||
queryParameters: {
|
data: {
|
||||||
'dynamic_id': dynamicId,
|
'dynamic_id': dynamicId,
|
||||||
'up': up,
|
'up': up,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
@ -175,12 +176,15 @@ class DynamicsHttp {
|
|||||||
'revs_id': {'dyn_type': 8, 'rid': oid}
|
'revs_id': {'dyn_type': 8, 'rid': oid}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var res = await Request().post(Api.dynamicCreate, queryParameters: {
|
var res = await Request().post(
|
||||||
|
Api.dynamicCreate,
|
||||||
|
queryParameters: {
|
||||||
'platform': 'web',
|
'platform': 'web',
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'},
|
'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'},
|
||||||
'x-bili-web-req-json': {'spm_id': '333.999'},
|
'x-bili-web-req-json': {'spm_id': '333.999'},
|
||||||
}, data: {
|
},
|
||||||
|
data: {
|
||||||
'dyn_req': {
|
'dyn_req': {
|
||||||
'content': {
|
'content': {
|
||||||
'contents': [
|
'contents': [
|
||||||
@ -195,7 +199,9 @@ class DynamicsHttp {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'web_repost_src': webRepostSrc
|
'web_repost_src': webRepostSrc
|
||||||
});
|
},
|
||||||
|
options: Options(contentType: 'application/json'),
|
||||||
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
'status': true,
|
'status': true,
|
||||||
|
|||||||
67
lib/http/fav.dart
Normal file
67
lib/http/fav.dart
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import 'index.dart';
|
||||||
|
|
||||||
|
class FavHttp {
|
||||||
|
/// 编辑收藏夹
|
||||||
|
static Future editFolder({
|
||||||
|
required String title,
|
||||||
|
required String intro,
|
||||||
|
required String mediaId,
|
||||||
|
String? cover,
|
||||||
|
int? privacy,
|
||||||
|
}) async {
|
||||||
|
var res = await Request().post(
|
||||||
|
Api.editFavFolder,
|
||||||
|
data: {
|
||||||
|
'title': title,
|
||||||
|
'intro': intro,
|
||||||
|
'media_id': mediaId,
|
||||||
|
'cover': cover ?? '',
|
||||||
|
'privacy': privacy ?? 0,
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data'],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 新建收藏夹
|
||||||
|
static Future addFolder({
|
||||||
|
required String title,
|
||||||
|
required String intro,
|
||||||
|
String? cover,
|
||||||
|
int? privacy,
|
||||||
|
}) async {
|
||||||
|
var res = await Request().post(
|
||||||
|
Api.addFavFolder,
|
||||||
|
data: {
|
||||||
|
'title': title,
|
||||||
|
'intro': intro,
|
||||||
|
'cover': cover ?? '',
|
||||||
|
'privacy': privacy ?? 0,
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data'],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,11 +27,14 @@ 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="([^"]+?)">');
|
static final RegExp spmPrefixExp =
|
||||||
|
RegExp(r'<meta name="spm_prefix" content="([^"]+?)">');
|
||||||
|
static String? buvid;
|
||||||
|
|
||||||
/// 设置cookie
|
/// 设置cookie
|
||||||
static setCookie() async {
|
static setCookie() async {
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
|
Box setting = GStrorage.setting;
|
||||||
final String cookiePath = await Utils.getCookiePath();
|
final String cookiePath = await Utils.getCookiePath();
|
||||||
final PersistCookieJar cookieJar = PersistCookieJar(
|
final PersistCookieJar cookieJar = PersistCookieJar(
|
||||||
ignoreExpires: true,
|
ignoreExpires: true,
|
||||||
@ -54,7 +57,11 @@ class Request {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
|
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
|
||||||
|
String baseUrlType = 'default';
|
||||||
|
if (setting.get(SettingBoxKey.enableGATMode, defaultValue: false)) {
|
||||||
|
baseUrlType = 'bangumi';
|
||||||
|
}
|
||||||
|
setBaseUrl(type: baseUrlType);
|
||||||
try {
|
try {
|
||||||
await buvidActivate();
|
await buvidActivate();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -64,6 +71,7 @@ class Request {
|
|||||||
final String cookieString = cookie
|
final String cookieString = cookie
|
||||||
.map((Cookie cookie) => '${cookie.name}=${cookie.value}')
|
.map((Cookie cookie) => '${cookie.name}=${cookie.value}')
|
||||||
.join('; ');
|
.join('; ');
|
||||||
|
|
||||||
dio.options.headers['cookie'] = cookieString;
|
dio.options.headers['cookie'] = cookieString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +86,30 @@ class Request {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<String> getBuvid() async {
|
||||||
|
if (buvid != null) {
|
||||||
|
return buvid!;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Cookie> cookies = await cookieManager.cookieJar
|
||||||
|
.loadForRequest(Uri.parse(HttpString.baseUrl));
|
||||||
|
buvid = cookies.firstWhere((cookie) => cookie.name == 'buvid3').value;
|
||||||
|
if (buvid == null) {
|
||||||
|
try {
|
||||||
|
var result = await Request().get(
|
||||||
|
"${HttpString.apiBaseUrl}/x/frontend/finger/spi",
|
||||||
|
);
|
||||||
|
buvid = result["data"]["b_3"].toString();
|
||||||
|
} catch (e) {
|
||||||
|
// 处理请求错误
|
||||||
|
buvid = '';
|
||||||
|
print("Error fetching buvid: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buvid!;
|
||||||
|
}
|
||||||
|
|
||||||
static setOptionsHeaders(userInfo, bool status) {
|
static setOptionsHeaders(userInfo, bool status) {
|
||||||
if (status) {
|
if (status) {
|
||||||
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
|
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
|
||||||
@ -98,8 +130,7 @@ class Request {
|
|||||||
List<int>.generate(32, (_) => rand.nextInt(256)) +
|
List<int>.generate(32, (_) => rand.nextInt(256)) +
|
||||||
List<int>.filled(4, 0) +
|
List<int>.filled(4, 0) +
|
||||||
[73, 69, 78, 68] +
|
[73, 69, 78, 68] +
|
||||||
List<int>.generate(4, (_) => rand.nextInt(256))
|
List<int>.generate(4, (_) => rand.nextInt(256)));
|
||||||
);
|
|
||||||
|
|
||||||
String jsonData = json.encode({
|
String jsonData = json.encode({
|
||||||
'3064': 1,
|
'3064': 1,
|
||||||
@ -110,11 +141,9 @@ class Request {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await Request().post(
|
await Request().post(Api.activateBuvidApi,
|
||||||
Api.activateBuvidApi,
|
|
||||||
data: {'payload': jsonData},
|
data: {'payload': jsonData},
|
||||||
options: Options(contentType: 'application/json')
|
options: Options(contentType: 'application/json'));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -188,12 +217,13 @@ class Request {
|
|||||||
/*
|
/*
|
||||||
* get请求
|
* get请求
|
||||||
*/
|
*/
|
||||||
get(url, {data, options, cancelToken, extra}) async {
|
get(url, {data, Options? options, cancelToken, extra}) async {
|
||||||
Response response;
|
Response response;
|
||||||
final Options options = Options();
|
options ??= Options(); // 如果 options 为 null,则初始化一个新的 Options 对象
|
||||||
ResponseType resType = ResponseType.json;
|
ResponseType resType = ResponseType.json;
|
||||||
|
|
||||||
if (extra != null) {
|
if (extra != null) {
|
||||||
resType = extra!['resType'] ?? ResponseType.json;
|
resType = extra['resType'] ?? ResponseType.json;
|
||||||
if (extra['ua'] != null) {
|
if (extra['ua'] != null) {
|
||||||
options.headers = {'user-agent': headerUa(type: extra['ua'])};
|
options.headers = {'user-agent': headerUa(type: extra['ua'])};
|
||||||
}
|
}
|
||||||
@ -209,14 +239,11 @@ class Request {
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
Response errResponse = Response(
|
return Response(
|
||||||
data: {
|
data: {'message': await ApiInterceptor.dioError(e)},
|
||||||
'message': await ApiInterceptor.dioError(e)
|
|
||||||
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
|
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
requestOptions: RequestOptions(),
|
requestOptions: RequestOptions(),
|
||||||
);
|
);
|
||||||
return errResponse;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +258,8 @@ class Request {
|
|||||||
url,
|
url,
|
||||||
data: data,
|
data: data,
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
options: options,
|
options:
|
||||||
|
options ?? Options(contentType: Headers.formUrlEncodedContentType),
|
||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
// print('post success: ${response.data}');
|
// print('post success: ${response.data}');
|
||||||
@ -294,4 +322,17 @@ class Request {
|
|||||||
}
|
}
|
||||||
return headerUa;
|
return headerUa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static setBaseUrl({String type = 'default'}) {
|
||||||
|
switch (type) {
|
||||||
|
case 'default':
|
||||||
|
dio.options.baseUrl = HttpString.apiBaseUrl;
|
||||||
|
break;
|
||||||
|
case 'bangumi':
|
||||||
|
dio.options.baseUrl = HttpString.bangumiBaseUrl;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dio.options.baseUrl = HttpString.apiBaseUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,8 @@ class ApiInterceptor extends Interceptor {
|
|||||||
// 处理网络请求错误
|
// 处理网络请求错误
|
||||||
// handler.next(err);
|
// handler.next(err);
|
||||||
String url = err.requestOptions.uri.toString();
|
String url = err.requestOptions.uri.toString();
|
||||||
if (!url.contains('heartbeat')) {
|
final excludedPatterns = RegExp(r'heartbeat|seg\.so|online/total');
|
||||||
|
if (!excludedPatterns.hasMatch(url)) {
|
||||||
SmartDialog.showToast(
|
SmartDialog.showToast(
|
||||||
await dioError(err),
|
await dioError(err),
|
||||||
displayType: SmartToastType.onlyRefresh,
|
displayType: SmartToastType.onlyRefresh,
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'package:pilipala/models/live/follow.dart';
|
||||||
|
|
||||||
import '../models/live/item.dart';
|
import '../models/live/item.dart';
|
||||||
import '../models/live/room_info.dart';
|
import '../models/live/room_info.dart';
|
||||||
import '../models/live/room_info_h5.dart';
|
import '../models/live/room_info_h5.dart';
|
||||||
@ -65,4 +67,96 @@ class LiveHttp {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取弹幕信息
|
||||||
|
static Future liveDanmakuInfo({roomId}) async {
|
||||||
|
var res = await Request().get(Api.getDanmuInfo, data: {
|
||||||
|
'id': roomId,
|
||||||
|
});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data'],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送弹幕
|
||||||
|
static Future sendDanmaku({roomId, msg}) async {
|
||||||
|
var res = await Request().post(
|
||||||
|
Api.sendLiveMsg,
|
||||||
|
data: {
|
||||||
|
'bubble': 0,
|
||||||
|
'msg': msg,
|
||||||
|
'color': 16777215, // 颜色
|
||||||
|
'mode': 1, // 模式
|
||||||
|
'room_type': 0,
|
||||||
|
'jumpfrom': 71001, // 直播间来源
|
||||||
|
'reply_mid': 0,
|
||||||
|
'reply_attr': 0,
|
||||||
|
'replay_dmid': '',
|
||||||
|
'statistics': {"appId": 100, "platform": 5},
|
||||||
|
'fontsize': 25, // 字体大小
|
||||||
|
'rnd': DateTime.now().millisecondsSinceEpoch ~/ 1000, // 时间戳
|
||||||
|
'roomid': roomId,
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
'csrf_token': await Request.getCsrf(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data'],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 我的关注 正在直播
|
||||||
|
static Future liveFollowing({int? pn, int? ps}) async {
|
||||||
|
var res = await Request().get(Api.getFollowingLive, data: {
|
||||||
|
'page': pn,
|
||||||
|
'page_size': ps,
|
||||||
|
'platform': 'web',
|
||||||
|
'ignoreRecord': 1,
|
||||||
|
'hit_ab': true,
|
||||||
|
});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': LiveFollowingModel.fromJson(res.data['data'])
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直播历史记录
|
||||||
|
static Future liveRoomEntry({required int roomId}) async {
|
||||||
|
await Request().post(
|
||||||
|
Api.liveRoomEntry,
|
||||||
|
data: {
|
||||||
|
'room_id': roomId,
|
||||||
|
'platform': 'pc',
|
||||||
|
'csrf_token': await Request.getCsrf(),
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
'visit_id': '',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,9 +71,6 @@ class LoginHttp {
|
|||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.webSmsCode,
|
Api.webSmsCode,
|
||||||
data: formData,
|
data: formData,
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
@ -106,9 +103,6 @@ class LoginHttp {
|
|||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.webSmsLogin,
|
Api.webSmsLogin,
|
||||||
data: formData,
|
data: formData,
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
@ -155,9 +149,6 @@ class LoginHttp {
|
|||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.appSmsCode,
|
Api.appSmsCode,
|
||||||
data: data,
|
data: data,
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
print(res);
|
print(res);
|
||||||
}
|
}
|
||||||
@ -208,9 +199,6 @@ class LoginHttp {
|
|||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.loginInByPwdApi,
|
Api.loginInByPwdApi,
|
||||||
data: data,
|
data: data,
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
print(res);
|
print(res);
|
||||||
}
|
}
|
||||||
@ -239,17 +227,27 @@ class LoginHttp {
|
|||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.loginInByWebPwd,
|
Api.loginInByWebPwd,
|
||||||
data: formData,
|
data: formData,
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
|
if (res.data['data']['status'] == 0) {
|
||||||
return {
|
return {
|
||||||
'status': true,
|
'status': true,
|
||||||
'data': res.data['data'],
|
'data': res.data['data'],
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
return {
|
||||||
|
'status': false,
|
||||||
|
'code': 1,
|
||||||
|
'data': res.data['data'],
|
||||||
|
'msg': res.data['data']['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:html/parser.dart';
|
||||||
|
import 'package:pilipala/models/member/article.dart';
|
||||||
|
import 'package:pilipala/models/member/like.dart';
|
||||||
|
import 'package:pilipala/utils/global_data_cache.dart';
|
||||||
import '../common/constants.dart';
|
import '../common/constants.dart';
|
||||||
import '../models/dynamics/result.dart';
|
import '../models/dynamics/result.dart';
|
||||||
import '../models/follow/result.dart';
|
import '../models/follow/result.dart';
|
||||||
@ -15,14 +20,20 @@ import 'index.dart';
|
|||||||
|
|
||||||
class MemberHttp {
|
class MemberHttp {
|
||||||
static Future memberInfo({
|
static Future memberInfo({
|
||||||
int? mid,
|
required int mid,
|
||||||
String token = '',
|
String token = '',
|
||||||
}) async {
|
}) async {
|
||||||
|
String? wWebid;
|
||||||
|
if ((await getWWebid(mid: mid))['status']) {
|
||||||
|
wWebid = GlobalDataCache().wWebid;
|
||||||
|
}
|
||||||
|
|
||||||
Map params = await WbiSign().makSign({
|
Map params = await WbiSign().makSign({
|
||||||
'mid': mid,
|
'mid': mid,
|
||||||
'token': token,
|
'token': token,
|
||||||
'platform': 'web',
|
'platform': 'web',
|
||||||
'web_location': 1550101,
|
'web_location': 1550101,
|
||||||
|
...wWebid != null ? {'w_webid': wWebid} : {},
|
||||||
});
|
});
|
||||||
var res = await Request().get(
|
var res = await Request().get(
|
||||||
Api.memberInfo,
|
Api.memberInfo,
|
||||||
@ -95,7 +106,14 @@ class MemberHttp {
|
|||||||
'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2),
|
'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2),
|
||||||
'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2),
|
'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2),
|
||||||
'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}',
|
'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}',
|
||||||
|
...order == 'charge'
|
||||||
|
? {
|
||||||
|
'order': 'pubdate',
|
||||||
|
'special_type': 'charging',
|
||||||
|
}
|
||||||
|
: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
var res = await Request().get(
|
var res = await Request().get(
|
||||||
Api.memberArchive,
|
Api.memberArchive,
|
||||||
data: params,
|
data: params,
|
||||||
@ -187,13 +205,15 @@ class MemberHttp {
|
|||||||
|
|
||||||
// 设置分组
|
// 设置分组
|
||||||
static Future addUsers(int? fids, String? tagids) async {
|
static Future addUsers(int? fids, String? tagids) async {
|
||||||
var res = await Request().post(Api.addUsers, queryParameters: {
|
var res = await Request().post(
|
||||||
|
Api.addUsers,
|
||||||
|
data: {
|
||||||
'fids': fids,
|
'fids': fids,
|
||||||
'tagids': tagids ?? '0',
|
'tagids': tagids ?? '0',
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
}, data: {
|
},
|
||||||
'cross_domain': true
|
queryParameters: {'cross_domain': true},
|
||||||
});
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': [], 'msg': '操作成功'};
|
return {'status': true, 'data': [], 'msg': '操作成功'};
|
||||||
} else {
|
} else {
|
||||||
@ -328,7 +348,9 @@ class MemberHttp {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
'status': true,
|
'status': true,
|
||||||
'data': MemberSeasonsDataModel.fromJson(res.data['data']['items_lists'])
|
'data': res.data['data']['list']
|
||||||
|
.map<MemberLikeDataModel>((e) => MemberLikeDataModel.fromJson(e))
|
||||||
|
.toList(),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
@ -409,11 +431,14 @@ class MemberHttp {
|
|||||||
static Future cookieToKey() async {
|
static Future cookieToKey() async {
|
||||||
var authCodeRes = await getTVCode();
|
var authCodeRes = await getTVCode();
|
||||||
if (authCodeRes['status']) {
|
if (authCodeRes['status']) {
|
||||||
var res = await Request().post(Api.cookieToKey, queryParameters: {
|
var res = await Request().post(
|
||||||
|
Api.cookieToKey,
|
||||||
|
data: {
|
||||||
'auth_code': authCodeRes['data'],
|
'auth_code': authCodeRes['data'],
|
||||||
'build': 708200,
|
'build': 708200,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
await Future.delayed(const Duration(milliseconds: 300));
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
await qrcodePoll(authCodeRes['data']);
|
await qrcodePoll(authCodeRes['data']);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
@ -510,4 +535,96 @@ class MemberHttp {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future getSeriesDetail({
|
||||||
|
required int mid,
|
||||||
|
required int currentMid,
|
||||||
|
required int seriesId,
|
||||||
|
required int pn,
|
||||||
|
}) async {
|
||||||
|
var res = await Request().get(
|
||||||
|
Api.getSeriesDetailApi,
|
||||||
|
data: {
|
||||||
|
'mid': mid,
|
||||||
|
'series_id': seriesId,
|
||||||
|
'only_normal': true,
|
||||||
|
'sort': 'desc',
|
||||||
|
'pn': pn,
|
||||||
|
'ps': 30,
|
||||||
|
'current_mid': currentMid,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': MemberSeasonsDataModel.fromJson(res.data['data'])
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
print(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future getWWebid({required int mid}) async {
|
||||||
|
String? wWebid = GlobalDataCache().wWebid;
|
||||||
|
if (wWebid != null) {
|
||||||
|
return {'status': true, 'data': wWebid};
|
||||||
|
}
|
||||||
|
var res = await Request().get('https://space.bilibili.com/$mid/article');
|
||||||
|
String? headContent = parse(res.data).head?.outerHtml;
|
||||||
|
final regex = RegExp(
|
||||||
|
r'<script id="__RENDER_DATA__" type="application/json">(.*?)</script>');
|
||||||
|
if (headContent != null) {
|
||||||
|
final match = regex.firstMatch(headContent);
|
||||||
|
if (match != null && match.groupCount >= 1) {
|
||||||
|
final content = match.group(1);
|
||||||
|
String decodedString = Uri.decodeComponent(content!);
|
||||||
|
Map<String, dynamic> map = jsonDecode(decodedString);
|
||||||
|
GlobalDataCache().wWebid = map['access_id'];
|
||||||
|
return {'status': true, 'data': map['access_id']};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'data': '请检查登录状态'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {'status': false, 'data': '请检查登录状态'};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户专栏
|
||||||
|
static Future getMemberArticle({
|
||||||
|
required int mid,
|
||||||
|
required int pn,
|
||||||
|
String? offset,
|
||||||
|
}) async {
|
||||||
|
String? wWebid;
|
||||||
|
if ((await getWWebid(mid: mid))['status']) {
|
||||||
|
wWebid = GlobalDataCache().wWebid;
|
||||||
|
}
|
||||||
|
Map params = await WbiSign().makSign({
|
||||||
|
'host_mid': mid,
|
||||||
|
'page': pn,
|
||||||
|
'offset': offset,
|
||||||
|
'web_location': 333.999,
|
||||||
|
...wWebid != null ? {'w_webid': wWebid} : {},
|
||||||
|
});
|
||||||
|
var res = await Request().get(Api.opusList, data: params);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': MemberArticleDataModel.fromJson(res.data['data'])
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'] ?? '请求异常',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:pilipala/models/msg/like.dart';
|
import 'package:pilipala/models/msg/like.dart';
|
||||||
import 'package:pilipala/models/msg/reply.dart';
|
import 'package:pilipala/models/msg/reply.dart';
|
||||||
|
import 'package:pilipala/models/msg/system.dart';
|
||||||
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';
|
||||||
@ -149,7 +149,7 @@ class MsgHttp {
|
|||||||
'msg[msg_status]': 0,
|
'msg[msg_status]': 0,
|
||||||
'msg[content]': jsonEncode(content),
|
'msg[content]': jsonEncode(content),
|
||||||
'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
'msg[new_face_version]': 0,
|
'msg[new_face_version]': 1,
|
||||||
'msg[dev_id]': getDevId(),
|
'msg[dev_id]': getDevId(),
|
||||||
'from_firework': 0,
|
'from_firework': 0,
|
||||||
'build': 0,
|
'build': 0,
|
||||||
@ -157,9 +157,6 @@ class MsgHttp {
|
|||||||
'csrf_token': csrf,
|
'csrf_token': csrf,
|
||||||
'csrf': csrf,
|
'csrf': csrf,
|
||||||
},
|
},
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
@ -287,4 +284,69 @@ class MsgHttp {
|
|||||||
return {'status': false, 'date': [], 'msg': res.data['message']};
|
return {'status': false, 'date': [], 'msg': res.data['message']};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future messageSystem() async {
|
||||||
|
var res = await Request().get(Api.messageSystemAPi, data: {
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
'page_size': 20,
|
||||||
|
'build': 0,
|
||||||
|
'mobi_app': 'web',
|
||||||
|
});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data']['system_notify_list']
|
||||||
|
.map<MessageSystemModel>((e) => MessageSystemModel.fromJson(e))
|
||||||
|
.toList(),
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return {'status': false, 'date': [], 'msg': err.toString()};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'date': [], 'msg': res.data['message']};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统消息标记已读
|
||||||
|
static Future systemMarkRead(int cursor) async {
|
||||||
|
String csrf = await Request.getCsrf();
|
||||||
|
var res = await Request().get(Api.systemMarkRead, data: {
|
||||||
|
'csrf': csrf,
|
||||||
|
'cursor': cursor,
|
||||||
|
});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future messageSystemAccount() async {
|
||||||
|
var res = await Request().get(Api.userMessageSystemAPi, data: {
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
'page_size': 20,
|
||||||
|
'build': 0,
|
||||||
|
'mobi_app': 'web',
|
||||||
|
});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data']['system_notify_list']
|
||||||
|
.map<MessageSystemModel>((e) => MessageSystemModel.fromJson(e))
|
||||||
|
.toList(),
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return {'status': false, 'date': [], 'msg': err.toString()};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'date': [], 'msg': res.data['message']};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
116
lib/http/read.dart
Normal file
116
lib/http/read.dart
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:html/parser.dart';
|
||||||
|
import 'package:pilipala/models/read/opus.dart';
|
||||||
|
import 'package:pilipala/models/read/read.dart';
|
||||||
|
import 'package:pilipala/utils/wbi_sign.dart';
|
||||||
|
import 'index.dart';
|
||||||
|
|
||||||
|
class ReadHttp {
|
||||||
|
static List<String> extractScriptContents(String htmlContent) {
|
||||||
|
RegExp scriptRegExp = RegExp(r'<script>([\s\S]*?)<\/script>');
|
||||||
|
Iterable<Match> matches = scriptRegExp.allMatches(htmlContent);
|
||||||
|
List<String> scriptContents = [];
|
||||||
|
for (Match match in matches) {
|
||||||
|
String scriptContent = match.group(1)!;
|
||||||
|
scriptContents.add(scriptContent);
|
||||||
|
}
|
||||||
|
return scriptContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析专栏 opus格式
|
||||||
|
static Future parseArticleOpus({required String id}) async {
|
||||||
|
var res = await Request().get('https://www.bilibili.com/opus/$id', extra: {
|
||||||
|
'ua': 'pc',
|
||||||
|
});
|
||||||
|
String? headContent = parse(res.data).head?.outerHtml;
|
||||||
|
var document = parse(headContent);
|
||||||
|
var linkTags = document.getElementsByTagName('link');
|
||||||
|
bool isCv = false;
|
||||||
|
String cvId = '';
|
||||||
|
for (var linkTag in linkTags) {
|
||||||
|
var attributes = linkTag.attributes;
|
||||||
|
if (attributes.containsKey('rel') &&
|
||||||
|
attributes['rel'] == 'canonical' &&
|
||||||
|
attributes.containsKey('data-vue-meta') &&
|
||||||
|
attributes['data-vue-meta'] == 'true') {
|
||||||
|
final String cvHref = linkTag.attributes['href']!;
|
||||||
|
RegExp regex = RegExp(r'cv(\d+)');
|
||||||
|
RegExpMatch? match = regex.firstMatch(cvHref);
|
||||||
|
if (match != null) {
|
||||||
|
cvId = match.group(1)!;
|
||||||
|
} else {
|
||||||
|
print('No match found.');
|
||||||
|
}
|
||||||
|
isCv = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String scriptContent =
|
||||||
|
extractScriptContents(parse(res.data).body!.outerHtml)[0];
|
||||||
|
int startIndex = scriptContent.indexOf('{');
|
||||||
|
int endIndex = scriptContent.lastIndexOf('};');
|
||||||
|
String jsonContent = scriptContent.substring(startIndex, endIndex + 1);
|
||||||
|
// 解析JSON字符串为Map
|
||||||
|
Map<String, dynamic> jsonData = json.decode(jsonContent);
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': OpusDataModel.fromJson(jsonData),
|
||||||
|
'isCv': isCv,
|
||||||
|
'cvId': cvId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析专栏 cv格式
|
||||||
|
static Future parseArticleCv({required String id}) async {
|
||||||
|
var res = await Request().get(
|
||||||
|
'https://www.bilibili.com/read/cv$id',
|
||||||
|
extra: {'ua': 'pc'},
|
||||||
|
);
|
||||||
|
String scriptContent =
|
||||||
|
extractScriptContents(parse(res.data).body!.outerHtml)[0];
|
||||||
|
int startIndex = scriptContent.indexOf('{');
|
||||||
|
int endIndex = scriptContent.lastIndexOf('};');
|
||||||
|
String jsonContent = scriptContent.substring(startIndex, endIndex + 1);
|
||||||
|
// 解析JSON字符串为Map
|
||||||
|
Map<String, dynamic> jsonData = json.decode(jsonContent);
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': ReadDataModel.fromJson(jsonData),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
static Future getViewInfo({required String id}) async {
|
||||||
|
Map params = await WbiSign().makSign({
|
||||||
|
'id': id,
|
||||||
|
'mobi_app': 'pc',
|
||||||
|
'from': 'web',
|
||||||
|
'gaia_source': 'main_web',
|
||||||
|
'web_location': 333.976,
|
||||||
|
});
|
||||||
|
var res = await Request().get(
|
||||||
|
Api.getViewInfo,
|
||||||
|
data: {
|
||||||
|
'id': id,
|
||||||
|
'mobi_app': 'pc',
|
||||||
|
'from': 'web',
|
||||||
|
'gaia_source': 'main_web',
|
||||||
|
'web_location': 333.976,
|
||||||
|
'w_rid': params['w_rid'],
|
||||||
|
'wts': params['wts'],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data'],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -78,7 +78,7 @@ class ReplyHttp {
|
|||||||
}) async {
|
}) async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.likeReply,
|
Api.likeReply,
|
||||||
queryParameters: {
|
data: {
|
||||||
'type': type,
|
'type': type,
|
||||||
'oid': oid,
|
'oid': oid,
|
||||||
'rpid': rpid,
|
'rpid': rpid,
|
||||||
@ -115,4 +115,25 @@ class ReplyHttp {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future replyDel({
|
||||||
|
required int type, //replyType
|
||||||
|
required int oid,
|
||||||
|
required int rpid,
|
||||||
|
}) async {
|
||||||
|
var res = await Request().post(
|
||||||
|
Api.replyDel,
|
||||||
|
queryParameters: {
|
||||||
|
'type': type, //type.index
|
||||||
|
'oid': oid,
|
||||||
|
'rpid': rpid,
|
||||||
|
'csrf': await Request.getCsrf(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'msg': '删除成功'};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'msg': res.data['message']};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,11 @@ class SearchHttp {
|
|||||||
if (tids != null && tids != -1) 'tids': tids,
|
if (tids != null && tids != -1) 'tids': tids,
|
||||||
};
|
};
|
||||||
var res = await Request().get(Api.searchByType, data: reqData);
|
var res = await Request().get(Api.searchByType, data: reqData);
|
||||||
if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) {
|
if (res.data['code'] == 0) {
|
||||||
|
if (res.data['data']['numPages'] == 0) {
|
||||||
|
// 我想返回数据,使得可以通过data.list 取值,结果为[]
|
||||||
|
return {'status': true, 'data': Data()};
|
||||||
|
}
|
||||||
Object data;
|
Object data;
|
||||||
try {
|
try {
|
||||||
switch (searchType) {
|
switch (searchType) {
|
||||||
@ -125,9 +129,7 @@ class SearchHttp {
|
|||||||
return {
|
return {
|
||||||
'status': false,
|
'status': false,
|
||||||
'data': [],
|
'data': [],
|
||||||
'msg': res.data['data'] != null && res.data['data']['numPages'] == 0
|
'msg': res.data['message'],
|
||||||
? '没有相关数据'
|
|
||||||
: res.data['message'],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +143,11 @@ class SearchHttp {
|
|||||||
}
|
}
|
||||||
final dynamic res =
|
final dynamic res =
|
||||||
await Request().get(Api.ab2c, data: <String, dynamic>{...data});
|
await Request().get(Api.ab2c, data: <String, dynamic>{...data});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
return res.data['data'].first['cid'];
|
return res.data['data'].first['cid'];
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> bangumiInfo(
|
static Future<Map<String, dynamic>> bangumiInfo(
|
||||||
@ -206,3 +212,9 @@ class SearchHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Data {
|
||||||
|
List<dynamic> list;
|
||||||
|
|
||||||
|
Data({this.list = const []});
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:html/parser.dart';
|
||||||
|
import 'package:pilipala/models/video/later.dart';
|
||||||
import '../common/constants.dart';
|
import '../common/constants.dart';
|
||||||
import '../models/model_hot_video_item.dart';
|
import '../models/model_hot_video_item.dart';
|
||||||
import '../models/user/fav_detail.dart';
|
import '../models/user/fav_detail.dart';
|
||||||
@ -57,6 +62,8 @@ class UserHttp {
|
|||||||
if (res.data['data'] != null) {
|
if (res.data['data'] != null) {
|
||||||
data = FavFolderData.fromJson(res.data['data']);
|
data = FavFolderData.fromJson(res.data['data']);
|
||||||
return {'status': true, 'data': data};
|
return {'status': true, 'data': data};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'msg': '收藏夹为空'};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
@ -146,7 +153,7 @@ class UserHttp {
|
|||||||
// 暂停switchStatus传true 否则false
|
// 暂停switchStatus传true 否则false
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.pauseHistory,
|
Api.pauseHistory,
|
||||||
queryParameters: {
|
data: {
|
||||||
'switch': switchStatus,
|
'switch': switchStatus,
|
||||||
'jsonp': 'jsonp',
|
'jsonp': 'jsonp',
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
@ -165,7 +172,7 @@ class UserHttp {
|
|||||||
static Future clearHistory() async {
|
static Future clearHistory() async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.clearHistory,
|
Api.clearHistory,
|
||||||
queryParameters: {
|
data: {
|
||||||
'jsonp': 'jsonp',
|
'jsonp': 'jsonp',
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
},
|
},
|
||||||
@ -183,7 +190,7 @@ class UserHttp {
|
|||||||
}
|
}
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.toViewLater,
|
Api.toViewLater,
|
||||||
queryParameters: data,
|
data: data,
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'msg': 'yeah!稍后再看'};
|
return {'status': true, 'msg': 'yeah!稍后再看'};
|
||||||
@ -202,7 +209,7 @@ class UserHttp {
|
|||||||
params[aid != null ? 'aid' : 'viewed'] = aid ?? true;
|
params[aid != null ? 'aid' : 'viewed'] = aid ?? true;
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.toViewDel,
|
Api.toViewDel,
|
||||||
queryParameters: params,
|
data: params,
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'msg': 'yeah!成功移除'};
|
return {'status': true, 'msg': 'yeah!成功移除'};
|
||||||
@ -234,7 +241,7 @@ class UserHttp {
|
|||||||
static Future toViewClear() async {
|
static Future toViewClear() async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.toViewClear,
|
Api.toViewClear,
|
||||||
queryParameters: {
|
data: {
|
||||||
'jsonp': 'jsonp',
|
'jsonp': 'jsonp',
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
},
|
},
|
||||||
@ -250,7 +257,7 @@ class UserHttp {
|
|||||||
static Future delHistory(kid) async {
|
static Future delHistory(kid) async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.delHistory,
|
Api.delHistory,
|
||||||
queryParameters: {
|
data: {
|
||||||
'kid': kid,
|
'kid': kid,
|
||||||
'jsonp': 'jsonp',
|
'jsonp': 'jsonp',
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
@ -399,7 +406,7 @@ class UserHttp {
|
|||||||
static Future cancelSub({required int seasonId}) async {
|
static Future cancelSub({required int seasonId}) async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.cancelSub,
|
Api.cancelSub,
|
||||||
queryParameters: {
|
data: {
|
||||||
'platform': 'web',
|
'platform': 'web',
|
||||||
'season_id': seasonId,
|
'season_id': seasonId,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
@ -416,7 +423,7 @@ class UserHttp {
|
|||||||
static Future delFavFolder({required int mediaIds}) async {
|
static Future delFavFolder({required int mediaIds}) async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.delFavFolder,
|
Api.delFavFolder,
|
||||||
queryParameters: {
|
data: {
|
||||||
'media_ids': mediaIds,
|
'media_ids': mediaIds,
|
||||||
'platform': 'web',
|
'platform': 'web',
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
@ -428,4 +435,106 @@ class UserHttp {
|
|||||||
return {'status': false, 'msg': res.data['message']};
|
return {'status': false, 'msg': res.data['message']};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 稍后再看播放全部
|
||||||
|
// static Future toViewPlayAll({required int oid, required String bvid}) async {
|
||||||
|
// var res = await Request().get(
|
||||||
|
// Api.watchLaterHtml,
|
||||||
|
// data: {
|
||||||
|
// 'oid': oid,
|
||||||
|
// 'bvid': bvid,
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// String scriptContent =
|
||||||
|
// extractScriptContents(parse(res.data).body!.outerHtml)[0];
|
||||||
|
// int startIndex = scriptContent.indexOf('{');
|
||||||
|
// int endIndex = scriptContent.lastIndexOf('};');
|
||||||
|
// String jsonContent = scriptContent.substring(startIndex, endIndex + 1);
|
||||||
|
// // 解析JSON字符串为Map
|
||||||
|
// Map<String, dynamic> jsonData = json.decode(jsonContent);
|
||||||
|
// // 输出解析后的数据
|
||||||
|
// return {
|
||||||
|
// 'status': true,
|
||||||
|
// 'data': jsonData['resourceList']
|
||||||
|
// .map((e) => MediaVideoItemModel.fromJson(e))
|
||||||
|
// .toList()
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
static List<String> extractScriptContents(String htmlContent) {
|
||||||
|
RegExp scriptRegExp = RegExp(r'<script>([\s\S]*?)<\/script>');
|
||||||
|
Iterable<Match> matches = scriptRegExp.allMatches(htmlContent);
|
||||||
|
List<String> scriptContents = [];
|
||||||
|
for (Match match in matches) {
|
||||||
|
String scriptContent = match.group(1)!;
|
||||||
|
scriptContents.add(scriptContent);
|
||||||
|
}
|
||||||
|
return scriptContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 稍后再看列表
|
||||||
|
static Future getMediaList({
|
||||||
|
required int type,
|
||||||
|
required int bizId,
|
||||||
|
required int ps,
|
||||||
|
int? oid,
|
||||||
|
}) async {
|
||||||
|
var res = await Request().get(
|
||||||
|
Api.mediaList,
|
||||||
|
data: {
|
||||||
|
'mobi_app': 'web',
|
||||||
|
'type': type,
|
||||||
|
'biz_id': bizId,
|
||||||
|
'oid': oid ?? '',
|
||||||
|
'otype': 2,
|
||||||
|
'ps': ps,
|
||||||
|
'direction': false,
|
||||||
|
'desc': true,
|
||||||
|
'sort_field': 1,
|
||||||
|
'tid': 0,
|
||||||
|
'with_current': false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': res.data['data']['media_list'] != null
|
||||||
|
? res.data['data']['media_list']
|
||||||
|
.map<MediaVideoItemModel>(
|
||||||
|
(e) => MediaVideoItemModel.fromJson(e))
|
||||||
|
.toList()
|
||||||
|
: []
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'msg': res.data['message']};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析收藏夹视频
|
||||||
|
static Future parseFavVideo({
|
||||||
|
required int mediaId,
|
||||||
|
required int oid,
|
||||||
|
required String bvid,
|
||||||
|
}) async {
|
||||||
|
var res = await Request().get(
|
||||||
|
'https://www.bilibili.com/list/ml$mediaId',
|
||||||
|
data: {
|
||||||
|
'oid': mediaId,
|
||||||
|
'bvid': bvid,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
String scriptContent =
|
||||||
|
extractScriptContents(parse(res.data).body!.outerHtml)[0];
|
||||||
|
int startIndex = scriptContent.indexOf('{');
|
||||||
|
int endIndex = scriptContent.lastIndexOf('};');
|
||||||
|
String jsonContent = scriptContent.substring(startIndex, endIndex + 1);
|
||||||
|
// 解析JSON字符串为Map
|
||||||
|
Map<String, dynamic> jsonData = json.decode(jsonContent);
|
||||||
|
return {
|
||||||
|
'status': true,
|
||||||
|
'data': jsonData['resourceList']
|
||||||
|
.map<MediaVideoItemModel>((e) => MediaVideoItemModel.fromJson(e))
|
||||||
|
.toList()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,6 @@ class VideoHttp {
|
|||||||
// 添加额外的loginState变量模拟未登录状态
|
// 添加额外的loginState变量模拟未登录状态
|
||||||
static Future rcmdVideoListApp(
|
static Future rcmdVideoListApp(
|
||||||
{bool loginStatus = true, required int freshIdx}) async {
|
{bool loginStatus = true, required int freshIdx}) async {
|
||||||
try {
|
|
||||||
var res = await Request().get(
|
var res = await Request().get(
|
||||||
Api.recommendListApp,
|
Api.recommendListApp,
|
||||||
data: {
|
data: {
|
||||||
@ -83,8 +82,8 @@ class VideoHttp {
|
|||||||
'pull': freshIdx == 0 ? 'true' : 'false',
|
'pull': freshIdx == 0 ? 'true' : 'false',
|
||||||
'appkey': Constants.appKey,
|
'appkey': Constants.appKey,
|
||||||
'access_key': loginStatus
|
'access_key': loginStatus
|
||||||
? (localCache.get(LocalCacheKey.accessKey,
|
? (localCache
|
||||||
defaultValue: {})['value'] ??
|
.get(LocalCacheKey.accessKey, defaultValue: {})['value'] ??
|
||||||
'')
|
'')
|
||||||
: ''
|
: ''
|
||||||
},
|
},
|
||||||
@ -109,9 +108,6 @@ class VideoHttp {
|
|||||||
} else {
|
} else {
|
||||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
return {'status': false, 'data': [], 'msg': err.toString()};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最热视频
|
// 最热视频
|
||||||
@ -247,7 +243,7 @@ class VideoHttp {
|
|||||||
static Future coinVideo({required String bvid, required int multiply}) async {
|
static Future coinVideo({required String bvid, required int multiply}) async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.coinVideo,
|
Api.coinVideo,
|
||||||
queryParameters: {
|
data: {
|
||||||
'bvid': bvid,
|
'bvid': bvid,
|
||||||
'multiply': multiply,
|
'multiply': multiply,
|
||||||
'select_like': 0,
|
'select_like': 0,
|
||||||
@ -275,7 +271,7 @@ class VideoHttp {
|
|||||||
static Future oneThree({required String bvid}) async {
|
static Future oneThree({required String bvid}) async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.oneThree,
|
Api.oneThree,
|
||||||
queryParameters: {
|
data: {
|
||||||
'bvid': bvid,
|
'bvid': bvid,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
},
|
},
|
||||||
@ -291,7 +287,7 @@ class VideoHttp {
|
|||||||
static Future likeVideo({required String bvid, required bool type}) async {
|
static Future likeVideo({required String bvid, required bool type}) async {
|
||||||
var res = await Request().post(
|
var res = await Request().post(
|
||||||
Api.likeVideo,
|
Api.likeVideo,
|
||||||
queryParameters: {
|
data: {
|
||||||
'bvid': bvid,
|
'bvid': bvid,
|
||||||
'like': type ? 1 : 2,
|
'like': type ? 1 : 2,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
@ -307,13 +303,16 @@ class VideoHttp {
|
|||||||
// (取消)收藏
|
// (取消)收藏
|
||||||
static Future favVideo(
|
static Future favVideo(
|
||||||
{required int aid, String? addIds, String? delIds}) async {
|
{required int aid, String? addIds, String? delIds}) async {
|
||||||
var res = await Request().post(Api.favVideo, queryParameters: {
|
var res = await Request().post(
|
||||||
|
Api.favVideo,
|
||||||
|
data: {
|
||||||
'rid': aid,
|
'rid': aid,
|
||||||
'type': 2,
|
'type': 2,
|
||||||
'add_media_ids': addIds ?? '',
|
'add_media_ids': addIds ?? '',
|
||||||
'del_media_ids': delIds ?? '',
|
'del_media_ids': delIds ?? '',
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': res.data['data']};
|
return {'status': true, 'data': res.data['data']};
|
||||||
} else {
|
} else {
|
||||||
@ -351,14 +350,17 @@ class VideoHttp {
|
|||||||
if (message == '') {
|
if (message == '') {
|
||||||
return {'status': false, 'data': [], 'msg': '请输入评论内容'};
|
return {'status': false, 'data': [], 'msg': '请输入评论内容'};
|
||||||
}
|
}
|
||||||
var res = await Request().post(Api.replyAdd, queryParameters: {
|
var res = await Request().post(
|
||||||
|
Api.replyAdd,
|
||||||
|
data: {
|
||||||
'type': type.index,
|
'type': type.index,
|
||||||
'oid': oid,
|
'oid': oid,
|
||||||
'root': root == null || root == 0 ? '' : root,
|
'root': root == null || root == 0 ? '' : root,
|
||||||
'parent': parent == null || parent == 0 ? '' : parent,
|
'parent': parent == null || parent == 0 ? '' : parent,
|
||||||
'message': message,
|
'message': message,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
log(res.toString());
|
log(res.toString());
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': res.data['data']};
|
return {'status': true, 'data': res.data['data']};
|
||||||
@ -380,12 +382,15 @@ class VideoHttp {
|
|||||||
// 操作用户关系
|
// 操作用户关系
|
||||||
static Future relationMod(
|
static Future relationMod(
|
||||||
{required int mid, required int act, required int reSrc}) async {
|
{required int mid, required int act, required int reSrc}) async {
|
||||||
var res = await Request().post(Api.relationMod, queryParameters: {
|
var res = await Request().post(
|
||||||
|
Api.relationMod,
|
||||||
|
data: {
|
||||||
'fid': mid,
|
'fid': mid,
|
||||||
'act': act,
|
'act': act,
|
||||||
're_src': reSrc,
|
're_src': reSrc,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
if (act == 5) {
|
if (act == 5) {
|
||||||
List<int> blackMidsList =
|
List<int> blackMidsList =
|
||||||
@ -401,7 +406,9 @@ class VideoHttp {
|
|||||||
|
|
||||||
// 视频播放进度
|
// 视频播放进度
|
||||||
static Future heartBeat({bvid, cid, progress, realtime}) async {
|
static Future heartBeat({bvid, cid, progress, realtime}) async {
|
||||||
await Request().post(Api.heartBeat, queryParameters: {
|
await Request().post(
|
||||||
|
Api.heartBeat,
|
||||||
|
data: {
|
||||||
// 'aid': aid,
|
// 'aid': aid,
|
||||||
'bvid': bvid,
|
'bvid': bvid,
|
||||||
'cid': cid,
|
'cid': cid,
|
||||||
@ -413,15 +420,19 @@ class VideoHttp {
|
|||||||
// 'type': '',
|
// 'type': '',
|
||||||
// 'sub_type': '',
|
// 'sub_type': '',
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加追番
|
// 添加追番
|
||||||
static Future bangumiAdd({int? seasonId}) async {
|
static Future bangumiAdd({int? seasonId}) async {
|
||||||
var res = await Request().post(Api.bangumiAdd, queryParameters: {
|
var res = await Request().post(
|
||||||
|
Api.bangumiAdd,
|
||||||
|
data: {
|
||||||
'season_id': seasonId,
|
'season_id': seasonId,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'msg': res.data['result']['toast']};
|
return {'status': true, 'msg': res.data['result']['toast']};
|
||||||
} else {
|
} else {
|
||||||
@ -431,10 +442,13 @@ class VideoHttp {
|
|||||||
|
|
||||||
// 取消追番
|
// 取消追番
|
||||||
static Future bangumiDel({int? seasonId}) async {
|
static Future bangumiDel({int? seasonId}) async {
|
||||||
var res = await Request().post(Api.bangumiDel, queryParameters: {
|
var res = await Request().post(
|
||||||
|
Api.bangumiDel,
|
||||||
|
data: {
|
||||||
'season_id': seasonId,
|
'season_id': seasonId,
|
||||||
'csrf': await Request.getCsrf(),
|
'csrf': await Request.getCsrf(),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'msg': res.data['result']['toast']};
|
return {'status': true, 'msg': res.data['result']['toast']};
|
||||||
} else {
|
} else {
|
||||||
@ -492,7 +506,7 @@ class VideoHttp {
|
|||||||
return {'status': false, 'data': [], 'msg': res.data['msg']};
|
return {'status': false, 'data': [], 'msg': res.data['msg']};
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
print(err);
|
return {'status': false, 'data': [], 'msg': res.data['msg']};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,7 +536,8 @@ class VideoHttp {
|
|||||||
// 获取字幕内容
|
// 获取字幕内容
|
||||||
static Future<Map<String, dynamic>> getSubtitleContent(url) async {
|
static Future<Map<String, dynamic>> getSubtitleContent(url) async {
|
||||||
var res = await Request().get('https:$url');
|
var res = await Request().get('https:$url');
|
||||||
final String content = SubTitleUtils.convertToWebVTT(res.data['body']);
|
final String content =
|
||||||
|
await SubTitleUtils.convertToWebVTT(res.data['body']);
|
||||||
final List body = res.data['body'];
|
final List body = res.data['body'];
|
||||||
return {'content': content, 'body': body};
|
return {'content': content, 'body': body};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import 'package:pilipala/pages/main/view.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';
|
||||||
import 'package:pilipala/utils/global_data.dart';
|
import 'package:pilipala/utils/global_data_cache.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:pilipala/utils/recommend_filter.dart';
|
import 'package:pilipala/utils/recommend_filter.dart';
|
||||||
@ -33,7 +33,6 @@ void main() async {
|
|||||||
await SystemChrome.setPreferredOrientations(
|
await SystemChrome.setPreferredOrientations(
|
||||||
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
|
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
|
||||||
await GStrorage.init();
|
await GStrorage.init();
|
||||||
await setupServiceLocator();
|
|
||||||
clearLogs();
|
clearLogs();
|
||||||
Request();
|
Request();
|
||||||
await Request.setCookie();
|
await Request.setCookie();
|
||||||
@ -61,10 +60,12 @@ void main() async {
|
|||||||
systemNavigationBarColor: Colors.transparent,
|
systemNavigationBarColor: Colors.transparent,
|
||||||
systemNavigationBarDividerColor: Colors.transparent,
|
systemNavigationBarDividerColor: Colors.transparent,
|
||||||
statusBarColor: Colors.transparent,
|
statusBarColor: Colors.transparent,
|
||||||
|
systemNavigationBarContrastEnforced: false,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
PiliSchame.init();
|
PiliSchame.init();
|
||||||
|
await GlobalDataCache().initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
@ -266,10 +267,10 @@ class BuildMainApp extends StatelessWidget {
|
|||||||
VideoDetailPage.routeObserver,
|
VideoDetailPage.routeObserver,
|
||||||
SearchPage.routeObserver,
|
SearchPage.routeObserver,
|
||||||
],
|
],
|
||||||
onInit: () {
|
onReady: () async {
|
||||||
RecommendFilter();
|
RecommendFilter();
|
||||||
Data.init();
|
Data.init();
|
||||||
GlobalData();
|
setupServiceLocator();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,9 +4,12 @@ enum FullScreenGestureMode {
|
|||||||
|
|
||||||
/// 从下滑到上
|
/// 从下滑到上
|
||||||
fromBottomtoTop,
|
fromBottomtoTop,
|
||||||
|
|
||||||
|
/// 关闭手势
|
||||||
|
none,
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FullScreenGestureModeExtension on FullScreenGestureMode {
|
extension FullScreenGestureModeExtension on FullScreenGestureMode {
|
||||||
String get values => ['fromToptoBottom', 'fromBottomtoTop'][index];
|
String get values => ['fromToptoBottom', 'fromBottomtoTop', 'none'][index];
|
||||||
String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index];
|
String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏', '关闭手势'][index];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ extension SearchTypeExtension on SearchType {
|
|||||||
String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index];
|
String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索类型为视频、专栏及相簿时
|
// 搜索类型为视频时
|
||||||
enum ArchiveFilterType {
|
enum ArchiveFilterType {
|
||||||
totalrank,
|
totalrank,
|
||||||
click,
|
click,
|
||||||
@ -44,3 +44,21 @@ extension ArchiveFilterTypeExtension on ArchiveFilterType {
|
|||||||
String get description =>
|
String get description =>
|
||||||
['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index];
|
['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 搜索类型为专栏时
|
||||||
|
enum ArticleFilterType {
|
||||||
|
// 综合排序
|
||||||
|
totalrank,
|
||||||
|
// 最新发布
|
||||||
|
pubdate,
|
||||||
|
// 最多点击
|
||||||
|
click,
|
||||||
|
// 最多喜欢
|
||||||
|
attention,
|
||||||
|
// 最多评论
|
||||||
|
scores,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ArticleFilterTypeExtension on ArticleFilterType {
|
||||||
|
String get description => ['综合排序', '最新发布', '最多点击', '最多喜欢', '最多评论'][index];
|
||||||
|
}
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
enum SubtitleType {
|
|
||||||
// 中文(中国)
|
|
||||||
zhCN,
|
|
||||||
// 中文(自动翻译)
|
|
||||||
aizh,
|
|
||||||
// 英语(自动生成)
|
|
||||||
aien,
|
|
||||||
// 中文(简体)
|
|
||||||
zhHans,
|
|
||||||
// 英文(美国)
|
|
||||||
enUS,
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SubtitleTypeExtension on SubtitleType {
|
|
||||||
String get description {
|
|
||||||
switch (this) {
|
|
||||||
case SubtitleType.zhCN:
|
|
||||||
return '中文(中国)';
|
|
||||||
case SubtitleType.aizh:
|
|
||||||
return '中文(自动翻译)';
|
|
||||||
case SubtitleType.aien:
|
|
||||||
return '英语(自动生成)';
|
|
||||||
case SubtitleType.zhHans:
|
|
||||||
return '中文(简体)';
|
|
||||||
case SubtitleType.enUS:
|
|
||||||
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';
|
|
||||||
case SubtitleType.zhHans:
|
|
||||||
return 'zh-Hans';
|
|
||||||
case SubtitleType.enUS:
|
|
||||||
return 'en-US';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SubtitleCodeExtension on SubtitleType {
|
|
||||||
int get code {
|
|
||||||
switch (this) {
|
|
||||||
case SubtitleType.zhCN:
|
|
||||||
return 1;
|
|
||||||
case SubtitleType.aizh:
|
|
||||||
return 2;
|
|
||||||
case SubtitleType.aien:
|
|
||||||
return 3;
|
|
||||||
case SubtitleType.zhHans:
|
|
||||||
return 4;
|
|
||||||
case SubtitleType.enUS:
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -34,7 +34,7 @@ class RecVideoItemAppModel {
|
|||||||
String? title;
|
String? title;
|
||||||
int? isFollowed;
|
int? isFollowed;
|
||||||
RcmdOwner? owner;
|
RcmdOwner? owner;
|
||||||
RcmdReason? rcmdReason;
|
String? rcmdReason;
|
||||||
String? goto;
|
String? goto;
|
||||||
int? param;
|
int? param;
|
||||||
String? uri;
|
String? uri;
|
||||||
@ -62,25 +62,19 @@ class RecVideoItemAppModel {
|
|||||||
duration =
|
duration =
|
||||||
json['player_args'] != null ? json['player_args']['duration'] : -1;
|
json['player_args'] != null ? json['player_args']['duration'] : -1;
|
||||||
//duration = json['cover_right_text'];
|
//duration = json['cover_right_text'];
|
||||||
title = json['title'];
|
title = json['title'] ?? '获取标题失败';
|
||||||
owner = RcmdOwner.fromJson(json);
|
owner = RcmdOwner.fromJson(json);
|
||||||
rcmdReason = json['rcmd_reason_style'] != null
|
rcmdReason = json['bottom_rcmd_reason'] ?? json['top_rcmd_reason'];
|
||||||
? RcmdReason.fromJson(json['rcmd_reason_style'])
|
|
||||||
: null;
|
|
||||||
// 由于app端api并不会直接返回与owner的关注状态
|
// 由于app端api并不会直接返回与owner的关注状态
|
||||||
// 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效
|
// 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效
|
||||||
RegExp regex = RegExp(r'已关注|新关注');
|
RegExp regex = RegExp(r'已关注|新关注');
|
||||||
isFollowed = rcmdReason != null &&
|
isFollowed = regex.hasMatch(rcmdReason ?? '') ? 1 : 0;
|
||||||
rcmdReason!.content != null &&
|
|
||||||
regex.hasMatch(rcmdReason!.content!)
|
|
||||||
? 1
|
|
||||||
: 0;
|
|
||||||
// 如果是,就无需再显示推荐原因,交由view统一处理即可
|
// 如果是,就无需再显示推荐原因,交由view统一处理即可
|
||||||
if (isFollowed == 1) {
|
if (isFollowed == 1) {
|
||||||
rcmdReason = null;
|
rcmdReason = null;
|
||||||
}
|
}
|
||||||
goto = json['goto'];
|
goto = json['goto'];
|
||||||
param = int.parse(json['param']);
|
param = int.parse(json['param'] ?? '-1');
|
||||||
uri = json['uri'];
|
uri = json['uri'];
|
||||||
talkBack = json['talk_back'];
|
talkBack = json['talk_back'];
|
||||||
|
|
||||||
|
|||||||
126
lib/models/live/follow.dart
Normal file
126
lib/models/live/follow.dart
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
class LiveFollowingModel {
|
||||||
|
int? count;
|
||||||
|
List<LiveFollowingItemModel>? list;
|
||||||
|
int? liveCount;
|
||||||
|
int? neverLivedCount;
|
||||||
|
List? neverLivedFaces;
|
||||||
|
int? pageSize;
|
||||||
|
String? title;
|
||||||
|
int? totalPage;
|
||||||
|
|
||||||
|
LiveFollowingModel({
|
||||||
|
this.count,
|
||||||
|
this.list,
|
||||||
|
this.liveCount,
|
||||||
|
this.neverLivedCount,
|
||||||
|
this.neverLivedFaces,
|
||||||
|
this.pageSize,
|
||||||
|
this.title,
|
||||||
|
this.totalPage,
|
||||||
|
});
|
||||||
|
|
||||||
|
LiveFollowingModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
count = json['count'];
|
||||||
|
if (json['list'] != null) {
|
||||||
|
list = <LiveFollowingItemModel>[];
|
||||||
|
json['list'].forEach((v) {
|
||||||
|
list!.add(LiveFollowingItemModel.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
liveCount = json['live_count'];
|
||||||
|
neverLivedCount = json['never_lived_count'];
|
||||||
|
if (json['never_lived_faces'] != null) {
|
||||||
|
neverLivedFaces = <dynamic>[];
|
||||||
|
json['never_lived_faces'].forEach((v) {
|
||||||
|
neverLivedFaces!.add(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pageSize = json['pageSize'];
|
||||||
|
title = json['title'];
|
||||||
|
totalPage = json['totalPage'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LiveFollowingItemModel {
|
||||||
|
int? roomId;
|
||||||
|
int? uid;
|
||||||
|
String? uname;
|
||||||
|
String? title;
|
||||||
|
String? face;
|
||||||
|
int? liveStatus;
|
||||||
|
int? recordNum;
|
||||||
|
String? recentRecordId;
|
||||||
|
int? isAttention;
|
||||||
|
int? clipNum;
|
||||||
|
int? fansNum;
|
||||||
|
String? areaName;
|
||||||
|
String? areaValue;
|
||||||
|
String? tags;
|
||||||
|
String? recentRecordIdV2;
|
||||||
|
int? recordNumV2;
|
||||||
|
int? recordLiveTime;
|
||||||
|
String? areaNameV2;
|
||||||
|
String? roomNews;
|
||||||
|
String? watchIcon;
|
||||||
|
String? textSmall;
|
||||||
|
String? roomCover;
|
||||||
|
String? pic;
|
||||||
|
int? parentAreaId;
|
||||||
|
int? areaId;
|
||||||
|
|
||||||
|
LiveFollowingItemModel({
|
||||||
|
this.roomId,
|
||||||
|
this.uid,
|
||||||
|
this.uname,
|
||||||
|
this.title,
|
||||||
|
this.face,
|
||||||
|
this.liveStatus,
|
||||||
|
this.recordNum,
|
||||||
|
this.recentRecordId,
|
||||||
|
this.isAttention,
|
||||||
|
this.clipNum,
|
||||||
|
this.fansNum,
|
||||||
|
this.areaName,
|
||||||
|
this.areaValue,
|
||||||
|
this.tags,
|
||||||
|
this.recentRecordIdV2,
|
||||||
|
this.recordNumV2,
|
||||||
|
this.recordLiveTime,
|
||||||
|
this.areaNameV2,
|
||||||
|
this.roomNews,
|
||||||
|
this.watchIcon,
|
||||||
|
this.textSmall,
|
||||||
|
this.roomCover,
|
||||||
|
this.pic,
|
||||||
|
this.parentAreaId,
|
||||||
|
this.areaId,
|
||||||
|
});
|
||||||
|
|
||||||
|
LiveFollowingItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
roomId = json['roomid'];
|
||||||
|
uid = json['uid'];
|
||||||
|
uname = json['uname'];
|
||||||
|
title = json['title'];
|
||||||
|
face = json['face'];
|
||||||
|
liveStatus = json['live_status'];
|
||||||
|
recordNum = json['record_num'];
|
||||||
|
recentRecordId = json['recent_record_id'];
|
||||||
|
isAttention = json['is_attention'];
|
||||||
|
clipNum = json['clipnum'];
|
||||||
|
fansNum = json['fans_num'];
|
||||||
|
areaName = json['area_name'];
|
||||||
|
areaValue = json['area_value'];
|
||||||
|
tags = json['tags'];
|
||||||
|
recentRecordIdV2 = json['recent_record_id_v2'];
|
||||||
|
recordNumV2 = json['record_num_v2'];
|
||||||
|
recordLiveTime = json['record_live_time'];
|
||||||
|
areaNameV2 = json['area_name_v2'];
|
||||||
|
roomNews = json['room_news'];
|
||||||
|
watchIcon = json['watch_icon'];
|
||||||
|
textSmall = json['text_small'];
|
||||||
|
roomCover = json['room_cover'];
|
||||||
|
pic = json['room_cover'];
|
||||||
|
parentAreaId = json['parent_area_id'];
|
||||||
|
areaId = json['area_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
101
lib/models/live/message.dart
Normal file
101
lib/models/live/message.dart
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
class LiveMessageModel {
|
||||||
|
// 消息类型
|
||||||
|
final LiveMessageType type;
|
||||||
|
|
||||||
|
// 用户名
|
||||||
|
final String userName;
|
||||||
|
|
||||||
|
// 信息
|
||||||
|
final String? message;
|
||||||
|
|
||||||
|
// 数据
|
||||||
|
final dynamic data;
|
||||||
|
|
||||||
|
final String? face;
|
||||||
|
final int? uid;
|
||||||
|
final Map<String, dynamic>? emots;
|
||||||
|
|
||||||
|
// 颜色
|
||||||
|
final LiveMessageColor color;
|
||||||
|
|
||||||
|
LiveMessageModel({
|
||||||
|
required this.type,
|
||||||
|
required this.userName,
|
||||||
|
required this.message,
|
||||||
|
required this.color,
|
||||||
|
this.data,
|
||||||
|
this.face,
|
||||||
|
this.uid,
|
||||||
|
this.emots,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class LiveSuperChatMessage {
|
||||||
|
final String backgroundBottomColor;
|
||||||
|
final String backgroundColor;
|
||||||
|
final DateTime endTime;
|
||||||
|
final String face;
|
||||||
|
final String message;
|
||||||
|
final String price;
|
||||||
|
final DateTime startTime;
|
||||||
|
final String userName;
|
||||||
|
|
||||||
|
LiveSuperChatMessage({
|
||||||
|
required this.backgroundBottomColor,
|
||||||
|
required this.backgroundColor,
|
||||||
|
required this.endTime,
|
||||||
|
required this.face,
|
||||||
|
required this.message,
|
||||||
|
required this.price,
|
||||||
|
required this.startTime,
|
||||||
|
required this.userName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LiveMessageType {
|
||||||
|
// 普通留言
|
||||||
|
chat,
|
||||||
|
// 醒目留言
|
||||||
|
superChat,
|
||||||
|
//
|
||||||
|
online,
|
||||||
|
// 加入
|
||||||
|
join,
|
||||||
|
// 关注
|
||||||
|
follow,
|
||||||
|
}
|
||||||
|
|
||||||
|
class LiveMessageColor {
|
||||||
|
final int r, g, b;
|
||||||
|
LiveMessageColor(this.r, this.g, this.b);
|
||||||
|
static LiveMessageColor get white => LiveMessageColor(255, 255, 255);
|
||||||
|
static LiveMessageColor numberToColor(int intColor) {
|
||||||
|
var obj = intColor.toRadixString(16);
|
||||||
|
|
||||||
|
LiveMessageColor color = LiveMessageColor.white;
|
||||||
|
if (obj.length == 4) {
|
||||||
|
obj = "00$obj";
|
||||||
|
}
|
||||||
|
if (obj.length == 6) {
|
||||||
|
var R = int.parse(obj.substring(0, 2), radix: 16);
|
||||||
|
var G = int.parse(obj.substring(2, 4), radix: 16);
|
||||||
|
var B = int.parse(obj.substring(4, 6), radix: 16);
|
||||||
|
|
||||||
|
color = LiveMessageColor(R, G, B);
|
||||||
|
}
|
||||||
|
if (obj.length == 8) {
|
||||||
|
var R = int.parse(obj.substring(2, 4), radix: 16);
|
||||||
|
var G = int.parse(obj.substring(4, 6), radix: 16);
|
||||||
|
var B = int.parse(obj.substring(6, 8), radix: 16);
|
||||||
|
//var A = int.parse(obj.substring(0, 2), radix: 16);
|
||||||
|
color = LiveMessageColor(R, G, B);
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return "#${r.toRadixString(16).padLeft(2, '0')}${g.toRadixString(16).padLeft(2, '0')}${b.toRadixString(16).padLeft(2, '0')}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,13 @@
|
|||||||
class RoomInfoModel {
|
class RoomInfoModel {
|
||||||
RoomInfoModel({
|
RoomInfoModel({
|
||||||
this.roomId,
|
this.roomId,
|
||||||
|
this.isPortrait,
|
||||||
this.liveStatus,
|
this.liveStatus,
|
||||||
this.liveTime,
|
this.liveTime,
|
||||||
this.playurlInfo,
|
this.playurlInfo,
|
||||||
});
|
});
|
||||||
int? roomId;
|
int? roomId;
|
||||||
|
bool? isPortrait;
|
||||||
int? liveStatus;
|
int? liveStatus;
|
||||||
int? liveTime;
|
int? liveTime;
|
||||||
PlayurlInfo? playurlInfo;
|
PlayurlInfo? playurlInfo;
|
||||||
@ -13,6 +15,7 @@ class RoomInfoModel {
|
|||||||
RoomInfoModel.fromJson(Map<String, dynamic> json) {
|
RoomInfoModel.fromJson(Map<String, dynamic> json) {
|
||||||
roomId = json['room_id'];
|
roomId = json['room_id'];
|
||||||
liveStatus = json['live_status'];
|
liveStatus = json['live_status'];
|
||||||
|
isPortrait = json['is_portrait'];
|
||||||
liveTime = json['live_time'];
|
liveTime = json['live_time'];
|
||||||
playurlInfo = PlayurlInfo.fromJson(json['playurl_info']);
|
playurlInfo = PlayurlInfo.fromJson(json['playurl_info']);
|
||||||
}
|
}
|
||||||
|
|||||||
46
lib/models/member/article.dart
Normal file
46
lib/models/member/article.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
class MemberArticleDataModel {
|
||||||
|
MemberArticleDataModel({
|
||||||
|
this.hasMore,
|
||||||
|
this.items,
|
||||||
|
this.offset,
|
||||||
|
this.updateNum,
|
||||||
|
});
|
||||||
|
|
||||||
|
bool? hasMore;
|
||||||
|
List<MemberArticleItemModel>? items;
|
||||||
|
String? offset;
|
||||||
|
int? updateNum;
|
||||||
|
|
||||||
|
MemberArticleDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
hasMore = json['has_more'];
|
||||||
|
items = json['items']
|
||||||
|
.map<MemberArticleItemModel>((e) => MemberArticleItemModel.fromJson(e))
|
||||||
|
.toList();
|
||||||
|
offset = json['offset'];
|
||||||
|
updateNum = json['update_num'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MemberArticleItemModel {
|
||||||
|
MemberArticleItemModel({
|
||||||
|
this.content,
|
||||||
|
this.cover,
|
||||||
|
this.jumpUrl,
|
||||||
|
this.opusId,
|
||||||
|
this.stat,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? content;
|
||||||
|
Map? cover;
|
||||||
|
String? jumpUrl;
|
||||||
|
String? opusId;
|
||||||
|
Map? stat;
|
||||||
|
|
||||||
|
MemberArticleItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
content = json['content'];
|
||||||
|
cover = json['cover'];
|
||||||
|
jumpUrl = json['jump_url'];
|
||||||
|
opusId = json['opus_id'];
|
||||||
|
stat = json['stat'];
|
||||||
|
}
|
||||||
|
}
|
||||||
210
lib/models/member/like.dart
Normal file
210
lib/models/member/like.dart
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
class MemberLikeDataModel {
|
||||||
|
MemberLikeDataModel({
|
||||||
|
this.aid,
|
||||||
|
this.videos,
|
||||||
|
this.tid,
|
||||||
|
this.tname,
|
||||||
|
this.pic,
|
||||||
|
this.title,
|
||||||
|
this.pubdate,
|
||||||
|
this.ctime,
|
||||||
|
this.desc,
|
||||||
|
this.state,
|
||||||
|
this.duration,
|
||||||
|
this.redirectUrl,
|
||||||
|
this.rights,
|
||||||
|
this.owner,
|
||||||
|
this.stat,
|
||||||
|
this.dimension,
|
||||||
|
this.cover43,
|
||||||
|
this.bvid,
|
||||||
|
this.interVideo,
|
||||||
|
this.resourceType,
|
||||||
|
this.subtitle,
|
||||||
|
this.enableVt,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int? aid;
|
||||||
|
final int? videos;
|
||||||
|
final int? tid;
|
||||||
|
final String? tname;
|
||||||
|
final String? pic;
|
||||||
|
final String? title;
|
||||||
|
final int? pubdate;
|
||||||
|
final int? ctime;
|
||||||
|
final String? desc;
|
||||||
|
final int? state;
|
||||||
|
final int? duration;
|
||||||
|
final String? redirectUrl;
|
||||||
|
final Rights? rights;
|
||||||
|
final Owner? owner;
|
||||||
|
final Stat? stat;
|
||||||
|
final Dimension? dimension;
|
||||||
|
final String? cover43;
|
||||||
|
final String? bvid;
|
||||||
|
final bool? interVideo;
|
||||||
|
final String? resourceType;
|
||||||
|
final String? subtitle;
|
||||||
|
final int? enableVt;
|
||||||
|
|
||||||
|
factory MemberLikeDataModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
MemberLikeDataModel(
|
||||||
|
aid: json["aid"],
|
||||||
|
videos: json["videos"],
|
||||||
|
tid: json["tid"],
|
||||||
|
tname: json["tname"],
|
||||||
|
pic: json["pic"],
|
||||||
|
title: json["title"],
|
||||||
|
pubdate: json["pubdate"],
|
||||||
|
ctime: json["ctime"],
|
||||||
|
desc: json["desc"],
|
||||||
|
state: json["state"],
|
||||||
|
duration: json["duration"],
|
||||||
|
redirectUrl: json["redirect_url"],
|
||||||
|
rights: Rights.fromJson(json["rights"]),
|
||||||
|
owner: Owner.fromJson(json["owner"]),
|
||||||
|
stat: Stat.fromJson(json["stat"]),
|
||||||
|
dimension: Dimension.fromJson(json["dimension"]),
|
||||||
|
cover43: json["cover43"],
|
||||||
|
bvid: json["bvid"],
|
||||||
|
interVideo: json["inter_video"],
|
||||||
|
resourceType: json["resource_type"],
|
||||||
|
subtitle: json["subtitle"],
|
||||||
|
enableVt: json["enable_vt"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dimension {
|
||||||
|
Dimension({
|
||||||
|
required this.width,
|
||||||
|
required this.height,
|
||||||
|
required this.rotate,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int width;
|
||||||
|
final int height;
|
||||||
|
final int rotate;
|
||||||
|
|
||||||
|
factory Dimension.fromJson(Map<String, dynamic> json) => Dimension(
|
||||||
|
width: json["width"],
|
||||||
|
height: json["height"],
|
||||||
|
rotate: json["rotate"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Owner {
|
||||||
|
Owner({
|
||||||
|
required this.mid,
|
||||||
|
required this.name,
|
||||||
|
required this.face,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int mid;
|
||||||
|
final String name;
|
||||||
|
final String face;
|
||||||
|
|
||||||
|
factory Owner.fromJson(Map<String, dynamic> json) => Owner(
|
||||||
|
mid: json["mid"],
|
||||||
|
name: json["name"],
|
||||||
|
face: json["face"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Rights {
|
||||||
|
Rights({
|
||||||
|
required this.bp,
|
||||||
|
required this.elec,
|
||||||
|
required this.download,
|
||||||
|
required this.movie,
|
||||||
|
required this.pay,
|
||||||
|
required this.hd5,
|
||||||
|
required this.noReprint,
|
||||||
|
required this.autoplay,
|
||||||
|
required this.ugcPay,
|
||||||
|
required this.isCooperation,
|
||||||
|
required this.ugcPayPreview,
|
||||||
|
required this.noBackground,
|
||||||
|
required this.arcPay,
|
||||||
|
required this.payFreeWatch,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int bp;
|
||||||
|
final int elec;
|
||||||
|
final int download;
|
||||||
|
final int movie;
|
||||||
|
final int pay;
|
||||||
|
final int hd5;
|
||||||
|
final int noReprint;
|
||||||
|
final int autoplay;
|
||||||
|
final int ugcPay;
|
||||||
|
final int isCooperation;
|
||||||
|
final int ugcPayPreview;
|
||||||
|
final int noBackground;
|
||||||
|
final int arcPay;
|
||||||
|
final int payFreeWatch;
|
||||||
|
|
||||||
|
factory Rights.fromJson(Map<String, dynamic> json) => Rights(
|
||||||
|
bp: json["bp"],
|
||||||
|
elec: json["elec"],
|
||||||
|
download: json["download"],
|
||||||
|
movie: json["movie"],
|
||||||
|
pay: json["pay"],
|
||||||
|
hd5: json["hd5"],
|
||||||
|
noReprint: json["no_reprint"],
|
||||||
|
autoplay: json["autoplay"],
|
||||||
|
ugcPay: json["ugc_pay"],
|
||||||
|
isCooperation: json["is_cooperation"],
|
||||||
|
ugcPayPreview: json["ugc_pay_preview"],
|
||||||
|
noBackground: json["no_background"],
|
||||||
|
arcPay: json["arc_pay"],
|
||||||
|
payFreeWatch: json["pay_free_watch"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Stat {
|
||||||
|
Stat({
|
||||||
|
required this.aid,
|
||||||
|
required this.view,
|
||||||
|
required this.danmaku,
|
||||||
|
required this.reply,
|
||||||
|
required this.favorite,
|
||||||
|
required this.coin,
|
||||||
|
required this.share,
|
||||||
|
required this.nowRank,
|
||||||
|
required this.hisRank,
|
||||||
|
required this.like,
|
||||||
|
required this.dislike,
|
||||||
|
required this.vt,
|
||||||
|
required this.vv,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int aid;
|
||||||
|
final int view;
|
||||||
|
final int danmaku;
|
||||||
|
final int reply;
|
||||||
|
final int favorite;
|
||||||
|
final int coin;
|
||||||
|
final int share;
|
||||||
|
final int nowRank;
|
||||||
|
final int hisRank;
|
||||||
|
final int like;
|
||||||
|
final int dislike;
|
||||||
|
final int vt;
|
||||||
|
final int vv;
|
||||||
|
|
||||||
|
factory Stat.fromJson(Map<String, dynamic> json) => Stat(
|
||||||
|
aid: json["aid"],
|
||||||
|
view: json["view"],
|
||||||
|
danmaku: json["danmaku"],
|
||||||
|
reply: json["reply"],
|
||||||
|
favorite: json["favorite"],
|
||||||
|
coin: json["coin"],
|
||||||
|
share: json["share"],
|
||||||
|
nowRank: json["now_rank"],
|
||||||
|
hisRank: json["his_rank"],
|
||||||
|
like: json["like"],
|
||||||
|
dislike: json["dislike"],
|
||||||
|
vt: json["vt"],
|
||||||
|
vv: json["vv"],
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -2,18 +2,32 @@ class MemberSeasonsDataModel {
|
|||||||
MemberSeasonsDataModel({
|
MemberSeasonsDataModel({
|
||||||
this.page,
|
this.page,
|
||||||
this.seasonsList,
|
this.seasonsList,
|
||||||
|
this.seriesList,
|
||||||
});
|
});
|
||||||
|
|
||||||
Map? page;
|
Map? page;
|
||||||
List<MemberSeasonsList>? seasonsList;
|
List<MemberSeasonsList>? seasonsList;
|
||||||
|
List<MemberArchiveItem>? seriesList;
|
||||||
|
|
||||||
MemberSeasonsDataModel.fromJson(Map<String, dynamic> json) {
|
MemberSeasonsDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
page = json['page'];
|
page = json['page'];
|
||||||
seasonsList = json['seasons_list'] != null
|
var tempList1 = json['seasons_list'] != null
|
||||||
? json['seasons_list']
|
? json['seasons_list']
|
||||||
.map<MemberSeasonsList>((e) => MemberSeasonsList.fromJson(e))
|
.map<MemberSeasonsList>((e) => MemberSeasonsList.fromJson(e))
|
||||||
.toList()
|
.toList()
|
||||||
: [];
|
: [];
|
||||||
|
var tempList2 = json['series_list'] != null
|
||||||
|
? json['series_list']
|
||||||
|
.map<MemberSeasonsList>((e) => MemberSeasonsList.fromJson(e))
|
||||||
|
.toList()
|
||||||
|
: [];
|
||||||
|
seriesList = json['archives'] != null
|
||||||
|
? json['archives']
|
||||||
|
.map<MemberArchiveItem>((e) => MemberArchiveItem.fromJson(e))
|
||||||
|
.toList()
|
||||||
|
: [];
|
||||||
|
|
||||||
|
seasonsList = [...tempList1, ...tempList2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +100,8 @@ class MamberMeta {
|
|||||||
this.ptime,
|
this.ptime,
|
||||||
this.seasonId,
|
this.seasonId,
|
||||||
this.total,
|
this.total,
|
||||||
|
this.seriesId,
|
||||||
|
this.category,
|
||||||
});
|
});
|
||||||
|
|
||||||
String? cover;
|
String? cover;
|
||||||
@ -95,6 +111,8 @@ class MamberMeta {
|
|||||||
int? ptime;
|
int? ptime;
|
||||||
int? seasonId;
|
int? seasonId;
|
||||||
int? total;
|
int? total;
|
||||||
|
int? seriesId;
|
||||||
|
int? category;
|
||||||
|
|
||||||
MamberMeta.fromJson(Map<String, dynamic> json) {
|
MamberMeta.fromJson(Map<String, dynamic> json) {
|
||||||
cover = json['cover'];
|
cover = json['cover'];
|
||||||
@ -104,5 +122,7 @@ class MamberMeta {
|
|||||||
ptime = json['ptime'];
|
ptime = json['ptime'];
|
||||||
seasonId = json['season_id'];
|
seasonId = json['season_id'];
|
||||||
total = json['total'];
|
total = json['total'];
|
||||||
|
seriesId = json['series_id'];
|
||||||
|
category = json['category'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,12 @@
|
|||||||
import 'package:hive/hive.dart';
|
|
||||||
|
|
||||||
part 'model_owner.g.dart';
|
|
||||||
|
|
||||||
@HiveType(typeId: 3)
|
|
||||||
class Owner {
|
class Owner {
|
||||||
Owner({
|
Owner({
|
||||||
this.mid,
|
this.mid,
|
||||||
this.name,
|
this.name,
|
||||||
this.face,
|
this.face,
|
||||||
});
|
});
|
||||||
@HiveField(0)
|
|
||||||
int? mid;
|
int? mid;
|
||||||
@HiveField(1)
|
|
||||||
String? name;
|
String? name;
|
||||||
@HiveField(2)
|
|
||||||
String? face;
|
String? face;
|
||||||
|
|
||||||
Owner.fromJson(Map<String, dynamic> json) {
|
Owner.fromJson(Map<String, dynamic> json) {
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'model_owner.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// TypeAdapterGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
class OwnerAdapter extends TypeAdapter<Owner> {
|
|
||||||
@override
|
|
||||||
final int typeId = 3;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Owner read(BinaryReader reader) {
|
|
||||||
final numOfFields = reader.readByte();
|
|
||||||
final fields = <int, dynamic>{
|
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
||||||
};
|
|
||||||
return Owner(
|
|
||||||
mid: fields[0] as int?,
|
|
||||||
name: fields[1] as String?,
|
|
||||||
face: fields[2] as String?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void write(BinaryWriter writer, Owner obj) {
|
|
||||||
writer
|
|
||||||
..writeByte(3)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.mid)
|
|
||||||
..writeByte(1)
|
|
||||||
..write(obj.name)
|
|
||||||
..writeByte(2)
|
|
||||||
..write(obj.face);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => typeId.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is OwnerAdapter &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
typeId == other.typeId;
|
|
||||||
}
|
|
||||||
@ -1,9 +1,5 @@
|
|||||||
import './model_owner.dart';
|
import './model_owner.dart';
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
|
|
||||||
part 'model_rec_video_item.g.dart';
|
|
||||||
|
|
||||||
@HiveType(typeId: 0)
|
|
||||||
class RecVideoItemModel {
|
class RecVideoItemModel {
|
||||||
RecVideoItemModel({
|
RecVideoItemModel({
|
||||||
this.id,
|
this.id,
|
||||||
@ -21,32 +17,19 @@ class RecVideoItemModel {
|
|||||||
this.rcmdReason,
|
this.rcmdReason,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
|
||||||
int? id = -1;
|
int? id = -1;
|
||||||
@HiveField(1)
|
|
||||||
String? bvid = '';
|
String? bvid = '';
|
||||||
@HiveField(2)
|
|
||||||
int? cid = -1;
|
int? cid = -1;
|
||||||
@HiveField(3)
|
|
||||||
String? goto = '';
|
String? goto = '';
|
||||||
@HiveField(4)
|
|
||||||
String? uri = '';
|
String? uri = '';
|
||||||
@HiveField(5)
|
|
||||||
String? pic = '';
|
String? pic = '';
|
||||||
@HiveField(6)
|
|
||||||
String? title = '';
|
String? title = '';
|
||||||
@HiveField(7)
|
|
||||||
int? duration = -1;
|
int? duration = -1;
|
||||||
@HiveField(8)
|
|
||||||
int? pubdate = -1;
|
int? pubdate = -1;
|
||||||
@HiveField(9)
|
|
||||||
Owner? owner;
|
Owner? owner;
|
||||||
@HiveField(10)
|
|
||||||
Stat? stat;
|
Stat? stat;
|
||||||
@HiveField(11)
|
|
||||||
int? isFollowed;
|
int? isFollowed;
|
||||||
@HiveField(12)
|
String? rcmdReason;
|
||||||
RcmdReason? rcmdReason;
|
|
||||||
|
|
||||||
RecVideoItemModel.fromJson(Map<String, dynamic> json) {
|
RecVideoItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
id = json["id"];
|
id = json["id"];
|
||||||
@ -61,26 +44,20 @@ class RecVideoItemModel {
|
|||||||
owner = Owner.fromJson(json["owner"]);
|
owner = Owner.fromJson(json["owner"]);
|
||||||
stat = Stat.fromJson(json["stat"]);
|
stat = Stat.fromJson(json["stat"]);
|
||||||
isFollowed = json["is_followed"] ?? 0;
|
isFollowed = json["is_followed"] ?? 0;
|
||||||
rcmdReason = json["rcmd_reason"] != null
|
rcmdReason = json["rcmd_reason"]?['content'];
|
||||||
? RcmdReason.fromJson(json["rcmd_reason"])
|
|
||||||
: RcmdReason(content: '');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HiveType(typeId: 1)
|
|
||||||
class Stat {
|
class Stat {
|
||||||
Stat({
|
Stat({
|
||||||
this.view,
|
this.view,
|
||||||
this.like,
|
this.like,
|
||||||
this.danmu,
|
this.danmu,
|
||||||
});
|
});
|
||||||
@HiveField(0)
|
|
||||||
int? view;
|
|
||||||
@HiveField(1)
|
|
||||||
int? like;
|
|
||||||
@HiveField(2)
|
|
||||||
int? danmu;
|
|
||||||
|
|
||||||
|
int? view;
|
||||||
|
int? like;
|
||||||
|
int? danmu;
|
||||||
Stat.fromJson(Map<String, dynamic> json) {
|
Stat.fromJson(Map<String, dynamic> json) {
|
||||||
// 无需在model中转换以保留原始数据,在view层处理即可
|
// 无需在model中转换以保留原始数据,在view层处理即可
|
||||||
view = json["view"];
|
view = json["view"];
|
||||||
@ -88,20 +65,3 @@ class Stat {
|
|||||||
danmu = json['danmaku'];
|
danmu = json['danmaku'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HiveType(typeId: 2)
|
|
||||||
class RcmdReason {
|
|
||||||
RcmdReason({
|
|
||||||
this.reasonType,
|
|
||||||
this.content,
|
|
||||||
});
|
|
||||||
@HiveField(0)
|
|
||||||
int? reasonType;
|
|
||||||
@HiveField(1)
|
|
||||||
String? content = '';
|
|
||||||
|
|
||||||
RcmdReason.fromJson(Map<String, dynamic> json) {
|
|
||||||
reasonType = json["reason_type"];
|
|
||||||
content = json["content"] ?? '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,154 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'model_rec_video_item.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// TypeAdapterGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
class RecVideoItemModelAdapter extends TypeAdapter<RecVideoItemModel> {
|
|
||||||
@override
|
|
||||||
final int typeId = 0;
|
|
||||||
|
|
||||||
@override
|
|
||||||
RecVideoItemModel read(BinaryReader reader) {
|
|
||||||
final numOfFields = reader.readByte();
|
|
||||||
final fields = <int, dynamic>{
|
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
||||||
};
|
|
||||||
return RecVideoItemModel(
|
|
||||||
id: fields[0] as int?,
|
|
||||||
bvid: fields[1] as String?,
|
|
||||||
cid: fields[2] as int?,
|
|
||||||
goto: fields[3] as String?,
|
|
||||||
uri: fields[4] as String?,
|
|
||||||
pic: fields[5] as String?,
|
|
||||||
title: fields[6] as String?,
|
|
||||||
duration: fields[7] as int?,
|
|
||||||
pubdate: fields[8] as int?,
|
|
||||||
owner: fields[9] as Owner?,
|
|
||||||
stat: fields[10] as Stat?,
|
|
||||||
isFollowed: fields[11] as int?,
|
|
||||||
rcmdReason: fields[12] as RcmdReason?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void write(BinaryWriter writer, RecVideoItemModel obj) {
|
|
||||||
writer
|
|
||||||
..writeByte(13)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.id)
|
|
||||||
..writeByte(1)
|
|
||||||
..write(obj.bvid)
|
|
||||||
..writeByte(2)
|
|
||||||
..write(obj.cid)
|
|
||||||
..writeByte(3)
|
|
||||||
..write(obj.goto)
|
|
||||||
..writeByte(4)
|
|
||||||
..write(obj.uri)
|
|
||||||
..writeByte(5)
|
|
||||||
..write(obj.pic)
|
|
||||||
..writeByte(6)
|
|
||||||
..write(obj.title)
|
|
||||||
..writeByte(7)
|
|
||||||
..write(obj.duration)
|
|
||||||
..writeByte(8)
|
|
||||||
..write(obj.pubdate)
|
|
||||||
..writeByte(9)
|
|
||||||
..write(obj.owner)
|
|
||||||
..writeByte(10)
|
|
||||||
..write(obj.stat)
|
|
||||||
..writeByte(11)
|
|
||||||
..write(obj.isFollowed)
|
|
||||||
..writeByte(12)
|
|
||||||
..write(obj.rcmdReason);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => typeId.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is RecVideoItemModelAdapter &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
typeId == other.typeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
class StatAdapter extends TypeAdapter<Stat> {
|
|
||||||
@override
|
|
||||||
final int typeId = 1;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Stat read(BinaryReader reader) {
|
|
||||||
final numOfFields = reader.readByte();
|
|
||||||
final fields = <int, dynamic>{
|
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
||||||
};
|
|
||||||
return Stat(
|
|
||||||
view: fields[0] as int?,
|
|
||||||
like: fields[1] as int?,
|
|
||||||
danmu: fields[2] as int?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void write(BinaryWriter writer, Stat obj) {
|
|
||||||
writer
|
|
||||||
..writeByte(3)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.view)
|
|
||||||
..writeByte(1)
|
|
||||||
..write(obj.like)
|
|
||||||
..writeByte(2)
|
|
||||||
..write(obj.danmu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => typeId.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is StatAdapter &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
typeId == other.typeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RcmdReasonAdapter extends TypeAdapter<RcmdReason> {
|
|
||||||
@override
|
|
||||||
final int typeId = 2;
|
|
||||||
|
|
||||||
@override
|
|
||||||
RcmdReason read(BinaryReader reader) {
|
|
||||||
final numOfFields = reader.readByte();
|
|
||||||
final fields = <int, dynamic>{
|
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
||||||
};
|
|
||||||
return RcmdReason(
|
|
||||||
reasonType: fields[0] as int?,
|
|
||||||
content: fields[1] as String?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void write(BinaryWriter writer, RcmdReason obj) {
|
|
||||||
writer
|
|
||||||
..writeByte(2)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.reasonType)
|
|
||||||
..writeByte(1)
|
|
||||||
..write(obj.content);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => typeId.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is RcmdReasonAdapter &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
typeId == other.typeId;
|
|
||||||
}
|
|
||||||
88
lib/models/msg/system.dart
Normal file
88
lib/models/msg/system.dart
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
class MessageSystemModel {
|
||||||
|
int? id;
|
||||||
|
int? cursor;
|
||||||
|
int? type;
|
||||||
|
String? title;
|
||||||
|
dynamic content;
|
||||||
|
Source? source;
|
||||||
|
String? timeAt;
|
||||||
|
int? cardType;
|
||||||
|
String? cardBrief;
|
||||||
|
String? cardMsgBrief;
|
||||||
|
String? cardCover;
|
||||||
|
String? cardStoryTitle;
|
||||||
|
String? cardLink;
|
||||||
|
String? mc;
|
||||||
|
int? isStation;
|
||||||
|
int? isSend;
|
||||||
|
int? notifyCursor;
|
||||||
|
|
||||||
|
MessageSystemModel({
|
||||||
|
this.id,
|
||||||
|
this.cursor,
|
||||||
|
this.type,
|
||||||
|
this.title,
|
||||||
|
this.content,
|
||||||
|
this.source,
|
||||||
|
this.timeAt,
|
||||||
|
this.cardType,
|
||||||
|
this.cardBrief,
|
||||||
|
this.cardMsgBrief,
|
||||||
|
this.cardCover,
|
||||||
|
this.cardStoryTitle,
|
||||||
|
this.cardLink,
|
||||||
|
this.mc,
|
||||||
|
this.isStation,
|
||||||
|
this.isSend,
|
||||||
|
this.notifyCursor,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory MessageSystemModel.fromJson(Map<String, dynamic> jsons) =>
|
||||||
|
MessageSystemModel(
|
||||||
|
id: jsons["id"],
|
||||||
|
cursor: jsons["cursor"],
|
||||||
|
type: jsons["type"],
|
||||||
|
title: jsons["title"],
|
||||||
|
content: isValidJson(jsons["content"])
|
||||||
|
? json.decode(jsons["content"])
|
||||||
|
: jsons["content"],
|
||||||
|
source: Source.fromJson(jsons["source"]),
|
||||||
|
timeAt: jsons["time_at"],
|
||||||
|
cardType: jsons["card_type"],
|
||||||
|
cardBrief: jsons["card_brief"],
|
||||||
|
cardMsgBrief: jsons["card_msg_brief"],
|
||||||
|
cardCover: jsons["card_cover"],
|
||||||
|
cardStoryTitle: jsons["card_story_title"],
|
||||||
|
cardLink: jsons["card_link"],
|
||||||
|
mc: jsons["mc"],
|
||||||
|
isStation: jsons["is_station"],
|
||||||
|
isSend: jsons["is_send"],
|
||||||
|
notifyCursor: jsons["notify_cursor"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Source {
|
||||||
|
String? name;
|
||||||
|
String? logo;
|
||||||
|
|
||||||
|
Source({
|
||||||
|
this.name,
|
||||||
|
this.logo,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Source.fromJson(Map<String, dynamic> json) => Source(
|
||||||
|
name: json["name"],
|
||||||
|
logo: json["logo"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValidJson(String str) {
|
||||||
|
try {
|
||||||
|
json.decode(str);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
485
lib/models/read/opus.dart
Normal file
485
lib/models/read/opus.dart
Normal file
@ -0,0 +1,485 @@
|
|||||||
|
class OpusDataModel {
|
||||||
|
OpusDataModel({
|
||||||
|
this.id,
|
||||||
|
this.detail,
|
||||||
|
this.type,
|
||||||
|
this.theme,
|
||||||
|
this.themeMode,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? id;
|
||||||
|
OpusDetailDataModel? detail;
|
||||||
|
int? type;
|
||||||
|
String? theme;
|
||||||
|
String? themeMode;
|
||||||
|
|
||||||
|
OpusDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
id = json['id'];
|
||||||
|
detail = json['detail'] != null
|
||||||
|
? OpusDetailDataModel.fromJson(json['detail'])
|
||||||
|
: null;
|
||||||
|
type = json['type'];
|
||||||
|
theme = json['theme'];
|
||||||
|
themeMode = json['themeMode'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpusDetailDataModel {
|
||||||
|
OpusDetailDataModel({
|
||||||
|
this.basic,
|
||||||
|
this.idStr,
|
||||||
|
this.modules,
|
||||||
|
this.type,
|
||||||
|
});
|
||||||
|
|
||||||
|
Basic? basic;
|
||||||
|
String? idStr;
|
||||||
|
List<OpusModuleDataModel>? modules;
|
||||||
|
int? type;
|
||||||
|
|
||||||
|
OpusDetailDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
basic = json['basic'] != null ? Basic.fromJson(json['basic']) : null;
|
||||||
|
idStr = json['id_str'];
|
||||||
|
if (json['modules'] != null) {
|
||||||
|
modules = <OpusModuleDataModel>[];
|
||||||
|
json['modules'].forEach((v) {
|
||||||
|
modules!.add(OpusModuleDataModel.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
type = json['type'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Basic {
|
||||||
|
Basic({
|
||||||
|
this.commentIdStr,
|
||||||
|
this.commentType,
|
||||||
|
this.ridStr,
|
||||||
|
this.title,
|
||||||
|
this.uid,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? commentIdStr;
|
||||||
|
int? commentType;
|
||||||
|
String? ridStr;
|
||||||
|
String? title;
|
||||||
|
int? uid;
|
||||||
|
|
||||||
|
Basic.fromJson(Map<String, dynamic> json) {
|
||||||
|
commentIdStr = json['comment_id_str'];
|
||||||
|
commentType = json['comment_type'];
|
||||||
|
ridStr = json['rid_str'];
|
||||||
|
title = json['title'];
|
||||||
|
uid = json['uid'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpusModuleDataModel {
|
||||||
|
OpusModuleDataModel({
|
||||||
|
this.moduleTitle,
|
||||||
|
this.moduleAuthor,
|
||||||
|
this.moduleContent,
|
||||||
|
this.moduleExtend,
|
||||||
|
this.moduleBottom,
|
||||||
|
this.moduleStat,
|
||||||
|
});
|
||||||
|
|
||||||
|
ModuleTop? moduleTop;
|
||||||
|
ModuleTitle? moduleTitle;
|
||||||
|
ModuleAuthor? moduleAuthor;
|
||||||
|
ModuleContent? moduleContent;
|
||||||
|
ModuleExtend? moduleExtend;
|
||||||
|
ModuleBottom? moduleBottom;
|
||||||
|
ModuleStat? moduleStat;
|
||||||
|
|
||||||
|
OpusModuleDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
moduleTop = json['module_top'] != null
|
||||||
|
? ModuleTop.fromJson(json['module_top'])
|
||||||
|
: null;
|
||||||
|
moduleTitle = json['module_title'] != null
|
||||||
|
? ModuleTitle.fromJson(json['module_title'])
|
||||||
|
: null;
|
||||||
|
moduleAuthor = json['module_author'] != null
|
||||||
|
? ModuleAuthor.fromJson(json['module_author'])
|
||||||
|
: null;
|
||||||
|
moduleContent = json['module_content'] != null
|
||||||
|
? ModuleContent.fromJson(json['module_content'])
|
||||||
|
: null;
|
||||||
|
moduleExtend = json['module_extend'] != null
|
||||||
|
? ModuleExtend.fromJson(json['module_extend'])
|
||||||
|
: null;
|
||||||
|
moduleBottom = json['module_bottom'] != null
|
||||||
|
? ModuleBottom.fromJson(json['module_bottom'])
|
||||||
|
: null;
|
||||||
|
moduleStat = json['module_stat'] != null
|
||||||
|
? ModuleStat.fromJson(json['module_stat'])
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleTop {
|
||||||
|
ModuleTop({
|
||||||
|
this.type,
|
||||||
|
this.video,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? type;
|
||||||
|
Map? video;
|
||||||
|
|
||||||
|
ModuleTop.fromJson(Map<String, dynamic> json) {
|
||||||
|
type = json['type'];
|
||||||
|
video = json['video'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleTitle {
|
||||||
|
ModuleTitle({
|
||||||
|
this.text,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? text;
|
||||||
|
|
||||||
|
ModuleTitle.fromJson(Map<String, dynamic> json) {
|
||||||
|
text = json['text'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleAuthor {
|
||||||
|
ModuleAuthor({
|
||||||
|
this.face,
|
||||||
|
this.mid,
|
||||||
|
this.name,
|
||||||
|
this.pubTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? face;
|
||||||
|
int? mid;
|
||||||
|
String? name;
|
||||||
|
String? pubTime;
|
||||||
|
|
||||||
|
ModuleAuthor.fromJson(Map<String, dynamic> json) {
|
||||||
|
face = json['face'];
|
||||||
|
mid = json['mid'];
|
||||||
|
name = json['name'];
|
||||||
|
pubTime = json['pub_time'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleContent {
|
||||||
|
ModuleContent({
|
||||||
|
this.paragraphs,
|
||||||
|
this.moduleType,
|
||||||
|
});
|
||||||
|
|
||||||
|
List<ModuleParagraph>? paragraphs;
|
||||||
|
String? moduleType;
|
||||||
|
|
||||||
|
ModuleContent.fromJson(Map<String, dynamic> json) {
|
||||||
|
if (json['paragraphs'] != null) {
|
||||||
|
paragraphs = <ModuleParagraph>[];
|
||||||
|
json['paragraphs'].forEach((v) {
|
||||||
|
paragraphs!.add(ModuleParagraph.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
moduleType = json['module_type'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleParagraph {
|
||||||
|
ModuleParagraph({
|
||||||
|
this.align,
|
||||||
|
this.paraType,
|
||||||
|
this.pic,
|
||||||
|
this.text,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 0 左对齐 1 居中 2 右对齐
|
||||||
|
int? align;
|
||||||
|
int? paraType;
|
||||||
|
Pics? pic;
|
||||||
|
ModuleParagraphText? text;
|
||||||
|
LinkCard? linkCard;
|
||||||
|
|
||||||
|
ModuleParagraph.fromJson(Map<String, dynamic> json) {
|
||||||
|
align = json['align'];
|
||||||
|
paraType = json['para_type'] == null && json['link_card'] != null
|
||||||
|
? 3
|
||||||
|
: json['para_type'];
|
||||||
|
pic = json['pic'] != null ? Pics.fromJson(json['pic']) : null;
|
||||||
|
text = json['text'] != null
|
||||||
|
? ModuleParagraphText.fromJson(json['text'])
|
||||||
|
: null;
|
||||||
|
linkCard =
|
||||||
|
json['link_card'] != null ? LinkCard.fromJson(json['link_card']) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pics {
|
||||||
|
Pics({
|
||||||
|
this.pics,
|
||||||
|
this.style,
|
||||||
|
});
|
||||||
|
|
||||||
|
List<Pic>? pics;
|
||||||
|
int? style;
|
||||||
|
|
||||||
|
Pics.fromJson(Map<String, dynamic> json) {
|
||||||
|
if (json['pics'] != null) {
|
||||||
|
pics = <Pic>[];
|
||||||
|
json['pics'].forEach((v) {
|
||||||
|
pics!.add(Pic.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
style = json['style'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pic {
|
||||||
|
Pic({
|
||||||
|
this.height,
|
||||||
|
this.size,
|
||||||
|
this.url,
|
||||||
|
this.width,
|
||||||
|
this.aspectRatio,
|
||||||
|
this.scale,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? height;
|
||||||
|
double? size;
|
||||||
|
String? url;
|
||||||
|
int? width;
|
||||||
|
double? aspectRatio;
|
||||||
|
double? scale;
|
||||||
|
|
||||||
|
Pic.fromJson(Map<String, dynamic> json) {
|
||||||
|
height = json['height'];
|
||||||
|
size = json['size'];
|
||||||
|
url = json['url'];
|
||||||
|
width = json['width'];
|
||||||
|
aspectRatio = json['width'] / json['height'];
|
||||||
|
scale = customDivision(json['width'], 600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LinkCard {
|
||||||
|
LinkCard({
|
||||||
|
this.cover,
|
||||||
|
this.descSecond,
|
||||||
|
this.duration,
|
||||||
|
this.jumpUrl,
|
||||||
|
this.title,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? cover;
|
||||||
|
String? descSecond;
|
||||||
|
String? duration;
|
||||||
|
String? jumpUrl;
|
||||||
|
String? title;
|
||||||
|
|
||||||
|
LinkCard.fromJson(Map<String, dynamic> json) {
|
||||||
|
cover = json['card']['cover'];
|
||||||
|
descSecond = json['card']['desc_second'];
|
||||||
|
duration = json['card']['duration'];
|
||||||
|
jumpUrl = json['card']['jump_url'];
|
||||||
|
title = json['card']['title'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleParagraphText {
|
||||||
|
ModuleParagraphText({
|
||||||
|
this.nodes,
|
||||||
|
});
|
||||||
|
|
||||||
|
List<ModuleParagraphTextNode>? nodes;
|
||||||
|
|
||||||
|
ModuleParagraphText.fromJson(Map<String, dynamic> json) {
|
||||||
|
if (json['nodes'] != null) {
|
||||||
|
nodes = <ModuleParagraphTextNode>[];
|
||||||
|
json['nodes'].forEach((v) {
|
||||||
|
nodes!.add(ModuleParagraphTextNode.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleParagraphTextNode {
|
||||||
|
ModuleParagraphTextNode({
|
||||||
|
this.type,
|
||||||
|
this.nodeType,
|
||||||
|
this.word,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? type;
|
||||||
|
int? nodeType;
|
||||||
|
ModuleParagraphTextNodeWord? word;
|
||||||
|
|
||||||
|
ModuleParagraphTextNode.fromJson(Map<String, dynamic> json) {
|
||||||
|
type = json['type'];
|
||||||
|
nodeType = json['node_type'];
|
||||||
|
word = json['word'] != null
|
||||||
|
? ModuleParagraphTextNodeWord.fromJson(json['word'])
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleParagraphTextNodeWord {
|
||||||
|
ModuleParagraphTextNodeWord({
|
||||||
|
this.color,
|
||||||
|
this.fontSize,
|
||||||
|
this.style,
|
||||||
|
this.words,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? color;
|
||||||
|
int? fontSize;
|
||||||
|
ModuleParagraphTextNodeWordStyle? style;
|
||||||
|
String? words;
|
||||||
|
|
||||||
|
ModuleParagraphTextNodeWord.fromJson(Map<String, dynamic> json) {
|
||||||
|
color = json['color'];
|
||||||
|
fontSize = json['font_size'];
|
||||||
|
style = json['style'] != null
|
||||||
|
? ModuleParagraphTextNodeWordStyle.fromJson(json['style'])
|
||||||
|
: null;
|
||||||
|
words = json['words'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleParagraphTextNodeWordStyle {
|
||||||
|
ModuleParagraphTextNodeWordStyle({
|
||||||
|
this.bold,
|
||||||
|
});
|
||||||
|
|
||||||
|
bool? bold;
|
||||||
|
|
||||||
|
ModuleParagraphTextNodeWordStyle.fromJson(Map<String, dynamic> json) {
|
||||||
|
bold = json['bold'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleExtend {
|
||||||
|
ModuleExtend({
|
||||||
|
this.items,
|
||||||
|
});
|
||||||
|
|
||||||
|
List<ModuleExtendItem>? items;
|
||||||
|
|
||||||
|
ModuleExtend.fromJson(Map<String, dynamic> json) {
|
||||||
|
if (json['items'] != null) {
|
||||||
|
items = <ModuleExtendItem>[];
|
||||||
|
json['items'].forEach((v) {
|
||||||
|
items!.add(ModuleExtendItem.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleExtendItem {
|
||||||
|
ModuleExtendItem({
|
||||||
|
this.bizId,
|
||||||
|
this.bizType,
|
||||||
|
this.icon,
|
||||||
|
this.jumpUrl,
|
||||||
|
this.text,
|
||||||
|
});
|
||||||
|
|
||||||
|
dynamic bizId;
|
||||||
|
int? bizType;
|
||||||
|
dynamic icon;
|
||||||
|
String? jumpUrl;
|
||||||
|
String? text;
|
||||||
|
|
||||||
|
ModuleExtendItem.fromJson(Map<String, dynamic> json) {
|
||||||
|
bizId = json['biz_id'];
|
||||||
|
bizType = json['biz_type'];
|
||||||
|
icon = json['icon'];
|
||||||
|
jumpUrl = json['jump_url'];
|
||||||
|
text = json['text'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleBottom {
|
||||||
|
ModuleBottom({
|
||||||
|
this.shareInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
ShareInfo? shareInfo;
|
||||||
|
|
||||||
|
ModuleBottom.fromJson(Map<String, dynamic> json) {
|
||||||
|
shareInfo = json['share_info'] != null
|
||||||
|
? ShareInfo.fromJson(json['share_info'])
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShareInfo {
|
||||||
|
ShareInfo({
|
||||||
|
this.pic,
|
||||||
|
this.summary,
|
||||||
|
this.title,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? pic;
|
||||||
|
String? summary;
|
||||||
|
String? title;
|
||||||
|
|
||||||
|
ShareInfo.fromJson(Map<String, dynamic> json) {
|
||||||
|
pic = json['pic'];
|
||||||
|
summary = json['summary'];
|
||||||
|
title = json['title'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleStat {
|
||||||
|
ModuleStat({
|
||||||
|
this.coin,
|
||||||
|
this.comment,
|
||||||
|
this.favorite,
|
||||||
|
this.forward,
|
||||||
|
this.like,
|
||||||
|
});
|
||||||
|
|
||||||
|
StatItem? coin;
|
||||||
|
StatItem? comment;
|
||||||
|
StatItem? favorite;
|
||||||
|
StatItem? forward;
|
||||||
|
StatItem? like;
|
||||||
|
|
||||||
|
ModuleStat.fromJson(Map<String, dynamic> json) {
|
||||||
|
coin = json['coin'] != null ? StatItem.fromJson(json['coin']) : null;
|
||||||
|
comment =
|
||||||
|
json['comment'] != null ? StatItem.fromJson(json['comment']) : null;
|
||||||
|
favorite =
|
||||||
|
json['favorite'] != null ? StatItem.fromJson(json['favorite']) : null;
|
||||||
|
forward =
|
||||||
|
json['forward'] != null ? StatItem.fromJson(json['forward']) : null;
|
||||||
|
like = json['like'] != null ? StatItem.fromJson(json['like']) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatItem {
|
||||||
|
StatItem({
|
||||||
|
this.count,
|
||||||
|
this.forbidden,
|
||||||
|
this.status,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? count;
|
||||||
|
bool? forbidden;
|
||||||
|
bool? status;
|
||||||
|
|
||||||
|
StatItem.fromJson(Map<String, dynamic> json) {
|
||||||
|
count = json['count'];
|
||||||
|
forbidden = json['forbidden'];
|
||||||
|
status = json['status'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double customDivision(int a, int b) {
|
||||||
|
double result = a / b;
|
||||||
|
if (result < 1) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
286
lib/models/read/read.dart
Normal file
286
lib/models/read/read.dart
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
import 'package:pilipala/models/member/info.dart';
|
||||||
|
|
||||||
|
import 'opus.dart';
|
||||||
|
|
||||||
|
class ReadDataModel {
|
||||||
|
ReadDataModel({
|
||||||
|
this.cvid,
|
||||||
|
this.readInfo,
|
||||||
|
this.readViewInfo,
|
||||||
|
this.upInfo,
|
||||||
|
this.catalogList,
|
||||||
|
this.recommendInfoList,
|
||||||
|
this.hiddenInteraction,
|
||||||
|
this.isModern,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? cvid;
|
||||||
|
ReadInfo? readInfo;
|
||||||
|
Map? readViewInfo;
|
||||||
|
Map? upInfo;
|
||||||
|
List<dynamic>? catalogList;
|
||||||
|
List<dynamic>? recommendInfoList;
|
||||||
|
bool? hiddenInteraction;
|
||||||
|
bool? isModern;
|
||||||
|
|
||||||
|
ReadDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
cvid = json['cvid'];
|
||||||
|
readInfo =
|
||||||
|
json['readInfo'] != null ? ReadInfo.fromJson(json['readInfo']) : null;
|
||||||
|
readViewInfo = json['readViewInfo'];
|
||||||
|
upInfo = json['upInfo'];
|
||||||
|
if (json['catalogList'] != null) {
|
||||||
|
catalogList = <dynamic>[];
|
||||||
|
json['catalogList'].forEach((v) {
|
||||||
|
catalogList!.add(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (json['recommendInfoList'] != null) {
|
||||||
|
recommendInfoList = <dynamic>[];
|
||||||
|
json['recommendInfoList'].forEach((v) {
|
||||||
|
recommendInfoList!.add(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
hiddenInteraction = json['hiddenInteraction'];
|
||||||
|
isModern = json['isModern'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReadInfo {
|
||||||
|
ReadInfo({
|
||||||
|
this.id,
|
||||||
|
this.category,
|
||||||
|
this.title,
|
||||||
|
this.summary,
|
||||||
|
this.bannerUrl,
|
||||||
|
this.author,
|
||||||
|
this.publishTime,
|
||||||
|
this.ctime,
|
||||||
|
this.mtime,
|
||||||
|
this.stats,
|
||||||
|
this.attributes,
|
||||||
|
this.words,
|
||||||
|
this.originImageUrls,
|
||||||
|
this.content,
|
||||||
|
this.opus,
|
||||||
|
this.dynIdStr,
|
||||||
|
this.totalArtNum,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? id;
|
||||||
|
Map? category;
|
||||||
|
String? title;
|
||||||
|
String? summary;
|
||||||
|
String? bannerUrl;
|
||||||
|
Author? author;
|
||||||
|
int? publishTime;
|
||||||
|
int? ctime;
|
||||||
|
int? mtime;
|
||||||
|
Map? stats;
|
||||||
|
int? attributes;
|
||||||
|
int? words;
|
||||||
|
List<String>? originImageUrls;
|
||||||
|
String? content;
|
||||||
|
Opus? opus;
|
||||||
|
String? dynIdStr;
|
||||||
|
int? totalArtNum;
|
||||||
|
|
||||||
|
ReadInfo.fromJson(Map<String, dynamic> json) {
|
||||||
|
id = json['id'];
|
||||||
|
category = json['category'];
|
||||||
|
title = json['title'];
|
||||||
|
summary = json['summary'];
|
||||||
|
bannerUrl = json['banner_url'];
|
||||||
|
author = Author.fromJson(json['author']);
|
||||||
|
publishTime = json['publish_time'];
|
||||||
|
ctime = json['ctime'];
|
||||||
|
mtime = json['mtime'];
|
||||||
|
stats = json['stats'];
|
||||||
|
attributes = json['attributes'];
|
||||||
|
words = json['words'];
|
||||||
|
if (json['origin_image_urls'] != null) {
|
||||||
|
originImageUrls = <String>[];
|
||||||
|
json['origin_image_urls'].forEach((v) {
|
||||||
|
originImageUrls!.add(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
content = json['content'];
|
||||||
|
opus = json['opus'] != null ? Opus.fromJson(json['opus']) : null;
|
||||||
|
dynIdStr = json['dyn_id_str'];
|
||||||
|
totalArtNum = json['total_art_num'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Author {
|
||||||
|
Author({
|
||||||
|
this.mid,
|
||||||
|
this.name,
|
||||||
|
this.face,
|
||||||
|
this.vip,
|
||||||
|
this.fans,
|
||||||
|
this.level,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? mid;
|
||||||
|
String? name;
|
||||||
|
String? face;
|
||||||
|
Vip? vip;
|
||||||
|
int? fans;
|
||||||
|
int? level;
|
||||||
|
|
||||||
|
Author.fromJson(Map<String, dynamic> json) {
|
||||||
|
mid = json['mid'];
|
||||||
|
name = json['name'];
|
||||||
|
face = json['face'];
|
||||||
|
vip = json['vip'] != null ? Vip.fromJson(json['vip']) : null;
|
||||||
|
fans = json['fans'];
|
||||||
|
level = json['level'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Opus {
|
||||||
|
// "opus_id": 976625853207150600,
|
||||||
|
// "opus_source": 2,
|
||||||
|
// "title": "真的很想骂人 但又没什么好骂的",
|
||||||
|
// "content": {
|
||||||
|
// "paragraphs": [{
|
||||||
|
// "para_type": 1,
|
||||||
|
// "text": {
|
||||||
|
// "nodes": [{
|
||||||
|
// "node_type": 1,
|
||||||
|
// "word": {
|
||||||
|
// "words": "21年玩到今年4月的号没了 ow1的时候45的号 玩了三年 后面第9赛季一个英杰5的号(虽然是偷的 但我任何违规行为都没有还是给我封了) 最近玩的号叫velleity 只和队友打天梯以及训练赛 又没了 连带着我一个一把没玩过只玩过一场训练赛的小号也没了 实在是无话可说了...",
|
||||||
|
// "font_size": 17,
|
||||||
|
// "style": {},
|
||||||
|
// "font_level": "regular"
|
||||||
|
// }
|
||||||
|
// }]
|
||||||
|
// }
|
||||||
|
// }, {
|
||||||
|
// "para_type": 2,
|
||||||
|
// "pic": {
|
||||||
|
// "pics": [{
|
||||||
|
// "url": "https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fnew_dyn\u002Fba4e57459451fe74dcb70fd20bde9823316082117.jpg",
|
||||||
|
// "width": 1600,
|
||||||
|
// "height": 1000,
|
||||||
|
// "size": 588.482421875
|
||||||
|
// }],
|
||||||
|
// "style": 1
|
||||||
|
// }
|
||||||
|
// }, {
|
||||||
|
// "para_type": 1,
|
||||||
|
// "text": {
|
||||||
|
// "nodes": [{
|
||||||
|
// "node_type": 1,
|
||||||
|
// "word": {
|
||||||
|
// "words": "\n",
|
||||||
|
// "font_size": 17,
|
||||||
|
// "style": {},
|
||||||
|
// "font_level": "regular"
|
||||||
|
// }
|
||||||
|
// }]
|
||||||
|
// }
|
||||||
|
// }, {
|
||||||
|
// "para_type": 2,
|
||||||
|
// "pic": {
|
||||||
|
// "pics": [{
|
||||||
|
// "url": "https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fnew_dyn\u002F0945be6b621091ddb8189482a87a36fb316082117.jpg",
|
||||||
|
// "width": 1600,
|
||||||
|
// "height": 1002,
|
||||||
|
// "size": 665.7861328125
|
||||||
|
// }],
|
||||||
|
// "style": 1
|
||||||
|
// }
|
||||||
|
// }, {
|
||||||
|
// "para_type": 1,
|
||||||
|
// "text": {
|
||||||
|
// "nodes": [{
|
||||||
|
// "node_type": 1,
|
||||||
|
// "word": {
|
||||||
|
// "words": "\n",
|
||||||
|
// "font_size": 17,
|
||||||
|
// "style": {},
|
||||||
|
// "font_level": "regular"
|
||||||
|
// }
|
||||||
|
// }]
|
||||||
|
// }
|
||||||
|
// }, {
|
||||||
|
// "para_type": 2,
|
||||||
|
// "pic": {
|
||||||
|
// "pics": [{
|
||||||
|
// "url": "https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fnew_dyn\u002Ffa60649f8786578a764a1e68a2c5d23f316082117.jpg",
|
||||||
|
// "width": 1600,
|
||||||
|
// "height": 999,
|
||||||
|
// "size": 332.970703125
|
||||||
|
// }],
|
||||||
|
// "style": 1
|
||||||
|
// }
|
||||||
|
// }, {
|
||||||
|
// "para_type": 1,
|
||||||
|
// "text": {
|
||||||
|
// "nodes": [{
|
||||||
|
// "node_type": 1,
|
||||||
|
// "word": {
|
||||||
|
// "words": "\n",
|
||||||
|
// "font_size": 17,
|
||||||
|
// "style": {},
|
||||||
|
// "font_level": "regular"
|
||||||
|
// }
|
||||||
|
// }]
|
||||||
|
// }
|
||||||
|
// }]
|
||||||
|
// },
|
||||||
|
// "pub_info": {
|
||||||
|
// "uid": 316082117,
|
||||||
|
// "pub_time": 1726226826
|
||||||
|
// },
|
||||||
|
// "article": {
|
||||||
|
// "category_id": 15,
|
||||||
|
// "cover": [{
|
||||||
|
// "url": "https:\u002F\u002Fi0.hdslb.com\u002Fbfs\u002Fnew_dyn\u002Fbanner\u002Feb10074186a62f98c18e1b5b9deb38be316082117.png",
|
||||||
|
// "width": 1071,
|
||||||
|
// "height": 315,
|
||||||
|
// "size": 225.625
|
||||||
|
// }]
|
||||||
|
// },
|
||||||
|
// "version": {
|
||||||
|
// "cvid": 38660379,
|
||||||
|
// "version_id": 101683514411343360
|
||||||
|
// }
|
||||||
|
Opus({
|
||||||
|
this.opusId,
|
||||||
|
this.opusSource,
|
||||||
|
this.title,
|
||||||
|
this.content,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? opusId;
|
||||||
|
int? opusSource;
|
||||||
|
String? title;
|
||||||
|
Content? content;
|
||||||
|
|
||||||
|
Opus.fromJson(Map<String, dynamic> json) {
|
||||||
|
opusId = json['opus_id'];
|
||||||
|
opusSource = json['opus_source'];
|
||||||
|
title = json['title'];
|
||||||
|
content =
|
||||||
|
json['content'] != null ? Content.fromJson(json['content']) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Content {
|
||||||
|
Content({
|
||||||
|
this.paragraphs,
|
||||||
|
});
|
||||||
|
|
||||||
|
List<ModuleParagraph>? paragraphs;
|
||||||
|
|
||||||
|
Content.fromJson(Map<String, dynamic> json) {
|
||||||
|
if (json['paragraphs'] != null) {
|
||||||
|
paragraphs = <ModuleParagraph>[];
|
||||||
|
json['paragraphs'].forEach((v) {
|
||||||
|
paragraphs!.add(ModuleParagraph.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,14 +1,8 @@
|
|||||||
import 'package:hive/hive.dart';
|
|
||||||
|
|
||||||
part 'hot.g.dart';
|
|
||||||
|
|
||||||
@HiveType(typeId: 6)
|
|
||||||
class HotSearchModel {
|
class HotSearchModel {
|
||||||
HotSearchModel({
|
HotSearchModel({
|
||||||
this.list,
|
this.list,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
|
||||||
List<HotSearchItem>? list;
|
List<HotSearchItem>? list;
|
||||||
|
|
||||||
HotSearchModel.fromJson(Map<String, dynamic> json) {
|
HotSearchModel.fromJson(Map<String, dynamic> json) {
|
||||||
@ -18,7 +12,6 @@ class HotSearchModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HiveType(typeId: 7)
|
|
||||||
class HotSearchItem {
|
class HotSearchItem {
|
||||||
HotSearchItem({
|
HotSearchItem({
|
||||||
this.keyword,
|
this.keyword,
|
||||||
@ -27,14 +20,10 @@ class HotSearchItem {
|
|||||||
this.icon,
|
this.icon,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
|
||||||
String? keyword;
|
String? keyword;
|
||||||
@HiveField(1)
|
|
||||||
String? showName;
|
String? showName;
|
||||||
// 4/5热 11话题 8普通 7直播
|
// 4/5热 11话题 8普通 7直播
|
||||||
@HiveField(2)
|
|
||||||
int? wordType;
|
int? wordType;
|
||||||
@HiveField(3)
|
|
||||||
String? icon;
|
String? icon;
|
||||||
|
|
||||||
HotSearchItem.fromJson(Map<String, dynamic> json) {
|
HotSearchItem.fromJson(Map<String, dynamic> json) {
|
||||||
|
|||||||
@ -1,84 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'hot.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// TypeAdapterGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
class HotSearchModelAdapter extends TypeAdapter<HotSearchModel> {
|
|
||||||
@override
|
|
||||||
final int typeId = 6;
|
|
||||||
|
|
||||||
@override
|
|
||||||
HotSearchModel read(BinaryReader reader) {
|
|
||||||
final numOfFields = reader.readByte();
|
|
||||||
final fields = <int, dynamic>{
|
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
||||||
};
|
|
||||||
return HotSearchModel(
|
|
||||||
list: (fields[0] as List?)?.cast<HotSearchItem>(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void write(BinaryWriter writer, HotSearchModel obj) {
|
|
||||||
writer
|
|
||||||
..writeByte(1)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => typeId.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is HotSearchModelAdapter &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
typeId == other.typeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HotSearchItemAdapter extends TypeAdapter<HotSearchItem> {
|
|
||||||
@override
|
|
||||||
final int typeId = 7;
|
|
||||||
|
|
||||||
@override
|
|
||||||
HotSearchItem read(BinaryReader reader) {
|
|
||||||
final numOfFields = reader.readByte();
|
|
||||||
final fields = <int, dynamic>{
|
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
||||||
};
|
|
||||||
return HotSearchItem(
|
|
||||||
keyword: fields[0] as String?,
|
|
||||||
showName: fields[1] as String?,
|
|
||||||
wordType: fields[2] as int?,
|
|
||||||
icon: fields[3] as String?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void write(BinaryWriter writer, HotSearchItem obj) {
|
|
||||||
writer
|
|
||||||
..writeByte(4)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.keyword)
|
|
||||||
..writeByte(1)
|
|
||||||
..write(obj.showName)
|
|
||||||
..writeByte(2)
|
|
||||||
..write(obj.wordType)
|
|
||||||
..writeByte(3)
|
|
||||||
..write(obj.icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => typeId.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is HotSearchItemAdapter &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
typeId == other.typeId;
|
|
||||||
}
|
|
||||||
@ -5,7 +5,9 @@ class SearchVideoModel {
|
|||||||
SearchVideoModel({this.list});
|
SearchVideoModel({this.list});
|
||||||
List<SearchVideoItemModel>? list;
|
List<SearchVideoItemModel>? list;
|
||||||
SearchVideoModel.fromJson(Map<String, dynamic> json) {
|
SearchVideoModel.fromJson(Map<String, dynamic> json) {
|
||||||
list = json['result']
|
list = json['result'] == null
|
||||||
|
? []
|
||||||
|
: json['result']
|
||||||
.where((e) => e['available'] == true)
|
.where((e) => e['available'] == true)
|
||||||
.map<SearchVideoItemModel>((e) => SearchVideoItemModel.fromJson(e))
|
.map<SearchVideoItemModel>((e) => SearchVideoItemModel.fromJson(e))
|
||||||
.toList();
|
.toList();
|
||||||
|
|||||||
@ -1,8 +1,3 @@
|
|||||||
import 'package:hive/hive.dart';
|
|
||||||
|
|
||||||
part 'stat.g.dart';
|
|
||||||
|
|
||||||
@HiveType(typeId: 1)
|
|
||||||
class UserStat {
|
class UserStat {
|
||||||
UserStat({
|
UserStat({
|
||||||
this.following,
|
this.following,
|
||||||
@ -10,11 +5,8 @@ class UserStat {
|
|||||||
this.dynamicCount,
|
this.dynamicCount,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
|
||||||
int? following;
|
int? following;
|
||||||
@HiveField(1)
|
|
||||||
int? follower;
|
int? follower;
|
||||||
@HiveField(2)
|
|
||||||
int? dynamicCount;
|
int? dynamicCount;
|
||||||
|
|
||||||
UserStat.fromJson(Map<String, dynamic> json) {
|
UserStat.fromJson(Map<String, dynamic> json) {
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'stat.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// TypeAdapterGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
class UserStatAdapter extends TypeAdapter<UserStat> {
|
|
||||||
@override
|
|
||||||
final int typeId = 1;
|
|
||||||
|
|
||||||
@override
|
|
||||||
UserStat read(BinaryReader reader) {
|
|
||||||
final numOfFields = reader.readByte();
|
|
||||||
final fields = <int, dynamic>{
|
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
||||||
};
|
|
||||||
return UserStat(
|
|
||||||
following: fields[0] as int?,
|
|
||||||
follower: fields[1] as int?,
|
|
||||||
dynamicCount: fields[2] as int?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void write(BinaryWriter writer, UserStat obj) {
|
|
||||||
writer
|
|
||||||
..writeByte(3)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.following)
|
|
||||||
..writeByte(1)
|
|
||||||
..write(obj.follower)
|
|
||||||
..writeByte(2)
|
|
||||||
..write(obj.dynamicCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => typeId.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is UserStatAdapter &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
typeId == other.typeId;
|
|
||||||
}
|
|
||||||
@ -39,11 +39,11 @@ class ModelResult {
|
|||||||
ModelResult.fromJson(Map<String, dynamic> json) {
|
ModelResult.fromJson(Map<String, dynamic> json) {
|
||||||
resultType = json['result_type'];
|
resultType = json['result_type'];
|
||||||
summary = json['summary'];
|
summary = json['summary'];
|
||||||
outline = json['result_type'] == 2
|
outline = json['result_type'] == 0
|
||||||
? json['outline']
|
? <OutlineItem>[]
|
||||||
|
: json['outline']
|
||||||
.map<OutlineItem>((e) => OutlineItem.fromJson(e))
|
.map<OutlineItem>((e) => OutlineItem.fromJson(e))
|
||||||
.toList()
|
.toList();
|
||||||
: <OutlineItem>[];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
276
lib/models/video/later.dart
Normal file
276
lib/models/video/later.dart
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
class MediaVideoItemModel {
|
||||||
|
MediaVideoItemModel({
|
||||||
|
this.id,
|
||||||
|
this.aid,
|
||||||
|
this.offset,
|
||||||
|
this.index,
|
||||||
|
this.intro,
|
||||||
|
this.attr,
|
||||||
|
this.tid,
|
||||||
|
this.copyRight,
|
||||||
|
this.cntInfo,
|
||||||
|
this.cover,
|
||||||
|
this.duration,
|
||||||
|
this.pubtime,
|
||||||
|
this.likeState,
|
||||||
|
this.favState,
|
||||||
|
this.page,
|
||||||
|
this.cid,
|
||||||
|
this.pages,
|
||||||
|
this.title,
|
||||||
|
this.type,
|
||||||
|
this.upper,
|
||||||
|
this.link,
|
||||||
|
this.bvid,
|
||||||
|
this.shortLink,
|
||||||
|
this.rights,
|
||||||
|
this.elecInfo,
|
||||||
|
this.coin,
|
||||||
|
this.progressPercent,
|
||||||
|
this.badge,
|
||||||
|
this.forbidFav,
|
||||||
|
this.moreType,
|
||||||
|
this.businessOid,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? id;
|
||||||
|
int? aid;
|
||||||
|
int? offset;
|
||||||
|
int? index;
|
||||||
|
String? intro;
|
||||||
|
int? attr;
|
||||||
|
int? tid;
|
||||||
|
int? copyRight;
|
||||||
|
Map? cntInfo;
|
||||||
|
String? cover;
|
||||||
|
int? duration;
|
||||||
|
int? pubtime;
|
||||||
|
int? likeState;
|
||||||
|
int? favState;
|
||||||
|
int? page;
|
||||||
|
int? cid;
|
||||||
|
List<Page>? pages;
|
||||||
|
String? title;
|
||||||
|
int? type;
|
||||||
|
Upper? upper;
|
||||||
|
String? link;
|
||||||
|
String? bvid;
|
||||||
|
String? shortLink;
|
||||||
|
Rights? rights;
|
||||||
|
dynamic elecInfo;
|
||||||
|
Coin? coin;
|
||||||
|
double? progressPercent;
|
||||||
|
dynamic badge;
|
||||||
|
bool? forbidFav;
|
||||||
|
int? moreType;
|
||||||
|
int? businessOid;
|
||||||
|
|
||||||
|
factory MediaVideoItemModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
MediaVideoItemModel(
|
||||||
|
id: json["id"],
|
||||||
|
aid: json["id"],
|
||||||
|
offset: json["offset"],
|
||||||
|
index: json["index"],
|
||||||
|
intro: json["intro"],
|
||||||
|
attr: json["attr"],
|
||||||
|
tid: json["tid"],
|
||||||
|
copyRight: json["copy_right"],
|
||||||
|
cntInfo: json["cnt_info"],
|
||||||
|
cover: json["cover"],
|
||||||
|
duration: json["duration"],
|
||||||
|
pubtime: json["pubtime"],
|
||||||
|
likeState: json["like_state"],
|
||||||
|
favState: json["fav_state"],
|
||||||
|
page: json["page"],
|
||||||
|
cid: json["pages"] == null ? -1 : json["pages"].first['id'],
|
||||||
|
// json["pages"] 可能为null
|
||||||
|
pages: json["pages"] == null
|
||||||
|
? []
|
||||||
|
: List<Page>.from(json["pages"].map((x) => Page.fromJson(x))),
|
||||||
|
title: json["title"],
|
||||||
|
type: json["type"],
|
||||||
|
upper: Upper.fromJson(json["upper"]),
|
||||||
|
link: json["link"],
|
||||||
|
bvid: json["bv_id"],
|
||||||
|
shortLink: json["short_link"],
|
||||||
|
rights: Rights.fromJson(json["rights"]),
|
||||||
|
elecInfo: json["elec_info"],
|
||||||
|
coin: Coin.fromJson(json["coin"]),
|
||||||
|
progressPercent: json["progress_percent"].toDouble(),
|
||||||
|
badge: json["badge"],
|
||||||
|
forbidFav: json["forbid_fav"],
|
||||||
|
moreType: json["more_type"],
|
||||||
|
businessOid: json["business_oid"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Coin {
|
||||||
|
Coin({
|
||||||
|
this.maxNum,
|
||||||
|
this.coinNumber,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? maxNum;
|
||||||
|
int? coinNumber;
|
||||||
|
|
||||||
|
factory Coin.fromJson(Map<String, dynamic> json) => Coin(
|
||||||
|
maxNum: json["max_num"],
|
||||||
|
coinNumber: json["coin_number"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Page {
|
||||||
|
Page({
|
||||||
|
this.id,
|
||||||
|
this.title,
|
||||||
|
this.intro,
|
||||||
|
this.duration,
|
||||||
|
this.link,
|
||||||
|
this.page,
|
||||||
|
this.metas,
|
||||||
|
this.from,
|
||||||
|
this.dimension,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? id;
|
||||||
|
String? title;
|
||||||
|
String? intro;
|
||||||
|
int? duration;
|
||||||
|
String? link;
|
||||||
|
int? page;
|
||||||
|
List<Meta>? metas;
|
||||||
|
String? from;
|
||||||
|
Dimension? dimension;
|
||||||
|
|
||||||
|
factory Page.fromJson(Map<String, dynamic> json) => Page(
|
||||||
|
id: json["id"],
|
||||||
|
title: json["title"],
|
||||||
|
intro: json["intro"],
|
||||||
|
duration: json["duration"],
|
||||||
|
link: json["link"],
|
||||||
|
page: json["page"],
|
||||||
|
metas: List<Meta>.from(json["metas"].map((x) => Meta.fromJson(x))),
|
||||||
|
from: json["from"],
|
||||||
|
dimension: Dimension.fromJson(json["dimension"]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dimension {
|
||||||
|
Dimension({
|
||||||
|
this.width,
|
||||||
|
this.height,
|
||||||
|
this.rotate,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? width;
|
||||||
|
int? height;
|
||||||
|
int? rotate;
|
||||||
|
|
||||||
|
factory Dimension.fromJson(Map<String, dynamic> json) => Dimension(
|
||||||
|
width: json["width"],
|
||||||
|
height: json["height"],
|
||||||
|
rotate: json["rotate"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Meta {
|
||||||
|
Meta({
|
||||||
|
this.quality,
|
||||||
|
this.size,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? quality;
|
||||||
|
int? size;
|
||||||
|
|
||||||
|
factory Meta.fromJson(Map<String, dynamic> json) => Meta(
|
||||||
|
quality: json["quality"],
|
||||||
|
size: json["size"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Rights {
|
||||||
|
Rights({
|
||||||
|
this.bp,
|
||||||
|
this.elec,
|
||||||
|
this.download,
|
||||||
|
this.movie,
|
||||||
|
this.pay,
|
||||||
|
this.ugcPay,
|
||||||
|
this.hd5,
|
||||||
|
this.noReprint,
|
||||||
|
this.autoplay,
|
||||||
|
this.noBackground,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? bp;
|
||||||
|
int? elec;
|
||||||
|
int? download;
|
||||||
|
int? movie;
|
||||||
|
int? pay;
|
||||||
|
int? ugcPay;
|
||||||
|
int? hd5;
|
||||||
|
int? noReprint;
|
||||||
|
int? autoplay;
|
||||||
|
int? noBackground;
|
||||||
|
|
||||||
|
factory Rights.fromJson(Map<String, dynamic> json) => Rights(
|
||||||
|
bp: json["bp"],
|
||||||
|
elec: json["elec"],
|
||||||
|
download: json["download"],
|
||||||
|
movie: json["movie"],
|
||||||
|
pay: json["pay"],
|
||||||
|
ugcPay: json["ugc_pay"],
|
||||||
|
hd5: json["hd5"],
|
||||||
|
noReprint: json["no_reprint"],
|
||||||
|
autoplay: json["autoplay"],
|
||||||
|
noBackground: json["no_background"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Upper {
|
||||||
|
Upper({
|
||||||
|
this.mid,
|
||||||
|
this.name,
|
||||||
|
this.face,
|
||||||
|
this.followed,
|
||||||
|
this.fans,
|
||||||
|
this.vipType,
|
||||||
|
this.vipStatue,
|
||||||
|
this.vipDueDate,
|
||||||
|
this.vipPayType,
|
||||||
|
this.officialRole,
|
||||||
|
this.officialTitle,
|
||||||
|
this.officialDesc,
|
||||||
|
this.displayName,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? mid;
|
||||||
|
String? name;
|
||||||
|
String? face;
|
||||||
|
int? followed;
|
||||||
|
int? fans;
|
||||||
|
int? vipType;
|
||||||
|
int? vipStatue;
|
||||||
|
int? vipDueDate;
|
||||||
|
int? vipPayType;
|
||||||
|
int? officialRole;
|
||||||
|
String? officialTitle;
|
||||||
|
String? officialDesc;
|
||||||
|
String? displayName;
|
||||||
|
|
||||||
|
factory Upper.fromJson(Map<String, dynamic> json) => Upper(
|
||||||
|
mid: json["mid"],
|
||||||
|
name: json["name"],
|
||||||
|
face: json["face"],
|
||||||
|
followed: json["followed"],
|
||||||
|
fans: json["fans"],
|
||||||
|
vipType: json["vip_type"],
|
||||||
|
vipStatue: json["vip_statue"],
|
||||||
|
vipDueDate: json["vip_due_date"],
|
||||||
|
vipPayType: json["vip_pay_type"],
|
||||||
|
officialRole: json["official_role"],
|
||||||
|
officialTitle: json["official_title"],
|
||||||
|
officialDesc: json["official_desc"],
|
||||||
|
displayName: json["display_name"],
|
||||||
|
);
|
||||||
|
}
|
||||||
6
lib/models/video/play/ao_output.dart
Normal file
6
lib/models/video/play/ao_output.dart
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
final List aoOutputList = [
|
||||||
|
{'title': 'audiotrack,opensles', 'value': '0'},
|
||||||
|
{'title': 'opensles,audiotrack', 'value': '1'},
|
||||||
|
{'title': 'audiotrack', 'value': '2'},
|
||||||
|
{'title': 'opensles', 'value': '3'},
|
||||||
|
];
|
||||||
@ -1,6 +1,3 @@
|
|||||||
import 'package:get/get.dart';
|
|
||||||
import '../../common/subtitle_type.dart';
|
|
||||||
|
|
||||||
class SubTitlteModel {
|
class SubTitlteModel {
|
||||||
SubTitlteModel({
|
SubTitlteModel({
|
||||||
this.aid,
|
this.aid,
|
||||||
@ -78,11 +75,6 @@ class SubTitlteItemModel {
|
|||||||
aiType: json["ai_type"],
|
aiType: json["ai_type"],
|
||||||
aiStatus: json["ai_status"],
|
aiStatus: json["ai_status"],
|
||||||
title: json["lan_doc"],
|
title: json["lan_doc"],
|
||||||
code: SubtitleType.values
|
|
||||||
.firstWhereOrNull(
|
|
||||||
(element) => element.id.toString() == json["lan"])
|
|
||||||
?.index ??
|
|
||||||
-1,
|
|
||||||
content: '',
|
content: '',
|
||||||
body: [],
|
body: [],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -295,7 +295,7 @@ class AboutController extends GetxController {
|
|||||||
displayTime: const Duration(milliseconds: 500),
|
displayTime: const Duration(milliseconds: 500),
|
||||||
).then(
|
).then(
|
||||||
(value) => launchUrl(
|
(value) => launchUrl(
|
||||||
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
|
Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'),
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -349,7 +349,7 @@ class AboutController extends GetxController {
|
|||||||
// 官网
|
// 官网
|
||||||
webSiteUrl() {
|
webSiteUrl() {
|
||||||
launchUrl(
|
launchUrl(
|
||||||
Uri.parse('https://pilipalanet.mysxl.cn'),
|
Uri.parse('https://pilipala.life'),
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -225,6 +225,8 @@ class BangumiIntroController extends GetxController {
|
|||||||
videoDetailCtr.oid.value = aid;
|
videoDetailCtr.oid.value = aid;
|
||||||
videoDetailCtr.cover.value = cover;
|
videoDetailCtr.cover.value = cover;
|
||||||
videoDetailCtr.queryVideoUrl();
|
videoDetailCtr.queryVideoUrl();
|
||||||
|
videoDetailCtr.getSubtitle();
|
||||||
|
videoDetailCtr.setSubtitleContent();
|
||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
try {
|
try {
|
||||||
/// 未渲染回复组件时可能异常
|
/// 未渲染回复组件时可能异常
|
||||||
|
|||||||
@ -189,8 +189,8 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
NetworkImgLayer(
|
NetworkImgLayer(
|
||||||
width: 105,
|
width: 115,
|
||||||
height: 160,
|
height: 115 / 0.75,
|
||||||
src: widget.bangumiDetail!.cover!,
|
src: widget.bangumiDetail!.cover!,
|
||||||
),
|
),
|
||||||
PBadge(
|
PBadge(
|
||||||
@ -208,7 +208,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => showIntroDetail(),
|
onTap: () => showIntroDetail(),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 158,
|
height: 115 / 0.75,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -255,13 +255,11 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(
|
StatView(
|
||||||
theme: 'gray',
|
|
||||||
view: widget.bangumiDetail!.stat!['views'],
|
view: widget.bangumiDetail!.stat!['views'],
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
StatDanMu(
|
StatDanMu(
|
||||||
theme: 'gray',
|
|
||||||
danmu: widget.bangumiDetail!.stat!['danmakus'],
|
danmu: widget.bangumiDetail!.stat!['danmakus'],
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
),
|
),
|
||||||
|
|||||||
@ -60,13 +60,11 @@ class IntroDetail extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(
|
StatView(
|
||||||
theme: 'gray',
|
|
||||||
view: bangumiDetail!.stat!['views'],
|
view: bangumiDetail!.stat!['views'],
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
StatDanMu(
|
StatDanMu(
|
||||||
theme: 'gray',
|
|
||||||
danmu: bangumiDetail!.stat!['danmakus'],
|
danmu: bangumiDetail!.stat!['danmakus'],
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
),
|
),
|
||||||
|
|||||||
@ -96,7 +96,8 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 268,
|
height: Get.size.width / 3 / 0.75 +
|
||||||
|
MediaQuery.textScalerOf(context).scale(50.0),
|
||||||
child: FutureBuilder(
|
child: FutureBuilder(
|
||||||
future: _futureBuilderFutureFollow,
|
future: _futureBuilderFutureFollow,
|
||||||
builder:
|
builder:
|
||||||
@ -117,7 +118,6 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Container(
|
return Container(
|
||||||
width: Get.size.width / 3,
|
width: Get.size.width / 3,
|
||||||
height: 254,
|
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
left: StyleString.safeSpace,
|
left: StyleString.safeSpace,
|
||||||
right: index ==
|
right: index ==
|
||||||
@ -208,8 +208,8 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
crossAxisSpacing: StyleString.cardSpace,
|
crossAxisSpacing: StyleString.cardSpace,
|
||||||
// 列数
|
// 列数
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
mainAxisExtent: Get.size.width / 3 / 0.65 +
|
mainAxisExtent: Get.size.width / 3 / 0.75 +
|
||||||
MediaQuery.textScalerOf(context).scale(32.0),
|
MediaQuery.textScalerOf(context).scale(42.0),
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@ -86,9 +86,11 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
item.aid,
|
item.aid,
|
||||||
item.cover,
|
item.cover,
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
if (_bottomSheetController != null) {
|
if (_bottomSheetController != null) {
|
||||||
_bottomSheetController?.close();
|
_bottomSheetController?.close();
|
||||||
}
|
}
|
||||||
|
} catch (_) {}
|
||||||
currentIndex.value = i;
|
currentIndex.value = i;
|
||||||
scrollToIndex();
|
scrollToIndex();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ class BangumiCardV extends StatelessWidget {
|
|||||||
StyleString.imgRadius,
|
StyleString.imgRadius,
|
||||||
),
|
),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 0.65,
|
aspectRatio: 0.75,
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
final double maxWidth = boxConstraints.maxWidth;
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
final double maxHeight = boxConstraints.maxHeight;
|
final double maxHeight = boxConstraints.maxHeight;
|
||||||
|
|||||||
@ -61,7 +61,7 @@ class _BlackListPageState extends State<BlackListPage> {
|
|||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
title: Obx(
|
title: Obx(
|
||||||
() => Text(
|
() => Text(
|
||||||
'黑名单管理 - ${_blackListController.total.value}',
|
'黑名单管理 ${_blackListController.total.value == 0 ? '' : '- ${_blackListController.total.value}'}',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -76,8 +76,12 @@ class _BlackListPageState extends State<BlackListPage> {
|
|||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
List<BlackListItem> list = _blackListController.blackList;
|
List<BlackListItem> list = _blackListController.blackList;
|
||||||
return Obx(
|
return Obx(
|
||||||
() => list.length == 1
|
() => list.isEmpty
|
||||||
? const SizedBox()
|
? CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
HttpError(errMsg: '你没有拉黑任何人哦~_~', fn: () => {})
|
||||||
|
],
|
||||||
|
)
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
|
|||||||
@ -2,8 +2,9 @@ import 'package:pilipala/http/danmaku.dart';
|
|||||||
import 'package:pilipala/models/danmaku/dm.pb.dart';
|
import 'package:pilipala/models/danmaku/dm.pb.dart';
|
||||||
|
|
||||||
class PlDanmakuController {
|
class PlDanmakuController {
|
||||||
PlDanmakuController(this.cid);
|
PlDanmakuController(this.cid, this.type);
|
||||||
final int cid;
|
final int cid;
|
||||||
|
final String type;
|
||||||
Map<int, List<DanmakuElem>> dmSegMap = {};
|
Map<int, List<DanmakuElem>> dmSegMap = {};
|
||||||
// 已请求的段落标记
|
// 已请求的段落标记
|
||||||
List<bool> requestedSeg = [];
|
List<bool> requestedSeg = [];
|
||||||
@ -17,7 +18,11 @@ class PlDanmakuController {
|
|||||||
int segCount = (videoDuration / segmentLength).ceil();
|
int segCount = (videoDuration / segmentLength).ceil();
|
||||||
requestedSeg = List<bool>.generate(segCount, (index) => false);
|
requestedSeg = List<bool>.generate(segCount, (index) => false);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
queryDanmaku(calcSegment(progress));
|
queryDanmaku(calcSegment(progress));
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -31,6 +36,7 @@ class PlDanmakuController {
|
|||||||
|
|
||||||
void queryDanmaku(int segmentIndex) async {
|
void queryDanmaku(int segmentIndex) async {
|
||||||
assert(requestedSeg[segmentIndex] == false);
|
assert(requestedSeg[segmentIndex] == false);
|
||||||
|
if (requestedSeg.length > segmentIndex) {
|
||||||
requestedSeg[segmentIndex] = true;
|
requestedSeg[segmentIndex] = true;
|
||||||
final DmSegMobileReply result = await DanmakaHttp.queryDanmaku(
|
final DmSegMobileReply result = await DanmakaHttp.queryDanmaku(
|
||||||
cid: cid, segmentIndex: segmentIndex + 1);
|
cid: cid, segmentIndex: segmentIndex + 1);
|
||||||
@ -44,6 +50,7 @@ class PlDanmakuController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<DanmakuElem>? getCurrentDanmaku(int progress) {
|
List<DanmakuElem>? getCurrentDanmaku(int progress) {
|
||||||
int segmentIndex = calcSegment(progress);
|
int segmentIndex = calcSegment(progress);
|
||||||
|
|||||||
@ -12,11 +12,15 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
class PlDanmaku extends StatefulWidget {
|
class PlDanmaku extends StatefulWidget {
|
||||||
final int cid;
|
final int cid;
|
||||||
final PlPlayerController playerController;
|
final PlPlayerController playerController;
|
||||||
|
final String type;
|
||||||
|
final Function(DanmakuController)? createdController;
|
||||||
|
|
||||||
const PlDanmaku({
|
const PlDanmaku({
|
||||||
super.key,
|
super.key,
|
||||||
required this.cid,
|
required this.cid,
|
||||||
required this.playerController,
|
required this.playerController,
|
||||||
|
this.type = 'video',
|
||||||
|
this.createdController,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -43,9 +47,9 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
enableShowDanmaku =
|
enableShowDanmaku =
|
||||||
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false);
|
||||||
_plDanmakuController = PlDanmakuController(widget.cid);
|
_plDanmakuController = PlDanmakuController(widget.cid, widget.type);
|
||||||
if (mounted) {
|
|
||||||
playerController = widget.playerController;
|
playerController = widget.playerController;
|
||||||
|
if (mounted && widget.type == 'video') {
|
||||||
if (enableShowDanmaku || playerController.isOpenDanmu.value) {
|
if (enableShowDanmaku || playerController.isOpenDanmu.value) {
|
||||||
_plDanmakuController.initiate(
|
_plDanmakuController.initiate(
|
||||||
playerController.duration.value.inMilliseconds,
|
playerController.duration.value.inMilliseconds,
|
||||||
@ -55,6 +59,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
..addStatusLister(playerListener)
|
..addStatusLister(playerListener)
|
||||||
..addPositionListener(videoPositionListen);
|
..addPositionListener(videoPositionListen);
|
||||||
}
|
}
|
||||||
|
if (widget.type == 'video') {
|
||||||
playerController.isOpenDanmu.listen((p0) {
|
playerController.isOpenDanmu.listen((p0) {
|
||||||
if (p0 && !_plDanmakuController.initiated) {
|
if (p0 && !_plDanmakuController.initiated) {
|
||||||
_plDanmakuController.initiate(
|
_plDanmakuController.initiate(
|
||||||
@ -62,6 +67,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
playerController.position.value.inMilliseconds);
|
playerController.position.value.inMilliseconds);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
blockTypes = playerController.blockTypes;
|
blockTypes = playerController.blockTypes;
|
||||||
showArea = playerController.showArea;
|
showArea = playerController.showArea;
|
||||||
opacityVal = playerController.opacityVal;
|
opacityVal = playerController.opacityVal;
|
||||||
@ -128,6 +134,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
child: DanmakuView(
|
child: DanmakuView(
|
||||||
createdController: (DanmakuController e) async {
|
createdController: (DanmakuController e) async {
|
||||||
playerController.danmakuController = _controller = e;
|
playerController.danmakuController = _controller = e;
|
||||||
|
widget.createdController?.call(e);
|
||||||
},
|
},
|
||||||
option: DanmakuOption(
|
option: DanmakuOption(
|
||||||
fontSize: 15 * fontSizeVal,
|
fontSize: 15 * fontSizeVal,
|
||||||
@ -136,8 +143,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
hideTop: blockTypes.contains(5),
|
hideTop: blockTypes.contains(5),
|
||||||
hideScroll: blockTypes.contains(2),
|
hideScroll: blockTypes.contains(2),
|
||||||
hideBottom: blockTypes.contains(4),
|
hideBottom: blockTypes.contains(4),
|
||||||
duration:
|
duration: danmakuDurationVal / playerController.playbackSpeed,
|
||||||
danmakuDurationVal / playerController.playbackSpeed,
|
|
||||||
strokeWidth: strokeWidth,
|
strokeWidth: strokeWidth,
|
||||||
// initDuration /
|
// initDuration /
|
||||||
// (danmakuSpeedVal * widget.playerController.playbackSpeed),
|
// (danmakuSpeedVal * widget.playerController.playbackSpeed),
|
||||||
|
|||||||
@ -6,9 +6,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/dynamics.dart';
|
import 'package:pilipala/http/dynamics.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
|
||||||
import 'package:pilipala/models/common/dynamics_type.dart';
|
import 'package:pilipala/models/common/dynamics_type.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/dynamics/result.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';
|
||||||
@ -16,7 +14,6 @@ import 'package:pilipala/utils/feed_back.dart';
|
|||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
import 'package:pilipala/utils/route_push.dart';
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
|
||||||
|
|
||||||
class DynamicsController extends GetxController {
|
class DynamicsController extends GetxController {
|
||||||
int page = 1;
|
int page = 1;
|
||||||
@ -149,20 +146,26 @@ class DynamicsController extends GetxController {
|
|||||||
/// 专栏文章查看
|
/// 专栏文章查看
|
||||||
case 'DYNAMIC_TYPE_ARTICLE':
|
case 'DYNAMIC_TYPE_ARTICLE':
|
||||||
String title = item.modules.moduleDynamic.major.opus.title;
|
String title = item.modules.moduleDynamic.major.opus.title;
|
||||||
String url = item.modules.moduleDynamic.major.opus.jumpUrl;
|
String jumpUrl = item.modules.moduleDynamic.major.opus.jumpUrl;
|
||||||
if (url.contains('opus') || url.contains('read')) {
|
String url =
|
||||||
|
jumpUrl.startsWith('//') ? jumpUrl.split('//').last : jumpUrl;
|
||||||
|
if (jumpUrl.contains('opus') || jumpUrl.contains('read')) {
|
||||||
RegExp digitRegExp = RegExp(r'\d+');
|
RegExp digitRegExp = RegExp(r'\d+');
|
||||||
Iterable<Match> matches = digitRegExp.allMatches(url);
|
Iterable<Match> matches = digitRegExp.allMatches(jumpUrl);
|
||||||
String number = matches.first.group(0)!;
|
String number = matches.first.group(0)!;
|
||||||
if (url.contains('read')) {
|
if (jumpUrl.contains('read')) {
|
||||||
number = 'cv$number';
|
Get.toNamed('/read', parameters: {
|
||||||
}
|
|
||||||
Get.toNamed('/htmlRender', parameters: {
|
|
||||||
'url': url.startsWith('//') ? url.split('//').last : url,
|
|
||||||
'title': title,
|
'title': title,
|
||||||
'id': number,
|
'id': number,
|
||||||
'dynamicType': url.split('//').last.split('/')[1]
|
'articleType': url.split('/')[1]
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Get.toNamed('/opus', parameters: {
|
||||||
|
'title': title,
|
||||||
|
'id': number,
|
||||||
|
'articleType': 'opus'
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/webview',
|
'/webview',
|
||||||
@ -282,4 +285,11 @@ class DynamicsController extends GetxController {
|
|||||||
dynamicsList.value = <DynamicItemModel>[];
|
dynamicsList.value = <DynamicItemModel>[];
|
||||||
queryFollowDynamic();
|
queryFollowDynamic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 点击up主
|
||||||
|
void onTapUp(data) {
|
||||||
|
mid.value = data.mid;
|
||||||
|
upInfo.value = data;
|
||||||
|
onSelectUp(data.mid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,8 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
late DynamicDetailController _dynamicDetailController;
|
late DynamicDetailController _dynamicDetailController;
|
||||||
late AnimationController fabAnimationCtr;
|
late AnimationController fabAnimationCtr;
|
||||||
Future? _futureBuilderFuture;
|
Future? _futureBuilderFuture;
|
||||||
late StreamController<bool> titleStreamC; // appBar title
|
late StreamController<bool> titleStreamC =
|
||||||
|
StreamController<bool>.broadcast(); // appBar title
|
||||||
late ScrollController scrollController;
|
late ScrollController scrollController;
|
||||||
bool _visibleTitle = false;
|
bool _visibleTitle = false;
|
||||||
String? action;
|
String? action;
|
||||||
@ -48,7 +49,6 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
super.initState();
|
super.initState();
|
||||||
// floor 1原创 2转发
|
// floor 1原创 2转发
|
||||||
init();
|
init();
|
||||||
titleStreamC = StreamController<bool>();
|
|
||||||
if (action == 'comment') {
|
if (action == 'comment') {
|
||||||
_visibleTitle = true;
|
_visibleTitle = true;
|
||||||
titleStreamC.add(true);
|
titleStreamC.add(true);
|
||||||
@ -106,7 +106,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查看二级评论
|
// 查看二级评论
|
||||||
void replyReply(replyItem) {
|
void replyReply(replyItem, currentReply, loadMore) {
|
||||||
int oid = replyItem.oid;
|
int oid = replyItem.oid;
|
||||||
int rpid = replyItem.rpid!;
|
int rpid = replyItem.rpid!;
|
||||||
Get.to(
|
Get.to(
|
||||||
@ -125,6 +125,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
source: 'dynamic',
|
source: 'dynamic',
|
||||||
replyType: ReplyType.values[replyType],
|
replyType: ReplyType.values[replyType],
|
||||||
firstFloor: replyItem,
|
firstFloor: replyItem,
|
||||||
|
loadMore: loadMore,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -324,8 +325,10 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
replyItem: replyList[index],
|
replyItem: replyList[index],
|
||||||
showReplyRow: true,
|
showReplyRow: true,
|
||||||
replyLevel: '1',
|
replyLevel: '1',
|
||||||
replyReply: (replyItem) =>
|
replyReply:
|
||||||
replyReply(replyItem),
|
(replyItem, currentReply, loadMore) =>
|
||||||
|
replyReply(replyItem,
|
||||||
|
currentReply, loadMore),
|
||||||
replyType: ReplyType.values[replyType],
|
replyType: ReplyType.values[replyType],
|
||||||
addReply: (replyItem) {
|
addReply: (replyItem) {
|
||||||
replyList[index]
|
replyList[index]
|
||||||
|
|||||||
46
lib/pages/dynamics/up_dynamic/controller.dart
Normal file
46
lib/pages/dynamics/up_dynamic/controller.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/http/dynamics.dart';
|
||||||
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
|
import 'package:pilipala/models/dynamics/up.dart';
|
||||||
|
|
||||||
|
class UpDynamicsController extends GetxController {
|
||||||
|
UpDynamicsController(this.upInfo);
|
||||||
|
UpItem upInfo;
|
||||||
|
RxList<DynamicItemModel> dynamicsList = <DynamicItemModel>[].obs;
|
||||||
|
RxBool isLoadingDynamic = false.obs;
|
||||||
|
String? offset = '';
|
||||||
|
int page = 1;
|
||||||
|
|
||||||
|
Future queryFollowDynamic({type = 'init'}) async {
|
||||||
|
if (type == 'init') {
|
||||||
|
dynamicsList.clear();
|
||||||
|
}
|
||||||
|
// 下拉刷新数据渲染时会触发onLoad
|
||||||
|
if (type == 'onLoad' && page == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isLoadingDynamic.value = true;
|
||||||
|
var res = await DynamicsHttp.followDynamic(
|
||||||
|
page: type == 'init' ? 1 : page,
|
||||||
|
type: 'all',
|
||||||
|
offset: offset,
|
||||||
|
mid: upInfo.mid,
|
||||||
|
);
|
||||||
|
isLoadingDynamic.value = false;
|
||||||
|
if (res['status']) {
|
||||||
|
if (type == 'onLoad' && res['data'].items.isEmpty) {
|
||||||
|
SmartDialog.showToast('没有更多了');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type == 'init') {
|
||||||
|
dynamicsList.value = res['data'].items;
|
||||||
|
} else {
|
||||||
|
dynamicsList.addAll(res['data'].items);
|
||||||
|
}
|
||||||
|
offset = res['data'].offset;
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
4
lib/pages/dynamics/up_dynamic/index.dart
Normal file
4
lib/pages/dynamics/up_dynamic/index.dart
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
library up_dynamics;
|
||||||
|
|
||||||
|
export './controller.dart';
|
||||||
|
export './view.dart';
|
||||||
151
lib/pages/dynamics/up_dynamic/route_panel.dart
Normal file
151
lib/pages/dynamics/up_dynamic/route_panel.dart
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:pilipala/models/dynamics/up.dart';
|
||||||
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
|
import '../controller.dart';
|
||||||
|
import 'index.dart';
|
||||||
|
|
||||||
|
class OverlayPanel extends StatefulWidget {
|
||||||
|
const OverlayPanel({super.key, required this.ctr, required this.upInfo});
|
||||||
|
|
||||||
|
final DynamicsController ctr;
|
||||||
|
final UpItem upInfo;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OverlayPanel> createState() => _OverlayPanelState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OverlayPanelState extends State<OverlayPanel>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
static const itemPadding = EdgeInsets.symmetric(horizontal: 6, vertical: 0);
|
||||||
|
final PageController pageController = PageController();
|
||||||
|
late double contentWidth = 50;
|
||||||
|
late List<UpItem> upList;
|
||||||
|
late RxInt currentMid = (-1).obs;
|
||||||
|
TabController? _tabController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
upList = widget.ctr.upData.value.upList!
|
||||||
|
.map<UpItem>((element) => element)
|
||||||
|
.toList();
|
||||||
|
upList.removeAt(0);
|
||||||
|
_tabController = TabController(length: upList.length, vsync: this);
|
||||||
|
|
||||||
|
currentMid.value = widget.upInfo.mid!;
|
||||||
|
|
||||||
|
pageController.addListener(() {
|
||||||
|
int index = pageController.page!.round();
|
||||||
|
int mid = upList[index].mid!;
|
||||||
|
if (mid != currentMid.value) {
|
||||||
|
currentMid.value = mid;
|
||||||
|
_tabController?.animateTo(index,
|
||||||
|
duration: Duration.zero, curve: Curves.linear);
|
||||||
|
onClickUp(upList[index], index, type: 'pageChange');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||||
|
int index =
|
||||||
|
upList.indexWhere((element) => element.mid == widget.upInfo.mid);
|
||||||
|
pageController.jumpToPage(index);
|
||||||
|
onClickUp(widget.upInfo, index);
|
||||||
|
_tabController?.animateTo(index,
|
||||||
|
duration: Duration.zero, curve: Curves.linear);
|
||||||
|
onClickUp(upList[index], index, type: 'pageChange');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void onClickUp(data, i, {type = 'click'}) {
|
||||||
|
if (type == 'click') {
|
||||||
|
pageController.jumpToPage(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: Get.width,
|
||||||
|
height: Get.height,
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
margin: EdgeInsets.fromLTRB(
|
||||||
|
0,
|
||||||
|
MediaQuery.of(context).padding.top + 4,
|
||||||
|
0,
|
||||||
|
MediaQuery.of(context).padding.bottom + 4,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 50,
|
||||||
|
child: TabBar(
|
||||||
|
controller: _tabController,
|
||||||
|
dividerColor: Colors.transparent,
|
||||||
|
automaticIndicatorColorAdjustment: false,
|
||||||
|
tabAlignment: TabAlignment.start,
|
||||||
|
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||||
|
indicatorPadding: EdgeInsets.zero,
|
||||||
|
indicatorSize: TabBarIndicatorSize.label,
|
||||||
|
indicator: const BoxDecoration(),
|
||||||
|
labelPadding: itemPadding,
|
||||||
|
indicatorWeight: 1,
|
||||||
|
isScrollable: true,
|
||||||
|
tabs: upList.map((e) => Tab(child: upItemBuild(e))).toList(),
|
||||||
|
onTap: (index) {
|
||||||
|
feedBack();
|
||||||
|
EasyThrottle.throttle(
|
||||||
|
'follow', const Duration(milliseconds: 200), () {
|
||||||
|
onClickUp(upList[index], index);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: PageView.builder(
|
||||||
|
itemCount: upList.length,
|
||||||
|
controller: pageController,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return Container(
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
margin: const EdgeInsets.fromLTRB(10, 12, 10, 0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: UpDyanmicsPage(upInfo: upList[index], ctr: widget.ctr),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget upItemBuild(data) {
|
||||||
|
return Obx(
|
||||||
|
() => AnimatedOpacity(
|
||||||
|
opacity: currentMid == data.mid ? 1 : 0.3,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
child: AnimatedScale(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
scale: currentMid == data.mid ? 1 : 0.9,
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
width: contentWidth,
|
||||||
|
height: contentWidth,
|
||||||
|
src: data.face,
|
||||||
|
type: 'avatar',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
178
lib/pages/dynamics/up_dynamic/view.dart
Normal file
178
lib/pages/dynamics/up_dynamic/view.dart
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/common/skeleton/dynamic_card.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/common/widgets/no_data.dart';
|
||||||
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
|
import 'package:pilipala/models/dynamics/up.dart';
|
||||||
|
import 'package:pilipala/pages/dynamics/up_dynamic/index.dart';
|
||||||
|
|
||||||
|
import '../index.dart';
|
||||||
|
import '../widgets/dynamic_panel.dart';
|
||||||
|
|
||||||
|
class UpDyanmicsPage extends StatefulWidget {
|
||||||
|
final UpItem upInfo;
|
||||||
|
final DynamicsController ctr;
|
||||||
|
|
||||||
|
const UpDyanmicsPage({
|
||||||
|
required this.upInfo,
|
||||||
|
required this.ctr,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<UpDyanmicsPage> createState() => _UpDyanmicsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UpDyanmicsPageState extends State<UpDyanmicsPage>
|
||||||
|
with AutomaticKeepAliveClientMixin {
|
||||||
|
late UpDynamicsController _upDynamicsController;
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
|
late Future _futureBuilderFuture;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_upDynamicsController = Get.put(UpDynamicsController(widget.upInfo),
|
||||||
|
tag: widget.upInfo.mid.toString());
|
||||||
|
_futureBuilderFuture = _upDynamicsController.queryFollowDynamic();
|
||||||
|
|
||||||
|
scrollController.addListener(
|
||||||
|
() async {
|
||||||
|
if (scrollController.position.pixels >=
|
||||||
|
scrollController.position.maxScrollExtent - 200) {
|
||||||
|
EasyThrottle.throttle(
|
||||||
|
'queryFollowDynamic', const Duration(seconds: 1), () {
|
||||||
|
_upDynamicsController.queryFollowDynamic(type: 'onLoad');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
|
return CustomScrollView(
|
||||||
|
controller: scrollController,
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
slivers: [
|
||||||
|
SliverPersistentHeader(
|
||||||
|
pinned: true,
|
||||||
|
floating: true,
|
||||||
|
delegate: _MySliverPersistentHeaderDelegate(
|
||||||
|
child: Container(
|
||||||
|
height: 50,
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 4, 4, 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
width: 0.1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.upInfo.uname!,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium!
|
||||||
|
.copyWith(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FutureBuilder(
|
||||||
|
future: _futureBuilderFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
if (snapshot.data == null) {
|
||||||
|
return const SliverToBoxAdapter(child: SizedBox());
|
||||||
|
}
|
||||||
|
Map? data = snapshot.data;
|
||||||
|
if (data != null && data['status']) {
|
||||||
|
List<DynamicItemModel> list =
|
||||||
|
_upDynamicsController.dynamicsList;
|
||||||
|
return Obx(
|
||||||
|
() {
|
||||||
|
if (list.isEmpty) {
|
||||||
|
if (_upDynamicsController.isLoadingDynamic.value) {
|
||||||
|
return skeleton();
|
||||||
|
} else {
|
||||||
|
return const NoData();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
return DynamicPanel(item: list[index]);
|
||||||
|
},
|
||||||
|
childCount: list.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return HttpError(
|
||||||
|
errMsg: data?['msg'] ?? '请求异常',
|
||||||
|
btnText: data?['code'] == -101 ? '去登录' : null,
|
||||||
|
fn: () {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return skeleton();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget skeleton() {
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
|
return const DynamicCardSkeleton();
|
||||||
|
}, childCount: 5),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||||
|
_MySliverPersistentHeaderDelegate({required this.child});
|
||||||
|
final double _minExtent = 50;
|
||||||
|
final double _maxExtent = 50;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(
|
||||||
|
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get maxExtent => _maxExtent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get minExtent => _minExtent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRebuild(covariant _MySliverPersistentHeaderDelegate oldDelegate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ import 'package:pilipala/common/skeleton/dynamic_card.dart';
|
|||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/no_data.dart';
|
import 'package:pilipala/common/widgets/no_data.dart';
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
|
import 'package:pilipala/plugin/pl_popup/index.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/main_stream.dart';
|
import 'package:pilipala/utils/main_stream.dart';
|
||||||
import 'package:pilipala/utils/route_push.dart';
|
import 'package:pilipala/utils/route_push.dart';
|
||||||
@ -17,6 +18,7 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
import '../mine/controller.dart';
|
import '../mine/controller.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/dynamic_panel.dart';
|
import 'widgets/dynamic_panel.dart';
|
||||||
|
import 'up_dynamic/route_panel.dart';
|
||||||
import 'widgets/up_panel.dart';
|
import 'widgets/up_panel.dart';
|
||||||
|
|
||||||
class DynamicsPage extends StatefulWidget {
|
class DynamicsPage extends StatefulWidget {
|
||||||
@ -80,7 +82,6 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
titleSpacing: 0,
|
|
||||||
title: SizedBox(
|
title: SizedBox(
|
||||||
height: 34,
|
height: 34,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@ -202,7 +203,21 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
}
|
}
|
||||||
Map data = snapshot.data;
|
Map data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
return Obx(() => UpPanel(_dynamicsController.upData.value));
|
return Obx(
|
||||||
|
() => UpPanel(
|
||||||
|
upData: _dynamicsController.upData.value,
|
||||||
|
onClickUpCb: (data) {
|
||||||
|
// _dynamicsController.onTapUp(data);
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
PlPopupRoute(
|
||||||
|
child: OverlayPanel(
|
||||||
|
ctr: _dynamicsController, upInfo: data),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return const SliverToBoxAdapter(
|
return const SliverToBoxAdapter(
|
||||||
child: SizedBox(height: 80),
|
child: SizedBox(height: 80),
|
||||||
|
|||||||
@ -4,8 +4,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import 'package:pilipala/common/widgets/badge.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
import 'package:pilipala/pages/preview/index.dart';
|
import 'package:pilipala/plugin/pl_gallery/index.dart';
|
||||||
|
|
||||||
import 'rich_node_panel.dart';
|
import 'rich_node_panel.dart';
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
@ -59,16 +58,14 @@ class _ContentState extends State<Content> {
|
|||||||
(pictureItem.height != null && pictureItem.width != null
|
(pictureItem.height != null && pictureItem.width != null
|
||||||
? pictureItem.height! / pictureItem.width!
|
? pictureItem.height! / pictureItem.width!
|
||||||
: 1);
|
: 1);
|
||||||
return GestureDetector(
|
return Hero(
|
||||||
onTap: () {
|
tag: pictureItem.url!,
|
||||||
showDialog(
|
placeholderBuilder:
|
||||||
useSafeArea: false,
|
(BuildContext context, Size heroSize, Widget child) {
|
||||||
context: context,
|
return child;
|
||||||
builder: (context) {
|
|
||||||
return ImagePreview(initialPage: 0, imgList: picList);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => onPreviewImg(picList, 1, context),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.only(top: 4),
|
padding: const EdgeInsets.only(top: 4),
|
||||||
constraints: BoxConstraints(maxHeight: maxHeight),
|
constraints: BoxConstraints(maxHeight: maxHeight),
|
||||||
@ -91,7 +88,9 @@ class _ContentState extends State<Content> {
|
|||||||
)
|
)
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
],
|
],
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -102,20 +101,16 @@ class _ContentState extends State<Content> {
|
|||||||
List<Widget> list = [];
|
List<Widget> list = [];
|
||||||
for (var i = 0; i < len; i++) {
|
for (var i = 0; i < len; i++) {
|
||||||
picList.add(pics[i].url!);
|
picList.add(pics[i].url!);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
list.add(
|
list.add(
|
||||||
LayoutBuilder(
|
LayoutBuilder(
|
||||||
builder: (context, BoxConstraints box) {
|
builder: (context, BoxConstraints box) {
|
||||||
double maxWidth = box.maxWidth.truncateToDouble();
|
double maxWidth = box.maxWidth.truncateToDouble();
|
||||||
return GestureDetector(
|
return Hero(
|
||||||
onTap: () {
|
tag: picList[i],
|
||||||
showDialog(
|
child: GestureDetector(
|
||||||
useSafeArea: false,
|
onTap: () => onPreviewImg(picList, i, context),
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return ImagePreview(initialPage: i, imgList: picList);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: pics[i].url,
|
src: pics[i].url,
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
@ -123,6 +118,7 @@ class _ContentState extends State<Content> {
|
|||||||
origAspectRatio:
|
origAspectRatio:
|
||||||
pics[i].width!.toInt() / pics[i].height!.toInt(),
|
pics[i].width!.toInt() / pics[i].height!.toInt(),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -163,6 +159,18 @@ class _ContentState extends State<Content> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onPreviewImg(picList, initIndex, context) {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
HeroDialogRoute<void>(
|
||||||
|
builder: (BuildContext context) => InteractiveviewerGallery(
|
||||||
|
sources: picList,
|
||||||
|
initIndex: initIndex,
|
||||||
|
onPageChanged: (int pageIndex) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
TextStyle authorStyle =
|
TextStyle authorStyle =
|
||||||
|
|||||||
@ -3,7 +3,19 @@ import 'package:get/get.dart';
|
|||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import 'package:pilipala/common/widgets/badge.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/pages/preview/index.dart';
|
import 'package:pilipala/plugin/pl_gallery/index.dart';
|
||||||
|
|
||||||
|
void onPreviewImg(currentUrl, picList, initIndex, context) {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
HeroDialogRoute<void>(
|
||||||
|
builder: (BuildContext context) => InteractiveviewerGallery(
|
||||||
|
sources: picList,
|
||||||
|
initIndex: initIndex,
|
||||||
|
onPageChanged: (int pageIndex) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget picWidget(item, context) {
|
Widget picWidget(item, context) {
|
||||||
String type = item.modules.moduleDynamic.major.type;
|
String type = item.modules.moduleDynamic.major.type;
|
||||||
@ -21,25 +33,25 @@ Widget picWidget(item, context) {
|
|||||||
List<Widget> list = [];
|
List<Widget> list = [];
|
||||||
for (var i = 0; i < len; i++) {
|
for (var i = 0; i < len; i++) {
|
||||||
picList.add(pictures[i].src ?? pictures[i].url);
|
picList.add(pictures[i].src ?? pictures[i].url);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
list.add(
|
list.add(
|
||||||
LayoutBuilder(
|
LayoutBuilder(
|
||||||
builder: (context, BoxConstraints box) {
|
builder: (context, BoxConstraints box) {
|
||||||
return GestureDetector(
|
return Hero(
|
||||||
onTap: () {
|
tag: picList[i],
|
||||||
showDialog(
|
placeholderBuilder:
|
||||||
useSafeArea: false,
|
(BuildContext context, Size heroSize, Widget child) {
|
||||||
context: context,
|
return child;
|
||||||
builder: (context) {
|
|
||||||
return ImagePreview(initialPage: i, imgList: picList);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => onPreviewImg(picList[i], picList, i, context),
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: pictures[i].src ?? pictures[i].url,
|
src: pictures[i].src ?? pictures[i].url,
|
||||||
width: box.maxWidth,
|
width: box.maxWidth,
|
||||||
height: box.maxWidth,
|
height: box.maxWidth,
|
||||||
),
|
),
|
||||||
// ),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -4,13 +4,18 @@ import 'package:get/get.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/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.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);
|
final Function? onClickUpCb;
|
||||||
|
|
||||||
|
const UpPanel({
|
||||||
|
super.key,
|
||||||
|
required this.upData,
|
||||||
|
this.onClickUpCb,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<UpPanel> createState() => _UpPanelState();
|
State<UpPanel> createState() => _UpPanelState();
|
||||||
@ -33,27 +38,25 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
|
|
||||||
void onClickUp(data, i) {
|
void onClickUp(data, i) {
|
||||||
currentMid = data.mid;
|
currentMid = data.mid;
|
||||||
Get.find<DynamicsController>().mid.value = data.mid;
|
widget.onClickUpCb?.call(data);
|
||||||
Get.find<DynamicsController>().upInfo.value = data;
|
// int liveLen = liveList.length;
|
||||||
Get.find<DynamicsController>().onSelectUp(data.mid);
|
// int upLen = upList.length;
|
||||||
int liveLen = liveList.length;
|
// double itemWidth = contentWidth + itemPadding.horizontal;
|
||||||
int upLen = upList.length;
|
// double screenWidth = MediaQuery.sizeOf(context).width;
|
||||||
double itemWidth = contentWidth + itemPadding.horizontal;
|
// double moveDistance = 0.0;
|
||||||
double screenWidth = MediaQuery.sizeOf(context).width;
|
// if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
||||||
double moveDistance = 0.0;
|
// } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
||||||
if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
// moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
|
||||||
} else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
// } else {
|
||||||
moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
|
// moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
|
||||||
} else {
|
// }
|
||||||
moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
|
// data.hasUpdate = false;
|
||||||
}
|
// scrollController.animateTo(
|
||||||
data.hasUpdate = false;
|
// moveDistance,
|
||||||
scrollController.animateTo(
|
// duration: const Duration(milliseconds: 200),
|
||||||
moveDistance,
|
// curve: Curves.linear,
|
||||||
duration: const Duration(milliseconds: 200),
|
// );
|
||||||
curve: Curves.linear,
|
// setState(() {});
|
||||||
);
|
|
||||||
setState(() {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -86,7 +86,7 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
|
|||||||
height: width / StyleString.aspectRatio,
|
height: width / StyleString.aspectRatio,
|
||||||
src: content.cover,
|
src: content.cover,
|
||||||
),
|
),
|
||||||
if (content.badge != null && type == 'pgc')
|
if (content.badge != null && content.badge['text'] != null)
|
||||||
PBadge(
|
PBadge(
|
||||||
text: content.badge['text'],
|
text: content.badge['text'],
|
||||||
top: 8.0,
|
top: 8.0,
|
||||||
|
|||||||
@ -49,7 +49,6 @@ class FansController extends GetxController {
|
|||||||
} else if (type == 'onLoad') {
|
} else if (type == 'onLoad') {
|
||||||
fansList.addAll(res['data'].list);
|
fansList.addAll(res['data'].list);
|
||||||
}
|
}
|
||||||
print(total);
|
|
||||||
if ((pn == 1 && total < ps) || res['data'].list.isEmpty) {
|
if ((pn == 1 && total < ps) || res['data'].list.isEmpty) {
|
||||||
loadingText.value = '没有更多了';
|
loadingText.value = '没有更多了';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,9 +103,17 @@ class _FansPageState extends State<FansPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return CustomScrollView(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
slivers: [
|
||||||
|
HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
fn: () => _fansController.queryFans('init'),
|
fn: () {
|
||||||
|
_futureBuilderFuture =
|
||||||
|
_fansController.queryFans('init');
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -16,10 +16,16 @@ class FavController extends GetxController {
|
|||||||
int currentPage = 1;
|
int currentPage = 1;
|
||||||
int pageSize = 60;
|
int pageSize = 60;
|
||||||
RxBool hasMore = true.obs;
|
RxBool hasMore = true.obs;
|
||||||
|
late int mid;
|
||||||
|
late int ownerMid;
|
||||||
|
RxBool isOwner = false.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
|
mid = int.parse(Get.parameters['mid'] ?? '-1');
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
|
ownerMid = userInfo != null ? userInfo!.mid! : -1;
|
||||||
|
isOwner.value = mid == -1 || mid == ownerMid;
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +39,7 @@ class FavController extends GetxController {
|
|||||||
var res = await UserHttp.userfavFolder(
|
var res = await UserHttp.userfavFolder(
|
||||||
pn: currentPage,
|
pn: currentPage,
|
||||||
ps: pageSize,
|
ps: pageSize,
|
||||||
mid: userInfo!.mid!,
|
mid: isOwner.value ? ownerMid : mid,
|
||||||
);
|
);
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
if (type == 'init') {
|
if (type == 'init') {
|
||||||
|
|||||||
@ -42,20 +42,58 @@ class _FavPageState extends State<FavPage> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: Text(
|
title: Obx(() => Text(
|
||||||
'我的收藏',
|
'${_favController.isOwner.value ? '我' : 'Ta'}的收藏',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
)),
|
||||||
actions: [
|
actions: [
|
||||||
|
Obx(() => !_favController.isOwner.value
|
||||||
|
? IconButton(
|
||||||
|
onPressed: () =>
|
||||||
|
Get.toNamed('/subscription?mid=${_favController.mid}'),
|
||||||
|
icon: const Icon(Icons.subscriptions_outlined, size: 21),
|
||||||
|
tooltip: 'Ta的订阅',
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink()),
|
||||||
|
|
||||||
|
// 新建收藏夹
|
||||||
|
Obx(() => _favController.isOwner.value
|
||||||
|
? IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await Get.toNamed('/favEdit');
|
||||||
|
_favController.hasMore.value = true;
|
||||||
|
_favController.currentPage = 1;
|
||||||
|
setState(() {
|
||||||
|
_futureBuilderFuture = _favController.queryFavFolder();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add_outlined),
|
||||||
|
tooltip: '新建收藏夹',
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink()),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Get.toNamed(
|
onPressed: () => Get.toNamed(
|
||||||
'/favSearch?searchType=1&mediaId=${_favController.favFolderData.value.list!.first.id}'),
|
'/favSearch?searchType=1&mediaId=${_favController.favFolderData.value.list!.first.id}'),
|
||||||
icon: const Icon(Icons.search_outlined),
|
icon: const Icon(Icons.search_outlined),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 14),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: FutureBuilder(
|
body: RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
_favController.hasMore.value = true;
|
||||||
|
_favController.currentPage = 1;
|
||||||
|
setState(() {
|
||||||
|
_futureBuilderFuture = _favController.queryFavFolder(type: 'init');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: _buildBody(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBody() {
|
||||||
|
return FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
@ -67,7 +105,9 @@ class _FavPageState extends State<FavPage> {
|
|||||||
itemCount: _favController.favFolderList.length,
|
itemCount: _favController.favFolderList.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return FavItem(
|
return FavItem(
|
||||||
favFolderItem: _favController.favFolderList[index]);
|
favFolderItem: _favController.favFolderList[index],
|
||||||
|
isOwner: _favController.isOwner.value,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -83,8 +123,7 @@ class _FavPageState extends State<FavPage> {
|
|||||||
RoutePush.loginRedirectPush();
|
RoutePush.loginRedirectPush();
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
_futureBuilderFuture =
|
_futureBuilderFuture = _favController.queryFavFolder();
|
||||||
_favController.queryFavFolder();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -102,7 +141,6 @@ class _FavPageState extends State<FavPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,9 @@ import 'package:pilipala/utils/utils.dart';
|
|||||||
class FavItem extends StatelessWidget {
|
class FavItem extends StatelessWidget {
|
||||||
// ignore: prefer_typing_uninitialized_variables
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
final favFolderItem;
|
final favFolderItem;
|
||||||
const FavItem({super.key, required this.favFolderItem});
|
final bool isOwner;
|
||||||
|
const FavItem(
|
||||||
|
{super.key, required this.favFolderItem, required this.isOwner});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -20,6 +22,7 @@ class FavItem extends StatelessWidget {
|
|||||||
parameters: {
|
parameters: {
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
'mediaId': favFolderItem.id.toString(),
|
'mediaId': favFolderItem.id.toString(),
|
||||||
|
'isOwner': isOwner ? '1' : '0',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -71,7 +74,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
|
padding: const EdgeInsets.fromLTRB(10, 2, 6, 10),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -91,6 +94,15 @@ class VideoContent extends StatelessWidget {
|
|||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
[23, 1].contains(favFolderItem.attr) ? '私密' : '公开',
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -6,26 +6,30 @@ import 'package:pilipala/http/video.dart';
|
|||||||
import 'package:pilipala/models/user/fav_detail.dart';
|
import 'package:pilipala/models/user/fav_detail.dart';
|
||||||
import 'package:pilipala/models/user/fav_folder.dart';
|
import 'package:pilipala/models/user/fav_folder.dart';
|
||||||
import 'package:pilipala/pages/fav/index.dart';
|
import 'package:pilipala/pages/fav/index.dart';
|
||||||
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class FavDetailController extends GetxController {
|
class FavDetailController extends GetxController {
|
||||||
FavFolderItemData? item;
|
FavFolderItemData? item;
|
||||||
Rx<FavDetailData> favDetailData = FavDetailData().obs;
|
RxString title = ''.obs;
|
||||||
|
|
||||||
int? mediaId;
|
int? mediaId;
|
||||||
late String heroTag;
|
late String heroTag;
|
||||||
int currentPage = 1;
|
int currentPage = 1;
|
||||||
bool isLoadingMore = false;
|
bool isLoadingMore = false;
|
||||||
RxMap favInfo = {}.obs;
|
RxMap favInfo = {}.obs;
|
||||||
RxList favList = [].obs;
|
RxList<FavDetailItemData> favList = <FavDetailItemData>[].obs;
|
||||||
RxString loadingText = '加载中...'.obs;
|
RxString loadingText = '加载中...'.obs;
|
||||||
RxInt mediaCount = 0.obs;
|
RxInt mediaCount = 0.obs;
|
||||||
|
late String isOwner;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
item = Get.arguments;
|
item = Get.arguments;
|
||||||
|
title.value = item!.title!;
|
||||||
if (Get.parameters.keys.isNotEmpty) {
|
if (Get.parameters.keys.isNotEmpty) {
|
||||||
mediaId = int.parse(Get.parameters['mediaId']!);
|
mediaId = int.parse(Get.parameters['mediaId']!);
|
||||||
heroTag = Get.parameters['heroTag']!;
|
heroTag = Get.parameters['heroTag']!;
|
||||||
|
isOwner = Get.parameters['isOwner']!;
|
||||||
}
|
}
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
@ -113,4 +117,37 @@ class FavDetailController extends GetxController {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEditFavFolder() async {
|
||||||
|
var res = await Get.toNamed(
|
||||||
|
'/favEdit',
|
||||||
|
arguments: {
|
||||||
|
'mediaId': mediaId.toString(),
|
||||||
|
'title': item!.title,
|
||||||
|
'intro': item!.intro,
|
||||||
|
'cover': item!.cover,
|
||||||
|
'privacy': [23, 1].contains(item!.attr) ? 1 : 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
title.value = res['title'];
|
||||||
|
print(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future toViewPlayAll() async {
|
||||||
|
final FavDetailItemData firstItem = favList.first;
|
||||||
|
final String heroTag = Utils.makeHeroTag(firstItem.bvid);
|
||||||
|
Get.toNamed(
|
||||||
|
'/video?bvid=${firstItem.bvid}&cid=${firstItem.cid}',
|
||||||
|
arguments: {
|
||||||
|
'videoItem': firstItem,
|
||||||
|
'heroTag': heroTag,
|
||||||
|
'sourceType': 'fav',
|
||||||
|
'mediaId': favInfo['id'],
|
||||||
|
'oid': firstItem.id,
|
||||||
|
'favTitle': favInfo['title'],
|
||||||
|
'favInfo': favInfo,
|
||||||
|
'count': favInfo['media_count'],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,8 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
late final ScrollController _controller = ScrollController();
|
late final ScrollController _controller = ScrollController();
|
||||||
final FavDetailController _favDetailController =
|
final FavDetailController _favDetailController =
|
||||||
Get.put(FavDetailController());
|
Get.put(FavDetailController());
|
||||||
late StreamController<bool> titleStreamC; // a
|
late StreamController<bool> titleStreamC =
|
||||||
|
StreamController<bool>.broadcast(); // a
|
||||||
Future? _futureBuilderFuture;
|
Future? _futureBuilderFuture;
|
||||||
late String mediaId;
|
late String mediaId;
|
||||||
|
|
||||||
@ -31,7 +32,6 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
mediaId = Get.parameters['mediaId']!;
|
mediaId = Get.parameters['mediaId']!;
|
||||||
_futureBuilderFuture = _favDetailController.queryUserFavFolderDetail();
|
_futureBuilderFuture = _favDetailController.queryUserFavFolderDetail();
|
||||||
titleStreamC = StreamController<bool>();
|
|
||||||
_controller.addListener(
|
_controller.addListener(
|
||||||
() {
|
() {
|
||||||
if (_controller.offset > 160) {
|
if (_controller.offset > 160) {
|
||||||
@ -80,10 +80,12 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Obx(
|
||||||
_favDetailController.item!.title!,
|
() => Text(
|
||||||
|
_favDetailController.title.value,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
'共${_favDetailController.mediaCount}条视频',
|
'共${_favDetailController.mediaCount}条视频',
|
||||||
style: Theme.of(context).textTheme.labelMedium,
|
style: Theme.of(context).textTheme.labelMedium,
|
||||||
@ -106,6 +108,11 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
position: PopupMenuPosition.under,
|
position: PopupMenuPosition.under,
|
||||||
onSelected: (String type) {},
|
onSelected: (String type) {},
|
||||||
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
|
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
onTap: () => _favDetailController.onEditFavFolder(),
|
||||||
|
value: 'edit',
|
||||||
|
child: const Text('编辑收藏夹'),
|
||||||
|
),
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
onTap: () => _favDetailController.onDelFavFolder(),
|
onTap: () => _favDetailController.onDelFavFolder(),
|
||||||
value: 'pause',
|
value: 'pause',
|
||||||
@ -151,8 +158,9 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Obx(
|
||||||
_favDetailController.item!.title!,
|
() => Text(
|
||||||
|
_favDetailController.title.value,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context)
|
fontSize: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
@ -160,6 +168,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
.fontSize,
|
.fontSize,
|
||||||
fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
_favDetailController.item!.upper!.name!,
|
_favDetailController.item!.upper!.name!,
|
||||||
@ -212,6 +221,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
SliverChildBuilderDelegate((context, index) {
|
SliverChildBuilderDelegate((context, index) {
|
||||||
return FavVideoCardH(
|
return FavVideoCardH(
|
||||||
videoItem: favList[index],
|
videoItem: favList[index],
|
||||||
|
isOwner: _favDetailController.isOwner,
|
||||||
callFn: () => _favDetailController
|
callFn: () => _favDetailController
|
||||||
.onCancelFav(favList[index].id),
|
.onCancelFav(favList[index].id),
|
||||||
);
|
);
|
||||||
@ -254,6 +264,15 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
floatingActionButton: Obx(
|
||||||
|
() => _favDetailController.mediaCount > 0
|
||||||
|
? FloatingActionButton.extended(
|
||||||
|
onPressed: _favDetailController.toViewPlayAll,
|
||||||
|
label: const Text('播放全部'),
|
||||||
|
icon: const Icon(Icons.playlist_play),
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,12 +18,14 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
final dynamic videoItem;
|
final dynamic videoItem;
|
||||||
final Function? callFn;
|
final Function? callFn;
|
||||||
final int? searchType;
|
final int? searchType;
|
||||||
|
final String isOwner;
|
||||||
|
|
||||||
const FavVideoCardH({
|
const FavVideoCardH({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.videoItem,
|
required this.videoItem,
|
||||||
this.callFn,
|
this.callFn,
|
||||||
this.searchType,
|
this.searchType,
|
||||||
|
required this.isOwner,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -123,6 +125,7 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
videoItem: videoItem,
|
videoItem: videoItem,
|
||||||
callFn: callFn,
|
callFn: callFn,
|
||||||
searchType: searchType,
|
searchType: searchType,
|
||||||
|
isOwner: isOwner,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -140,11 +143,13 @@ class VideoContent extends StatelessWidget {
|
|||||||
final dynamic videoItem;
|
final dynamic videoItem;
|
||||||
final Function? callFn;
|
final Function? callFn;
|
||||||
final int? searchType;
|
final int? searchType;
|
||||||
|
final String isOwner;
|
||||||
const VideoContent({
|
const VideoContent({
|
||||||
super.key,
|
super.key,
|
||||||
required this.videoItem,
|
required this.videoItem,
|
||||||
this.callFn,
|
this.callFn,
|
||||||
this.searchType,
|
this.searchType,
|
||||||
|
required this.isOwner,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -198,20 +203,16 @@ class VideoContent extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(top: 2),
|
padding: const EdgeInsets.only(top: 2),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(
|
StatView(view: videoItem.cntInfo['play']),
|
||||||
theme: 'gray',
|
|
||||||
view: videoItem.cntInfo['play'],
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
StatDanMu(
|
StatDanMu(danmu: videoItem.cntInfo['danmaku']),
|
||||||
theme: 'gray', danmu: videoItem.cntInfo['danmaku']),
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
searchType != 1
|
searchType != 1 && isOwner == '1'
|
||||||
? Positioned(
|
? Positioned(
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: -4,
|
bottom: -4,
|
||||||
|
|||||||
77
lib/pages/fav_edit/controller.dart
Normal file
77
lib/pages/fav_edit/controller.dart
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/http/fav.dart';
|
||||||
|
|
||||||
|
class FavEditController extends GetxController {
|
||||||
|
final GlobalKey formKey = GlobalKey<FormState>();
|
||||||
|
final TextEditingController titleController = TextEditingController();
|
||||||
|
final TextEditingController contentController = TextEditingController();
|
||||||
|
|
||||||
|
final FocusNode titleTextFieldNode = FocusNode();
|
||||||
|
final FocusNode contentTextFieldNode = FocusNode();
|
||||||
|
|
||||||
|
// 默认新建
|
||||||
|
RxString type = 'add'.obs;
|
||||||
|
|
||||||
|
String? mediaId;
|
||||||
|
String cover = ''; // 封面
|
||||||
|
String title = ''; // 名称
|
||||||
|
String intro = ''; // 简介
|
||||||
|
RxInt privacy = 0.obs; // 是否公开 0公开 1私密
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
var args = Get.arguments;
|
||||||
|
if (args != null) {
|
||||||
|
type.value = 'edit';
|
||||||
|
mediaId = args['mediaId'];
|
||||||
|
title = args['title'];
|
||||||
|
intro = args['intro'];
|
||||||
|
cover = args['cover'];
|
||||||
|
privacy.value = args['privacy'];
|
||||||
|
titleController.text = title;
|
||||||
|
contentController.text = intro;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSubmit() async {
|
||||||
|
// 表单验证
|
||||||
|
if ((formKey.currentState as FormState).validate()) {
|
||||||
|
if (type.value == 'edit') {
|
||||||
|
await editFolder();
|
||||||
|
} else {
|
||||||
|
await addFolder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> editFolder() async {
|
||||||
|
var res = await FavHttp.editFolder(
|
||||||
|
title: title,
|
||||||
|
intro: intro,
|
||||||
|
mediaId: mediaId!,
|
||||||
|
cover: cover,
|
||||||
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
SmartDialog.showToast('编辑成功');
|
||||||
|
Get.back(result: {'title': title});
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addFolder() async {
|
||||||
|
var res = await FavHttp.addFolder(
|
||||||
|
title: title,
|
||||||
|
intro: intro,
|
||||||
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
SmartDialog.showToast('新建成功');
|
||||||
|
Get.back();
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
library preview;
|
library fav_edit;
|
||||||
|
|
||||||
export './controller.dart';
|
export './controller.dart';
|
||||||
export './view.dart';
|
export './view.dart';
|
||||||
111
lib/pages/fav_edit/view.dart
Normal file
111
lib/pages/fav_edit/view.dart
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'controller.dart';
|
||||||
|
|
||||||
|
class FavEditPage extends StatefulWidget {
|
||||||
|
const FavEditPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FavEditPage> createState() => _FavEditPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FavEditPageState extends State<FavEditPage> {
|
||||||
|
final FavEditController _favEditController = Get.put(FavEditController());
|
||||||
|
String title = '';
|
||||||
|
String content = '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
|
title: Obx(
|
||||||
|
() => _favEditController.type.value == 'add'
|
||||||
|
? Text(
|
||||||
|
'新建收藏夹',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
'编辑收藏夹',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
centerTitle: false,
|
||||||
|
actions: [
|
||||||
|
Obx(
|
||||||
|
() => _favEditController.privacy.value == 0
|
||||||
|
? IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
_favEditController.privacy.value = 1;
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.lock_open_outlined))
|
||||||
|
: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
_favEditController.privacy.value = 0;
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
Icons.lock_outlined,
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: _favEditController.onSubmit, child: const Text('保存')),
|
||||||
|
const SizedBox(width: 14),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Form(
|
||||||
|
key: _favEditController.formKey, //设置globalKey,用于后面获取FormState
|
||||||
|
autovalidateMode: AutovalidateMode.disabled,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(14, 10, 14, 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: TextFormField(
|
||||||
|
autofocus: true,
|
||||||
|
controller: _favEditController.titleController,
|
||||||
|
focusNode: _favEditController.titleTextFieldNode,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "收藏夹名称",
|
||||||
|
enabledBorder: InputBorder.none,
|
||||||
|
focusedBorder: InputBorder.none,
|
||||||
|
),
|
||||||
|
// 校验标题
|
||||||
|
validator: (v) {
|
||||||
|
return v!.trim().isNotEmpty ? null : "请输入收藏夹名称";
|
||||||
|
},
|
||||||
|
onChanged: (val) {
|
||||||
|
_favEditController.title = val;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 14, vertical: 5),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _favEditController.contentController,
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: 5,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: '输入收藏夹简介', border: InputBorder.none),
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
onChanged: (val) {
|
||||||
|
_favEditController.intro = val;
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -100,6 +100,7 @@ class _FavSearchPageState extends State<FavSearchPage> {
|
|||||||
return FavVideoCardH(
|
return FavVideoCardH(
|
||||||
videoItem: _favSearchCtr.favList[index],
|
videoItem: _favSearchCtr.favList[index],
|
||||||
searchType: searchType,
|
searchType: searchType,
|
||||||
|
isOwner: '0',
|
||||||
callFn: () => searchType != 1
|
callFn: () => searchType != 1
|
||||||
? _favSearchCtr
|
? _favSearchCtr
|
||||||
.onCancelFav(_favSearchCtr.favList[index].id!)
|
.onCancelFav(_favSearchCtr.favList[index].id!)
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
@ -47,9 +48,24 @@ class FollowItem extends StatelessWidget {
|
|||||||
height: 34,
|
height: 34,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await Get.bottomSheet(
|
await showFlexibleBottomSheet(
|
||||||
GroupPanel(mid: item.mid!),
|
bottomSheetBorderRadius: const BorderRadius.only(
|
||||||
isScrollControlled: true,
|
topLeft: Radius.circular(16),
|
||||||
|
topRight: Radius.circular(16),
|
||||||
|
),
|
||||||
|
minHeight: 1,
|
||||||
|
initHeight: 1,
|
||||||
|
maxHeight: 1,
|
||||||
|
context: Get.context!,
|
||||||
|
builder: (BuildContext context,
|
||||||
|
ScrollController scrollController, double offset) {
|
||||||
|
return GroupPanel(
|
||||||
|
mid: item.mid!,
|
||||||
|
scrollController: scrollController,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
anchors: [1],
|
||||||
|
isSafeArea: true,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
|
|||||||
@ -48,7 +48,7 @@ class FollowSearchController extends GetxController {
|
|||||||
return {'status': true, 'data': <FollowItemModel>[].obs};
|
return {'status': true, 'data': <FollowItemModel>[].obs};
|
||||||
}
|
}
|
||||||
if (type == 'init') {
|
if (type == 'init') {
|
||||||
ps = 1;
|
pn = 1;
|
||||||
}
|
}
|
||||||
var res = await MemberHttp.getfollowSearch(
|
var res = await MemberHttp.getfollowSearch(
|
||||||
mid: mid,
|
mid: mid,
|
||||||
|
|||||||
@ -43,14 +43,17 @@ class HistoryItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
if (videoItem.history.business.contains('article')) {
|
if (videoItem.history.business.contains('article')) {
|
||||||
int cid = videoItem.history.cid ??
|
int cid = videoItem.history.cid ??
|
||||||
// videoItem.history.oid ??
|
videoItem.history.oid ??
|
||||||
await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
await SearchHttp.ab2c(aid: aid, bvid: bvid);
|
||||||
|
if (cid == -1) {
|
||||||
|
return SmartDialog.showToast('无法获取文章内容');
|
||||||
|
}
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/webview',
|
'/read',
|
||||||
parameters: {
|
parameters: {
|
||||||
'url': 'https://www.bilibili.com/read/cv$cid',
|
'title': videoItem.title,
|
||||||
'type': 'note',
|
'id': cid.toString(),
|
||||||
'pageTitle': videoItem.title
|
'articleType': 'read',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (videoItem.history.business == 'live') {
|
} else if (videoItem.history.business == 'live') {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
import '../../http/index.dart';
|
import '../../http/index.dart';
|
||||||
|
|
||||||
class HomeController extends GetxController with GetTickerProviderStateMixin {
|
class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||||
bool flag = false;
|
bool flag = true;
|
||||||
late RxList tabs = [].obs;
|
late RxList tabs = [].obs;
|
||||||
RxInt initialIndex = 1.obs;
|
RxInt initialIndex = 1.obs;
|
||||||
late TabController tabController;
|
late TabController tabController;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -46,54 +47,26 @@ class _HomePageState extends State<HomePage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
Brightness currentBrightness = MediaQuery.of(context).platformBrightness;
|
|
||||||
// 设置状态栏图标的亮度
|
|
||||||
if (_homeController.enableGradientBg) {
|
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
|
||||||
statusBarIconBrightness: currentBrightness == Brightness.light
|
|
||||||
? Brightness.dark
|
|
||||||
: Brightness.light,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
appBar: _homeController.enableGradientBg
|
backgroundColor: Colors.transparent,
|
||||||
? null
|
appBar: AppBar(
|
||||||
: AppBar(toolbarHeight: 0, elevation: 0),
|
toolbarHeight: 0,
|
||||||
body: Stack(
|
elevation: 0,
|
||||||
children: [
|
backgroundColor: Colors.transparent,
|
||||||
// gradient background
|
systemOverlayStyle: Platform.isAndroid
|
||||||
if (_homeController.enableGradientBg) ...[
|
? SystemUiOverlayStyle(
|
||||||
Align(
|
statusBarIconBrightness:
|
||||||
alignment: Alignment.topLeft,
|
Theme.of(context).brightness == Brightness.dark
|
||||||
child: Opacity(
|
? Brightness.light
|
||||||
opacity: 0.6,
|
: Brightness.dark,
|
||||||
child: Container(
|
)
|
||||||
width: MediaQuery.of(context).size.width,
|
: Theme.of(context).brightness == Brightness.dark
|
||||||
height: MediaQuery.of(context).size.height,
|
? SystemUiOverlayStyle.light
|
||||||
decoration: BoxDecoration(
|
: SystemUiOverlayStyle.dark,
|
||||||
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]),
|
|
||||||
),
|
),
|
||||||
),
|
body: Column(
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
Column(
|
|
||||||
children: [
|
children: [
|
||||||
CustomAppBar(
|
CustomAppBar(
|
||||||
stream: _homeController.hideSearchBar
|
stream: _homeController.hideSearchBar
|
||||||
@ -106,17 +79,16 @@ class _HomePageState extends State<HomePage>
|
|||||||
if (_homeController.enableGradientBg) ...[
|
if (_homeController.enableGradientBg) ...[
|
||||||
const CustomTabs(),
|
const CustomTabs(),
|
||||||
] else ...[
|
] else ...[
|
||||||
const SizedBox(height: 4),
|
Container(
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 42,
|
height: 42,
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
controller: _homeController.tabController,
|
controller: _homeController.tabController,
|
||||||
tabs: [
|
tabs: [
|
||||||
for (var i in _homeController.tabs)
|
for (var i in _homeController.tabs) Tab(text: i['label'])
|
||||||
Tab(text: i['label'])
|
|
||||||
],
|
],
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
dividerColor: Colors.transparent,
|
dividerColor: Colors.transparent,
|
||||||
@ -145,8 +117,6 @@ class _HomePageState extends State<HomePage>
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,7 +250,10 @@ class DefaultUser extends StatelessWidget {
|
|||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
backgroundColor: MaterialStateProperty.resolveWith((states) {
|
backgroundColor: MaterialStateProperty.resolveWith((states) {
|
||||||
return Theme.of(context).colorScheme.onInverseSurface;
|
return Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondaryContainer
|
||||||
|
.withOpacity(0.05);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
onPressed: () => callback?.call(),
|
onPressed: () => callback?.call(),
|
||||||
@ -317,7 +290,7 @@ class _CustomTabsState extends State<CustomTabs> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 44,
|
height: 44,
|
||||||
margin: const EdgeInsets.only(top: 4),
|
margin: const EdgeInsets.only(top: 8),
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => ListView.separated(
|
() => ListView.separated(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14.0),
|
padding: const EdgeInsets.symmetric(horizontal: 14.0),
|
||||||
@ -357,25 +330,29 @@ class CustomChip extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ColorScheme colorTheme = Theme.of(context).colorScheme;
|
final ColorScheme colorTheme = Theme.of(context).colorScheme;
|
||||||
final Color secondaryContainer = colorTheme.secondaryContainer;
|
final Color secondaryContainer = colorTheme.secondaryContainer;
|
||||||
|
final Color onPrimary = colorTheme.onPrimary;
|
||||||
|
final Color primary = colorTheme.primary;
|
||||||
final TextStyle chipTextStyle = selected
|
final TextStyle chipTextStyle = selected
|
||||||
? const TextStyle(fontWeight: FontWeight.bold, fontSize: 13)
|
? TextStyle(fontSize: 13, color: onPrimary)
|
||||||
: const TextStyle(fontSize: 13);
|
: TextStyle(fontSize: 13, color: colorTheme.onSecondaryContainer);
|
||||||
final ColorScheme colorScheme = Theme.of(context).colorScheme;
|
|
||||||
const VisualDensity visualDensity =
|
const VisualDensity visualDensity =
|
||||||
VisualDensity(horizontal: -4.0, vertical: -2.0);
|
VisualDensity(horizontal: -4.0, vertical: -2.0);
|
||||||
return InputChip(
|
return InputChip(
|
||||||
side: BorderSide(
|
side: BorderSide.none,
|
||||||
color: selected
|
|
||||||
? colorScheme.onSecondaryContainer.withOpacity(0.2)
|
|
||||||
: Colors.transparent,
|
|
||||||
),
|
|
||||||
backgroundColor: secondaryContainer,
|
backgroundColor: secondaryContainer,
|
||||||
selectedColor: secondaryContainer,
|
color: MaterialStateProperty.resolveWith((states) {
|
||||||
color: MaterialStateProperty.resolveWith<Color>(
|
if (states.contains(MaterialState.selected) ||
|
||||||
(Set<MaterialState> states) => secondaryContainer.withAlpha(200)),
|
states.contains(MaterialState.hovered)) {
|
||||||
padding: const EdgeInsets.fromLTRB(7, 1, 7, 1),
|
return primary;
|
||||||
|
}
|
||||||
|
return colorTheme.secondaryContainer;
|
||||||
|
}),
|
||||||
|
padding: const EdgeInsets.fromLTRB(6, 1, 6, 1),
|
||||||
label: Text(label, style: chipTextStyle),
|
label: Text(label, style: chipTextStyle),
|
||||||
onPressed: () => onTap(),
|
onPressed: () => onTap(),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
selected: selected,
|
selected: selected,
|
||||||
showCheckmark: false,
|
showCheckmark: false,
|
||||||
visualDensity: visualDensity,
|
visualDensity: visualDensity,
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import 'package:pilipala/http/user.dart';
|
|||||||
import 'package:pilipala/models/model_hot_video_item.dart';
|
import 'package:pilipala/models/model_hot_video_item.dart';
|
||||||
import 'package:pilipala/models/user/info.dart';
|
import 'package:pilipala/models/user/info.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class LaterController extends GetxController {
|
class LaterController extends GetxController {
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
@ -48,7 +49,7 @@ class LaterController extends GetxController {
|
|||||||
aid != null ? '即将移除该视频,确定是否移除' : '即将删除所有已观看视频,此操作不可恢复。确定是否删除?'),
|
aid != null ? '即将移除该视频,确定是否移除' : '即将删除所有已观看视频,此操作不可恢复。确定是否删除?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: SmartDialog.dismiss,
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
@ -87,7 +88,7 @@ class LaterController extends GetxController {
|
|||||||
content: const Text('确定要清空你的稍后再看列表吗?'),
|
content: const Text('确定要清空你的稍后再看列表吗?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => SmartDialog.dismiss(),
|
onPressed: SmartDialog.dismiss,
|
||||||
child: Text(
|
child: Text(
|
||||||
'取消',
|
'取消',
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||||
@ -109,4 +110,19 @@ class LaterController extends GetxController {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 稍后再看播放全部
|
||||||
|
Future toViewPlayAll() async {
|
||||||
|
final HotVideoItemModel firstItem = laterList.first;
|
||||||
|
final String heroTag = Utils.makeHeroTag(firstItem.bvid);
|
||||||
|
Get.toNamed(
|
||||||
|
'/video?bvid=${firstItem.bvid}&cid=${firstItem.cid}',
|
||||||
|
arguments: {
|
||||||
|
'videoItem': firstItem,
|
||||||
|
'heroTag': heroTag,
|
||||||
|
'sourceType': 'watchLater',
|
||||||
|
'count': laterList.length,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,6 +128,15 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
floatingActionButton: Obx(
|
||||||
|
() => _laterController.laterList.isNotEmpty
|
||||||
|
? FloatingActionButton.extended(
|
||||||
|
onPressed: _laterController.toViewPlayAll,
|
||||||
|
label: const Text('播放全部'),
|
||||||
|
icon: const Icon(Icons.playlist_play),
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/live.dart';
|
import 'package:pilipala/http/live.dart';
|
||||||
|
import 'package:pilipala/models/live/follow.dart';
|
||||||
import 'package:pilipala/models/live/item.dart';
|
import 'package:pilipala/models/live/item.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
@ -11,6 +12,8 @@ class LiveController extends GetxController {
|
|||||||
int _currentPage = 1;
|
int _currentPage = 1;
|
||||||
RxInt crossAxisCount = 2.obs;
|
RxInt crossAxisCount = 2.obs;
|
||||||
RxList<LiveItemModel> liveList = <LiveItemModel>[].obs;
|
RxList<LiveItemModel> liveList = <LiveItemModel>[].obs;
|
||||||
|
RxList<LiveFollowingItemModel> liveFollowingList =
|
||||||
|
<LiveFollowingItemModel>[].obs;
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
OverlayEntry? popupDialog;
|
OverlayEntry? popupDialog;
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
@ -44,6 +47,7 @@ class LiveController extends GetxController {
|
|||||||
// 下拉刷新
|
// 下拉刷新
|
||||||
Future onRefresh() async {
|
Future onRefresh() async {
|
||||||
queryLiveList('init');
|
queryLiveList('init');
|
||||||
|
fetchLiveFollowing();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上拉加载
|
// 上拉加载
|
||||||
@ -61,4 +65,17 @@ class LiveController extends GetxController {
|
|||||||
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
|
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
Future fetchLiveFollowing() async {
|
||||||
|
var res = await LiveHttp.liveFollowing(pn: 1, ps: 20);
|
||||||
|
if (res['status']) {
|
||||||
|
liveFollowingList.value =
|
||||||
|
(res['data'].list as List<LiveFollowingItemModel>)
|
||||||
|
.where((LiveFollowingItemModel item) =>
|
||||||
|
item.liveStatus == 1 && item.recordLiveTime == 0) // 根据条件过滤
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user