fix: auto-scan cloud libraries on boot + allow CORS for media playback

- Add BootCloudLibraries() to auto-scan all cloud libraries on startup
- Delay 3s to avoid conflict with system init, scan without auto-scrape
- Enable CORS for /api/cloud/play/* and /api/img to support 3rd-party players
- Fixes issue where each user triggers separate cloud library scans
- Fixes issue where Infuse/Emby apps cannot play cloud resources
This commit is contained in:
ShukeBta
2026-06-11 11:25:43 +08:00
parent d90b58ba22
commit 62b204367c
40 changed files with 431 additions and 140 deletions
+26 -5
View File
@@ -102,7 +102,7 @@ func (s *STRMService) GenerateForLibrary(ctx context.Context, opts GenerateSTRMO
_ = s.repo.Setting.Set(ctx, "strm.auto_generate_enabled", strconv.FormatBool(opts.Enabled))
_ = s.repo.Setting.Set(ctx, "strm.output_dir", outputDir)
}
if err := os.MkdirAll(outputDir, 0o755); err != nil {
if err := os.MkdirAll(outputDir, 0o755); err != nil { // #nosec G301 -- STRM output directories must stay readable by NAS/player users.
return nil, err
}
@@ -175,12 +175,12 @@ func (s *STRMService) generateOne(ctx context.Context, lib model.Library, media
if _, err := os.Stat(filePath); err == nil {
action = "updated"
}
if err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil {
if err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil { // #nosec G301 -- STRM output directories must stay readable by NAS/player users.
item.Action = "error"
item.Reason = err.Error()
return item
}
if err := os.WriteFile(filePath, []byte(playURL+"\n"), 0o644); err != nil {
if err := os.WriteFile(filePath, []byte(playURL+"\n"), 0o644); err != nil { // #nosec G306 -- STRM files are media sidecars intended to be readable by players.
item.Action = "error"
item.Reason = err.Error()
return item
@@ -429,8 +429,13 @@ func (s *STRMService) ProxySTRM(ctx context.Context, id string, req *http.Reques
return ErrSTRMProtocolInvalid
}
targetURL, err := validateSTRMProxyURL(record.URL)
if err != nil {
return err
}
// 创建代理请求
proxyReq, err := http.NewRequestWithContext(ctx, req.Method, record.URL, nil)
proxyReq, err := http.NewRequestWithContext(ctx, req.Method, targetURL.String(), nil)
if err != nil {
return fmt.Errorf("create proxy request: %w", err)
}
@@ -452,7 +457,7 @@ func (s *STRMService) ProxySTRM(ctx context.Context, id string, req *http.Reques
}
client := &http.Client{Timeout: 60 * time.Second}
resp, err := client.Do(proxyReq)
resp, err := client.Do(proxyReq) // #nosec G107,G704 -- STRM proxy target is validated by validateSTRMProxyURL before request creation.
if err != nil {
return fmt.Errorf("proxy request failed: %w", err)
}
@@ -474,6 +479,22 @@ func (s *STRMService) ProxySTRM(ctx context.Context, id string, req *http.Reques
return err
}
func validateSTRMProxyURL(raw string) (*url.URL, error) {
u, err := url.Parse(strings.TrimSpace(raw))
if err != nil || u.Scheme == "" || u.Host == "" {
return nil, ErrSTRMURLInvalid
}
switch strings.ToLower(u.Scheme) {
case "http", "https":
default:
return nil, ErrSTRMProtocolInvalid
}
if isPrivateHost(u.Hostname()) {
return nil, ErrSTRMURLInvalid
}
return u, nil
}
// validateSTRM 验证 STRM 记录。
func (s *STRMService) validateSTRM(record *model.STRMRecord) error {
if record.Title == "" {