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"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
|
"embed"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@ -58,6 +59,9 @@ func setCoverModelStore(s *ModelStore) { coverModelStore = s }
|
|||||||
|
|
||||||
var errCoverNotSupported = errors.New("cover not supported")
|
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
|
// Covers: generated/covers/<category>.<ext> + info.json
|
||||||
// Routes:
|
// Routes:
|
||||||
@ -1501,6 +1505,99 @@ func serveLivePreviewJPGBytes(w http.ResponseWriter, b []byte) {
|
|||||||
_, _ = w.Write(b)
|
_, _ = 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) {
|
func servePreviewJPGAlias(w http.ResponseWriter, r *http.Request, id string) {
|
||||||
jobsMu.Lock()
|
jobsMu.Lock()
|
||||||
job := jobs[id]
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1649,15 +1746,15 @@ func recordPreviewWithBase(w http.ResponseWriter, r *http.Request, basePath stri
|
|||||||
jobsMu.Unlock()
|
jobsMu.Unlock()
|
||||||
|
|
||||||
if state == "private" {
|
if state == "private" {
|
||||||
servePreviewStatusSVG(w, "Private", http.StatusOK)
|
serveProviderFallbackOrStatus(w, r, job, job.Output, "Private", http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if state == "offline" {
|
if state == "offline" {
|
||||||
servePreviewStatusSVG(w, "Offline", http.StatusOK)
|
serveProviderFallbackOrStatus(w, r, job, job.Output, "Offline", http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
servePreviewStatusSVG(w, "Preview", http.StatusOK)
|
serveProviderFallbackOrStatus(w, r, job, job.Output, "Preview", http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1792,6 +1889,11 @@ func servePreviewForFinishedFile(w http.ResponseWriter, r *http.Request, id stri
|
|||||||
return
|
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)
|
genCtx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@ import {
|
|||||||
AdjustmentsHorizontalIcon,
|
AdjustmentsHorizontalIcon,
|
||||||
CheckCircleIcon,
|
CheckCircleIcon,
|
||||||
ExclamationTriangleIcon,
|
ExclamationTriangleIcon,
|
||||||
SparklesIcon,
|
ClockIcon,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
XMarkIcon,
|
XMarkIcon,
|
||||||
ArchiveBoxIcon,
|
ArchiveBoxIcon,
|
||||||
@ -1146,7 +1146,7 @@ export function renderFinishedPostworkBadge(
|
|||||||
{summary.state === 'running' ? (
|
{summary.state === 'running' ? (
|
||||||
<LoadingSpinner size="sm" className="shrink-0" srLabel={summary.label} />
|
<LoadingSpinner size="sm" className="shrink-0" srLabel={summary.label} />
|
||||||
) : summary.state === 'queued' ? (
|
) : summary.state === 'queued' ? (
|
||||||
<SparklesIcon className="size-3.5 shrink-0" />
|
<ClockIcon className="size-3.5 shrink-0" />
|
||||||
) : summary.state === 'done' ? (
|
) : summary.state === 'done' ? (
|
||||||
<CheckCircleIcon className="size-3.5 shrink-0" />
|
<CheckCircleIcon className="size-3.5 shrink-0" />
|
||||||
) : summary.state === 'error' ? (
|
) : summary.state === 'error' ? (
|
||||||
@ -1162,7 +1162,7 @@ export function renderFinishedPostworkBadge(
|
|||||||
{summary.state === 'running' ? (
|
{summary.state === 'running' ? (
|
||||||
<LoadingSpinner size="sm" className="shrink-0" srLabel={summary.label} />
|
<LoadingSpinner size="sm" className="shrink-0" srLabel={summary.label} />
|
||||||
) : summary.state === 'queued' ? (
|
) : summary.state === 'queued' ? (
|
||||||
<SparklesIcon className="size-3.5 shrink-0" />
|
<ClockIcon className="size-3.5 shrink-0" />
|
||||||
) : summary.state === 'done' ? (
|
) : summary.state === 'done' ? (
|
||||||
<CheckCircleIcon className="size-3.5 shrink-0" />
|
<CheckCircleIcon className="size-3.5 shrink-0" />
|
||||||
) : summary.state === 'error' ? (
|
) : summary.state === 'error' ? (
|
||||||
@ -1208,7 +1208,7 @@ export function renderFinishedPostworkBadge(
|
|||||||
</>
|
</>
|
||||||
) : step.state === 'queued' ? (
|
) : 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">
|
<span className="text-amber-700 dark:text-amber-200">
|
||||||
{typeof step.position === 'number' && step.position > 0
|
{typeof step.position === 'number' && step.position > 0
|
||||||
? `Wartet #${step.position}`
|
? `Wartet #${step.position}`
|
||||||
|
|||||||
@ -142,7 +142,7 @@ export default function HoverPopover({ children, content }: HoverPopoverProps) {
|
|||||||
createPortal(
|
createPortal(
|
||||||
<div
|
<div
|
||||||
ref={popoverRef}
|
ref={popoverRef}
|
||||||
className="fixed z-50"
|
className="fixed z-[120]"
|
||||||
style={{
|
style={{
|
||||||
left: pos?.left ?? -9999,
|
left: pos?.left ?? -9999,
|
||||||
top: pos?.top ?? -9999,
|
top: pos?.top ?? -9999,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user