Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
eaef7a1
feat: add offline video download and caching
0Chencc Jan 31, 2026
7a55996
refactor: decouple DownloadController and fix queued downloads never …
0Chencc Jan 31, 2026
2072960
refactor: Windows WebView 控制器改用 HeadlessWebview
0Chencc Feb 2, 2026
f416826
feat: 下载前展开嵌套 M3U8 播放列表
0Chencc Feb 2, 2026
2576463
test: 添加 M3U8 解析器验证测试
0Chencc Feb 2, 2026
29766e6
fix: 显式处理 #EXT-X-PLAYLIST-TYPE:VOD 标签
0Chencc Feb 2, 2026
8626ca4
fix: 修复 Windows WebView dispose 可能的竞争条件
0Chencc Feb 3, 2026
ac30952
chore: 合并上游 main 分支
0Chencc Feb 3, 2026
71e16cc
feat: 播放器菜单新增下载入口
0Chencc Feb 3, 2026
da4fad5
refactor: 引入 VideoSourceProvider 抽象层解决 WebView 状态污染
0Chencc Feb 3, 2026
812bb6d
feat: 离线播放复用主播放器架构
0Chencc Feb 3, 2026
f9f009a
refactor: 解耦 VideoPageController 对 DownloadController 的依赖
0Chencc Feb 3, 2026
e8203d1
refactor: 移除 WebView Widget 层技术债务并改进 Hive 错误处理
0Chencc Feb 3, 2026
532147f
feat: 离线弹幕缓存功能
0Chencc Feb 3, 2026
1b9f4f5
feat: 下载速率显示、并发配置与稳定性改进
0Chencc Feb 3, 2026
a5dd468
refactor: 代码清理与健壮性改进
0Chencc Feb 4, 2026
616c155
fix: 修复离线播放时 currentEpisode 越界导致崩溃
0Chencc Feb 4, 2026
aab1f1f
fix: 使用 URL 匹配替代位置匹配修复下载集数重排序问题
0Chencc Feb 4, 2026
16d6b7b
refactor: 重写 WebviewItemControllerImpel 从 webview_flutter 迁移到 flutter…
0Chencc Feb 4, 2026
0945243
chore: 清理阶段4未使用的 VideoSourceProvider 实现
0Chencc Feb 4, 2026
fc6b565
fix: 修复旧版 Android WebView onLoadStart 时 callHandler 未就绪导致脚本中止
0Chencc Feb 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/hive_registrar.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:kazumi/modules/bangumi/bangumi_item.dart';
import 'package:kazumi/modules/bangumi/bangumi_tag.dart';
import 'package:kazumi/modules/collect/collect_change_module.dart';
import 'package:kazumi/modules/collect/collect_module.dart';
import 'package:kazumi/modules/download/download_module.dart';
import 'package:kazumi/modules/history/history_module.dart';
import 'package:kazumi/modules/search/search_history_module.dart';

Expand All @@ -16,6 +17,8 @@ extension HiveRegistrar on HiveInterface {
registerAdapter(BangumiTagAdapter());
registerAdapter(CollectedBangumiAdapter());
registerAdapter(CollectedBangumiChangeAdapter());
registerAdapter(DownloadEpisodeAdapter());
registerAdapter(DownloadRecordAdapter());
registerAdapter(HistoryAdapter());
registerAdapter(ProgressAdapter());
registerAdapter(SearchHistoryAdapter());
Expand All @@ -28,6 +31,8 @@ extension IsolatedHiveRegistrar on IsolatedHiveInterface {
registerAdapter(BangumiTagAdapter());
registerAdapter(CollectedBangumiAdapter());
registerAdapter(CollectedBangumiChangeAdapter());
registerAdapter(DownloadEpisodeAdapter());
registerAdapter(DownloadRecordAdapter());
registerAdapter(HistoryAdapter());
registerAdapter(ProgressAdapter());
registerAdapter(SearchHistoryAdapter());
Expand Down
9 changes: 6 additions & 3 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ void main() async {
}

try {
await Hive.initFlutter(
'${(await getApplicationSupportDirectory()).path}/hive');
final hivePath = '${(await getApplicationSupportDirectory()).path}/hive';
await Hive.initFlutter(hivePath);
await GStorage.init();
} catch (_) {
} catch (e) {
// Log the error for debugging (if logger is available)
debugPrint('Storage initialization failed: $e');

if (Platform.isWindows) {
await windowManager.ensureInitialized();
windowManager.waitUntilReadyToShow(null, () async {
Expand Down
12 changes: 12 additions & 0 deletions lib/modules/danmaku/danmaku_module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,16 @@ class Danmaku {
String sourceValue = parts[3];
return Danmaku(time: timeValue, message: messageValue, type: typeValue, color: color, source: sourceValue);
}

/// 序列化为 JSON 格式 (与 fromJson 格式一致)
Map<String, dynamic> toJson() {
// 只存储 RGB 部分 (与 DanDanPlay API 格式一致)
final colorValue = ((color.r * 255).toInt() << 16) |
((color.g * 255).toInt() << 8) |
(color.b * 255).toInt();
return {
'm': message,
'p': '$time,$type,$colorValue,$source',
};
}
}
117 changes: 117 additions & 0 deletions lib/modules/download/download_module.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import 'package:hive_ce/hive.dart';

part 'download_module.g.dart';

@HiveType(typeId: 7)
class DownloadRecord {
@HiveField(0)
int bangumiId;

@HiveField(1)
String bangumiName;

@HiveField(2)
String bangumiCover;

@HiveField(3)
String pluginName;

@HiveField(4)
Map<int, DownloadEpisode> episodes;

@HiveField(5)
DateTime createdAt;

String get key => '${pluginName}_$bangumiId';

DownloadRecord(
this.bangumiId,
this.bangumiName,
this.bangumiCover,
this.pluginName,
this.episodes,
this.createdAt,
);
}

@HiveType(typeId: 8)
class DownloadEpisode {
@HiveField(0)
int episodeNumber;

@HiveField(1)
String episodeName;

@HiveField(2)
int road;

/// 0=pending 1=resolving 2=downloading 3=completed 4=failed 5=paused
@HiveField(3)
int status;

@HiveField(4)
double progressPercent;

@HiveField(5)
int totalSegments;

@HiveField(6)
int downloadedSegments;

@HiveField(7)
String localM3u8Path;

@HiveField(8)
String downloadDirectory;

@HiveField(9)
String networkM3u8Url;

@HiveField(10)
DateTime? completedAt;

@HiveField(11, defaultValue: '')
String errorMessage;

@HiveField(12, defaultValue: 0)
int totalBytes;

@HiveField(13, defaultValue: '')
String episodePageUrl;

/// 缓存的弹幕数据 (JSON 字符串格式)
@HiveField(14, defaultValue: '')
String danmakuData;

/// DanDanPlay 番剧 ID (用于弹幕查询缓存)
@HiveField(15, defaultValue: 0)
int danDanBangumiID;

DownloadEpisode(
this.episodeNumber,
this.episodeName,
this.road,
this.status,
this.progressPercent,
this.totalSegments,
this.downloadedSegments,
this.localM3u8Path,
this.downloadDirectory,
this.networkM3u8Url,
this.completedAt,
this.errorMessage,
this.totalBytes,
this.episodePageUrl, {
this.danmakuData = '',
this.danDanBangumiID = 0,
});
}

class DownloadStatus {
static const int pending = 0;
static const int resolving = 1;
static const int downloading = 2;
static const int completed = 3;
static const int failed = 4;
static const int paused = 5;
}
135 changes: 135 additions & 0 deletions lib/modules/download/download_module.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading