@@ -2,8 +2,10 @@ package core
22
33import (
44 "encoding/json"
5+ "os"
6+ "os/user"
7+ "path/filepath"
58 "runtime"
6- "strconv"
79 "strings"
810 "sync"
911)
@@ -30,128 +32,184 @@ type Config struct {
3032 AutoProxy bool `json:"AutoProxy"`
3133 WxAction bool `json:"WxAction"`
3234 TaskNumber int `json:"TaskNumber"`
35+ DownNumber int `json:"DownNumber"`
3336 UserAgent string `json:"UserAgent"`
3437 UseHeaders string `json:"UseHeaders"`
38+ InsertTail bool `json:"InsertTail"`
3539 MimeMap map [string ]MimeInfo `json:"MimeMap"`
40+ Rule string `json:"Rule"`
3641}
3742
3843var (
3944 mimeMux sync.RWMutex
4045)
4146
4247func initConfig () * Config {
43- if globalConfig == nil {
44- def := `
45- {
46- "Host": "127.0.0.1",
47- "Port": "8899",
48- "Theme": "lightTheme",
49- "Locale": "zh",
50- "Quality": 0,
51- "SaveDirectory": "",
52- "FilenameLen": 0,
53- "FilenameTime": true,
54- "UpstreamProxy": "",
55- "OpenProxy": false,
56- "DownloadProxy": false,
57- "AutoProxy": false,
58- "WxAction": true,
59- "TaskNumber": __TaskNumber__,
60- "UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
61- "UseHeaders": "User-Agent,Referer,Authorization,Cookie",
62- "MimeMap": {
63- "image/png": { "Type": "image", "Suffix": ".png" },
64- "image/webp": { "Type": "image", "Suffix": ".webp" },
65- "image/jpeg": { "Type": "image", "Suffix": ".jpeg" },
66- "image/jpg": { "Type": "image", "Suffix": ".jpg" },
67- "image/gif": { "Type": "image", "Suffix": ".gif" },
68- "image/avif": { "Type": "image", "Suffix": ".avif" },
69- "image/bmp": { "Type": "image", "Suffix": ".bmp" },
70- "image/tiff": { "Type": "image", "Suffix": ".tiff" },
71- "image/heic": { "Type": "image", "Suffix": ".heic" },
72- "image/x-icon": { "Type": "image", "Suffix": ".ico" },
73- "image/svg+xml": { "Type": "image", "Suffix": ".svg" },
74- "image/vnd.adobe.photoshop": { "Type": "image", "Suffix": ".psd" },
75- "image/jp2": { "Type": "image", "Suffix": ".jp2" },
76- "image/jpeg2000": { "Type": "image", "Suffix": ".jp2" },
77- "image/apng": { "Type": "image", "Suffix": ".apng" },
78- "audio/mpeg": { "Type": "audio", "Suffix": ".mp3" },
79- "audio/mp3": { "Type": "audio", "Suffix": ".mp3" },
80- "audio/wav": { "Type": "audio", "Suffix": ".wav" },
81- "audio/aiff": { "Type": "audio", "Suffix": ".aiff" },
82- "audio/x-aiff": { "Type": "audio", "Suffix": ".aiff" },
83- "audio/aac": { "Type": "audio", "Suffix": ".aac" },
84- "audio/ogg": { "Type": "audio", "Suffix": ".ogg" },
85- "audio/flac": { "Type": "audio", "Suffix": ".flac" },
86- "audio/midi": { "Type": "audio", "Suffix": ".mid" },
87- "audio/x-midi": { "Type": "audio", "Suffix": ".mid" },
88- "audio/x-ms-wma": { "Type": "audio", "Suffix": ".wma" },
89- "audio/opus": { "Type": "audio", "Suffix": ".opus" },
90- "audio/webm": { "Type": "audio", "Suffix": ".webm" },
91- "audio/mp4": { "Type": "audio", "Suffix": ".m4a" },
92- "audio/amr": { "Type": "audio", "Suffix": ".amr" },
93- "video/mp4": { "Type": "video", "Suffix": ".mp4" },
94- "video/webm": { "Type": "video", "Suffix": ".webm" },
95- "video/ogg": { "Type": "video", "Suffix": ".ogv" },
96- "video/x-msvideo": { "Type": "video", "Suffix": ".avi" },
97- "video/mpeg": { "Type": "video", "Suffix": ".mpeg" },
98- "video/quicktime": { "Type": "video", "Suffix": ".mov" },
99- "video/x-ms-wmv": { "Type": "video", "Suffix": ".wmv" },
100- "video/3gpp": { "Type": "video", "Suffix": ".3gp" },
101- "video/x-matroska": { "Type": "video", "Suffix": ".mkv" },
102- "audio/video": { "Type": "live", "Suffix": ".flv" },
103- "video/x-flv": { "Type": "live", "Suffix": ".flv" },
104- "application/dash+xml": { "Type": "live", "Suffix": ".mpd" },
105- "application/vnd.apple.mpegurl": { "Type": "m3u8", "Suffix": ".m3u8" },
106- "application/x-mpegurl": { "Type": "m3u8", "Suffix": ".m3u8" },
107- "application/x-mpeg": { "Type": "m3u8", "Suffix": ".m3u8" },
108- "application/pdf": { "Type": "pdf", "Suffix": ".pdf" },
109- "application/vnd.ms-powerpoint": { "Type": "ppt", "Suffix": ".ppt" },
110- "application/vnd.openxmlformats-officedocument.presentationml.presentation": { "Type": "ppt", "Suffix": ".pptx" },
111- "application/vnd.ms-excel": { "Type": "xls", "Suffix": ".xls" },
112- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { "Type": "xls", "Suffix": ".xlsx" },
113- "text/csv": { "Type": "xls", "Suffix": ".csv" },
114- "application/msword": { "Type": "doc", "Suffix": ".doc" },
115- "application/rtf": { "Type": "doc", "Suffix": ".rtf" },
116- "text/rtf": { "Type": "doc", "Suffix": ".rtf" },
117- "application/vnd.oasis.opendocument.text": { "Type": "doc", "Suffix": ".odt" },
118- "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { "Type": "doc", "Suffix": ".docx" },
119- "font/woff": { "Type": "font", "Suffix": ".woff" }
48+ if globalConfig != nil {
49+ return globalConfig
50+ }
51+
52+ defaultConfig := & Config {
53+ Theme : "lightTheme" ,
54+ Locale : "zh" ,
55+ Host : "127.0.0.1" ,
56+ Port : "8899" ,
57+ Quality : 0 ,
58+ SaveDirectory : getDefaultDownloadDir (),
59+ FilenameLen : 0 ,
60+ FilenameTime : true ,
61+ UpstreamProxy : "" ,
62+ OpenProxy : false ,
63+ DownloadProxy : false ,
64+ AutoProxy : false ,
65+ WxAction : true ,
66+ TaskNumber : runtime .NumCPU () * 2 ,
67+ DownNumber : 3 ,
68+ UserAgent : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36" ,
69+ UseHeaders : "default" ,
70+ InsertTail : true ,
71+ MimeMap : getDefaultMimeMap (),
72+ Rule : "*" ,
12073 }
121- }
122- `
123- def = strings .ReplaceAll (def , "__TaskNumber__" , strconv .Itoa (runtime .NumCPU ()* 2 ))
124- globalConfig = & Config {
125- storage : NewStorage ("config.json" , []byte (def )),
126- }
12774
128- defaultMap := make (map [string ]interface {})
129- _ = json .Unmarshal ([]byte (def ), & defaultMap )
75+ rawDefaults , err := json .Marshal (defaultConfig )
76+ if err != nil {
77+ return globalConfig
78+ }
79+
80+ storage := NewStorage ("config.json" , rawDefaults )
81+ defaultConfig .storage = storage
82+ globalConfig = defaultConfig
13083
131- data , err := globalConfig .storage .Load ()
132- if err == nil {
133- var loadedMap map [string ]interface {}
134- _ = json .Unmarshal (data , & loadedMap )
84+ data , err := storage .Load ()
85+ if err != nil {
86+ globalLogger .Esg (err , "load config failed, using defaults" )
87+ return globalConfig
88+ }
13589
136- for key , val := range defaultMap {
137- if _ , ok := loadedMap [ key ]; ! ok {
138- loadedMap [ key ] = val
139- }
140- }
90+ var cacheMap map [ string ] interface {}
91+ if err := json . Unmarshal ( data , & cacheMap ); err != nil {
92+ globalLogger . Esg ( err , "parse cached config failed, using defaults" )
93+ return globalConfig
94+ }
14195
142- finalBytes , _ := json .Marshal (loadedMap )
143- _ = json .Unmarshal (finalBytes , & globalConfig )
96+ var defaultMap map [string ]interface {}
97+ defaultBytes , _ := json .Marshal (defaultConfig )
98+ _ = json .Unmarshal (defaultBytes , & defaultMap )
14499
145- } else {
146- globalLogger .Esg (err , "load config err" )
100+ for k , v := range cacheMap {
101+ if _ , ok := defaultMap [k ]; ok {
102+ defaultMap [k ] = v
147103 }
148104 }
105+
106+ finalBytes , err := json .Marshal (defaultMap )
107+ if err != nil {
108+ globalLogger .Esg (err , "marshal merged config failed" )
109+ return globalConfig
110+ }
111+
112+ if err := json .Unmarshal (finalBytes , globalConfig ); err != nil {
113+ globalLogger .Esg (err , "unmarshal merged config to struct failed" )
114+ }
115+
149116 return globalConfig
150117}
151118
119+ func getDefaultMimeMap () map [string ]MimeInfo {
120+ return map [string ]MimeInfo {
121+ "image/png" : {Type : "image" , Suffix : ".png" },
122+ "image/webp" : {Type : "image" , Suffix : ".webp" },
123+ "image/jpeg" : {Type : "image" , Suffix : ".jpeg" },
124+ "image/jpg" : {Type : "image" , Suffix : ".jpg" },
125+ "image/gif" : {Type : "image" , Suffix : ".gif" },
126+ "image/avif" : {Type : "image" , Suffix : ".avif" },
127+ "image/bmp" : {Type : "image" , Suffix : ".bmp" },
128+ "image/tiff" : {Type : "image" , Suffix : ".tiff" },
129+ "image/heic" : {Type : "image" , Suffix : ".heic" },
130+ "image/x-icon" : {Type : "image" , Suffix : ".ico" },
131+ "image/svg+xml" : {Type : "image" , Suffix : ".svg" },
132+ "image/vnd.adobe.photoshop" : {Type : "image" , Suffix : ".psd" },
133+ "image/jp2" : {Type : "image" , Suffix : ".jp2" },
134+ "image/jpeg2000" : {Type : "image" , Suffix : ".jp2" },
135+ "image/apng" : {Type : "image" , Suffix : ".apng" },
136+ "audio/mpeg" : {Type : "audio" , Suffix : ".mp3" },
137+ "audio/mp3" : {Type : "audio" , Suffix : ".mp3" },
138+ "audio/wav" : {Type : "audio" , Suffix : ".wav" },
139+ "audio/aiff" : {Type : "audio" , Suffix : ".aiff" },
140+ "audio/x-aiff" : {Type : "audio" , Suffix : ".aiff" },
141+ "audio/aac" : {Type : "audio" , Suffix : ".aac" },
142+ "audio/ogg" : {Type : "audio" , Suffix : ".ogg" },
143+ "audio/flac" : {Type : "audio" , Suffix : ".flac" },
144+ "audio/midi" : {Type : "audio" , Suffix : ".mid" },
145+ "audio/x-midi" : {Type : "audio" , Suffix : ".mid" },
146+ "audio/x-ms-wma" : {Type : "audio" , Suffix : ".wma" },
147+ "audio/opus" : {Type : "audio" , Suffix : ".opus" },
148+ "audio/webm" : {Type : "audio" , Suffix : ".webm" },
149+ "audio/mp4" : {Type : "audio" , Suffix : ".m4a" },
150+ "audio/amr" : {Type : "audio" , Suffix : ".amr" },
151+ "video/mp4" : {Type : "video" , Suffix : ".mp4" },
152+ "video/webm" : {Type : "video" , Suffix : ".webm" },
153+ "video/ogg" : {Type : "video" , Suffix : ".ogv" },
154+ "video/x-msvideo" : {Type : "video" , Suffix : ".avi" },
155+ "video/mpeg" : {Type : "video" , Suffix : ".mpeg" },
156+ "video/quicktime" : {Type : "video" , Suffix : ".mov" },
157+ "video/x-ms-wmv" : {Type : "video" , Suffix : ".wmv" },
158+ "video/3gpp" : {Type : "video" , Suffix : ".3gp" },
159+ "video/x-matroska" : {Type : "video" , Suffix : ".mkv" },
160+ "audio/video" : {Type : "live" , Suffix : ".flv" },
161+ "video/x-flv" : {Type : "live" , Suffix : ".flv" },
162+ "application/dash+xml" : {Type : "live" , Suffix : ".mpd" },
163+ "application/vnd.apple.mpegurl" : {Type : "m3u8" , Suffix : ".m3u8" },
164+ "application/x-mpegurl" : {Type : "m3u8" , Suffix : ".m3u8" },
165+ "application/x-mpeg" : {Type : "m3u8" , Suffix : ".m3u8" },
166+ "audio/x-mpegurl" : {Type : "m3u8" , Suffix : ".m3u8" },
167+ "application/pdf" : {Type : "pdf" , Suffix : ".pdf" },
168+ "application/vnd.ms-powerpoint" : {Type : "ppt" , Suffix : ".ppt" },
169+ "application/vnd.openxmlformats-officedocument.presentationml.presentation" : {Type : "ppt" , Suffix : ".pptx" },
170+ "application/vnd.ms-excel" : {Type : "xls" , Suffix : ".xls" },
171+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : {Type : "xls" , Suffix : ".xlsx" },
172+ "text/csv" : {Type : "xls" , Suffix : ".csv" },
173+ "application/msword" : {Type : "doc" , Suffix : ".doc" },
174+ "application/rtf" : {Type : "doc" , Suffix : ".rtf" },
175+ "text/rtf" : {Type : "doc" , Suffix : ".rtf" },
176+ "application/vnd.oasis.opendocument.text" : {Type : "doc" , Suffix : ".odt" },
177+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : {Type : "doc" , Suffix : ".docx" },
178+ "font/woff" : {Type : "font" , Suffix : ".woff" },
179+ "application/octet-stream" : {Type : "stream" , Suffix : "default" },
180+ }
181+ }
182+
183+ func getDefaultDownloadDir () string {
184+ usr , err := user .Current ()
185+ if err != nil {
186+ return ""
187+ }
188+
189+ homeDir := usr .HomeDir
190+ var downloadDir string
191+
192+ switch runtime .GOOS {
193+ case "windows" , "darwin" :
194+ downloadDir = filepath .Join (homeDir , "Downloads" )
195+ case "linux" :
196+ downloadDir = filepath .Join (homeDir , "Downloads" )
197+ if xdgDir := os .Getenv ("XDG_DOWNLOAD_DIR" ); xdgDir != "" {
198+ downloadDir = xdgDir
199+ }
200+ }
201+
202+ if stat , err := os .Stat (downloadDir ); err == nil && stat .IsDir () {
203+ return downloadDir
204+ }
205+
206+ return ""
207+ }
208+
152209func (c * Config ) setConfig (config Config ) {
153210 oldProxy := c .UpstreamProxy
154211 openProxy := c .OpenProxy
212+ oldRule := c .Rule
155213 c .Host = config .Host
156214 c .Port = config .Port
157215 c .Theme = config .Theme
@@ -166,12 +224,22 @@ func (c *Config) setConfig(config Config) {
166224 c .DownloadProxy = config .DownloadProxy
167225 c .AutoProxy = config .AutoProxy
168226 c .TaskNumber = config .TaskNumber
227+ c .DownNumber = config .DownNumber
169228 c .WxAction = config .WxAction
170229 c .UseHeaders = config .UseHeaders
230+ c .InsertTail = config .InsertTail
231+ c .Rule = config .Rule
171232 if oldProxy != c .UpstreamProxy || openProxy != c .OpenProxy {
172233 proxyOnce .setTransport ()
173234 }
174235
236+ if oldRule != c .Rule {
237+ err := ruleOnce .Load (c .Rule )
238+ if err != nil {
239+ globalLogger .Esg (err , "set rule failed" )
240+ }
241+ }
242+
175243 mimeMux .Lock ()
176244 c .MimeMap = config .MimeMap
177245 mimeMux .Unlock ()
@@ -212,14 +280,20 @@ func (c *Config) getConfig(key string) interface{} {
212280 return c .AutoProxy
213281 case "TaskNumber" :
214282 return c .TaskNumber
283+ case "DownNumber" :
284+ return c .DownNumber
215285 case "WxAction" :
216286 return c .WxAction
217287 case "UseHeaders" :
218288 return c .UseHeaders
289+ case "InsertTail" :
290+ return c .InsertTail
219291 case "MimeMap" :
220292 mimeMux .RLock ()
221293 defer mimeMux .RUnlock ()
222294 return c .MimeMap
295+ case "Rule" :
296+ return c .Rule
223297 default :
224298 return nil
225299 }
0 commit comments