### 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 = `
✏️ Edit Text
🏷️ Edit Tags
↓ Split Line After
🗑️ Delete Word
`; 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.