### Install PyCaps (Full) Source: https://github.com/francozanardi/pycaps/blob/main/README.md Installs PyCaps with all available features. Ensure FFmpeg is installed and accessible in your PATH. ```bash pip install "git+https://github.com/francozanardi/pycaps.git#egg=pycaps[all]" ``` -------------------------------- ### Install PyCaps (Base Dependencies) Source: https://github.com/francozanardi/pycaps/blob/main/README.md Installs PyCaps with basic dependencies for default configuration, including Whisper and Playwright. FFmpeg must be pre-installed. ```bash pip install "git+https://github.com/francozanardi/pycaps.git#egg=pycaps[base]" ``` -------------------------------- ### Install PyCaps (Browser Rendering) Source: https://github.com/francozanardi/pycaps/blob/main/README.md Installs PyCaps with dependencies for browser-based rendering, using Google Cloud Speech to Text and html2pic. This option does not include the subtitles editor. FFmpeg must be pre-installed. ```bash pip install "git+https://github.com/francozanardi/pycaps.git#egg=pycaps[browser]" ``` -------------------------------- ### Load Template and Add Animation Programmatically (Python) Source: https://github.com/francozanardi/pycaps/blob/main/README.md This example demonstrates loading a template and programmatically adding an animation (FadeIn) to specific elements (SEGMENT) based on an event (ON_NARRATION_STARTS). ```python from pycaps import * # Load a template and configure it builder = TemplateLoader("default").with_input_video("my_video.mp4").load(False) # Programmatically add an animation builder.add_animation( animation=FadeIn(), when=EventType.ON_NARRATION_STARTS, what=ElementType.SEGMENT ) # Build and run the pipeline pipeline = builder.build() pipeline.run() ``` -------------------------------- ### Build Captioning Pipeline Programmatically (Python) Source: https://github.com/francozanardi/pycaps/blob/main/README.md Use CapsPipelineBuilder in Python for full control over the captioning process. This example shows how to set the input video and add a CSS file. ```python from pycaps import CapsPipelineBuilder # The pipeline contains multiples stages to render the final video pipeline = ( CapsPipelineBuilder() .with_input_video("input.mp4") .add_css("css_file.css") .build() ) pipeline.run() # When this is executed, it starts to render the video ``` -------------------------------- ### Install Browser Dependencies for Playwright Source: https://github.com/francozanardi/pycaps/blob/main/README.md Installs the Chromium browser required by Playwright for the default CSS subtitle rendering. This is an optional step if you plan to use `CssSubtitleRenderer`. ```bash playwright install chromium ``` -------------------------------- ### JSON Tag Condition for Animations Source: https://github.com/francozanardi/pycaps/blob/main/docs/TAGS.md Define animation conditions in JSON using a string-based tag condition. This example targets the 'pop_in' animation for words that have the 'highlight' tag but are not the first word in a line. ```json { "animations": [ { "type": "pop_in", "when": "narration-starts", "what": "word", "tag_condition": "highlight and not first-word-in-line" } ] } ``` -------------------------------- ### Main Application Logic Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/renderer/previewer/previewer.html Initializes the application by waiting for the pywebview API, setting up references to DOM elements, and defining core functions for updating the subtitle preview. It handles data structures for segment, line, and word states. ```javascript async function main() { const api = await waitForPywebviewReady(); const subtitlePreviewFrame = document.getElementById('subtitle-preview-frame'); const segmentTextarea = document.getElementById('segment-text'); const updatePreviewBtn = document.getElementById('update-preview-btn'); const lineStateSelect = document.getElementById('line-state'); const lineTagsTextarea = document.getElementById('line-tags'); const wordSelect = document.getElementById('word-select'); const wordStateSelect = document.getElementById('word-state'); const wordTagsTextarea = document.getElementById('word-tags'); const segmentTagsTextarea = document.getElementById('segment-tags'); let currentSegmentData = { text: segmentTextarea.value, tags: [], line: { text: '', state: 'line-not-narrated-yet', tags: [], words: [] } }; function rebuildSegmentData() { currentSegmentData.line.text = segmentTextarea.value; currentSegmentData.line.words = segmentTextarea.value.split(/\s+/).map(wordText => ({ text: wordText, state: 'word-not-narrated-yet', tags: [] })); populateWordSelect(); } async function updateIframeContent() { const iframeContent = await api.get_renderer_html(currentSegmentData); subtitlePreviewFrame.contentDocument.open(); subtitlePreviewFrame.contentDocument.write(iframeContent); subtitlePreviewFrame.contentDocument.body.style.backgroundColor = '#fff'; subtitlePreviewFrame.contentDocument.body.style.backgroundImage = 'linear-gradient(45deg, #ddd 25%, transparent 25%),' + 'linear-gradient(-45deg, #ddd 25%, transparent 25%),' + 'linear-gradient(45deg, transparent 75%, #ddd 75%),' + 'linear-gradient(-45deg, transparent 75%, #ddd 75%)'; subtitlePreviewFrame.contentDocument.body.style.backgroundSize = '20px 20px'; subtitlePreviewFrame.contentDocument.body.style.backgroundPosition = '0 0, 0 10px, 10px -10px, -10px 0px'; subtitlePreviewFrame.contentDocument.close(); } const debouncedUpdateIframeContent = debounce(updateIframeContent); function populateWordSelect() { wordSelect.innerH ``` -------------------------------- ### Preview styles with GUI Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md Launch the style previewer to design subtitle aesthetics using CSS files or templates. ```bash # Preview styles from a CSS file and a resources folder pycaps preview-styles --css my-styles.css --resources ./fonts # Or, preview the styles directly from a template pycaps preview-styles --template my-cool-template ``` -------------------------------- ### Initialize PyCaps Editor Frontend Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/transcriber/editor/editor.html Establishes a connection to the pywebview API and renders the initial document state. ```javascript function waitForPywebviewReady() { function isPywebviewReady() { if (!window.pywebview || !window.pywebview.api) { return false; } const apis = [ "get_document_as_json", "save", "cancel" ] for (const api of apis) { if (typeof window.pywebview.api[api] !== "function") { return false; } } return true; } return new Promise((resolve, reject) => { if (isPywebviewReady()) { resolve(); } else { const interval = setInterval(() => { if (isPywebviewReady()) { clearInterval(interval); resolve(window.pywebview.api); } }, 100); } }); } async function main() { const api = await waitForPywebviewReady() const initialDocument = await api.get_document_as_json(); const editorContainer = document.getElementById('editor-container'); const tagModal = document.getElementById('tag-modal'); let documentState = JSON.parse(JSON.stringify(initialDocument)); let currentWordDropdown = null; let currentEditingWord = null; function render() { editorContainer.innerHTML = ''; documentState.segments.forEach((segment, segIndex) => { const segDiv = document.createElement('div'); segDiv.className = 'segment'; const segHeader = document.createElement('div'); segHeader.className = 'segment-header'; const segTitle = document.createElement('div'); segTitle.className = 'segment-title'; segTitle.textContent = `Segment ${segIndex + 1} (${segment.time.start.toFixed(2)}s - ${segment.time.end.toFixed(2)}s)`; const segControls = document.createElement('div'); segControls.className = 'segment-controls'; if (segIndex < documentState.segments.length - 1) { const mergeBtn = document.createElement('button'); mergeBtn.className = 'control-btn merge-btn'; mergeBtn.textContent = 'Merge Next'; mergeBtn.addEventListener('click', () => mergeSegments(segIndex)); segControls.appendChild(mergeBtn); } segHeader.appendChild(segTitle); segHeader.appendChild(segControls); segDiv.appendChild(segHeader); segment.lines.forEach((line, lineIndex) => { const lineDiv = document.createElement('div'); lineDiv.className = 'line'; line.words.forEach((word, wordIndex) => { const wordSpan = document.createElement('span'); wordSpan.className = 'word'; wordSpan.dataset.segIndex = segIndex; wordSpan.dataset.lineIndex = lineIndex; wordSpan.dataset.wordIndex = wordIndex; wordSpan.textContent = word.text; if (word.semantic_tags && word.semantic_tags.length > 0) { const tagsTooltip = document.createElement('span'); tagsTooltip.className = 'word-tags'; tagsTooltip.textContent = word.semantic_tags.map(t => t.name).join(', '); wordSpan.appendChild(tagsTooltip); } wordSpan.addEventListener('click', (e) => { e.stopPropagation(); showWordDropdown(wordSpan, segIndex, lineIndex, wordIndex); }); lineDiv.appendChild(wordSpan); }); const lineControls = document.createElement('div'); lineControls.className = 'line-controls'; if (lineIndex < segment.lines.length - 1) { const mergeBtn = document.createElement('button'); mergeBtn.className = 'line-control-btn merge-btn'; mergeBtn.textContent = 'Merge with next line'; mergeBtn.addEventListener('click', () => mergeLines(segIndex, lineIndex)); lineControls.appendChild(mergeBtn); const splitBtn = document.createElement('button'); splitBtn.className = 'line-control-btn split-btn'; splitBtn.textContent = 'Split into two segments'; splitBtn.addEventListener('click', () => splitSegments(segIndex, lineIndex)); lineControls.appendChild(splitBtn); } lineDiv.appendChild(lineControls); segDiv.appendChild(lineDiv); }); editorContainer.appendChild(segDiv); }); } function showWordDropdown(wordElement, segIndex, lineIndex, wordIndex) { // Cerrar dropdown anterior si existe if (currentWordDropdown) { currentWordDropdown.remove(); } const dropdown = document.createElement('div'); dropdown.className = 'word-dropdown show'; const rect = wordElement.getBoundingClientRect(); dropdown.style.top = `${rect.bottom}px`; dropdown.style.left = `${rect.left}px`; const word = documentState.segments[segIndex].lines[lineIndex].words[wordIndex]; dropdown.innerHTML = ` `; dropdown.addEventListener('click', (e) => { const action = e.target.dataset.action; if (!action) return; switch (action) { case 'edit-text': editWordText(segIndex, lineIndex, wordIndex); break; case 'edit-tags': editWordTags(segIndex, lineIndex, wordIndex); break; case 'split-after': splitLine(segIndex, lineIndex, wordIndex); break; case 'delete': deleteWord(segIndex, lineIndex, wordIndex); break; } dropdown.remove(); currentWordDropdown = null; }); document.body.appendChild(dropdown); currentWordDropdown = dropdown; } function editWordText(segIndex, lineIndex, wordIndex) { const ``` -------------------------------- ### Pycaps Preview Styles Command Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md Launches a GUI to help design subtitle styles in real-time without needing a video. ```APIDOC ## GET /api/users/{id} ### Description Retrieves the details of a specific user based on their unique identifier. ### Method GET ### Endpoint /api/users/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The unique identifier of the user to retrieve. #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response (200) - **id** (string) - The unique identifier for the user. - **username** (string) - The username of the user. - **email** (string) - The email address of the user. - **created_at** (string) - The timestamp when the user was created. #### Response Example { "id": "user-12345", "username": "johndoe", "email": "john.doe@example.com", "created_at": "2023-10-27T10:00:00Z" } ``` -------------------------------- ### Render a video preview Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md Specify a time range to render a short preview of the video. ```bash # Preview from 10.5 seconds to 15 seconds pycaps render ... --preview-time 10.5,15 ``` -------------------------------- ### Initialize Frontend Event Listeners Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/transcriber/editor/editor.html Sets up event handlers for modal interactions, word dropdown management, and document persistence. ```javascript tate.segments[segIndex]; if (combinedSegment.lines.length > 0) { combinedSegment.time.start = combinedSegment.lines[0].time.start; combinedSegment.time.end = combinedSegment.lines[combinedSegment.lines.length - 1].time.end; } documentState.segments.splice(segIndex + 1, 1); render(); } } document.getElementById('modal-save').addEventListener('click', () => { if (currentEditingWord) { const { segIndex, lineIndex, wordIndex } = currentEditingWord; const tagsText = document.getElementById('modal-tags').value; const tagNames = tagsText.split(',').map(t => t.trim()).filter(t => t); documentState.segments[segIndex].lines[lineIndex].words[wordIndex].semantic_tags = tagNames.map(name => ({ name })); tagModal.classList.remove('show'); currentEditingWord = null; render(); } }); document.getElementById('modal-cancel').addEventListener('click', () => { tagModal.classList.remove('show'); currentEditingWord = null; }); document.addEventListener('click', (e) => { if (currentWordDropdown && !e.target.closest('.word-dropdown')) { currentWordDropdown.remove(); currentWordDropdown = null; } }); document.getElementById('save-btn').addEventListener('click', () => { documentState.segments.forEach(seg => { if (seg.lines && seg.lines.length > 0) { seg.lines.forEach(line => { if (line.words && line.words.length > 0) { line.time.start = line.words[0].time.start; line.time.end = line.words[line.words.length - 1].time.end; } }); seg.time.start = seg.lines[0].time.start; seg.time.end = seg.lines[seg.lines.length - 1].time.end; } }); api.save(documentState); }); document.getElementById('cancel-btn').addEventListener('click', () => { api.cancel(); }); render(); } main(); ``` -------------------------------- ### Render Video with Minimalist Template (CLI) Source: https://github.com/francozanardi/pycaps/blob/main/README.md Use this command to quickly render a video with the 'minimalist' template. It handles transcription and styling automatically. ```bash pycaps render --input my_video.mp4 --template minimalist ``` -------------------------------- ### Run Template via CLI Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Command to render using a template directory. ```bash pycaps render --input video.mp4 --template my_template ``` -------------------------------- ### Run Render via CLI Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Command to execute a render using a configuration file. ```bash pycaps render --config config.json ``` -------------------------------- ### Minimal JSON Configuration Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md A basic configuration file for rendering video subtitles with animations. ```json { "input": "video.mp4", "output": "video_with_subs.mp4", "css": "styles.css", "layout": { "max_number_of_lines": 2 }, "animations": [ { "type": "fade_in", "when": "narration-starts", "what": "segment", "duration": 0.3 } ] } ``` -------------------------------- ### Render Video with Template via CLI Source: https://github.com/francozanardi/pycaps/blob/main/docs/TEMPLATES.md Apply a template to a video rendering task using the command line interface. ```bash pycaps render --input my_video.mp4 --template my-awesome-template ``` -------------------------------- ### Create a new template Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md Generate a new template directory based on an existing one. ```bash # Create a new template called 'my-new-style' based on the 'default' one pycaps template create --name my-new-style --from default ``` -------------------------------- ### Create New Template via CLI Source: https://github.com/francozanardi/pycaps/blob/main/docs/TEMPLATES.md Generate a new template directory based on the default template. ```bash # Creates a new folder named 'my-new-style' in the current directory pycaps template create --name my-new-style ``` -------------------------------- ### Template Supporting Files Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Wordlist and CSS files for the advanced template. ```text pycaps amazing wow ``` ```css .word { color: white; font-size: 25px; } .word.special-word { color: #34d399; /* A nice green color */ font-weight: bold; } ``` -------------------------------- ### Render with Transcript Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Rendering video using an existing transcript file via CLI or Python. ```bash pycaps render --input my_video.mp4 --template minimalist --transcript transcript.srt ``` ```python from pycaps import CapsPipelineBuilder, TranscriptFormat pipeline = ( CapsPipelineBuilder() .with_input_video("my_video.mp4") .with_transcription_file("transcript.vtt", TranscriptFormat.VTT) .add_css("styles.css") .build() ) pipeline.run() ``` -------------------------------- ### Build and Execute the Pipeline Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Finalizes the builder configuration and triggers the pipeline execution. ```python pipeline = builder.build() pipeline.run() print("Advanced pipeline finished successfully!") ``` -------------------------------- ### Render a video with pycaps Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md The basic command to process an input video using a specified template. ```bash pycaps render --input my_video.mp4 --template default ``` -------------------------------- ### Load Template via Python Source: https://github.com/francozanardi/pycaps/blob/main/docs/TEMPLATES.md Initialize a pipeline builder using the TemplateLoader class in Python. ```python from pycaps import TemplateLoader # Load a builder from a template named 'my-awesome-template' builder = TemplateLoader("my-awesome-template").with_input_video("my_video.mp4").load(False) # You can further customize the builder here if needed builder.with_video_quality("high") # Build and run pipeline = builder.build() pipeline.run() ``` -------------------------------- ### Run Render via Python Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Programmatic execution of a render using the JsonConfigLoader. ```python from pycaps import JsonConfigLoader loader = JsonConfigLoader("config.json") pipeline = loader.load() # you can use loader.load(False) if you can receive the builder pipeline.run() ``` -------------------------------- ### Advanced Python Pipeline with Custom Logic Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Building a complex pipeline with custom taggers, punctuation removal, and conditional sound effects. ```python from pycaps import * # --- 1. Create a custom tagger --- tagger = SemanticTagger() tagger.add_regex_rule(Tag("shoutout"), r"(?i)shoutout to \w+") tagger.add_wordlist_rule(Tag("important"), ["key", "critical", "important"]) # or you can use ai here: tagger.add_ai_rule(Tag("important"), "most important phrases or words") # --- 2. Build the pipeline --- builder = CapsPipelineBuilder() builder.with_input_video("podcast_clip.mp4") builder.with_output_video("podcast_clip_final.mp4") builder.with_video_quality(VideoQuality.HIGH) builder.with_semantic_tagger(tagger) # Use our custom tagger builder.add_css("styles.css") # --- 3. Add Effects --- # Remove trailing commas and periods builder.add_effect(RemovePunctuationMarksEffect( punctuation_marks=[".", ","], exception_marks=["..."] )) # Add a whoosh sound only on the first line of a segment builder.add_effect(SoundEffect( sound=BuiltinSound.WHOOSH, when=EventType.ON_NARRATION_STARTS, what=ElementType.LINE, tag_condition=TagConditionFactory.HAS(BuiltinTag.FIRST_LINE_IN_SEGMENT) )) ``` -------------------------------- ### Build Pipeline Programmatically Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Constructing a pipeline in Python with specific animations and CSS. ```python from pycaps import CapsPipelineBuilder from pycaps.animation import PopIn from pycaps.common import EventType, ElementType # 1. Initialize the builder builder = CapsPipelineBuilder() # 2. Configure the pipeline builder.with_input_video("my_video.mp4") builder.add_css("path/to/my_styles.css") builder.add_animation( animation=PopIn(duration=0.3), when=EventType.ON_NARRATION_STARTS, what=ElementType.WORD ) # 3. Build and run pipeline = builder.build() pipeline.run() print("Video has been rendered!") ``` -------------------------------- ### Pycaps Template Commands Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md Commands for managing project templates, including listing and creating new templates. ```APIDOC ## PUT /api/users/{id} ### Description Updates the details of an existing user. ### Method PUT ### Endpoint /api/users/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The unique identifier of the user to update. #### Query Parameters None #### Request Body - **username** (string) - Optional - The new username for the user. - **email** (string) - Optional - The new email address for the user. ### Request Example { "email": "john.doe.updated@example.com" } ### Response #### Success Response (200) - **id** (string) - The unique identifier for the updated user. - **username** (string) - The updated username. - **email** (string) - The updated email address. - **updated_at** (string) - The timestamp when the user was last updated. #### Response Example { "id": "user-12345", "username": "johndoe", "email": "john.doe.updated@example.com", "updated_at": "2023-10-27T11:00:00Z" } ``` -------------------------------- ### Template Directory Structure Source: https://github.com/francozanardi/pycaps/blob/main/docs/TEMPLATES.md The standard file layout for a pycaps template. ```text my-awesome-template/ ├── pycaps.template.json # Main configuration for the pipeline ├── style.css # All CSS styles for the subtitles └── resources/ # Optional folder for assets └── my-font.ttf ``` -------------------------------- ### Render Video with Pre-existing Transcript (CLI) Source: https://github.com/francozanardi/pycaps/blob/main/README.md When you have an existing transcript file, use this command to skip PyCaps's built-in transcription. Supported formats include whisper_json, pycaps_json, srt, and vtt. ```bash pycaps render --input my_video.mp4 --template minimalist --transcript transcript.json ``` -------------------------------- ### Pycaps Config Commands Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md Commands for managing the Pycaps API key, including setting and unsetting it. ```APIDOC ## DELETE /api/users/{id} ### Description Deletes a user from the system. ### Method DELETE ### Endpoint /api/users/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The unique identifier of the user to delete. #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response (204) No content is returned upon successful deletion. #### Response Example None ``` -------------------------------- ### Pycaps Render Command Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md The main command to process and render a video with subtitles. It supports various options for input, output, templating, styling, and transcript handling. ```APIDOC ## POST /api/users ### Description This endpoint allows for the creation of a new user in the system. ### Method POST ### Endpoint /api/users ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **username** (string) - Required - The desired username for the new user. - **email** (string) - Required - The email address of the new user. - **password** (string) - Required - The password for the new user. ### Request Example { "username": "johndoe", "email": "john.doe@example.com", "password": "securepassword123" } ### Response #### Success Response (201) - **id** (string) - The unique identifier for the newly created user. - **username** (string) - The username of the created user. - **email** (string) - The email address of the created user. #### Response Example { "id": "user-12345", "username": "johndoe", "email": "john.doe@example.com" } ``` -------------------------------- ### Render with external transcript Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md Use pre-transcribed files to skip the built-in speech-to-text process. ```bash pycaps render --input my_video.mp4 --template minimalist --transcript transcript.json ``` ```bash pycaps render --input my_video.mp4 --template minimalist --transcript subtitles.vtt --transcript-format vtt ``` -------------------------------- ### Configure Pycaps API Key Source: https://github.com/francozanardi/pycaps/blob/main/docs/API_USAGE.md Set your Pycaps API key for accessing AI features. This command stores the key locally. ```bash pycaps config --set-api-key YOUR_PYCAPS_API_KEY ``` -------------------------------- ### Wait for pywebview API Ready Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/renderer/previewer/previewer.html Ensures the pywebview API is fully loaded and ready before proceeding. It checks for the existence of `window.pywebview` and specific API functions. This is crucial for any script that needs to interact with the pywebview backend. ```javascript function waitForPywebviewReady() { function isPywebviewReady() { if (!window.pywebview || !window.pywebview.api) { return false; } const apis = [ "get_renderer_html", ] for (const api of apis) { if (typeof window.pywebview.api[api] !== "function") { return false; } } return true; } return new Promise((resolve, reject) => { if (isPywebviewReady()) { resolve(); } else { const interval = setInterval(() => { if (isPywebviewReady()) { clearInterval(interval); resolve(window.pywebview.api); } }, 100); } }); } ``` -------------------------------- ### Configure Animations for Segments and Words Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Applies slide-in animations to specific segment tags and zoom effects to words marked as important. ```python builder.add_animation( animation=SlideIn(direction="left"), when=EventType.ON_NARRATION_STARTS, what=ElementType.SEGMENT, tag_condition=TagConditionFactory.HAS(BuiltinTag.FIRST_LINE_IN_SEGMENT ) builder.add_animation( animation=SlideIn(direction="right"), when=EventType.ON_NARRATION_STARTS, what=ElementType.SEGMENT, tag_condition=TagConditionFactory.HAS(BuiltinTag.LAST_LINE_IN_SEGMENT ) # "Important" words zoom out when spoken builder.add_animation( animation=ZoomOut(duration=0.2), when=EventType.ON_NARRATION_STARTS, what=ElementType.WORD, tag_condition=TagConditionFactory.parse("important") ) ``` -------------------------------- ### Advanced JSON Template Configuration Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md Complex JSON configuration using tagger rules and sound effects. ```json { "css": "style.css", "layout": { "max_width_ratio": 0.85, "vertical_align": { "align": "bottom", "offset": -0.05 } }, "tagger_rules": [ { "type": "wordlist", "tag": "special-word", "filename": "special_words.txt" } ], "sound_effects": [ { "type": "preset", "name": "ding-short", "when": "narration-starts", "what": "word", "tag_condition": "special-word" } ] } ``` -------------------------------- ### CSS Styling with Tags Source: https://github.com/francozanardi/pycaps/blob/main/docs/TAGS.md Use tags as CSS classes to style elements. Combine tags for precise targeting, such as styling the first word of every line or words with a specific custom tag. ```css /* Make the first word of every line pop */ .word.first-word-in-line { font-weight: bold; color: #5eead4; } /* Style words tagged with our custom 'brand-name' tag */ .word.brand-name { background-color: blue; color: white; padding: 2px 8px; border-radius: 5px; } ``` -------------------------------- ### Minimal CSS Styles Source: https://github.com/francozanardi/pycaps/blob/main/docs/EXAMPLES.md CSS definitions for styling subtitle words. ```css .word { font-size: 30px; color: white; text-shadow: 2px 2px 5px black; } .word-being-narrated { color: yellow; } ``` -------------------------------- ### Populate Word Selection Dropdown Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/renderer/previewer/previewer.html Dynamically generates option elements for a word selection dropdown based on current segment data. ```javascript TML = ''; currentSegmentData.line.words.forEach((word, index) => { const option = document.createElement('option'); option.value = index; option.textContent = `Word ${index + 1}: "${word.text}"`; wordSelect.appendChild(option); }); wordSelect.value = ""; } ``` -------------------------------- ### Set OpenAI API Key Environment Variable (macOS/Linux) Source: https://github.com/francozanardi/pycaps/blob/main/docs/API_USAGE.md Configure your OpenAI API key as an environment variable for Pycaps to use. Add this to your shell profile for persistence. ```bash export PYCAPS_OPENAI_API_KEY="sk-YourOpenAIKeyHere" ``` -------------------------------- ### Manage Word Tags Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/transcriber/editor/editor.html Opens a modal to edit semantic tags for a specific word. ```javascript function editWordTags(segIndex, lineIndex, wordIndex) { const word = documentState.segments[segIndex].lines[lineIndex].words[wordIndex]; currentEditingWord = { segIndex, lineIndex, wordIndex }; document.getElementById('modal-word-text').textContent = word.text; document.getElementById('modal-tags').value = word.semantic_tags.map(t => t.name).join(', '); tagModal.classList.add('show'); } ``` -------------------------------- ### Set OpenAI API Key Environment Variable (Windows Command Prompt) Source: https://github.com/francozanardi/pycaps/blob/main/docs/API_USAGE.md Set your OpenAI API key as an environment variable on Windows Command Prompt. A terminal restart may be required. ```powershell setx PYCAPS_OPENAI_API_KEY "sk-YourOpenAIKeyHere" ``` -------------------------------- ### UI Event Listeners Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/renderer/previewer/previewer.html Handles user interactions for updating segment states, tags, and word-specific metadata. ```javascript updatePreviewBtn.addEventListener('click', () => { rebuildSegmentData(); updateIframeContent(); }); lineStateSelect.addEventListener('change', (event) => { currentSegmentData.line.state = event.target.value; updateIframeContent(); }); lineTagsTextarea.addEventListener('input', (event) => { currentSegmentData.line.tags = event.target.value.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0); debouncedUpdateIframeContent(); }); wordSelect.addEventListener('change', (event) => { const selectedWordIndex = event.target.value; if (selectedWordIndex !== "") { const word = currentSegmentData.line.words[selectedWordIndex]; wordStateSelect.value = word.state; wordTagsTextarea.value = word.tags.join(', '); } else { wordStateSelect.value = 'word-not-narrated-yet'; wordTagsTextarea.value = ''; } }); wordStateSelect.addEventListener('change', (event) => { const selectedWordIndex = wordSelect.value; if (selectedWordIndex !== "") { currentSegmentData.line.words[selectedWordIndex].state = event.target.value; updateIframeContent(); } }); wordTagsTextarea.addEventListener('input', (event) => { const selectedWordIndex = wordSelect.value; if (selectedWordIndex !== "") { currentSegmentData.line.words[selectedWordIndex].tags = event.target.value.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0); debouncedUpdateIframeContent(); } }); segmentTagsTextarea.addEventListener('input', (event) => { currentSegmentData.tags = event.target.value.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0); debouncedUpdateIframeContent(); }); rebuildSegmentData(); updateIframeContent(); } main(); ``` -------------------------------- ### Override CSS styles during render Source: https://github.com/francozanardi/pycaps/blob/main/docs/CLI.md Use the --style flag to modify CSS properties on the fly during the rendering process. ```bash # Example: Make the current word yellow and bigger pycaps render ... --style ".word-being-narrated.color=yellow" --style ".word-being-narrated.font-size=72px" ``` -------------------------------- ### Python Tag Condition Creation Source: https://github.com/francozanardi/pycaps/blob/main/docs/TAGS.md Create tag conditions programmatically in Python using TagConditionFactory. Supports AND, OR, and NOT operators, and can parse string conditions similar to JSON. ```python from pycaps.tag import TagConditionFactory, BuiltinTag, Tag # Create a condition programmatically condition = TagConditionFactory.AND( TagConditionFactory.HAS(Tag("highlight")), TagConditionFactory.NOT(BuiltinTag.FIRST_WORD_IN_LINE) ) # You can also parse a string, just like in the JSON condition_from_string = TagConditionFactory.parse("highlight and not first-word-in-line") ``` -------------------------------- ### Edit and Split Words Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/transcriber/editor/editor.html Handles word editing via prompt and supports splitting a single word into multiple words with proportional timestamp distribution. ```javascript word = documentState.segments[segIndex].lines[lineIndex].words[wordIndex]; const newText = prompt('Edit word (use [SPACE] key to split words):', word.text); if (!newText || !newText.trim()) { return; } const words = newText.trim().split(/\s+/); if (words.length === 1) { const newWord = { text: newText, time: { start: word.time.start, end: word.time.end }, semantic_tags: [], structure_tags: [], clips: [], max_layout: { position: {x:0, y:0}, size: {width:0, height:0} } }; documentState.segments[segIndex].lines[lineIndex].words.splice(wordIndex, 1, newWord); render(); return; } const originalStart = word.time.start.toFixed(2); const originalEnd = word.time.end.toFixed(2); const totalDuration = word.time.end - word.time.start; const totalChars = words.reduce((sum, w) => sum + w.length, 0); let currentTime = word.time.start; const timestamps = words.map((text, index) => { const duration = totalDuration * (text.length / totalChars); const start = currentTime.toFixed(2); const end = (currentTime + duration).toFixed(2); currentTime = parseFloat(end); return { text, start, end }; }); const exampleMessage = timestamps.map(t => `"${t.text}" (${t.start}s - ${t.end}s)` ).join(' and '); const warningMessage = `Warning: You are splitting one word into multiple words!\n\n` + `Original word: "${word.text}" (${originalStart}s - ${originalEnd}s)\n` + `Will be split into: ${exampleMessage}\n\n` + `The original timestamp will be proportionally distributed based on word length.\n\n` + `Do you want to proceed with this split?`; if (confirm(warningMessage)) { const replacementWords = timestamps.map(t => ({ text: t.text, time: { start: parseFloat(t.start), end: parseFloat(t.end) }, semantic_tags: [], structure_tags: [], clips: [], max_layout: { position: {x:0, y:0}, size: {width:0, height:0} } })); documentState.segments[segIndex].lines[lineIndex].words.splice(wordIndex, 1, ...replacementWords); render(); } } ``` -------------------------------- ### Split and Merge Segments Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/transcriber/editor/editor.html Functions to split a segment at a specific line index or merge two adjacent segments. ```javascript function splitSegments(segIndex, lineIndex) { const segment = documentState.segments[segIndex]; const splitAt = lineIndex + 1; if (splitAt > 0 && splitAt < segment.lines.length) { const linesToMove = segment.lines.splice(splitAt); const newSegment = { lines: linesToMove, structure_tags: [], time: { start: linesToMove[0].time.start, end: linesToMove[linesToMove.length - 1].time.end }, max_layout: { position: {x:0, y:0}, size: {width:0, height:0} } }; if (segment.lines.length > 0) { segment.time.end = segment.lines[segment.lines.length - 1].time.end; } documentState.segments.splice(segIndex + 1, 0, newSegment); render(); } } ``` ```javascript function mergeSegments(segIndex) { if (segIndex + 1 < documentState.segments.length) { const nextSegmentLines = documentState.segments[segIndex + 1].lines; documentState.segments[segIndex].lines.push(...nextSegmentLines); const combinedSegment = documentS ``` -------------------------------- ### Split and Merge Lines Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/transcriber/editor/editor.html Functions to split a line at a specific word index or merge two adjacent lines within a segment. ```javascript function splitLine(segIndex, lineIndex, wordIndex) { const line = documentState.segments[segIndex].lines[lineIndex]; const splitAt = wordIndex + 1; if (splitAt > 0 && splitAt < line.words.length) { const wordsToMove = line.words.splice(splitAt); const newLine = { words: wordsToMove, structure_tags: [], time: { start: wordsToMove[0].time.start, end: wordsToMove[wordsToMove.length - 1].time.end }, max_layout: { position: {x:0, y:0}, size: {width:0, height:0} } }; if (line.words.length > 0) { line.time.end = line.words[line.words.length - 1].time.end; } documentState.segments[segIndex].lines.splice(lineIndex + 1, 0, newLine); render(); } } ``` ```javascript function mergeLines(segIndex, lineIndex) { if (lineIndex + 1 < documentState.segments[segIndex].lines.length) { const nextLineWords = documentState.segments[segIndex].lines[lineIndex + 1].words; documentState.segments[segIndex].lines[lineIndex].words.push(...nextLineWords); const combinedLine = documentState.segments[segIndex].lines[lineIndex]; if (combinedLine.words.length > 0) { combinedLine.time.start = combinedLine.words[0].time.start; combinedLine.time.end = combinedLine.words[combinedLine.words.length - 1].time.end; } documentState.segments[segIndex].lines.splice(lineIndex + 1, 1); render(); } } ``` -------------------------------- ### Debounce Utility Function Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/renderer/previewer/previewer.html Limits the frequency of function execution to improve performance during rapid input events. ```javascript function debounce(func, delay = 500) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; } ``` -------------------------------- ### Delete Word Source: https://github.com/francozanardi/pycaps/blob/main/src/pycaps/transcriber/editor/editor.html Removes a word from the document state and cleans up empty lines or segments if necessary. ```javascript function deleteWord(segIndex, lineIndex, wordIndex) { if (confirm('Delete this word?')) { documentState.segments[segIndex].lines[lineIndex].words.splice(wordIndex, 1); if (documentState.segments[segIndex].lines[lineIndex].words.length === 0) { documentState.segments[segIndex].lines.splice(lineIndex, 1); } if (documentState.segments[segIndex].lines.length === 0) { documentState.segments.splice(segIndex, 1); } render(); } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.