Metadata-Version: 2.4
Name: cjm-transcription-source-select
Version: 0.0.10
Summary: FastHTML source selection component for transcription workflows that provides local file browsing with audio/video preview, selection ordering, and integrated audio extraction from video sources.
Author-email: "Christian J. Mills" <9126128+cj-mills@users.noreply.github.com>
License: Apache-2.0
Project-URL: Repository, https://github.com/cj-mills/cjm-transcription-source-select
Project-URL: Documentation, https://cj-mills.github.io/cjm-transcription-source-select
Keywords: nbdev,jupyter,notebook,python
Classifier: Natural Language :: English
Classifier: Intended Audience :: Developers
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cjm-plugin-system
Requires-Dist: cjm-media-plugin-system
Requires-Dist: cjm-file-discovery
Requires-Dist: cjm-fasthtml-app-core
Requires-Dist: cjm-fasthtml-tailwind
Requires-Dist: cjm-fasthtml-daisyui
Requires-Dist: cjm_fasthtml_lucide_icons
Requires-Dist: cjm_fasthtml_file_browser>=0.0.17
Requires-Dist: cjm_fasthtml_keyboard_navigation>=0.0.20
Requires-Dist: cjm_workflow_state
Requires-Dist: cjm_fasthtml_interactions
Requires-Dist: cjm_fasthtml_media_gallery
Requires-Dist: cjm-fasthtml-viewport-fit
Requires-Dist: cjm_fasthtml_sortable_queue>=0.0.13
Requires-Dist: cjm_fasthtml_design_system>=0.0.9
Dynamic: license-file

# cjm-transcription-source-select


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Install

``` bash
pip install cjm_transcription_source_select
```

## Project Structure

    nbs/
    ├── components/ (6)
    │   ├── file_browser_panel.ipynb  # File browser panel configuration and rendering for browsing local audio/video files
    │   ├── helpers.ipynb             # Shared helper functions for the transcription source selection step
    │   ├── preview_panel.ipynb       # Collapsible preview panel with audio/video player and file metadata
    │   ├── selection_panel.ipynb     # Selection panel component showing selected files with drag-drop reordering
    │   ├── stats_panel.ipynb         # Stats summary and verify selection button
    │   └── step_renderer.ipynb       # Main step renderer combining all panels for the source selection step
    ├── routes/ (6)
    │   ├── browser.ipynb    # Route handlers for file browser navigation and file selection
    │   ├── core.ipynb       # State management helpers for the transcription source selection step
    │   ├── init.ipynb       # Router assembly — creates all sub-routers, populates URL bundle, returns unified interface
    │   ├── preview.ipynb    # Route handlers for media file serving and preview panel rendering
    │   ├── selection.ipynb  # Route handlers for selection queue management (remove, reorder, clear)
    │   └── verify.ipynb     # Route handler for verify selection + audio extraction from video sources
    ├── services/ (1)
    │   └── source_select.ipynb  # Service layer for file operations and audio extraction from video via FFmpeg plugin
    ├── html_ids.ipynb  # HTML ID constants for the transcription source selection step
    ├── models.ipynb    # Data models and URL bundles for the transcription source selection step
    └── utils.ipynb     # Utility functions for file type detection, duration formatting, and extension filtering

Total: 16 notebooks across 3 directories

## Module Dependencies

``` mermaid
graph LR
    components_file_browser_panel[components.file_browser_panel<br/>components/file_browser_panel]
    components_helpers[components.helpers<br/>components/helpers]
    components_preview_panel[components.preview_panel<br/>components/preview_panel]
    components_selection_panel[components.selection_panel<br/>components/selection_panel]
    components_stats_panel[components.stats_panel<br/>components/stats_panel]
    components_step_renderer[components.step_renderer<br/>components/step_renderer]
    html_ids[html_ids<br/>html_ids]
    models[models<br/>models]
    routes_browser[routes.browser<br/>routes/browser]
    routes_core[routes.core<br/>routes/core]
    routes_init[routes.init<br/>routes/init]
    routes_preview[routes.preview<br/>routes/preview]
    routes_selection[routes.selection<br/>routes/selection]
    routes_verify[routes.verify<br/>routes/verify]
    services_source_select[services.source_select<br/>services/source_select]
    utils[utils<br/>utils]

    components_file_browser_panel --> html_ids
    components_preview_panel --> models
    components_preview_panel --> utils
    components_preview_panel --> html_ids
    components_selection_panel --> models
    components_selection_panel --> utils
    components_selection_panel --> html_ids
    components_stats_panel --> models
    components_stats_panel --> html_ids
    components_step_renderer --> models
    components_step_renderer --> components_selection_panel
    components_step_renderer --> components_preview_panel
    components_step_renderer --> html_ids
    components_step_renderer --> components_stats_panel
    routes_browser --> models
    routes_browser --> utils
    routes_browser --> components_file_browser_panel
    routes_browser --> routes_core
    routes_browser --> components_selection_panel
    routes_browser --> components_stats_panel
    routes_core --> models
    routes_init --> models
    routes_init --> routes_selection
    routes_init --> services_source_select
    routes_init --> routes_browser
    routes_init --> routes_preview
    routes_init --> routes_verify
    routes_preview --> routes_core
    routes_preview --> models
    routes_preview --> components_preview_panel
    routes_selection --> models
    routes_selection --> utils
    routes_selection --> routes_core
    routes_selection --> components_selection_panel
    routes_selection --> components_stats_panel
    routes_verify --> routes_core
    routes_verify --> services_source_select
    routes_verify --> models
    routes_verify --> components_stats_panel
    routes_verify --> components_selection_panel
```

*40 cross-module dependencies detected*

## CLI Reference

No CLI commands found in this project.

## Module Overview

Detailed documentation for each module in the project:

### routes/browser (`browser.ipynb`)

> Route handlers for file browser navigation and file selection

#### Import

``` python
from cjm_transcription_source_select.routes.browser import (
    init_browser_router
)
```

#### Functions

``` python
def _list_media_in_folder(
    folder_path: str,  # Directory to scan for media files
) -> List[SelectedFile]:  # Media files found as SelectedFile dicts
    "List media files in a directory (shallow, no recursion)."
```

``` python
def _toggle_file(
    path: str,  # File path to toggle
    selected_files: List[Dict[str, Any]],  # Current selection (mutated)
    selected_folders: List[str],  # Current folder selections (mutated)
    extraction_results: Dict[str, Any],  # Extraction results (mutated)
) -> None:  # Mutates lists in place
    "Toggle a single file in/out of the selection."
```

``` python
def _toggle_folder(
    path: str,  # Folder path to toggle
    selected_files: List[Dict[str, Any]],  # Current selection (mutated)
    selected_folders: List[str],  # Current folder selections (mutated)
    extraction_results: Dict[str, Any],  # Extraction results (mutated)
) -> None:  # Mutates lists in place
    "Toggle all media files in a folder (shallow bulk-select)."
```

``` python
def init_browser_router(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    provider: LocalFileSystemProvider,  # File system provider
    config: FileBrowserConfig,  # Browser configuration
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # Mutable URL bundle (for OOB rendering)
    home_path: str = "",  # Home directory for nav buttons
    prefix: str = "/browser",  # Route prefix
) -> BrowserResult:  # Browser result with routers, render, restore, and reset
    "Initialize the file browser with selection callbacks."
```

### routes/core (`core.ipynb`)

> State management helpers for the transcription source selection step

#### Import

``` python
from cjm_transcription_source_select.routes.core import (
    STEP_KEY,
    get_step_state,
    update_step_state,
    get_session_id_from_sess
)
```

#### Functions

``` python
def get_step_state(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    session_id: str,  # Session identifier
) -> SourceSelectState:  # Current step state (empty dict if not found)
    "Retrieve the source selection step state."
```

``` python
def update_step_state(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    session_id: str,  # Session identifier
    **fields,  # Fields to update in the step state
) -> SourceSelectState:  # Updated step state
    "Update specific fields in the source selection step state."
```

``` python
def get_session_id_from_sess(
    sess: Any  # Session object from FastHTML
) -> str:  # Session identifier string
    "Extract session ID from a FastHTML session object."
```

#### Variables

``` python
STEP_KEY = 'source_select'
```

### components/file_browser_panel (`file_browser_panel.ipynb`)

> File browser panel configuration and rendering for browsing local
> audio/video files

#### Import

``` python
from cjm_transcription_source_select.components.file_browser_panel import (
    AUDIO_FILTER_EXTENSIONS,
    VIDEO_FILTER_EXTENSIONS,
    MEDIA_FILTER_EXTENSIONS,
    create_media_browser_config,
    get_browser_state,
    sync_browser_selection,
    render_browser_panel
)
```

#### Functions

``` python
def create_media_browser_config() -> FileBrowserConfig:  # Configured for audio/video file selection
    "Create file browser config for audio/video file selection with folder bulk-select."
```

``` python
def get_browser_state(
    step_state: Dict[str, Any],  # Current step state
    default_path: str = "",  # Default directory if no state exists
) -> BrowserState:  # Browser state for the file browser
    "Get or create BrowserState from step state."
```

``` python
def sync_browser_selection(
    browser_state: BrowserState,  # Browser state to update
    selected_files: List[Dict[str, Any]],  # Current selected_files from step state
    selected_folders: Optional[List[str]] = None,  # Folder paths toggled for bulk select
) -> None:  # Modifies browser_state in place
    "Sync browser selection state with selected files and folders."
```

``` python
def render_browser_panel(
    render_fn: Callable,  # FileBrowserRouters.render callable
) -> Any:  # Rendered file browser in a flex container
    "Render the file browser panel using the library's self-contained VC component."
```

#### Variables

``` python
AUDIO_FILTER_EXTENSIONS = [8 items]
VIDEO_FILTER_EXTENSIONS = [7 items]
MEDIA_FILTER_EXTENSIONS
```

### html_ids (`html_ids.ipynb`)

> HTML ID constants for the transcription source selection step

#### Import

``` python
from cjm_transcription_source_select.html_ids import (
    SourceSelectHtmlIds
)
```

#### Classes

``` python
class SourceSelectHtmlIds:
    "HTML ID constants for the transcription source selection step."
    
    def as_selector(
            id_str: str  # The HTML ID to convert
        ) -> str:  # CSS selector with # prefix
        "Convert an ID to a CSS selector format."
    
    def file_item(
            file_path: str  # Absolute path to the file
        ) -> str:  # HTML ID for the file item in the browser
        "Generate HTML ID for a file item in the browser list."
    
    def selection_item(
            file_path: str  # Absolute path to the file
        ) -> str:  # HTML ID for the selection list item
        "Generate HTML ID for a selected file item."
    
    def extraction_status(
            file_path: str  # Absolute path to the video file
        ) -> str:  # HTML ID for the extraction status indicator
        "Generate HTML ID for a video file's extraction status."
```

### routes/init (`init.ipynb`)

> Router assembly — creates all sub-routers, populates URL bundle,
> returns unified interface

#### Import

``` python
from cjm_transcription_source_select.routes.init import (
    init_source_select_routers
)
```

#### Functions

``` python
def init_source_select_routers(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    provider: LocalFileSystemProvider,  # File system provider
    browser_config: FileBrowserConfig,  # Browser configuration
    workflow_id: str,  # Workflow identifier
    service: SourceSelectService,  # Source select service (FFmpeg plugin)
    home_path: str = "",  # Home directory for nav buttons
    prefix: str = "/source_select",  # Base route prefix
) -> SourceSelectResult:  # Source select result with routers, urls, render, restore, and reset
    "Initialize all source selection routers and populate URL bundle."
```

### models (`models.ipynb`)

> Data models and URL bundles for the transcription source selection
> step

#### Import

``` python
from cjm_transcription_source_select.models import (
    SelectedFile,
    ExtractionResult,
    SourceSelectState,
    SourceSelectUrls,
    BrowserResult,
    SourceSelectResult
)
```

#### Functions

``` python
def _no_op_restore(session_id: str) -> None:
    """Default no-op for restore_state."""
    pass

def _no_op_reset() -> None
    "Default no-op for restore_state."
```

``` python
def _no_op_reset() -> None:
    """Default no-op for reset_state."""
    pass

@dataclass
class BrowserResult
    "Default no-op for reset_state."
```

#### Classes

``` python
class SelectedFile(TypedDict):
    "A selected audio or video file."
```

``` python
class ExtractionResult(TypedDict):
    "Audio extraction result for a video file."
```

``` python
class SourceSelectState(TypedDict):
    "State for the transcription source selection step."
```

``` python
@dataclass
class SourceSelectUrls:
    "URL bundle for transcription source selection routes."
    
    remove: str = ''  # Remove file from selection
    reorder: str = ''  # Reorder selection (SortableJS)
    clear: str = ''  # Clear all selected files
    toggle_all: str = ''  # Toggle all media in current directory
    preview: str = ''  # Preview a file (render player)
    media_src: str = ''  # Serve a local media file for HTML5 players
    verify: str = ''  # Verify selection + trigger extraction
    extraction_status: str = ''  # Poll extraction status
```

``` python
@dataclass
class BrowserResult:
    "Return type from init_browser_router."
    
    routers: FileBrowserRouters  # File browser routers bundle
    render_panel: Callable  # (selected_files?, selected_folders?, session_id?) -> rendered panel
    restore_state: Callable = field(...)  # (session_id) -> None, restore persisted state
    reset_state: Callable = field(...)  # () -> None, reset in-memory caches
```

``` python
@dataclass
class SourceSelectResult:
    "Return type from init_source_select_routers."
    
    routers: List[APIRouter] = field(...)  # All routers to register
    urls: 'SourceSelectUrls'  # URL bundle
    render_panel: Callable  # Render fn for browser panel
    restore_state: Callable = field(...)  # (session_id) -> None, restore persisted state
    reset_state: Callable = field(...)  # () -> None, reset in-memory caches
```

### routes/preview (`preview.ipynb`)

> Route handlers for media file serving and preview panel rendering

#### Import

``` python
from cjm_transcription_source_select.routes.preview import (
    init_preview_router
)
```

#### Functions

``` python
def _handle_media_src(
    path: str,  # Absolute path to the media file
) -> Response:  # FileResponse or 404
    "Serve a local media file with appropriate MIME type."
```

``` python
def _handle_preview(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # URL bundle
    sess,  # FastHTML session
    path: str,  # File path to preview
):  # Rendered preview panel
    "Render the preview panel for a selected file."
```

``` python
def init_preview_router(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # Mutable URL bundle
    prefix: str = "/preview",  # Route prefix
) -> Tuple[APIRouter, Dict[str, Callable]]:  # (router, route_dict)
    "Initialize preview routes for media serving and preview panel rendering."
```

### components/preview_panel (`preview_panel.ipynb`)

> Collapsible preview panel with audio/video player and file metadata

#### Import

``` python
from cjm_transcription_source_select.components.preview_panel import (
    render_preview_panel
)
```

#### Functions

``` python
def _render_metadata_row(
    label: str,  # Label text
    value: str,  # Value text
) -> Div:  # Metadata row element
    "Render a single metadata label-value pair."
```

``` python
def _render_file_metadata(
    selected_file: SelectedFile,  # File to display metadata for
) -> Div:  # Metadata section
    "Render file metadata section."
```

``` python
def render_preview_panel(
    selected_file: Optional[SelectedFile] = None,  # File to preview (None for empty state)
    media_src_url: str = "",  # Base URL for media file serving
    is_open: bool = False,  # Whether panel starts open
) -> Div:  # Preview panel component
    "Render a collapsible preview panel with audio/video player."
```

### routes/selection (`selection.ipynb`)

> Route handlers for selection queue management (remove, reorder, clear)

#### Import

``` python
from cjm_transcription_source_select.routes.selection import (
    DEBUG_REORDER,
    init_selection_router
)
```

#### Functions

``` python
def _render_oob_checkboxes(
    fb_routers: FileBrowserRouters,  # File browser routers
    selected_files: list,            # Current selected files
    selected_folders: list,          # Current selected folders
    changed_paths: list,             # Paths whose checkbox state changed
) -> tuple:  # OOB cell elements for visible checkboxes
    "Sync selection state and return targeted checkbox OOBs for changed paths."
```

``` python
def _handle_remove(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # URL bundle
    fb_routers: FileBrowserRouters,  # File browser routers
    sess,  # FastHTML session
    key: str,  # Item key (file path) to remove
):  # OOB tuple (selection panel, checkbox OOBs, stats content)
    "Remove a file from the selection by key."
```

``` python
async def _handle_reorder(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # URL bundle
    request,  # FastHTML request
    sess,  # FastHTML session
):  # Rendered selection panel
    "Reorder selected files based on SortableJS drag or keyboard Shift+Arrow."
```

``` python
def _handle_clear(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # URL bundle
    fb_routers: FileBrowserRouters,  # File browser routers
    sess,  # FastHTML session
):  # OOB tuple (selection panel, checkbox OOBs, stats content)
    "Clear all selected files and folders."
```

``` python
def _handle_toggle_all(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # URL bundle
    fb_routers: FileBrowserRouters,  # File browser routers
    sess,  # FastHTML session
):  # OOB tuple (selection panel, checkbox OOBs, stats content)
    "Toggle all media files in the current directory."
```

``` python
def init_selection_router(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    provider: LocalFileSystemProvider,  # File system provider (for OOB browser updates)
    config: FileBrowserConfig,  # Browser configuration (for OOB browser updates)
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # Mutable URL bundle
    home_path: str = "",  # Home directory path
    fb_routers: FileBrowserRouters = None,  # File browser routers (for OOB sync)
    prefix: str = "/selection",  # Route prefix
) -> Tuple[APIRouter, Dict[str, Callable]]:  # (router, route_dict)
    "Initialize selection queue management routes."
```

#### Variables

``` python
DEBUG_REORDER = False
```

### components/selection_panel (`selection_panel.ipynb`)

> Selection panel component showing selected files with drag-drop
> reordering

#### Import

``` python
from cjm_transcription_source_select.components.selection_panel import (
    TSS_QUEUE_PREFIX,
    TSS_QUEUE_CONFIG,
    TSS_QUEUE_IDS,
    render_selection_panel
)
```

#### Functions

``` python
def _render_type_badge(
    file_type: str,  # "audio" or "video"
) -> Span:  # Badge component
    "Render a type badge for a selected file."
```

``` python
def _render_extraction_status(
    extraction_result: Optional[ExtractionResult],  # Extraction result for this video file
    file_path: str,  # File path for HTML ID
) -> Optional[Span]:  # Status indicator or None
    "Render extraction status indicator for a video file."
```

``` python
def _make_render_content(
    urls: SourceSelectUrls,  # URL bundle for preview button
    extraction_results: Optional[Dict[str, ExtractionResult]] = None,  # video_path → result
):  # Returns a render_content callback
    "Create a render_content callback with access to URLs and extraction results."
```

``` python
def _render_queue_empty() -> Any:  # Empty state element
    """Render the custom empty state for the file selection queue."""
    return P(
        "Click files in the browser to select them",
        cls=combine_classes(text_tiers.muted, text_align.center, font_size.md, p(2))
    )


def render_selection_panel(
    selected_files: List[SelectedFile],  # Ordered list of selected files
    urls: SourceSelectUrls,  # URL bundle
    extraction_results: Optional[Dict[str, ExtractionResult]] = None,  # video_path → result
) -> Div:  # Selection panel component
    "Render the custom empty state for the file selection queue."
```

``` python
def render_selection_panel(
    selected_files: List[SelectedFile],  # Ordered list of selected files
    urls: SourceSelectUrls,  # URL bundle
    extraction_results: Optional[Dict[str, ExtractionResult]] = None,  # video_path → result
) -> Div:  # Selection panel component
    "Render the selection panel via cjm-fasthtml-sortable-queue."
```

#### Variables

``` python
TSS_QUEUE_PREFIX = 'tss'
TSS_QUEUE_CONFIG
TSS_QUEUE_IDS
```

### services/source_select (`source_select.ipynb`)

> Service layer for file operations and audio extraction from video via
> FFmpeg plugin

#### Import

``` python
from cjm_transcription_source_select.services.source_select import (
    SourceSelectService
)
```

#### Classes

``` python
class SourceSelectService:
    def __init__(self,
                 plugin_manager: PluginManager,  # Plugin manager instance
                 plugin_name: str = "cjm-media-plugin-ffmpeg",  # FFmpeg plugin name
                )
    "Service for file metadata and audio extraction from video."
    
    def __init__(self,
                     plugin_manager: PluginManager,  # Plugin manager instance
                     plugin_name: str = "cjm-media-plugin-ffmpeg",  # FFmpeg plugin name
                    )
    
    def is_available(self) -> bool:  # True if plugin is loaded
            """Check if the FFmpeg plugin is loaded."""
            return self._manager.get_plugin(self._plugin_name) is not None
    
        def ensure_loaded(self,
                          config: Optional[Dict[str, Any]] = None,  # Plugin config override
                         ) -> bool:  # True if plugin is now loaded
        "Check if the FFmpeg plugin is loaded."
    
    def ensure_loaded(self,
                          config: Optional[Dict[str, Any]] = None,  # Plugin config override
                         ) -> bool:  # True if plugin is now loaded
        "Ensure the FFmpeg plugin is loaded, loading it if necessary."
    
    async def get_file_info(
            self,
            file_path: str,  # Path to media file
        ) -> Dict[str, Any]:  # MediaMetadata dict (duration, streams, etc.)
        "Get media file metadata via FFmpeg plugin."
    
    async def extract_audio(
            self,
            video_path: str,  # Path to video file
        ) -> Dict[str, Any]:  # {job_id, output_path, duration, codec, stream_copy}
        "Extract audio from a video file via FFmpeg plugin (stream copy)."
    
    def extract_audio_sync(
            self,
            video_path: str,  # Path to video file
        ) -> Dict[str, Any]:  # {job_id, output_path, duration, codec, stream_copy}
        "Synchronous wrapper for extract_audio."
```

### components/stats_panel (`stats_panel.ipynb`)

> Stats summary and verify selection button

#### Import

``` python
from cjm_transcription_source_select.components.stats_panel import (
    render_stats_content,
    render_stats_panel
)
```

#### Functions

``` python
def render_stats_content(
    selected_files: List[SelectedFile],  # Current selection
    urls: SourceSelectUrls,  # URL bundle
    extraction_results: Optional[Dict[str, ExtractionResult]] = None,  # Extraction results
    verified: bool = False,  # Whether selection is verified
    oob: bool = False,  # Whether to render as OOB outerHTML swap
) -> Div:  # Stats content (inner content only, no container chrome)
    "Render stats panel inner content for OOB updates."
```

``` python
def render_stats_panel(
    selected_files: List[SelectedFile],  # Current selection
    urls: SourceSelectUrls,  # URL bundle
    extraction_results: Optional[Dict[str, ExtractionResult]] = None,  # Extraction results
    verified: bool = False,  # Whether selection is verified
) -> Div:  # Full stats panel with container chrome
    "Render the full stats panel (container + content) for initial render."
```

#### Variables

``` python
_STATS_CONTAINER_CLS
```

### components/step_renderer (`step_renderer.ipynb`)

> Main step renderer combining all panels for the source selection step

#### Import

``` python
from cjm_transcription_source_select.components.step_renderer import (
    render_source_select_step
)
```

#### Functions

``` python
def _create_parent_keyboard_manager() -> ZoneManager:  # Parent keyboard manager for hierarchy
    "Create the parent keyboard manager with two ghost zones for column switching."
```

``` python
def _generate_hierarchy_js() -> Script:  # Script element with hierarchy wiring
    "Generate JavaScript for keyboard system hierarchy and child activation."
```

``` python
def render_source_select_step(
    selected_files: List[SelectedFile],  # Ordered selection
    extraction_results: Dict[str, ExtractionResult],  # video_path -> result
    verified: bool,  # Whether selection is verified
    urls: SourceSelectUrls,  # URL bundle
    render_browser_panel_fn: Callable,  # Browser panel render function from init_browser_router
    selected_folders: Optional[List[str]] = None,  # Folder paths for checkbox sync
) -> Div:  # Complete step view
    "Render the complete source selection step."
```

#### Variables

``` python
_VIEWPORT_FIT_CONFIG
_ZONE_FOCUS_CLASSES
```

### utils (`utils.ipynb`)

> Utility functions for file type detection, duration formatting, and
> extension filtering

#### Import

``` python
from cjm_transcription_source_select.utils import (
    AUDIO_EXTENSIONS,
    VIDEO_EXTENSIONS,
    MEDIA_EXTENSIONS,
    detect_file_type,
    is_media_file,
    format_duration
)
```

#### Functions

``` python
def detect_file_type(
    file_path: str  # Path to the file
) -> Optional[str]:  # "audio", "video", or None if not a media file
    "Detect whether a file is audio, video, or not a media file based on extension."
```

``` python
def is_media_file(
    file_path: str  # Path to the file
) -> bool:  # True if the file has a supported media extension
    "Check if a file has a supported audio or video extension."
```

``` python
def format_duration(
    seconds: Optional[float]  # Duration in seconds, or None
) -> str:  # Formatted duration string (e.g. "1:23:45" or "42:17")
    "Format a duration in seconds as H:MM:SS or MM:SS."
```

#### Variables

``` python
AUDIO_EXTENSIONS
VIDEO_EXTENSIONS
MEDIA_EXTENSIONS
```

### routes/verify (`verify.ipynb`)

> Route handler for verify selection + audio extraction from video
> sources

#### Import

``` python
from cjm_transcription_source_select.routes.verify import (
    logger,
    init_verify_router
)
```

#### Functions

``` python
async def _handle_verify(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # URL bundle
    service: SourceSelectService,  # Source select service
    sess,  # FastHTML session
):  # OOB tuple (stats content, selection panel)
    "Verify selection and extract audio from video files."
```

``` python
def init_verify_router(
    state_store: SQLiteWorkflowStateStore,  # Workflow state store
    workflow_id: str,  # Workflow identifier
    urls: SourceSelectUrls,  # Mutable URL bundle
    service: SourceSelectService,  # Source select service
    prefix: str = "/verify",  # Route prefix
) -> Tuple[APIRouter, Dict[str, Callable]]:  # (router, route_dict)
    "Initialize verify route for selection verification and audio extraction."
```
