subsyt/main.go
Viktor Varland 1b340e25f3
Some checks are pending
build / build (push) Waiting to run
fix: generate metadata for misc videos
2025-09-30 22:02:10 +02:00

171 lines
3.8 KiB
Go

package main
import (
"context"
"errors"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"time"
"git.meatbag.se/varl/subsyt/internal/config"
"git.meatbag.se/varl/subsyt/internal/dl"
"git.meatbag.se/varl/subsyt/internal/format"
"git.meatbag.se/varl/subsyt/internal/metadata"
"git.meatbag.se/varl/subsyt/internal/scheduler"
"git.meatbag.se/varl/subsyt/internal/server"
)
func run(cfg config.Config) {
provider := cfg.Provider["youtube"]
if err := dl.UpgradeYtDlp(provider.Cmd); err != nil {
log.Fatalf("failed to ensure yt-dlp is up to date: %v", err)
}
opml, err := format.OpmlLoad(provider.Opml_file)
if err != nil {
panic(err)
}
for _, outlines := range opml.Body.Outline {
log.Printf("Archiving videos from OPML: %s\n", outlines.Title)
for _, outline := range outlines.Outlines {
rssData, err := dl.RssDownloader(outline.XmlUrl)
if err != nil {
log.Printf("Failed to download RSS for %s: %v", outline.Title, err)
continue
}
feed, err := format.RssLoad(rssData)
if err != nil {
log.Printf("Failed to parse RSS for %s: %v", feed.Title, err)
continue
}
dl.Youtube(dl.Download{
Url: feed.Author.Uri,
OutDir: filepath.Join(cfg.Out_dir, outline.Title),
DryRun: cfg.Dry_run,
Metadata: true,
}, provider)
log.Printf("Downloaded RSS feed for %s with %d entries", feed.Title, len(feed.Entries))
for _, entry := range feed.Entries {
url := fmt.Sprintf("%s/watch?v=%s", provider.Url, entry.VideoId)
log.Printf("Entry: %#v", entry)
dl.Youtube(dl.Download{
Url: url,
OutDir: filepath.Join(cfg.Out_dir, feed.Title),
DryRun: cfg.Dry_run,
Metadata: false,
}, provider)
}
metadata.Generate(cfg.Out_dir, feed.Title, cfg.Dry_run)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
configPtr := flag.String("config", "", "path to config file")
flag.Parse()
configEnv := os.Getenv("CONFIG")
var configPath string
if *configPtr != "" {
configPath = *configPtr
} else if configEnv != "" {
configPath = configEnv
} else {
configPath = "./config.json"
}
log.Println("resolved config file", configPath)
cfg, err := config.Load(configPath)
if err != nil {
panic(err)
}
provider, ok := cfg.Provider["youtube"]
if !ok {
log.Fatal("youtube provider configuration missing")
}
if cfg.Http_api.Enable {
if err := setupAPIServer(ctx, cfg, provider); err != nil {
log.Fatalf("failed to start http api: %v", err)
}
}
if cfg.Daemon {
log.Println("running with scheduler")
s := scheduler.Scheduler{}
s.Start(run, cfg)
} else {
log.Println("running standalone")
run(cfg)
}
}
func setupAPIServer(ctx context.Context, cfg config.Config, provider config.Provider) error {
queue, err := server.NewQueue(cfg.Http_api.Queue_file)
if err != nil {
return fmt.Errorf("load queue: %w", err)
}
go queueWorker(ctx, queue, cfg, provider)
srv := server.NewServer(cfg.Http_api, cfg.Out_dir, queue)
go func() {
if err := srv.Start(ctx); err != nil {
log.Printf("http api server stopped with error: %v", err)
}
}()
return nil
}
func queueWorker(ctx context.Context, q *server.Queue, cfg config.Config, provider config.Provider) {
for {
item, err := q.Next(ctx)
if err != nil {
if errors.Is(err, context.Canceled) {
return
}
log.Printf("queue wait error: %v", err)
select {
case <-time.After(5 * time.Second):
case <-ctx.Done():
return
}
continue
}
outPath := filepath.Join(cfg.Out_dir, "_misc")
dl.Youtube(dl.Download{
Url: item.Request.URL,
OutDir: outPath,
DryRun: cfg.Dry_run,
Metadata: false,
}, provider)
metadata.Generate(cfg.Out_dir, "_misc", cfg.Dry_run)
if err := q.MarkDone(item.ID); err != nil {
log.Printf("queue mark done failed: %v", err)
}
}
}