Files
BiliDown/cmd/bilidown/main.go
T
yw1573 dd8eaef97d refactor: restructure project to standard Go layout
- Reorganize code into cmd/bilidown and internal/ packages
- Rename client/ to web/ for frontend source
- Remove systray dependency for headless web service
- Embed static files into binary using go:embed
- Update import paths to use internal/ prefix
- Update .gitignore with common patterns

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 17:21:49 +08:00

131 lines
3.4 KiB
Go

package main
import (
"database/sql"
"embed"
"fmt"
"io/fs"
"log"
"net/http"
"os"
"bilidown/internal/router"
"bilidown/internal/util"
_ "modernc.org/sqlite"
)
//go:embed all:build/static
var staticFiles embed.FS
const (
HTTP_PORT = 8098 // HTTP 服务器端口
HTTP_HOST = "" // HTTP 服务器主机
VERSION = "v2.1.1" // 软件版本号
)
var urlLocal = fmt.Sprintf("http://127.0.0.1:%d", HTTP_PORT)
func main() {
checkFFmpeg()
mustInitTables()
mustRunServer()
fmt.Printf("Bilidown %s server running at %s\n", VERSION, urlLocal)
select {} // 保持运行
}
// checkFFmpeg 检测 ffmpeg 的安装情况
func checkFFmpeg() {
if _, err := util.GetFFmpegPath(); err != nil {
fmt.Println("FFmpeg is missing. Install it from https://www.ffmpeg.org/download.html, then restart.")
os.Exit(1)
}
}
// mustRunServer 配置和启动 HTTP 服务器
func mustRunServer() {
// 从嵌入的文件系统中获取静态文件
staticFS, err := fs.Sub(staticFiles, "build/static")
if err != nil {
log.Fatal("Failed to load static files:", err)
}
// 前端静态文件
http.Handle("/", http.FileServer(http.FS(staticFS)))
// 后端 API 接口
http.Handle("/api/", http.StripPrefix("/api", router.API()))
// 启动 HTTP 服务器
go func() {
err := http.ListenAndServe(fmt.Sprintf("%s:%d", HTTP_HOST, HTTP_PORT), nil)
if err != nil {
log.Fatal("http.ListenAndServe:", err)
}
}()
}
// mustInitTables 初始化数据表
func mustInitTables() {
db := util.MustGetDB()
defer db.Close()
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS "field" (
"name" TEXT PRIMARY KEY NOT NULL,
"value" TEXT
)`); err != nil {
log.Fatalln("create table field:", err)
}
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS "log" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"content" TEXT NOT NULL,
"create_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP
)`); err != nil {
log.Fatalln("create table log:", err)
}
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS "task" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"bvid" text NOT NULL,
"cid" integer NOT NULL,
"format" integer NOT NULL,
"title" text NOT NULL,
"owner" text NOT NULL,
"cover" text NOT NULL,
"status" text NOT NULL,
"folder" text NOT NULL,
"duration" integer NOT NULL,
"download_type" text NOT NULL DEFAULT 'merge',
"create_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP
)`); err != nil {
log.Fatalln("create table task:", err)
}
if _, err := util.GetCurrentFolder(db); err != nil {
log.Fatalln("util.GetCurrentFolder:", err)
}
if err := initHistoryTask(db); err != nil {
log.Fatalln("initHistoryTask:", err)
}
// 添加可能缺失的列(用于数据库迁移)
if err := addMissingColumns(db); err != nil {
log.Fatalln("addMissingColumns:", err)
}
}
// addMissingColumns 添加可能缺失的列(用于数据库迁移)
func addMissingColumns(db *sql.DB) error {
util.SqliteLock.Lock()
_, _ = db.Exec(`ALTER TABLE "task" ADD COLUMN "download_type" TEXT DEFAULT 'merge'`)
_, _ = db.Exec(`UPDATE "task" SET "download_type" = 'merge' WHERE "download_type" IS NULL`)
util.SqliteLock.Unlock()
return nil
}
// initHistoryTask 将上一次程序运行时未完成的任务状态变为 error
func initHistoryTask(db *sql.DB) error {
util.SqliteLock.Lock()
_, err := db.Exec(`UPDATE "task" SET "status" = 'error' WHERE "status" IN ('waiting', 'running')`)
util.SqliteLock.Unlock()
return err
}