Skip to content

Commit 403a898

Browse files
authored
Merge pull request #99 from ahzmr/main
fix: 解决macos环境中下载出错与闪退的问题
2 parents e3f2f09 + 147dedd commit 403a898

11 files changed

Lines changed: 70 additions & 166 deletions

File tree

backend/app/download.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ func (d *CourseDownload) Download() error {
122122
progress.Total = total
123123
curr++
124124
progress.Current = curr
125-
progress.Pct = curr * 100 / progress.Total
125+
if total > 0 {
126+
progress.Pct = curr * 100 / progress.Total
127+
}
126128
progress.Value = datum.Title
127129
runtime.EventsEmit(d.Ctx, "courseDownload", progress)
128130
emitProgress(d.ProgressCB, progress)
@@ -192,7 +194,9 @@ func (d *OdobDownload) Download() error {
192194
progress.Total = total
193195
curr++
194196
progress.Current = curr
195-
progress.Pct = curr * 100 / progress.Total
197+
if total > 0 {
198+
progress.Pct = curr * 100 / progress.Total
199+
}
196200
progress.Value = datum.Title + ".mp3"
197201
runtime.EventsEmit(d.Ctx, "odobDownload", progress)
198202
emitProgress(d.ProgressCB, progress)
@@ -629,7 +633,9 @@ func DownloadPdfCourse(list []downloader.Datum, path string, ctx context.Context
629633
progress.Total = total
630634
curr++
631635
progress.Current = curr
632-
progress.Pct = curr * 100 / progress.Total
636+
if total > 0 {
637+
progress.Pct = curr * 100 / progress.Total
638+
}
633639
progress.Value = v.Title
634640
runtime.EventsEmit(ctx, "courseDownload", progress)
635641
emitProgress(cb, progress)
@@ -678,7 +684,9 @@ func DownloadMarkdown(list *services.ArticleList, aid int, path string, ctx cont
678684
progress.Total = total
679685
curr++
680686
progress.Current = curr
681-
progress.Pct = curr * 100 / progress.Total
687+
if total > 0 {
688+
progress.Pct = curr * 100 / progress.Total
689+
}
682690
progress.Value = v.Title
683691
runtime.EventsEmit(ctx, "courseDownload", progress)
684692
emitProgress(cb, progress)

backend/app/ebook.go

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ func EbookPage(ctx context.Context, enID string, cb ProgressCallback) (info *ser
6363
wgp := utils.NewWaitGroupPool(5)
6464
total, curr := len(info.BookInfo.Orders), 0
6565
var chapterMap sync.Map
66+
var mu sync.Mutex
67+
var firstErr error
68+
var svgList []*utils.SvgContent
6669
for _, ebookToc := range info.BookInfo.Toc {
6770
key := ebookToc.Href
6871
href := strings.Split(ebookToc.Href, "#")
@@ -76,7 +79,9 @@ func EbookPage(ctx context.Context, enID string, cb ProgressCallback) (info *ser
7679
progress.Total = total
7780
curr++
7881
progress.Current = curr
79-
progress.Pct = curr * 100 / progress.Total
82+
if total > 0 {
83+
progress.Pct = curr * 100 / progress.Total
84+
}
8085
value, ok := chapterMap.Load(order.ChapterID)
8186
if ok {
8287
progress.Value = value.(utils.EbookToc).Text
@@ -87,24 +92,42 @@ func EbookPage(ctx context.Context, enID string, cb ProgressCallback) (info *ser
8792
wgp.Add()
8893
go func(i int, order services.EbookOrders) {
8994
defer func() {
95+
if r := recover(); r != nil {
96+
mu.Lock()
97+
if firstErr == nil {
98+
firstErr = fmt.Errorf("goroutine panic: %v", r)
99+
}
100+
mu.Unlock()
101+
}
90102
wgp.Done()
91103
}()
92104
index, count, offset := 0, 20, 0
93-
svgList, err1 := generateEbookPages(enID, order.ChapterID, token.Token, index, count, offset)
105+
pages, err1 := generateEbookPages(enID, order.ChapterID, token.Token, index, count, offset)
94106
if err1 != nil {
95-
err = err1
107+
mu.Lock()
108+
if firstErr == nil {
109+
firstErr = err1
110+
}
111+
mu.Unlock()
96112
return
97113
}
98114

99-
svgContent = append(svgContent, &utils.SvgContent{
100-
Contents: svgList,
115+
mu.Lock()
116+
svgList = append(svgList, &utils.SvgContent{
117+
Contents: pages,
101118
ChapterID: order.ChapterID,
102119
PathInEpub: order.PathInEpub,
103120
OrderIndex: i,
104121
})
122+
mu.Unlock()
105123
}(i, order)
106124
}
107125
wgp.Wait()
126+
if firstErr != nil {
127+
err = firstErr
128+
return
129+
}
130+
svgContent = svgList
108131
return
109132
}
110133

@@ -154,7 +177,13 @@ func generateEbookPages(enid, chapterID, token string, index, count, offset int)
154177
// PKCS7Unpad 实现PKCS7去填充
155178
func PKCS7Unpad(data []byte) []byte {
156179
length := len(data)
180+
if length == 0 {
181+
return data
182+
}
157183
unpadding := int(data[length-1])
184+
if unpadding == 0 || unpadding > length {
185+
return data
186+
}
158187
return data[:(length - unpadding)]
159188
}
160189

@@ -175,6 +204,10 @@ func DecryptAES(contents string) string {
175204
}
176205

177206
blockSize := block.BlockSize()
207+
// CryptBlocks 要求密文长度必须是 block 大小的整数倍,否则会 panic
208+
if len(ciphertext) == 0 || len(ciphertext)%blockSize != 0 {
209+
return ""
210+
}
178211
mode := cipher.NewCBCDecrypter(block, iv[:blockSize])
179212
plaintext := make([]byte, len(ciphertext))
180213
mode.CryptBlocks(plaintext, ciphertext)

backend/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func (c *ConfigsData) Save() error {
129129
data, err := jsoniter.MarshalIndent(conf, "", " ")
130130

131131
if err != nil {
132-
panic(err)
132+
return err
133133
}
134134

135135
// 减掉多余的部分

backend/downloader/downloader.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ func Download(v Datum, stream, path string) error {
2222
title = fmt.Sprintf("%03d.%s", v.OrderNum, title)
2323
}
2424
if stream == "" {
25+
if len(v.sortedStreams) == 0 {
26+
return fmt.Errorf("没有可用的下载流:%s", v.Title)
27+
}
2528
stream = v.sortedStreams[0].name
2629
}
2730
data, ok := v.Streams[stream]

backend/downloadmgr/repository.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,11 @@ func (r *Repository) ClearTasks(clearAll bool) error {
288288
}
289289

290290
func defaultDBPath() (string, error) {
291-
wd, err := os.Getwd()
291+
configDir, err := os.UserConfigDir()
292292
if err != nil {
293-
return "", err
293+
return "", fmt.Errorf("获取用户配置目录失败: %w", err)
294294
}
295-
dir := filepath.Join(wd, ".cache", "download")
295+
dir := filepath.Join(configDir, "dedao", "download")
296296
if err = os.MkdirAll(dir, 0755); err != nil {
297297
return "", fmt.Errorf("创建下载数据库目录失败: %w", err)
298298
}

backend/utils/badger.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package utils
33
import (
44
"encoding/json"
55
"fmt"
6-
"log"
76
"os"
87
"path/filepath"
98
"strings"
@@ -36,13 +35,13 @@ func ensureDir(dirPath string) error {
3635

3736
// GetBadgerDB 获取全局 BadgerDB 实例
3837
func GetBadgerDB(dbPath string) (*BadgerDB, error) {
38+
var initErr error
3939
once.Do(func() {
40-
var err error
41-
badgerInstance, err = NewBadgerDB(dbPath)
42-
if err != nil {
43-
log.Fatalf("初始化 BadgerDB 失败: %v", err)
44-
}
40+
badgerInstance, initErr = NewBadgerDB(dbPath)
4541
})
42+
if badgerInstance == nil {
43+
return nil, fmt.Errorf("初始化 BadgerDB 失败: %w", initErr)
44+
}
4645
return badgerInstance, nil
4746
}
4847

@@ -270,9 +269,10 @@ func FormatKey(category string, id int) string {
270269

271270
// GetDefaultBadgerDBPath 获取默认的 BadgerDB 路径
272271
func GetDefaultBadgerDBPath() string {
273-
configDir, err := os.Getwd()
272+
// 使用 UserConfigDir 而非 os.Getwd(),避免 macOS .app 包中返回 "/" 的问题
273+
configDir, err := os.UserConfigDir()
274274
if err != nil {
275275
configDir = os.TempDir()
276276
}
277-
return filepath.Join(configDir, ".cache", "db")
277+
return filepath.Join(configDir, "dedao", ".cache", "db")
278278
}

backend/utils/svg2html.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ func GenLineContentByElement(chapterID string, element *svgparser.Element) (line
696696
// footnote image with text in one line
697697
yInt, _ := strconv.ParseFloat(ele.Y, 64)
698698
w, _ := strconv.ParseFloat(ele.Width, 64)
699-
if children.Name == "image" && w < footNoteImgW {
699+
if children.Name == "image" && w < footNoteImgW && k > 0 {
700700
attrPre := element.Children[k-1].Attributes
701701
yInt, _ = strconv.ParseFloat(attrPre["y"], 64)
702702
ele.Y = attrPre["y"]

frontend/wailsjs/runtime/runtime.d.ts

Lines changed: 1 addition & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -246,85 +246,4 @@ export function OnFileDropOff() :void
246246
export function CanResolveFilePaths(): boolean;
247247

248248
// Resolves file paths for an array of files
249-
export function ResolveFilePaths(files: File[]): void
250-
251-
// Notification types
252-
export interface NotificationOptions {
253-
id: string;
254-
title: string;
255-
subtitle?: string; // macOS and Linux only
256-
body?: string;
257-
categoryId?: string;
258-
data?: { [key: string]: any };
259-
}
260-
261-
export interface NotificationAction {
262-
id?: string;
263-
title?: string;
264-
destructive?: boolean; // macOS-specific
265-
}
266-
267-
export interface NotificationCategory {
268-
id?: string;
269-
actions?: NotificationAction[];
270-
hasReplyField?: boolean;
271-
replyPlaceholder?: string;
272-
replyButtonTitle?: string;
273-
}
274-
275-
// [InitializeNotifications](https://wails.io/docs/reference/runtime/notification#initializenotifications)
276-
// Initializes the notification service for the application.
277-
// This must be called before sending any notifications.
278-
export function InitializeNotifications(): Promise<void>;
279-
280-
// [CleanupNotifications](https://wails.io/docs/reference/runtime/notification#cleanupnotifications)
281-
// Cleans up notification resources and releases any held connections.
282-
export function CleanupNotifications(): Promise<void>;
283-
284-
// [IsNotificationAvailable](https://wails.io/docs/reference/runtime/notification#isnotificationavailable)
285-
// Checks if notifications are available on the current platform.
286-
export function IsNotificationAvailable(): Promise<boolean>;
287-
288-
// [RequestNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#requestnotificationauthorization)
289-
// Requests notification authorization from the user (macOS only).
290-
export function RequestNotificationAuthorization(): Promise<boolean>;
291-
292-
// [CheckNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#checknotificationauthorization)
293-
// Checks the current notification authorization status (macOS only).
294-
export function CheckNotificationAuthorization(): Promise<boolean>;
295-
296-
// [SendNotification](https://wails.io/docs/reference/runtime/notification#sendnotification)
297-
// Sends a basic notification with the given options.
298-
export function SendNotification(options: NotificationOptions): Promise<void>;
299-
300-
// [SendNotificationWithActions](https://wails.io/docs/reference/runtime/notification#sendnotificationwithactions)
301-
// Sends a notification with action buttons. Requires a registered category.
302-
export function SendNotificationWithActions(options: NotificationOptions): Promise<void>;
303-
304-
// [RegisterNotificationCategory](https://wails.io/docs/reference/runtime/notification#registernotificationcategory)
305-
// Registers a notification category that can be used with SendNotificationWithActions.
306-
export function RegisterNotificationCategory(category: NotificationCategory): Promise<void>;
307-
308-
// [RemoveNotificationCategory](https://wails.io/docs/reference/runtime/notification#removenotificationcategory)
309-
// Removes a previously registered notification category.
310-
export function RemoveNotificationCategory(categoryId: string): Promise<void>;
311-
312-
// [RemoveAllPendingNotifications](https://wails.io/docs/reference/runtime/notification#removeallpendingnotifications)
313-
// Removes all pending notifications from the notification center.
314-
export function RemoveAllPendingNotifications(): Promise<void>;
315-
316-
// [RemovePendingNotification](https://wails.io/docs/reference/runtime/notification#removependingnotification)
317-
// Removes a specific pending notification by its identifier.
318-
export function RemovePendingNotification(identifier: string): Promise<void>;
319-
320-
// [RemoveAllDeliveredNotifications](https://wails.io/docs/reference/runtime/notification#removealldeliverednotifications)
321-
// Removes all delivered notifications from the notification center.
322-
export function RemoveAllDeliveredNotifications(): Promise<void>;
323-
324-
// [RemoveDeliveredNotification](https://wails.io/docs/reference/runtime/notification#removedeliverednotification)
325-
// Removes a specific delivered notification by its identifier.
326-
export function RemoveDeliveredNotification(identifier: string): Promise<void>;
327-
328-
// [RemoveNotification](https://wails.io/docs/reference/runtime/notification#removenotification)
329-
// Removes a notification by its identifier (cross-platform convenience function).
330-
export function RemoveNotification(identifier: string): Promise<void>;
249+
export function ResolveFilePaths(files: File[]): void

frontend/wailsjs/runtime/runtime.js

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -239,60 +239,4 @@ export function CanResolveFilePaths() {
239239

240240
export function ResolveFilePaths(files) {
241241
return window.runtime.ResolveFilePaths(files);
242-
}
243-
244-
export function InitializeNotifications() {
245-
return window.runtime.InitializeNotifications();
246-
}
247-
248-
export function CleanupNotifications() {
249-
return window.runtime.CleanupNotifications();
250-
}
251-
252-
export function IsNotificationAvailable() {
253-
return window.runtime.IsNotificationAvailable();
254-
}
255-
256-
export function RequestNotificationAuthorization() {
257-
return window.runtime.RequestNotificationAuthorization();
258-
}
259-
260-
export function CheckNotificationAuthorization() {
261-
return window.runtime.CheckNotificationAuthorization();
262-
}
263-
264-
export function SendNotification(options) {
265-
return window.runtime.SendNotification(options);
266-
}
267-
268-
export function SendNotificationWithActions(options) {
269-
return window.runtime.SendNotificationWithActions(options);
270-
}
271-
272-
export function RegisterNotificationCategory(category) {
273-
return window.runtime.RegisterNotificationCategory(category);
274-
}
275-
276-
export function RemoveNotificationCategory(categoryId) {
277-
return window.runtime.RemoveNotificationCategory(categoryId);
278-
}
279-
280-
export function RemoveAllPendingNotifications() {
281-
return window.runtime.RemoveAllPendingNotifications();
282-
}
283-
284-
export function RemovePendingNotification(identifier) {
285-
return window.runtime.RemovePendingNotification(identifier);
286-
}
287-
288-
export function RemoveAllDeliveredNotifications() {
289-
return window.runtime.RemoveAllDeliveredNotifications();
290-
}
291-
292-
export function RemoveDeliveredNotification(identifier) {
293-
return window.runtime.RemoveDeliveredNotification(identifier);
294-
}
295-
296-
export function RemoveNotification(identifier) {
297-
return window.runtime.RemoveNotification(identifier);
298242
}

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@ require (
1717
github.com/json-iterator/go v1.1.12
1818
github.com/mattn/go-colorable v0.1.14
1919
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
20-
github.com/wailsapp/wails/v2 v2.12.0
20+
github.com/wailsapp/wails/v2 v2.11.0
2121
golang.org/x/sync v0.12.0
2222
gorm.io/driver/sqlite v1.6.0
2323
gorm.io/gorm v1.31.1
2424
)
2525

2626
require (
27-
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect
2827
github.com/andybalholm/cascadia v1.3.2 // indirect
2928
github.com/bep/debounce v1.2.1 // indirect
3029
github.com/cespare/xxhash/v2 v2.3.0 // indirect

0 commit comments

Comments
 (0)