updated
This commit is contained in:
parent
e19e735997
commit
2385778c80
BIN
backend/assets/img/cb.jpg
Normal file
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
BIN
backend/assets/img/mfc.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
@ -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()
|
||||
|
||||
|
||||
@ -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}`
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user