// Package config 负责读程序的配置文件(application.properties)。 // // 大白话理解:程序启动时先看看配置文件里写了啥,没写就用默认值。 // 配置文件就在程序旁边,叫 application.properties,用记事本就能改。 package config import ( "runtime" "strconv" "strings" "github.com/magiconair/properties" ) // Config 程序配置: // - Port:网页服务用哪个端口(默认 8080) // - Dsn:数据库文件叫什么(默认 media-info.db,放在程序目录下) // - AutoOpen:启动后要不要自动打开浏览器 // - FFmpegPath:ffmpeg 程序路径(用于把带 EXIF 旋转标记的图片转正);留空自动在 PATH 里找 // - RotateImages:是否处理图片 EXIF 旋转(默认 true);false 则完全不处理,按存储方向/尺寸归类 // - Workers:探测/MIME/移动阶段的并发 worker 数(默认 4;设 0 表示用满全部 CPU 核数) // - NSFWEnabled:是否开启色情图片识别(本地 python + NudeNet,默认 false) // - NSFWPython:python 可执行文件路径;留空自动在 PATH 里找 // - NSFWThreshold:判定阈值 0~1(默认 0.5) // - HentaiEnabled:是否开启二次元图片识别(本地 python + wd-tagger,默认 false) // - HentaiPython:python 可执行文件路径;留空自动在 PATH 里找 // - HentaiThreshold:判定阈值 0~1(默认 0.5) type Config struct { Port int Dsn string AutoOpen bool FFmpegPath string RotateImages bool Workers int NSFWEnabled bool NSFWPython string NSFWThreshold float64 HentaiEnabled bool HentaiPython string HentaiThreshold float64 } // appConfig 当前生效的配置(程序运行期间一直不变)。 var appConfig Config // GetAppConfig 返回当前生效的配置。 func GetAppConfig() Config { return appConfig } // LoadAppConfig 从 application.properties 读配置;文件不存在就用默认值。 func LoadAppConfig() { // 先给一套默认值 appConfig = Config{Port: 8080, Dsn: "media-info.db", AutoOpen: true, RotateImages: true, Workers: 4, NSFWThreshold: 0.5, HentaiThreshold: 0.5} p, err := properties.LoadFile("application.properties", properties.UTF8) if err != nil { return // 没有配置文件,就用默认值 } // 配置文件里写了就覆盖默认值 if port := p.GetInt("server.port", 0); port > 0 { appConfig.Port = port } if v, ok := p.Get("database.dsn"); ok && v != "" { appConfig.Dsn = v } appConfig.AutoOpen = p.GetBool("server.autoOpen", true) // ffmpeg 路径:留空表示自动在 PATH 里找 if v, ok := p.Get("ffmpeg.path"); ok && v != "" { appConfig.FFmpegPath = v } appConfig.RotateImages = p.GetBool("organize.rotateImages", true) // 并发 worker 数:只认非负整数,填了别的就保持默认 if v, ok := p.Get("organize.workers"); ok && strings.TrimSpace(v) != "" { if n, err := strconv.Atoi(strings.TrimSpace(v)); err == nil && n >= 0 { appConfig.Workers = n } } // 色情识别:默认关;python 路径留空自动在 PATH 找;阈值只认 0~1 之间的小数 appConfig.NSFWEnabled = p.GetBool("nsfw.enabled", false) if v, ok := p.Get("nsfw.python"); ok && v != "" { appConfig.NSFWPython = v } if v, ok := p.Get("nsfw.threshold"); ok && strings.TrimSpace(v) != "" { if t, err := strconv.ParseFloat(strings.TrimSpace(v), 64); err == nil && t > 0 && t < 1 { appConfig.NSFWThreshold = t } } // 二次元识别:默认关;python 路径留空自动在 PATH 找;阈值只认 0~1 之间的小数 appConfig.HentaiEnabled = p.GetBool("hentai.enabled", false) if v, ok := p.Get("hentai.python"); ok && v != "" { appConfig.HentaiPython = v } if v, ok := p.Get("hentai.threshold"); ok && strings.TrimSpace(v) != "" { if t, err := strconv.ParseFloat(strings.TrimSpace(v), 64); err == nil && t > 0 && t < 1 { appConfig.HentaiThreshold = t } } } // WorkerCount 返回探测/MIME/移动阶段使用的并发 worker 数: // - 配置里 organize.workers > 0 时用配置值(默认 4); // - 配置为 0 时用满全部 CPU 核数。 func WorkerCount() int { if appConfig.Workers > 0 { return appConfig.Workers } n := runtime.NumCPU() if n < 1 { return 1 } return n }