From 7a21f53f0ef4ac71dfbaae62becc3d4927d9fd74 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sun, 6 Jul 2025 21:24:06 +0100 Subject: [PATCH] chore: fix line length + rename video --- ...ontentGenerator.tsx => VideoGenerator.tsx} | 38 +++++++++---------- cms/schemaTypes/ama.ts | 4 +- ...on-config.ts => video-animation-config.ts} | 0 ...animations.ts => video-gsap-animations.ts} | 10 ++--- ...ok-video-recorder.ts => video-recorder.ts} | 23 +++++------ 5 files changed, 35 insertions(+), 40 deletions(-) rename cms/components/{TikTokContentGenerator.tsx => VideoGenerator.tsx} (96%) rename cms/utils/{tiktok-animation-config.ts => video-animation-config.ts} (100%) rename cms/utils/{tiktok-gsap-animations.ts => video-gsap-animations.ts} (95%) rename cms/utils/{tiktok-video-recorder.ts => video-recorder.ts} (94%) diff --git a/cms/components/TikTokContentGenerator.tsx b/cms/components/VideoGenerator.tsx similarity index 96% rename from cms/components/TikTokContentGenerator.tsx rename to cms/components/VideoGenerator.tsx index be515e89..73191c58 100644 --- a/cms/components/TikTokContentGenerator.tsx +++ b/cms/components/VideoGenerator.tsx @@ -2,10 +2,10 @@ import React, { useState, useCallback, useRef, useEffect } from 'react' import { Stack, Text, Card, Button, Box, Badge, Flex, Switch } from '@sanity/ui' import type { PortableTextTextBlock } from 'sanity' import { set, useFormValue, useClient } from 'sanity' -import { createTikTokVideo } from '../utils/tiktok-video-recorder' -import { calculateVideoDuration } from '../utils/tiktok-animation-config' -import type { TikTokGSAPTimeline } from '../utils/tiktok-gsap-animations' -import { createPreviewAnimation, stopAnimations } from '../utils/tiktok-gsap-animations' +import { createVideo } from '../utils/video-recorder' +import { calculateVideoDuration } from '../utils/video-animation-config' +import type { GSAPTimeline } from '../utils/video-gsap-animations' +import { createPreviewAnimation, stopAnimations } from '../utils/video-gsap-animations' import { getHumanRelativeDate } from '../utils/date-formatting' import { fetchAudioTracks, @@ -15,12 +15,12 @@ import { type AudioTrack, } from '../utils/audio-track-selector' -interface TikTokContentGeneratorProps { +interface VideoGeneratorProps { onChange: (event: any) => void renderDefault: (props: any) => React.ReactElement } -export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { +export function VideoContentGenerator (props: VideoGeneratorProps) { const { onChange, renderDefault } = props const [isGenerating, setIsGenerating] = useState(false) const [generationProgress, setGenerationProgress] = useState(0) @@ -33,7 +33,7 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { const videoContainerRef = useRef(null) const videoRef = useRef(null) const questionContentRef = useRef(null) - const gsapAnimationRef = useRef(null) + const gsapAnimationRef = useRef(null) const dateOfDocument = useFormValue(['_createdAt']) as string const relativeDate = getHumanRelativeDate(dateOfDocument) @@ -247,7 +247,7 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { const questionLines = [formattedQuestion] const videoAnswerLines = answerLines.map(line => line.text) - const videoBlob = await createTikTokVideo({ + const videoBlob = await createVideo({ element: videoContainerRef.current, question, questionLines, @@ -274,7 +274,7 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { setVideoUrl(url) const asset = await client.assets.upload('file', videoBlob, { - filename: `tiktok-video-${Date.now()}.webm`, + filename: `video-${Date.now()}.webm`, }) onChange(set({ @@ -287,7 +287,7 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { } } catch (error) { - console.error('Error generating TikTok video:', error) + console.error('Error generating video:', error) } finally { setIsGenerating(false) @@ -303,8 +303,7 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { } }, [videoUrl]) - // Generate TikTok metadata - const generateTikTokMetadata = useCallback((question: string, answer: string): { title: string, description: string, hashtags: string[] } => { + const generateVideoMetadata = useCallback((question: string, answer: string): { title: string, description: string, hashtags: string[] } => { // Generate title - truncate question if too long const title = question.length > 47 ? question.slice(0, 47) + '...' @@ -375,8 +374,8 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { if (!formattedQuestion || !formattedAnswer) { return { title: '', description: '', hashtags: [] } } - return generateTikTokMetadata(formattedQuestion, formattedAnswer) - }, [formattedQuestion, formattedAnswer, generateTikTokMetadata]) + return generateVideoMetadata(formattedQuestion, formattedAnswer) + }, [formattedQuestion, formattedAnswer, generateVideoMetadata]) // GSAP Animation Control for Preview useEffect(() => { @@ -420,7 +419,7 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { - TikTok Video Generator + Video Generator @@ -469,7 +468,7 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { {!hasPreview && ( - Add a question and response first to generate the TikTok video. + Add a question and response first to generate the video. )} @@ -551,11 +550,10 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) { )} - {/* TikTok Metadata Copy Buttons */} {metadata.title && ( - TikTok Metadata + Video Metadata @@ -618,7 +616,7 @@ export function TikTokContentGenerator (props: TikTokContentGeneratorProps) {
{ const words = formattedQuestion.split(/\s+/).filter(Boolean) let globalCharIndex = 0 - const maxLineLength = 40 + const maxLineLength = 39 const lines: string[] = [] let currentLine = '' diff --git a/cms/schemaTypes/ama.ts b/cms/schemaTypes/ama.ts index aa1cfb3e..dff83388 100644 --- a/cms/schemaTypes/ama.ts +++ b/cms/schemaTypes/ama.ts @@ -2,7 +2,7 @@ import type { PortableTextTextBlock } from 'sanity' import { defineField, defineType } from 'sanity' import { CharacterCountInput } from '../components/CharacterCountInput' import { ImageGenerator } from '../components/ImageGenerator' -import { TikTokContentGenerator } from '../components/TikTokContentGenerator' +import { VideoContentGenerator } from '../components/VideoGenerator' export default defineType({ name: 'ama', @@ -193,7 +193,7 @@ export default defineType({ accept: 'video/*', }, components: { - input: TikTokContentGenerator, + input: VideoContentGenerator, }, description: 'Use this to generate video content for TikTok and YouTube Shorts', readOnly: true, diff --git a/cms/utils/tiktok-animation-config.ts b/cms/utils/video-animation-config.ts similarity index 100% rename from cms/utils/tiktok-animation-config.ts rename to cms/utils/video-animation-config.ts diff --git a/cms/utils/tiktok-gsap-animations.ts b/cms/utils/video-gsap-animations.ts similarity index 95% rename from cms/utils/tiktok-gsap-animations.ts rename to cms/utils/video-gsap-animations.ts index 89b16a75..d02e337e 100644 --- a/cms/utils/tiktok-gsap-animations.ts +++ b/cms/utils/video-gsap-animations.ts @@ -1,13 +1,13 @@ import { gsap } from 'gsap' -import { ANIMATION_CONFIG } from './tiktok-animation-config' +import { ANIMATION_CONFIG } from './video-animation-config' -export interface TikTokGSAPTimeline { +export interface GSAPTimeline { timeline: gsap.core.Timeline duration: number destroy: () => void } -export function createTikTokGSAPTimeline (container: HTMLElement, typingIntervals?: number[]): TikTokGSAPTimeline { +export function createGSAPTimeline (container: HTMLElement, typingIntervals?: number[]): GSAPTimeline { const questionWindow = container.querySelector('.question-window') as HTMLElement const questionContent = container.querySelector('.question-content') as HTMLElement const answerContainer = container.querySelector('.answer-container') as HTMLElement @@ -151,8 +151,8 @@ export function createTikTokGSAPTimeline (container: HTMLElement, typingInterval } } -export function createPreviewAnimation (container: HTMLElement, typingIntervals?: number[]): TikTokGSAPTimeline { - const animation = createTikTokGSAPTimeline(container, typingIntervals) +export function createPreviewAnimation (container: HTMLElement, typingIntervals?: number[]): GSAPTimeline { + const animation = createGSAPTimeline(container, typingIntervals) animation.timeline.repeat(-1) animation.timeline.play() return animation diff --git a/cms/utils/tiktok-video-recorder.ts b/cms/utils/video-recorder.ts similarity index 94% rename from cms/utils/tiktok-video-recorder.ts rename to cms/utils/video-recorder.ts index a40d2b5b..257857d7 100644 --- a/cms/utils/tiktok-video-recorder.ts +++ b/cms/utils/video-recorder.ts @@ -4,8 +4,8 @@ import { Muxer, ArrayBufferTarget } from 'webm-muxer' import { ANIMATION_CONFIG, calculateVideoDuration, -} from './tiktok-animation-config' -import { createTikTokGSAPTimeline, resetCharacterStates } from './tiktok-gsap-animations' +} from './video-animation-config' +import { createGSAPTimeline, resetCharacterStates } from './video-gsap-animations' interface AudioTrack { id: string @@ -16,7 +16,7 @@ interface AudioTrack { tags: string[] } -interface TikTokVideoOptions { +interface VideoOptions { element: HTMLElement question: string questionLines: string[] @@ -30,7 +30,7 @@ interface TikTokVideoOptions { onProgress?: (progress: number) => void } -export async function createTikTokVideo (options: TikTokVideoOptions): Promise { +export async function createVideo (options: VideoOptions): Promise { const { questionLines, answerLines, question, typingIntervals, audioTrack, audioStartTime = 0, audioVolume = 0.7, onProgress } = options const targetFPS = ANIMATION_CONFIG.recordingFrameRate || 24 @@ -40,7 +40,7 @@ export async function createTikTokVideo (options: TikTokVideoOptions): Promise { +export async function recordVideo (options: PureWebCodecsRecorderOptions): Promise { const { element, targetFPS, duration, width, height, typingIntervals, audioTrack, audioStartTime = 0, audioVolume = 0.7, onProgress } = options - let container = element.querySelector('.tiktok-container') as HTMLElement - if (!container && element.classList.contains('tiktok-container')) { + let container = element.querySelector('.video-container') as HTMLElement + if (!container && element.classList.contains('video-container')) { container = element } if (!container) { - throw new Error('TikTok container not found') + throw new Error('Video container not found') } // Reset all character states before starting animation resetCharacterStates(container) - const { timeline, destroy } = createTikTokGSAPTimeline(container, typingIntervals) + const { timeline, destroy } = createGSAPTimeline(container, typingIntervals) const totalFrames = Math.ceil(duration * targetFPS) const frameDurationMicros = Math.floor(1000000 / targetFPS) -- 2.51.2