This commit is contained in:
Linrador 2026-03-28 12:41:17 +01:00
parent e19e735997
commit 2385778c80
5 changed files with 111 additions and 9 deletions

BIN
backend/assets/img/cb.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
backend/assets/img/mfc.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -6,6 +6,7 @@ import (
"bytes"
"context"
"crypto/sha1"
"embed"
"encoding/hex"
"encoding/json"
"errors"
@ -58,6 +59,9 @@ func setCoverModelStore(s *ModelStore) { coverModelStore = s }
var errCoverNotSupported = errors.New("cover not supported")
//go:embed assets/img/cb.jpg assets/img/mfc.jpg
var embeddedFallbackImages embed.FS
// ============================================================
// Covers: generated/covers/<category>.<ext> + info.json
// Routes:
@ -1501,6 +1505,99 @@ func serveLivePreviewJPGBytes(w http.ResponseWriter, b []byte) {
_, _ = w.Write(b)
}
func detectPreviewProvider(job *RecordJob, outPath string) string {
if job != nil {
sourceURL := strings.ToLower(strings.TrimSpace(job.SourceURL))
switch {
case strings.Contains(sourceURL, "chaturbate.com"):
return "chaturbate"
case strings.Contains(sourceURL, "myfreecams.com"):
return "mfc"
}
}
modelKey := strings.TrimSpace(inferModelFromThumbLike(outPath))
if modelKey == "" || coverModelStore == nil {
return ""
}
modelKey = strings.ToLower(modelKey)
models := coverModelStore.List()
for _, m := range models {
if strings.ToLower(strings.TrimSpace(m.ModelKey)) != modelKey {
continue
}
host := strings.ToLower(strings.TrimSpace(m.Host))
input := strings.ToLower(strings.TrimSpace(m.Input))
switch {
case strings.Contains(host, "chaturbate"), strings.Contains(input, "chaturbate.com"):
return "chaturbate"
case strings.Contains(host, "myfreecams"), strings.Contains(input, "myfreecams.com"):
return "mfc"
}
}
return ""
}
func fallbackPreviewImageAssetPathForProvider(provider string) (string, error) {
switch strings.ToLower(strings.TrimSpace(provider)) {
case "chaturbate":
return "assets/img/cb.jpg", nil
case "mfc":
return "assets/img/mfc.jpg", nil
default:
return "", fmt.Errorf("unknown provider")
}
}
func serveFallbackPreviewImage(w http.ResponseWriter, r *http.Request, job *RecordJob, outPath string) bool {
provider := detectPreviewProvider(job, outPath)
if provider == "" {
return false
}
assetPath, err := fallbackPreviewImageAssetPathForProvider(provider)
if err != nil {
return false
}
b, err := embeddedFallbackImages.ReadFile(assetPath)
if err != nil || len(b) == 0 {
return false
}
ext := strings.ToLower(filepath.Ext(assetPath))
switch ext {
case ".png":
w.Header().Set("Content-Type", "image/png")
default:
w.Header().Set("Content-Type", "image/jpeg")
}
w.Header().Set("Cache-Control", "public, max-age=60")
w.Header().Set("X-Content-Type-Options", "nosniff")
if r.Method == http.MethodHead {
w.WriteHeader(http.StatusOK)
return true
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write(b)
return true
}
func serveProviderFallbackOrStatus(w http.ResponseWriter, r *http.Request, job *RecordJob, outPath string, label string, status int) {
if serveFallbackPreviewImage(w, r, job, outPath) {
return
}
servePreviewStatusSVG(w, label, status)
}
func servePreviewJPGAlias(w http.ResponseWriter, r *http.Request, id string) {
jobsMu.Lock()
job := jobs[id]
@ -1531,7 +1628,7 @@ func servePreviewJPGAlias(w http.ResponseWriter, r *http.Request, id string) {
}
}
servePreviewStatusSVG(w, "Preview", http.StatusOK)
serveProviderFallbackOrStatus(w, r, job, job.Output, "Preview", http.StatusOK)
return
}
@ -1649,15 +1746,15 @@ func recordPreviewWithBase(w http.ResponseWriter, r *http.Request, basePath stri
jobsMu.Unlock()
if state == "private" {
servePreviewStatusSVG(w, "Private", http.StatusOK)
serveProviderFallbackOrStatus(w, r, job, job.Output, "Private", http.StatusOK)
return
}
if state == "offline" {
servePreviewStatusSVG(w, "Offline", http.StatusOK)
serveProviderFallbackOrStatus(w, r, job, job.Output, "Offline", http.StatusOK)
return
}
servePreviewStatusSVG(w, "Preview", http.StatusOK)
serveProviderFallbackOrStatus(w, r, job, job.Output, "Preview", http.StatusOK)
return
}
@ -1792,6 +1889,11 @@ func servePreviewForFinishedFile(w http.ResponseWriter, r *http.Request, id stri
return
}
// Wenn noch kein generiertes preview.jpg existiert, Provider-Fallbackbild liefern
if serveFallbackPreviewImage(w, r, nil, outPath) {
return
}
genCtx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
defer cancel()

View File

@ -28,7 +28,7 @@ import {
AdjustmentsHorizontalIcon,
CheckCircleIcon,
ExclamationTriangleIcon,
SparklesIcon,
ClockIcon,
CheckIcon,
XMarkIcon,
ArchiveBoxIcon,
@ -1146,7 +1146,7 @@ export function renderFinishedPostworkBadge(
{summary.state === 'running' ? (
<LoadingSpinner size="sm" className="shrink-0" srLabel={summary.label} />
) : summary.state === 'queued' ? (
<SparklesIcon className="size-3.5 shrink-0" />
<ClockIcon className="size-3.5 shrink-0" />
) : summary.state === 'done' ? (
<CheckCircleIcon className="size-3.5 shrink-0" />
) : summary.state === 'error' ? (
@ -1162,7 +1162,7 @@ export function renderFinishedPostworkBadge(
{summary.state === 'running' ? (
<LoadingSpinner size="sm" className="shrink-0" srLabel={summary.label} />
) : summary.state === 'queued' ? (
<SparklesIcon className="size-3.5 shrink-0" />
<ClockIcon className="size-3.5 shrink-0" />
) : summary.state === 'done' ? (
<CheckCircleIcon className="size-3.5 shrink-0" />
) : summary.state === 'error' ? (
@ -1208,7 +1208,7 @@ export function renderFinishedPostworkBadge(
</>
) : step.state === 'queued' ? (
<>
<SparklesIcon className="size-3.5 shrink-0 text-amber-600 dark:text-amber-300" />
<ClockIcon className="size-3.5 shrink-0 text-amber-600 dark:text-amber-300" />
<span className="text-amber-700 dark:text-amber-200">
{typeof step.position === 'number' && step.position > 0
? `Wartet #${step.position}`

View File

@ -142,7 +142,7 @@ export default function HoverPopover({ children, content }: HoverPopoverProps) {
createPortal(
<div
ref={popoverRef}
className="fixed z-50"
className="fixed z-[120]"
style={{
left: pos?.left ?? -9999,
top: pos?.top ?? -9999,