add an unlink buttton to unlink timestamps in the segments table
Add progress bar for uploading
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
import sys
|
||||
|
||||
def patch_file():
|
||||
with open('frontend/src/App.tsx', 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 1. Add state variables
|
||||
old1 = ''' const [job, setJob] = useState<Job | null>(null);
|
||||
const [outputs, setOutputs] = useState<string[]>([]);
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);'''
|
||||
new1 = ''' const [job, setJob] = useState<Job | null>(null);
|
||||
const [outputs, setOutputs] = useState<string[]>([]);
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
const [uploadProgress, setUploadProgress] = useState(0);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
type SegmentRow = {
|
||||
kind: "intro" | "core" | "outro";
|
||||
label: string;
|
||||
start: number;
|
||||
end: number;
|
||||
startEditable: boolean;
|
||||
endEditable: boolean;
|
||||
coreIndex?: number;
|
||||
};
|
||||
|
||||
const [isLinked, setIsLinked] = useState(true);
|
||||
const [customSegmentRows, setCustomSegmentRows] = useState<SegmentRow[]>([]);
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);'''
|
||||
if old1 not in content: print('Failed old1')
|
||||
content = content.replace(old1, new1)
|
||||
|
||||
# 2. Remove duplicate SegmentRow type declaration
|
||||
old2 = ''' type SegmentRow = {
|
||||
kind: "intro" | "core" | "outro";
|
||||
label: string;
|
||||
start: number;
|
||||
end: number;
|
||||
startEditable: boolean;
|
||||
endEditable: boolean;
|
||||
coreIndex?: number;
|
||||
};
|
||||
|
||||
const introBoundary = useMemo(() => {'''
|
||||
new2 = ''' const introBoundary = useMemo(() => {'''
|
||||
if old2 not in content: print('Failed old2')
|
||||
content = content.replace(old2, new2)
|
||||
|
||||
# 3. Rename segmentRows to derivedSegmentRows
|
||||
old3 = ''' const segmentRows = useMemo(() => {
|
||||
if (!duration || !project || coreBoundaries.length < 2) return [] as SegmentRow[];'''
|
||||
new3 = ''' const derivedSegmentRows = useMemo(() => {
|
||||
if (!duration || !project || coreBoundaries.length < 2) return [] as SegmentRow[];'''
|
||||
if old3 not in content: print('Failed old3')
|
||||
content = content.replace(old3, new3)
|
||||
|
||||
# 4. Add segmentRows constant
|
||||
old4 = ''' });
|
||||
return rows;
|
||||
}, [duration, project, coreBoundaries, introBoundary, alignedOutroBoundary, lastFrameTime, frameStep, segments]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!segmentRows.length) {'''
|
||||
new4 = ''' });
|
||||
return rows;
|
||||
}, [duration, project, coreBoundaries, introBoundary, alignedOutroBoundary, lastFrameTime, frameStep, segments]);
|
||||
|
||||
const segmentRows = isLinked ? derivedSegmentRows : customSegmentRows;
|
||||
|
||||
useEffect(() => {
|
||||
if (!segmentRows.length) {'''
|
||||
if old4 not in content: print('Failed old4')
|
||||
content = content.replace(old4, new4)
|
||||
|
||||
# 5. handleUpload changes
|
||||
old5 = ''' const handleUpload = async () => {
|
||||
if (!project) {
|
||||
setError("Configure project settings before uploading.");
|
||||
setProjectDrawerOpen(true);
|
||||
return;
|
||||
}
|
||||
if (videoFiles.length === 0) return;
|
||||
setError(null);
|
||||
setIsUploading(true);
|
||||
|
||||
try {
|
||||
const uploadedItems = await uploadVideos(videoFiles);'''
|
||||
new5 = ''' const handleUpload = async () => {
|
||||
if (!project) {
|
||||
setError("Configure project settings before uploading.");
|
||||
setProjectDrawerOpen(true);
|
||||
return;
|
||||
}
|
||||
if (videoFiles.length === 0) return;
|
||||
setError(null);
|
||||
setIsUploading(true);
|
||||
setUploadProgress(0);
|
||||
|
||||
try {
|
||||
const uploadedItems = await uploadVideos(videoFiles, (index, total, progress) => {
|
||||
setUploadProgress((index + progress) / total);
|
||||
});'''
|
||||
if old5 not in content: print('Failed old5')
|
||||
content = content.replace(old5, new5)
|
||||
|
||||
# 6. handleUpload linked reset
|
||||
old6 = ''' setJob(null);
|
||||
setOutputPrefix(uploaded.filename.replace(/\.[^.]+$/, ""));
|
||||
}
|
||||
setVideoFiles([]);'''
|
||||
new6 = ''' setJob(null);
|
||||
setOutputPrefix(uploaded.filename.replace(/\.[^.]+$/, ""));
|
||||
setIsLinked(true);
|
||||
}
|
||||
setVideoFiles([]);'''
|
||||
if old6 not in content: print('Failed old6')
|
||||
content = content.replace(old6, new6)
|
||||
|
||||
# 7. handleSelectVideo reset
|
||||
old7 = ''' setOutputPrefix(item.filename.replace(/\.[^.]+$/, ""));
|
||||
setVideoFiles([]);
|
||||
setPreviewUrl("");
|
||||
try {'''
|
||||
new7 = ''' setOutputPrefix(item.filename.replace(/\.[^.]+$/, ""));
|
||||
setVideoFiles([]);
|
||||
setPreviewUrl("");
|
||||
setIsLinked(true);
|
||||
try {'''
|
||||
if old7 not in content: print('Failed old7')
|
||||
content = content.replace(old7, new7)
|
||||
|
||||
# 8. commitSegmentDraft bypass
|
||||
old8 = ''' const snapped = snapToFrame(parsed);
|
||||
let boundaryIndex = -1;
|
||||
let boundaryValue = snapped;'''
|
||||
new8 = ''' const snapped = snapToFrame(parsed);
|
||||
|
||||
if (!isLinked) {
|
||||
setCustomSegmentRows((prev) => {
|
||||
const next = [...prev];
|
||||
next[rowIndex] = { ...next[rowIndex], [field]: snapped };
|
||||
return next;
|
||||
});
|
||||
setPendingSegmentEditPersist(true);
|
||||
return;
|
||||
}
|
||||
|
||||
let boundaryIndex = -1;
|
||||
let boundaryValue = snapped;'''
|
||||
if old8 not in content: print('Failed old8')
|
||||
content = content.replace(old8, new8)
|
||||
|
||||
# 9. handleDeleteSegment bypass
|
||||
old9 = ''' const handleDeleteSegment = async (row: SegmentRow) => {
|
||||
setError(null);
|
||||
if (row.kind === "intro") {'''
|
||||
new9 = ''' const handleDeleteSegment = async (row: SegmentRow) => {
|
||||
setError(null);
|
||||
if (!isLinked) {
|
||||
setCustomSegmentRows((prev) => prev.filter((r) => r !== row));
|
||||
setPendingSegmentEditPersist(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (row.kind === "intro") {'''
|
||||
if old9 not in content: print('Failed old9')
|
||||
content = content.replace(old9, new9)
|
||||
|
||||
# 10. upload progress UI
|
||||
old10 = ''' <Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleUpload}
|
||||
disabled={videoFiles.length === 0 || isBusy || !project}
|
||||
>
|
||||
{isUploading ? "Uploading..." : "Upload to Backend"}
|
||||
</Button>
|
||||
<Button sx={{ ml: 1 }} onClick={() => setProjectDrawerOpen(true)}>
|
||||
Project settings
|
||||
</Button>
|
||||
</Box>
|
||||
<Box>
|
||||
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 1 }}>'''
|
||||
new10 = ''' <Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleUpload}
|
||||
disabled={videoFiles.length === 0 || isBusy || !project}
|
||||
>
|
||||
{isUploading ? "Uploading..." : "Upload to Backend"}
|
||||
</Button>
|
||||
<Button sx={{ ml: 1 }} onClick={() => setProjectDrawerOpen(true)}>
|
||||
Project settings
|
||||
</Button>
|
||||
</Box>
|
||||
{isUploading && (
|
||||
<Box sx={{ mt: 1 }}>
|
||||
<LinearProgress variant="determinate" value={uploadProgress * 100} />
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
|
||||
Uploading... {Math.round(uploadProgress * 100)}%
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<Box>
|
||||
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 1 }}>'''
|
||||
if old10 not in content: print('Failed old10')
|
||||
content = content.replace(old10, new10)
|
||||
|
||||
# 11. segments table unlink button
|
||||
old11 = ''' <Grid item xs={12}>
|
||||
<Stack direction={{ xs: "column", sm: "row" }} spacing={2} alignItems={{ sm: "center" }}>
|
||||
<Button variant="outlined" onClick={handleAddMarker} disabled={!duration}>
|
||||
Add marker at playhead
|
||||
</Button>
|
||||
<Button variant="outlined" onClick={handleSaveMarkers} disabled={!video || markers.length === 0}>
|
||||
Save markers
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>'''
|
||||
new11 = ''' <Grid item xs={12}>
|
||||
<Stack direction={{ xs: "column", sm: "row" }} spacing={2} alignItems={{ sm: "center" }}>
|
||||
<Button variant="outlined" onClick={handleAddMarker} disabled={!duration}>
|
||||
Add marker at playhead
|
||||
</Button>
|
||||
<Button variant="outlined" onClick={handleSaveMarkers} disabled={!video || markers.length === 0}>
|
||||
Save markers
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={isLinked ? <LinkOff /> : <Link />}
|
||||
onClick={() => {
|
||||
if (isLinked) {
|
||||
setCustomSegmentRows(derivedSegmentRows);
|
||||
}
|
||||
setIsLinked(!isLinked);
|
||||
}}
|
||||
disabled={!duration}
|
||||
>
|
||||
{isLinked ? "Unlink timestamps" : "Link timestamps"}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>'''
|
||||
if old11 not in content: print('Failed old11')
|
||||
content = content.replace(old11, new11)
|
||||
|
||||
with open('frontend/src/App.tsx', 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
patch_file()
|
||||
Reference in New Issue
Block a user