Revert "feat: 前端预览资源切换到 CDN 域名 airflow-play.airlabs.art"
This reverts commit bc47bd09c4562ad48398fa2146921dfbcee82ac2.
This commit is contained in:
parent
bc47bd09c4
commit
00eb2e62d8
@ -4,7 +4,7 @@ import type { GenerationTask } from '../types';
|
|||||||
import { useGenerationStore } from '../store/generation';
|
import { useGenerationStore } from '../store/generation';
|
||||||
import { showToast } from './Toast';
|
import { showToast } from './Toast';
|
||||||
import { ConfirmModal } from './ConfirmModal';
|
import { ConfirmModal } from './ConfirmModal';
|
||||||
import { tosThumb, rewriteTosUrl } from '../lib/api';
|
import { tosThumb } from '../lib/api';
|
||||||
import styles from './GenerationCard.module.css';
|
import styles from './GenerationCard.module.css';
|
||||||
|
|
||||||
const EditIcon = () => (
|
const EditIcon = () => (
|
||||||
@ -299,7 +299,7 @@ export function GenerationCard({ task, onOpenDetail }: Props) {
|
|||||||
onMouseLeave={() => setRefPreview(null)}
|
onMouseLeave={() => setRefPreview(null)}
|
||||||
>
|
>
|
||||||
{ref.type === 'video' ? (
|
{ref.type === 'video' ? (
|
||||||
<video src={rewriteTosUrl(ref.previewUrl)} className={styles.refMedia} muted />
|
<video src={ref.previewUrl} className={styles.refMedia} muted />
|
||||||
) : ref.type === 'audio' ? (
|
) : ref.type === 'audio' ? (
|
||||||
<div className={styles.audioThumb}>
|
<div className={styles.audioThumb}>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
|
||||||
@ -422,7 +422,7 @@ export function GenerationCard({ task, onOpenDetail }: Props) {
|
|||||||
{refPreview && createPortal(
|
{refPreview && createPortal(
|
||||||
<div className={styles.mentionPreview} style={{ top: refPreview.top, left: refPreview.left }}>
|
<div className={styles.mentionPreview} style={{ top: refPreview.top, left: refPreview.left }}>
|
||||||
{refPreview.type === 'video' ? (
|
{refPreview.type === 'video' ? (
|
||||||
<video src={rewriteTosUrl(refPreview.url)} className={styles.mentionPreviewImg} autoPlay loop muted playsInline />
|
<video src={refPreview.url} className={styles.mentionPreviewImg} autoPlay loop muted playsInline />
|
||||||
) : (
|
) : (
|
||||||
<img src={tosThumb(refPreview.url, 300)} alt={refPreview.label} className={styles.mentionPreviewImg} />
|
<img src={tosThumb(refPreview.url, 300)} alt={refPreview.label} className={styles.mentionPreviewImg} />
|
||||||
)}
|
)}
|
||||||
@ -460,7 +460,7 @@ export function GenerationCard({ task, onOpenDetail }: Props) {
|
|||||||
>
|
>
|
||||||
<video
|
<video
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
src={rewriteTosUrl(task.resultUrl)}
|
src={task.resultUrl}
|
||||||
className={styles.resultMedia}
|
className={styles.resultMedia}
|
||||||
loop
|
loop
|
||||||
preload="metadata"
|
preload="metadata"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useRef, useEffect, useCallback, useState } from 'react';
|
import { useRef, useEffect, useCallback, useState } from 'react';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import { useInputBarStore } from '../store/inputBar';
|
import { useInputBarStore } from '../store/inputBar';
|
||||||
import { assetsApi, tosThumb, rewriteTosUrl } from '../lib/api';
|
import { assetsApi, tosThumb } from '../lib/api';
|
||||||
import type { UploadedFile, AssetSearchResult } from '../types';
|
import type { UploadedFile, AssetSearchResult } from '../types';
|
||||||
import { parseAssetMentionsFromDOM } from '../lib/assetMentions';
|
import { parseAssetMentionsFromDOM } from '../lib/assetMentions';
|
||||||
import { showToast } from './Toast';
|
import { showToast } from './Toast';
|
||||||
@ -694,7 +694,7 @@ export function PromptInput() {
|
|||||||
>
|
>
|
||||||
<div className={styles.mentionThumb}>
|
<div className={styles.mentionThumb}>
|
||||||
{ref.type === 'video' ? (
|
{ref.type === 'video' ? (
|
||||||
<video src={rewriteTosUrl(ref.previewUrl)} muted className={styles.thumbMedia} />
|
<video src={ref.previewUrl} muted className={styles.thumbMedia} />
|
||||||
) : ref.type === 'audio' ? (
|
) : ref.type === 'audio' ? (
|
||||||
<span style={{ fontSize: 16 }}>{'\u266B'}</span>
|
<span style={{ fontSize: 16 }}>{'\u266B'}</span>
|
||||||
) : (
|
) : (
|
||||||
@ -752,7 +752,7 @@ export function PromptInput() {
|
|||||||
>
|
>
|
||||||
{hoverRef.type === 'video' ? (
|
{hoverRef.type === 'video' ? (
|
||||||
<video
|
<video
|
||||||
src={rewriteTosUrl(hoverRef.previewUrl)}
|
src={hoverRef.previewUrl}
|
||||||
autoPlay
|
autoPlay
|
||||||
loop
|
loop
|
||||||
muted
|
muted
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useRef, useState } from 'react';
|
|||||||
import { useInputBarStore } from '../store/inputBar';
|
import { useInputBarStore } from '../store/inputBar';
|
||||||
import { showToast } from './Toast';
|
import { showToast } from './Toast';
|
||||||
import { ImageLightbox } from './ImageLightbox';
|
import { ImageLightbox } from './ImageLightbox';
|
||||||
import { tosThumb, rewriteTosUrl } from '../lib/api';
|
import { tosThumb } from '../lib/api';
|
||||||
import styles from './UniversalUpload.module.css';
|
import styles from './UniversalUpload.module.css';
|
||||||
|
|
||||||
const Spinner = () => (
|
const Spinner = () => (
|
||||||
@ -143,7 +143,7 @@ export function UniversalUpload() {
|
|||||||
>
|
>
|
||||||
<div className={styles.thumbInner}>
|
<div className={styles.thumbInner}>
|
||||||
{ref.type === 'video' ? (
|
{ref.type === 'video' ? (
|
||||||
<video src={rewriteTosUrl(ref.previewUrl)} className={styles.thumbMedia} muted />
|
<video src={ref.previewUrl} className={styles.thumbMedia} muted />
|
||||||
) : ref.type === 'audio' ? (
|
) : ref.type === 'audio' ? (
|
||||||
<div className={styles.audioPlaceholder}>
|
<div className={styles.audioPlaceholder}>
|
||||||
<AudioIcon />
|
<AudioIcon />
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { ConfirmModal } from './ConfirmModal';
|
|||||||
import { ImageLightbox } from './ImageLightbox';
|
import { ImageLightbox } from './ImageLightbox';
|
||||||
import { useInputBarStore } from '../store/inputBar';
|
import { useInputBarStore } from '../store/inputBar';
|
||||||
import { renderPromptWithMentions } from './GenerationCard';
|
import { renderPromptWithMentions } from './GenerationCard';
|
||||||
import { tosThumb, rewriteTosUrl } from '../lib/api';
|
import { tosThumb } from '../lib/api';
|
||||||
import styles from './VideoDetailModal.module.css';
|
import styles from './VideoDetailModal.module.css';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -301,7 +301,7 @@ export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDele
|
|||||||
>
|
>
|
||||||
<video
|
<video
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
src={rewriteTosUrl(task.resultUrl)}
|
src={task.resultUrl}
|
||||||
className={styles.video}
|
className={styles.video}
|
||||||
onTimeUpdate={handleTimeUpdate}
|
onTimeUpdate={handleTimeUpdate}
|
||||||
onLoadedMetadata={handleLoadedMetadata}
|
onLoadedMetadata={handleLoadedMetadata}
|
||||||
@ -486,7 +486,7 @@ export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDele
|
|||||||
<div key={ref.id} className={styles.refItem}>
|
<div key={ref.id} className={styles.refItem}>
|
||||||
<div style={{ position: 'relative', width: 56, height: 56 }}>
|
<div style={{ position: 'relative', width: 56, height: 56 }}>
|
||||||
{ref.type === 'video' ? (
|
{ref.type === 'video' ? (
|
||||||
<video src={rewriteTosUrl(ref.previewUrl)} className={styles.refImg} muted style={{ cursor: 'pointer' }} onClick={() => ref.previewUrl && setRefMediaPreview({ url: ref.previewUrl, type: 'video' })} />
|
<video src={ref.previewUrl} className={styles.refImg} muted style={{ cursor: 'pointer' }} onClick={() => ref.previewUrl && setRefMediaPreview({ url: ref.previewUrl, type: 'video' })} />
|
||||||
) : ref.type === 'audio' ? (
|
) : ref.type === 'audio' ? (
|
||||||
<div className={styles.refAudioPlaceholder} style={{ cursor: 'pointer' }} onClick={() => ref.previewUrl && setRefMediaPreview({ url: ref.previewUrl, type: 'audio' })}>
|
<div className={styles.refAudioPlaceholder} style={{ cursor: 'pointer' }} onClick={() => ref.previewUrl && setRefMediaPreview({ url: ref.previewUrl, type: 'audio' })}>
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
|
||||||
@ -569,11 +569,11 @@ export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDele
|
|||||||
<div style={{ position: 'relative', background: '#111118', borderRadius: 12, padding: 24, border: '1px solid #2a2a38' }} onClick={(e) => e.stopPropagation()}>
|
<div style={{ position: 'relative', background: '#111118', borderRadius: 12, padding: 24, border: '1px solid #2a2a38' }} onClick={(e) => e.stopPropagation()}>
|
||||||
<button style={{ position: 'absolute', top: 8, right: 12, background: 'none', border: 'none', color: '#888', fontSize: 16, cursor: 'pointer' }} onClick={() => setRefMediaPreview(null)}>✕</button>
|
<button style={{ position: 'absolute', top: 8, right: 12, background: 'none', border: 'none', color: '#888', fontSize: 16, cursor: 'pointer' }} onClick={() => setRefMediaPreview(null)}>✕</button>
|
||||||
{refMediaPreview.type === 'video' ? (
|
{refMediaPreview.type === 'video' ? (
|
||||||
<video src={rewriteTosUrl(refMediaPreview.url)} controls autoPlay style={{ maxWidth: '80vw', maxHeight: '70vh', borderRadius: 8 }} />
|
<video src={refMediaPreview.url} controls autoPlay style={{ maxWidth: '80vw', maxHeight: '70vh', borderRadius: 8 }} />
|
||||||
) : (
|
) : (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '20px 40px', color: '#888' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '20px 40px', color: '#888' }}>
|
||||||
<div style={{ fontSize: 48, marginBottom: 16 }}>♫</div>
|
<div style={{ fontSize: 48, marginBottom: 16 }}>♫</div>
|
||||||
<audio src={rewriteTosUrl(refMediaPreview.url)} controls autoPlay style={{ width: 320 }} />
|
<audio src={refMediaPreview.url} controls autoPlay style={{ width: 320 }} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -434,20 +434,6 @@ export const assetsApi = {
|
|||||||
api.get<{ id: number; status: string; url: string; error_message: string }>(`/assets/${id}/status`),
|
api.get<{ id: number; status: string; url: string; error_message: string }>(`/assets/${id}/status`),
|
||||||
};
|
};
|
||||||
|
|
||||||
const TOS_ORIGIN = 'https://airdrama-media.tos-cn-beijing.volces.com';
|
|
||||||
const PREVIEW_ORIGIN = 'https://airflow-play.airlabs.art';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 把 TOS 直连域名换成 CDN 预览域名,仅用于 <video>/<audio>/<img> 等浏览器拉流场景。
|
|
||||||
* 下载(fetch / a.href download)继续使用原 TOS 域名以避免 CDN CORS 配置依赖。
|
|
||||||
* 非我们桶 / blob: / 已是预览域名 → 原样返回。
|
|
||||||
*/
|
|
||||||
export function rewriteTosUrl(url: string | undefined): string {
|
|
||||||
if (!url) return '';
|
|
||||||
if (!url.startsWith(TOS_ORIGIN)) return url;
|
|
||||||
return PREVIEW_ORIGIN + url.slice(TOS_ORIGIN.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append TOS image resize parameter to reduce loading size.
|
* Append TOS image resize parameter to reduce loading size.
|
||||||
* Only applies to TOS image URLs (volces.com with image extensions).
|
* Only applies to TOS image URLs (volces.com with image extensions).
|
||||||
@ -457,9 +443,8 @@ export function tosThumb(url: string | undefined, height: number): string {
|
|||||||
// 只对我们自己的 TOS 桶生效(airdrama-media),不处理火山内部桶(ark-media-asset 等)
|
// 只对我们自己的 TOS 桶生效(airdrama-media),不处理火山内部桶(ark-media-asset 等)
|
||||||
if (!url.includes('airdrama-media')) return url;
|
if (!url.includes('airdrama-media')) return url;
|
||||||
if (!/\.(png|jpg|jpeg|webp|gif)/i.test(url)) return url;
|
if (!/\.(png|jpg|jpeg|webp|gif)/i.test(url)) return url;
|
||||||
const rewritten = rewriteTosUrl(url);
|
const sep = url.includes('?') ? '&' : '?';
|
||||||
const sep = rewritten.includes('?') ? '&' : '?';
|
return `${url}${sep}x-tos-process=image/resize,h_${height}`;
|
||||||
return `${rewritten}${sep}x-tos-process=image/resize,h_${height}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default api;
|
export default api;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState, useRef, useCallback } from 'react';
|
import { useEffect, useState, useRef, useCallback } from 'react';
|
||||||
import { adminApi, rewriteTosUrl } from '../lib/api';
|
import { adminApi } from '../lib/api';
|
||||||
import { VideoDetailModal } from '../components/VideoDetailModal';
|
import { VideoDetailModal } from '../components/VideoDetailModal';
|
||||||
import type { AssetTeamSummary, AssetMemberSummary, AssetVideo, GenerationTask } from '../types';
|
import type { AssetTeamSummary, AssetMemberSummary, AssetVideo, GenerationTask } from '../types';
|
||||||
import styles from './AdminAssetsPage.module.css';
|
import styles from './AdminAssetsPage.module.css';
|
||||||
@ -21,7 +21,7 @@ function VideoThumbnail({ video, onClick }: { video: AssetVideo; onClick: () =>
|
|||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{video.result_url ? (
|
{video.result_url ? (
|
||||||
<video ref={videoRef} src={rewriteTosUrl(video.result_url)} className={styles.thumbVideo} muted loop preload="metadata" />
|
<video ref={videoRef} src={video.result_url} className={styles.thumbVideo} muted loop preload="metadata" />
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.thumbPlaceholder} />
|
<div className={styles.thumbPlaceholder} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { useEffect, useState, useRef, useMemo, useCallback } from 'react';
|
|||||||
import { Sidebar } from '../components/Sidebar';
|
import { Sidebar } from '../components/Sidebar';
|
||||||
import { VideoDetailModal } from '../components/VideoDetailModal';
|
import { VideoDetailModal } from '../components/VideoDetailModal';
|
||||||
import { useGenerationStore } from '../store/generation';
|
import { useGenerationStore } from '../store/generation';
|
||||||
import { rewriteTosUrl } from '../lib/api';
|
|
||||||
import { ConfirmModal } from '../components/ConfirmModal';
|
import { ConfirmModal } from '../components/ConfirmModal';
|
||||||
import type { GenerationTask } from '../types';
|
import type { GenerationTask } from '../types';
|
||||||
import styles from './AssetsPage.module.css';
|
import styles from './AssetsPage.module.css';
|
||||||
@ -71,7 +70,7 @@ function VideoThumbnail({
|
|||||||
{task.resultUrl ? (
|
{task.resultUrl ? (
|
||||||
<video
|
<video
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
src={rewriteTosUrl(task.resultUrl)}
|
src={task.resultUrl}
|
||||||
className={styles.thumbVideo}
|
className={styles.thumbVideo}
|
||||||
muted
|
muted
|
||||||
loop
|
loop
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState, useRef, useCallback } from 'react';
|
import { useEffect, useState, useRef, useCallback } from 'react';
|
||||||
import { teamApi, rewriteTosUrl } from '../lib/api';
|
import { teamApi } from '../lib/api';
|
||||||
import { VideoDetailModal } from '../components/VideoDetailModal';
|
import { VideoDetailModal } from '../components/VideoDetailModal';
|
||||||
import type { AssetMemberSummary, AssetVideo, GenerationTask } from '../types';
|
import type { AssetMemberSummary, AssetVideo, GenerationTask } from '../types';
|
||||||
import styles from './AdminAssetsPage.module.css';
|
import styles from './AdminAssetsPage.module.css';
|
||||||
@ -21,7 +21,7 @@ function VideoThumbnail({ video, onClick }: { video: AssetVideo; onClick: () =>
|
|||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{video.result_url ? (
|
{video.result_url ? (
|
||||||
<video ref={videoRef} src={rewriteTosUrl(video.result_url)} className={styles.thumbVideo} muted loop preload="metadata" />
|
<video ref={videoRef} src={video.result_url} className={styles.thumbVideo} muted loop preload="metadata" />
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.thumbPlaceholder} />
|
<div className={styles.thumbPlaceholder} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user