mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-31 08:08:19 +08:00
feat: video download
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
469
lib/models_new/download/bili_download_entry_info.dart
Normal file
469
lib/models_new/download/bili_download_entry_info.dart
Normal file
@@ -0,0 +1,469 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/models/common/video/video_type.dart';
|
||||
import 'package:PiliPlus/services/download/download_service.dart';
|
||||
import 'package:PiliPlus/utils/cache_manager.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
|
||||
import 'package:get/route_manager.dart';
|
||||
|
||||
class BiliDownloadEntryInfo {
|
||||
int mediaType;
|
||||
final bool hasDashAudio;
|
||||
bool isCompleted;
|
||||
int totalBytes;
|
||||
int downloadedBytes;
|
||||
final String title;
|
||||
String? typeTag;
|
||||
final String cover;
|
||||
int? videoQuality;
|
||||
int preferedVideoQuality;
|
||||
String qualityPithyDescription;
|
||||
final int guessedTotalBytes;
|
||||
int totalTimeMilli;
|
||||
final int danmakuCount;
|
||||
final int timeUpdateStamp;
|
||||
final int timeCreateStamp;
|
||||
final bool canPlayInAdvance;
|
||||
bool interruptTransformTempFile;
|
||||
final int avid;
|
||||
final int? spid;
|
||||
final String bvid;
|
||||
final int? ownerId;
|
||||
final String? ownerName;
|
||||
PageInfo? pageData;
|
||||
final String? seasonId;
|
||||
final SourceInfo? source;
|
||||
EpInfo? ep;
|
||||
|
||||
late String pageDirPath;
|
||||
late String entryDirPath;
|
||||
DownloadStatus? status;
|
||||
|
||||
int get cid => source?.cid ?? pageData!.cid;
|
||||
|
||||
String get pageId => seasonId ?? avid.toString();
|
||||
|
||||
int get sortKey => ep?.sortIndex ?? pageData!.cid;
|
||||
|
||||
String get showTitle {
|
||||
if (pageData case final pageData?) {
|
||||
return pageData.part?.isNotEmpty == true ? pageData.part! : title;
|
||||
}
|
||||
if (ep case final ep?) {
|
||||
return ep.showTitle ?? '${ep.index} ${ep.indexTitle}';
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
Widget moreBtn(ThemeData theme) => SizedBox(
|
||||
width: 29,
|
||||
height: 29,
|
||||
child: PopupMenuButton(
|
||||
padding: EdgeInsets.zero,
|
||||
position: PopupMenuPosition.under,
|
||||
icon: Icon(
|
||||
Icons.more_vert_outlined,
|
||||
color: theme.colorScheme.outline,
|
||||
size: 18,
|
||||
),
|
||||
itemBuilder: (_) => [
|
||||
PopupMenuItem(
|
||||
height: 35,
|
||||
child: const Text(
|
||||
'查看详情页',
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
onTap: () {
|
||||
if (ep case final ep?) {
|
||||
if (ep.from == VideoType.pugv.name) {
|
||||
PageUtils.viewPugv(
|
||||
seasonId: seasonId,
|
||||
epId: ep.episodeId,
|
||||
);
|
||||
} else {
|
||||
PageUtils.viewPgc(
|
||||
seasonId: seasonId,
|
||||
epId: ep.episodeId,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
PageUtils.toVideoPage(
|
||||
aid: avid,
|
||||
bvid: bvid,
|
||||
cid: cid,
|
||||
epId: ep?.episodeId,
|
||||
title: title,
|
||||
cover: cover,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (ownerId case final mid?)
|
||||
PopupMenuItem(
|
||||
height: 35,
|
||||
child: Text(
|
||||
'访问${ownerName != null ? ':$ownerName' : '用户主页'}',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
onTap: () => Get.toNamed('/member?mid=$mid'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget progressWidget({
|
||||
required ThemeData theme,
|
||||
required DownloadService downloadService,
|
||||
required bool isPage,
|
||||
}) {
|
||||
return Obx(() {
|
||||
final curDownload = downloadService.curDownload.value;
|
||||
final isCurr =
|
||||
curDownload != null &&
|
||||
(isPage ? curDownload.pageId == pageId : curDownload.cid == cid);
|
||||
late final status = curDownload?.status;
|
||||
late final isDownloading = status == DownloadStatus.downloading;
|
||||
late final color = isCurr && status != DownloadStatus.pause
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.outline;
|
||||
return Column(
|
||||
spacing: 6,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
isCurr ? status!.message : '暂停中',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
height: 1,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
isCurr
|
||||
? isDownloading || status == DownloadStatus.pause
|
||||
? ' ${CacheManager.formatSize(curDownload.downloadedBytes)}/${CacheManager.formatSize(curDownload.totalBytes)}'
|
||||
: ''
|
||||
: totalBytes == 0
|
||||
? ''
|
||||
: ' ${CacheManager.formatSize(downloadedBytes)}/${CacheManager.formatSize(totalBytes)}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
height: 1,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
LinearProgressIndicator(
|
||||
// ignore: deprecated_member_use
|
||||
year2023: true,
|
||||
minHeight: 2.5,
|
||||
borderRadius: StyleString.mdRadius,
|
||||
color: color,
|
||||
backgroundColor: theme.highlightColor,
|
||||
value: isCurr
|
||||
? curDownload.totalBytes == 0
|
||||
? 0
|
||||
: curDownload.downloadedBytes / curDownload.totalBytes
|
||||
: totalBytes == 0
|
||||
? 0
|
||||
: downloadedBytes / totalBytes,
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
BiliDownloadEntryInfo({
|
||||
this.mediaType = 1,
|
||||
this.hasDashAudio = false,
|
||||
required this.isCompleted,
|
||||
required this.totalBytes,
|
||||
required this.downloadedBytes,
|
||||
required this.title,
|
||||
this.typeTag,
|
||||
required this.cover,
|
||||
this.videoQuality,
|
||||
required this.preferedVideoQuality,
|
||||
this.qualityPithyDescription = '',
|
||||
required this.guessedTotalBytes,
|
||||
required this.totalTimeMilli,
|
||||
required this.danmakuCount,
|
||||
this.timeUpdateStamp = 0,
|
||||
this.timeCreateStamp = 0,
|
||||
this.canPlayInAdvance = false,
|
||||
this.interruptTransformTempFile = false,
|
||||
required this.avid,
|
||||
this.spid,
|
||||
required this.bvid,
|
||||
this.ownerId,
|
||||
this.ownerName,
|
||||
this.pageData,
|
||||
this.seasonId,
|
||||
this.source,
|
||||
this.ep,
|
||||
});
|
||||
|
||||
factory BiliDownloadEntryInfo.fromJson(Map<String, dynamic> json) =>
|
||||
BiliDownloadEntryInfo(
|
||||
mediaType: json['media_type'] as int,
|
||||
hasDashAudio: json['has_dash_audio'] as bool,
|
||||
isCompleted: json['is_completed'] as bool,
|
||||
totalBytes: json['total_bytes'] as int,
|
||||
downloadedBytes: json['downloaded_bytes'] as int,
|
||||
title: json['title'] as String,
|
||||
typeTag: json['type_tag'] as String?,
|
||||
cover: json['cover'] as String,
|
||||
videoQuality: json['video_quality'] as int?,
|
||||
preferedVideoQuality: json['prefered_video_quality'] as int,
|
||||
qualityPithyDescription: json['quality_pithy_description'] as String,
|
||||
guessedTotalBytes: json['guessed_total_bytes'] as int,
|
||||
totalTimeMilli: json['total_time_milli'] as int,
|
||||
danmakuCount: json['danmaku_count'] as int,
|
||||
timeUpdateStamp: json['time_update_stamp'] as int,
|
||||
timeCreateStamp: json['time_create_stamp'] as int,
|
||||
canPlayInAdvance: json['can_play_in_advance'] as bool,
|
||||
interruptTransformTempFile:
|
||||
json['interrupt_transform_temp_file'] as bool,
|
||||
avid: json['avid'] as int,
|
||||
spid: json['spid'] as int?,
|
||||
bvid: json['bvid'] as String,
|
||||
ownerId: json['owner_id'] as int?,
|
||||
ownerName: json['owner_name'] as String?,
|
||||
pageData: json['page_data'] != null
|
||||
? PageInfo.fromJson(json['page_data'] as Map<String, dynamic>)
|
||||
: null,
|
||||
seasonId: json['season_id'] as String?,
|
||||
source: json['source'] != null
|
||||
? SourceInfo.fromJson(json['source'] as Map<String, dynamic>)
|
||||
: null,
|
||||
ep: json['ep'] != null
|
||||
? EpInfo.fromJson(json['ep'] as Map<String, dynamic>)
|
||||
: null,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'media_type': mediaType,
|
||||
'has_dash_audio': hasDashAudio,
|
||||
'is_completed': isCompleted,
|
||||
'total_bytes': totalBytes,
|
||||
'downloaded_bytes': downloadedBytes,
|
||||
'title': title,
|
||||
'type_tag': ?typeTag,
|
||||
'cover': cover,
|
||||
'video_quality': ?videoQuality,
|
||||
'prefered_video_quality': preferedVideoQuality,
|
||||
'quality_pithy_description': qualityPithyDescription,
|
||||
'guessed_total_bytes': guessedTotalBytes,
|
||||
'total_time_milli': totalTimeMilli,
|
||||
'danmaku_count': danmakuCount,
|
||||
'time_update_stamp': timeUpdateStamp,
|
||||
'time_create_stamp': timeCreateStamp,
|
||||
'can_play_in_advance': canPlayInAdvance,
|
||||
'interrupt_transform_temp_file': interruptTransformTempFile,
|
||||
'avid': avid,
|
||||
'spid': ?spid,
|
||||
'bvid': bvid,
|
||||
'owner_id': ownerId,
|
||||
'owner_name': ownerName,
|
||||
'page_data': ?pageData?.toJson(),
|
||||
'season_id': ?seasonId,
|
||||
'source': ?source?.toJson(),
|
||||
'ep': ?ep?.toJson(),
|
||||
};
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other is BiliDownloadEntryInfo) {
|
||||
return cid == other.cid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => cid.hashCode;
|
||||
}
|
||||
|
||||
class PageInfo {
|
||||
final int cid;
|
||||
final int page;
|
||||
final String? from;
|
||||
final String? part;
|
||||
final String? vid;
|
||||
final bool hasAlias;
|
||||
final int tid;
|
||||
int width;
|
||||
int height;
|
||||
final int rotate;
|
||||
final String? downloadTitle;
|
||||
final String? downloadSubtitle;
|
||||
|
||||
PageInfo({
|
||||
required this.cid,
|
||||
required this.page,
|
||||
this.from,
|
||||
this.part,
|
||||
this.vid,
|
||||
required this.hasAlias,
|
||||
required this.tid,
|
||||
this.width = 0,
|
||||
this.height = 0,
|
||||
this.rotate = 0,
|
||||
this.downloadTitle,
|
||||
this.downloadSubtitle,
|
||||
});
|
||||
|
||||
factory PageInfo.fromJson(Map<String, dynamic> json) => PageInfo(
|
||||
cid: json['cid'] as int,
|
||||
page: json['page'] as int,
|
||||
from: json['from'] as String?,
|
||||
part: json['part'] as String?,
|
||||
vid: json['vid'] as String?,
|
||||
hasAlias: json['has_alias'] as bool,
|
||||
tid: json['tid'] as int,
|
||||
width: json['width'] as int,
|
||||
height: json['height'] as int,
|
||||
rotate: json['rotate'] as int,
|
||||
downloadTitle: json['download_title'] as String?,
|
||||
downloadSubtitle: json['download_subtitle'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'cid': cid,
|
||||
'page': page,
|
||||
'from': ?from,
|
||||
'part': ?part,
|
||||
'vid': ?vid,
|
||||
'has_alias': hasAlias,
|
||||
'tid': tid,
|
||||
'width': width,
|
||||
'height': height,
|
||||
'rotate': rotate,
|
||||
'download_title': downloadTitle,
|
||||
'download_subtitle': downloadSubtitle,
|
||||
};
|
||||
}
|
||||
|
||||
class SourceInfo {
|
||||
final int avId;
|
||||
final int cid;
|
||||
|
||||
SourceInfo({
|
||||
required this.avId,
|
||||
required this.cid,
|
||||
});
|
||||
|
||||
factory SourceInfo.fromJson(Map<String, dynamic> json) => SourceInfo(
|
||||
avId: json['av_id'] as int,
|
||||
cid: json['cid'] as int,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'av_id': avId,
|
||||
'cid': cid,
|
||||
};
|
||||
}
|
||||
|
||||
class EpInfo {
|
||||
final int avId;
|
||||
final int page;
|
||||
final int danmaku;
|
||||
final String cover;
|
||||
final int episodeId;
|
||||
final String index;
|
||||
final String indexTitle;
|
||||
final String? showTitle;
|
||||
final String from;
|
||||
final int seasonType;
|
||||
int width;
|
||||
int height;
|
||||
final int rotate;
|
||||
final String link;
|
||||
final String bvid;
|
||||
final int sortIndex;
|
||||
|
||||
EpInfo({
|
||||
required this.avId,
|
||||
required this.page,
|
||||
required this.danmaku,
|
||||
required this.cover,
|
||||
required this.episodeId,
|
||||
required this.index,
|
||||
required this.indexTitle,
|
||||
this.showTitle,
|
||||
required this.from,
|
||||
required this.seasonType,
|
||||
required this.width,
|
||||
required this.height,
|
||||
required this.rotate,
|
||||
this.link = '',
|
||||
this.bvid = '',
|
||||
this.sortIndex = 0,
|
||||
});
|
||||
|
||||
factory EpInfo.fromJson(Map<String, dynamic> json) => EpInfo(
|
||||
avId: json['av_id'] as int,
|
||||
page: json['page'] as int,
|
||||
danmaku: json['danmaku'] as int,
|
||||
cover: json['cover'] as String,
|
||||
episodeId: json['episode_id'] as int,
|
||||
index: json['index'] as String,
|
||||
indexTitle: json['index_title'] as String,
|
||||
showTitle: json['show_title'] as String?,
|
||||
from: json['from'] as String,
|
||||
seasonType: json['season_type'] as int,
|
||||
width: json['width'] as int,
|
||||
height: json['height'] as int,
|
||||
rotate: json['rotate'] as int,
|
||||
link: json['link'] as String,
|
||||
bvid: json['bvid'] as String,
|
||||
sortIndex: json['sort_index'] as int,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'av_id': avId,
|
||||
'page': page,
|
||||
'danmaku': danmaku,
|
||||
'cover': cover,
|
||||
'episode_id': episodeId,
|
||||
'index': index,
|
||||
'index_title': indexTitle,
|
||||
'show_title': showTitle,
|
||||
'from': from,
|
||||
'season_type': seasonType,
|
||||
'width': width,
|
||||
'height': height,
|
||||
'rotate': rotate,
|
||||
'link': link,
|
||||
'bvid': bvid,
|
||||
'sort_index': sortIndex,
|
||||
};
|
||||
}
|
||||
|
||||
enum DownloadStatus {
|
||||
downloading('正在下载'),
|
||||
audioDownloading('正在下载音频'),
|
||||
getDanmaku('获取弹幕'),
|
||||
getPlayUrl('获取播放地址'),
|
||||
//
|
||||
completed('下载完成'),
|
||||
failDownload('下载失败'),
|
||||
failDownloadAudio('音频下载失败'),
|
||||
failDanmaku('获取弹幕失败'),
|
||||
failPlayUrl('获取播放地址失败'),
|
||||
pause('暂停中'),
|
||||
wait('等待中');
|
||||
|
||||
final String message;
|
||||
const DownloadStatus(this.message);
|
||||
}
|
||||
295
lib/models_new/download/bili_download_media_file_info.dart
Normal file
295
lib/models_new/download/bili_download_media_file_info.dart
Normal file
@@ -0,0 +1,295 @@
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
|
||||
sealed class BiliDownloadMediaInfo {
|
||||
const BiliDownloadMediaInfo();
|
||||
|
||||
Map<String, String> get httpHeader => {};
|
||||
|
||||
Map<String, dynamic> toJson();
|
||||
}
|
||||
|
||||
class Type1 extends BiliDownloadMediaInfo {
|
||||
final int availablePeriodMilli;
|
||||
final String description;
|
||||
final String format;
|
||||
final String? from;
|
||||
final bool intact;
|
||||
final bool isDownloaded;
|
||||
final bool isResolved;
|
||||
final String marlinToken;
|
||||
final bool needLogin;
|
||||
final bool needVip;
|
||||
final int parseTimestampMilli;
|
||||
final List<Type1PlayerCodecConfig> playerCodecConfigList;
|
||||
final int playerError;
|
||||
final int quality;
|
||||
final List<Type1Segment> segmentList;
|
||||
final int timeLength;
|
||||
final String? typeTag;
|
||||
final String? userAgent;
|
||||
final String? referer;
|
||||
final int videoCodecId;
|
||||
final bool videoProject;
|
||||
|
||||
@override
|
||||
Map<String, String> get httpHeader => {
|
||||
if (referer?.isNotEmpty ?? false) 'referer': referer!,
|
||||
if (userAgent?.isNotEmpty ?? false) 'user-agent': userAgent!,
|
||||
};
|
||||
|
||||
Type1({
|
||||
required this.availablePeriodMilli,
|
||||
required this.description,
|
||||
required this.format,
|
||||
this.from,
|
||||
required this.intact,
|
||||
required this.isDownloaded,
|
||||
required this.isResolved,
|
||||
required this.marlinToken,
|
||||
required this.needLogin,
|
||||
required this.needVip,
|
||||
required this.parseTimestampMilli,
|
||||
required this.playerCodecConfigList,
|
||||
required this.playerError,
|
||||
required this.quality,
|
||||
required this.segmentList,
|
||||
required this.timeLength,
|
||||
this.typeTag,
|
||||
this.userAgent,
|
||||
this.referer,
|
||||
required this.videoCodecId,
|
||||
required this.videoProject,
|
||||
});
|
||||
|
||||
factory Type1.fromJson(Map<String, dynamic> json) => Type1(
|
||||
availablePeriodMilli: json['available_period_milli'] as int,
|
||||
description: json['description'] as String,
|
||||
format: json['format'] as String,
|
||||
from: json['from'] as String?,
|
||||
intact: json['intact'] as bool,
|
||||
isDownloaded: json['is_downloaded'] as bool,
|
||||
isResolved: json['is_resolved'] as bool,
|
||||
marlinToken: json['marlin_token'] as String,
|
||||
needLogin: json['need_login'] as bool,
|
||||
needVip: json['need_vip'] as bool,
|
||||
parseTimestampMilli: json['parse_timestamp_milli'] as int,
|
||||
playerCodecConfigList: (json['player_codec_config_list'] as List<dynamic>)
|
||||
.map((e) => Type1PlayerCodecConfig.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
playerError: json['player_error'] as int,
|
||||
quality: json['quality'] as int,
|
||||
segmentList: (json['segment_list'] as List<dynamic>)
|
||||
.map((e) => Type1Segment.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
timeLength: json['time_length'] as int,
|
||||
typeTag: json['type_tag'] as String?,
|
||||
userAgent: json['user_agent'] as String?,
|
||||
referer: json['referer'] as String?,
|
||||
videoCodecId: json['video_codec_id'] as int,
|
||||
videoProject: json['video_project'] as bool,
|
||||
);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'available_period_milli': availablePeriodMilli,
|
||||
'description': description,
|
||||
'format': format,
|
||||
'from': ?from,
|
||||
'intact': intact,
|
||||
'is_downloaded': isDownloaded,
|
||||
'is_resolved': isResolved,
|
||||
'marlin_token': marlinToken,
|
||||
'need_login': needLogin,
|
||||
'need_vip': needVip,
|
||||
'parse_timestamp_milli': parseTimestampMilli,
|
||||
'player_codec_config_list': playerCodecConfigList
|
||||
.map((e) => e.toJson())
|
||||
.toList(),
|
||||
'player_error': playerError,
|
||||
'quality': quality,
|
||||
'segment_list': segmentList.map((e) => e.toJson()).toList(),
|
||||
'time_length': timeLength,
|
||||
'type_tag': ?typeTag,
|
||||
'user_agent': ?userAgent,
|
||||
'referer': ?referer,
|
||||
'video_codec_id': videoCodecId,
|
||||
'video_project': videoProject,
|
||||
};
|
||||
}
|
||||
|
||||
class Type1PlayerCodecConfig {
|
||||
final String player;
|
||||
final bool useIjkMediaCodec;
|
||||
|
||||
Type1PlayerCodecConfig({
|
||||
required this.player,
|
||||
required this.useIjkMediaCodec,
|
||||
});
|
||||
|
||||
factory Type1PlayerCodecConfig.fromJson(Map<String, dynamic> json) =>
|
||||
Type1PlayerCodecConfig(
|
||||
player: json['player'] as String,
|
||||
useIjkMediaCodec: json['use_ijk_media_codec'] as bool,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'player': player,
|
||||
'use_ijk_media_codec': useIjkMediaCodec,
|
||||
};
|
||||
}
|
||||
|
||||
class Type1Segment {
|
||||
final List<String> backupUrls;
|
||||
final int bytes;
|
||||
final int duration;
|
||||
final String md5;
|
||||
final String metaUrl;
|
||||
final int order;
|
||||
final String url;
|
||||
|
||||
Type1Segment({
|
||||
required this.backupUrls,
|
||||
required this.bytes,
|
||||
this.duration = 0,
|
||||
required this.md5,
|
||||
required this.metaUrl,
|
||||
required this.order,
|
||||
required this.url,
|
||||
});
|
||||
|
||||
factory Type1Segment.fromJson(Map<String, dynamic> json) => Type1Segment(
|
||||
backupUrls: List<String>.from(json['backup_urls']),
|
||||
bytes: json['bytes'] as int,
|
||||
duration: json['duration'] as int,
|
||||
md5: json['md5'] as String,
|
||||
metaUrl: json['meta_url'] as String,
|
||||
order: json['order'] as int,
|
||||
url: json['url'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'backup_urls': backupUrls,
|
||||
'bytes': bytes,
|
||||
'duration': duration,
|
||||
'md5': md5,
|
||||
'meta_url': metaUrl,
|
||||
'order': order,
|
||||
'url': url,
|
||||
};
|
||||
}
|
||||
|
||||
class Type2 extends BiliDownloadMediaInfo {
|
||||
final int duration;
|
||||
final List<Type2File> video;
|
||||
final List<Type2File>? audio;
|
||||
final String? userAgent;
|
||||
final String? referer;
|
||||
|
||||
Type2({
|
||||
this.duration = 0,
|
||||
required this.video,
|
||||
this.audio,
|
||||
this.userAgent,
|
||||
this.referer,
|
||||
});
|
||||
|
||||
@override
|
||||
Map<String, String> get httpHeader => {
|
||||
if (referer?.isNotEmpty ?? false) 'referer': referer!,
|
||||
if (userAgent?.isNotEmpty ?? false) 'user-agent': userAgent!,
|
||||
};
|
||||
|
||||
factory Type2.fromJson(Map<String, dynamic> json) => Type2(
|
||||
duration: json['duration'] as int,
|
||||
video: (json['video'] as List<dynamic>)
|
||||
.map((e) => Type2File.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
audio: (json['audio'] as List<dynamic>?)
|
||||
?.map((e) => Type2File.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
userAgent: json['user_agent'] as String?,
|
||||
referer: json['referer'] as String?,
|
||||
);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'duration': duration,
|
||||
'video': video.map((e) => e.toJson()).toList(),
|
||||
'audio': ?audio?.map((e) => e.toJson()).toList(),
|
||||
'user_agent': ?userAgent,
|
||||
'referer': ?referer,
|
||||
};
|
||||
}
|
||||
|
||||
class Type2File {
|
||||
final int id;
|
||||
final String baseUrl;
|
||||
final List<String>? backupUrl;
|
||||
final int bandwidth;
|
||||
final int codecid;
|
||||
int size;
|
||||
final String md5;
|
||||
final bool noRexcode;
|
||||
final String frameRate;
|
||||
final int width;
|
||||
final int height;
|
||||
final int dashDrmType;
|
||||
|
||||
Type2File({
|
||||
required this.id,
|
||||
required this.baseUrl,
|
||||
this.backupUrl,
|
||||
required this.bandwidth,
|
||||
required this.codecid,
|
||||
required this.size,
|
||||
required this.md5,
|
||||
required this.noRexcode,
|
||||
this.frameRate = '',
|
||||
this.width = 1,
|
||||
this.height = 1,
|
||||
this.dashDrmType = 0,
|
||||
});
|
||||
|
||||
factory Type2File.fromJson(Map<String, dynamic> json) => Type2File(
|
||||
id: json['id'] as int,
|
||||
baseUrl: json['base_url'] as String,
|
||||
backupUrl: (json['backup_url'] as List<dynamic>?)?.fromCast(),
|
||||
bandwidth: json['bandwidth'] as int,
|
||||
codecid: json['codecid'] as int,
|
||||
size: json['size'] as int,
|
||||
md5: json['md5'] as String,
|
||||
noRexcode: json['no_rexcode'] as bool,
|
||||
frameRate: json['frame_rate'] as String? ?? '',
|
||||
width: json['width'] as int,
|
||||
height: json['height'] as int,
|
||||
dashDrmType: json['dash_drm_type'] as int,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||
'id': id,
|
||||
'base_url': baseUrl,
|
||||
'backup_url': ?backupUrl,
|
||||
'bandwidth': bandwidth,
|
||||
'codecid': codecid,
|
||||
'size': size,
|
||||
'md5': md5,
|
||||
'no_rexcode': noRexcode,
|
||||
'frame_rate': frameRate,
|
||||
'width': width,
|
||||
'height': height,
|
||||
'dash_drm_type': dashDrmType,
|
||||
};
|
||||
}
|
||||
|
||||
class None extends BiliDownloadMediaInfo {
|
||||
final String message;
|
||||
|
||||
const None({
|
||||
required this.message,
|
||||
});
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
23
lib/models_new/download/download_info.dart
Normal file
23
lib/models_new/download/download_info.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:PiliPlus/models_new/download/bili_download_entry_info.dart';
|
||||
|
||||
class DownloadPageInfo {
|
||||
final String pageId;
|
||||
final String dirPath;
|
||||
final String title;
|
||||
String cover;
|
||||
int sortKey;
|
||||
final int? seasonType;
|
||||
final List<BiliDownloadEntryInfo> entrys;
|
||||
BiliDownloadEntryInfo? entry;
|
||||
|
||||
DownloadPageInfo({
|
||||
required this.pageId,
|
||||
required this.dirPath,
|
||||
required this.title,
|
||||
required this.cover,
|
||||
required this.sortKey,
|
||||
this.seasonType,
|
||||
required this.entrys,
|
||||
this.entry,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user