Files
Transcriptarr/docs/FRONTEND.md
Dasemu 8373d8765f docs: add comprehensive project documentation
- Replace original Subgen README with TranscriptorIO documentation
- Add docs/API.md with 45+ REST endpoint documentation
- Add docs/ARCHITECTURE.md with backend component details
- Add docs/FRONTEND.md with Vue 3 frontend structure
- Add docs/CONFIGURATION.md with settings system documentation
- Remove outdated backend/README.md
2026-01-16 15:10:41 +01:00

14 KiB

TranscriptorIO Frontend

Technical documentation for the Vue 3 frontend application.

Table of Contents


Overview

The TranscriptorIO frontend is a Single Page Application (SPA) built with Vue 3, featuring:

  • 6 Complete Views: Dashboard, Queue, Scanner, Rules, Workers, Settings
  • Real-time Updates: Polling-based status updates
  • Dark Theme: Tdarr-inspired dark UI
  • Type Safety: Full TypeScript support
  • State Management: Pinia stores for shared state

Technology Stack

Technology Version Purpose
Vue.js 3.4+ UI Framework
Vue Router 4.2+ Client-side routing
Pinia 2.1+ State management
Axios 1.6+ HTTP client
TypeScript 5.3+ Type safety
Vite 5.0+ Build tool / dev server

Directory Structure

frontend/
├── public/                     # Static assets (favicon, etc.)
├── src/
│   ├── main.ts                 # Application entry point
│   ├── App.vue                 # Root component + navigation
│   │
│   ├── views/                  # Page components (routed)
│   │   ├── DashboardView.vue   # System overview + resources
│   │   ├── QueueView.vue       # Job management
│   │   ├── ScannerView.vue     # Scanner control
│   │   ├── RulesView.vue       # Scan rules CRUD
│   │   ├── WorkersView.vue     # Worker pool management
│   │   └── SettingsView.vue    # Settings management
│   │
│   ├── components/             # Reusable components
│   │   ├── ConnectionWarning.vue  # Backend connection status
│   │   ├── PathBrowser.vue        # Filesystem browser modal
│   │   └── SetupWizard.vue        # First-run setup wizard
│   │
│   ├── stores/                 # Pinia state stores
│   │   ├── config.ts           # Configuration store
│   │   ├── system.ts           # System status store
│   │   ├── workers.ts          # Workers store
│   │   └── jobs.ts             # Jobs store
│   │
│   ├── services/
│   │   └── api.ts              # Axios API client
│   │
│   ├── router/
│   │   └── index.ts            # Vue Router configuration
│   │
│   ├── types/
│   │   └── api.ts              # TypeScript interfaces
│   │
│   └── assets/
│       └── css/
│           └── main.css        # Global styles (dark theme)
│
├── index.html                  # HTML template
├── vite.config.ts              # Vite configuration
├── tsconfig.json               # TypeScript configuration
└── package.json                # Dependencies

Development Setup

Prerequisites

  • Node.js 18+ and npm
  • Backend server running on port 8000

Installation

cd frontend

# Install dependencies
npm install

# Start development server (with proxy to backend)
npm run dev

Development URLs

URL Description
http://localhost:3000 Frontend dev server
http://localhost:8000 Backend API
http://localhost:8000/docs Swagger API docs

Scripts

npm run dev      # Start dev server with HMR
npm run build    # Build for production
npm run preview  # Preview production build
npm run lint     # Run ESLint

Views

DashboardView

Path: /

System overview with real-time resource monitoring.

Features:

  • System status (running/stopped)
  • CPU usage gauge
  • RAM usage gauge
  • GPU usage gauges (per device)
  • Recent jobs list
  • Worker pool summary
  • Scanner status

Data Sources:

  • GET /api/status
  • GET /api/system/resources
  • GET /api/jobs?page_size=10

QueueView

Path: /queue

Job queue management with filtering and pagination.

Features:

  • Job list with status icons
  • Status filter (All/Queued/Processing/Completed/Failed)
  • Pagination controls
  • Retry failed jobs
  • Cancel queued/processing jobs
  • Clear completed jobs
  • Job progress display
  • Processing time display

Data Sources:

  • GET /api/jobs
  • GET /api/jobs/stats
  • POST /api/jobs/{id}/retry
  • DELETE /api/jobs/{id}
  • POST /api/jobs/queue/clear

ScannerView

Path: /scanner

Library scanner control and configuration.

Features:

  • Scanner status display
  • Start/stop scheduler
  • Start/stop file watcher
  • Manual scan trigger
  • Scan results display
  • Next scan time
  • Total files scanned counter

Data Sources:

  • GET /api/scanner/status
  • POST /api/scanner/scan
  • POST /api/scanner/scheduler/start
  • POST /api/scanner/scheduler/stop
  • POST /api/scanner/watcher/start
  • POST /api/scanner/watcher/stop

RulesView

Path: /rules

Scan rules CRUD management.

Features:

  • Rules list with priority ordering
  • Create new rule (modal)
  • Edit existing rule (modal)
  • Delete rule (with confirmation)
  • Toggle rule enabled/disabled
  • Condition configuration
  • Action configuration

Data Sources:

  • GET /api/scan-rules
  • POST /api/scan-rules
  • PUT /api/scan-rules/{id}
  • DELETE /api/scan-rules/{id}
  • POST /api/scan-rules/{id}/toggle

WorkersView

Path: /workers

Worker pool management.

Features:

  • Worker list with status
  • Add CPU worker
  • Add GPU worker (with device selection)
  • Remove worker
  • Start/stop pool
  • Worker statistics
  • Current job display per worker
  • Progress and ETA display

Data Sources:

  • GET /api/workers
  • GET /api/workers/stats
  • POST /api/workers
  • DELETE /api/workers/{id}
  • POST /api/workers/pool/start
  • POST /api/workers/pool/stop

SettingsView

Path: /settings

Database-backed settings management.

Features:

  • Settings grouped by category
  • Category tabs (General, Workers, Transcription, Scanner, Bazarr)
  • Edit settings in-place
  • Save changes button
  • Change detection (unsaved changes warning)
  • Setting descriptions

Data Sources:

  • GET /api/settings
  • PUT /api/settings/{key}
  • POST /api/settings/bulk-update

Components

ConnectionWarning

Displays warning banner when backend is unreachable.

Props: None

State: Uses systemStore.isConnected

PathBrowser

Modal component for browsing filesystem paths.

Props:

  • show: boolean - Show/hide modal
  • initialPath: string - Starting path

Emits:

  • select(path: string) - Path selected
  • close() - Modal closed

API Calls:

  • GET /api/filesystem/browse?path={path}
  • GET /api/filesystem/common-paths

SetupWizard

First-run setup wizard component.

Props: None

Features:

  • Mode selection (Standalone/Bazarr)
  • Library path configuration
  • Scan rule creation
  • Worker configuration
  • Scanner interval setting

API Calls:

  • GET /api/setup/status
  • POST /api/setup/standalone
  • POST /api/setup/bazarr-slave
  • POST /api/setup/skip

State Management

Pinia Stores

systemStore (stores/system.ts)

Global system state.

interface SystemState {
  isConnected: boolean
  status: SystemStatus | null
  resources: SystemResources | null
  loading: boolean
  error: string | null
}

// Actions
fetchStatus()      // Fetch /api/status
fetchResources()   // Fetch /api/system/resources
startPolling()     // Start auto-refresh
stopPolling()      // Stop auto-refresh

workersStore (stores/workers.ts)

Worker pool state.

interface WorkersState {
  workers: Worker[]
  stats: WorkerStats | null
  loading: boolean
  error: string | null
}

// Actions
fetchWorkers()                      // Fetch all workers
fetchStats()                        // Fetch pool stats
addWorker(type, deviceId?)          // Add worker
removeWorker(id)                    // Remove worker
startPool(cpuCount, gpuCount)       // Start pool
stopPool()                          // Stop pool

jobsStore (stores/jobs.ts)

Job queue state.

interface JobsState {
  jobs: Job[]
  stats: QueueStats | null
  total: number
  page: number
  pageSize: number
  statusFilter: string | null
  loading: boolean
  error: string | null
}

// Actions
fetchJobs()                 // Fetch with current filters
fetchStats()                // Fetch queue stats
retryJob(id)                // Retry failed job
cancelJob(id)               // Cancel job
clearCompleted()            // Clear completed jobs
setStatusFilter(status)     // Update filter
setPage(page)               // Change page

configStore (stores/config.ts)

Settings configuration state.

interface ConfigState {
  settings: Setting[]
  loading: boolean
  error: string | null
  pendingChanges: Record<string, string>
}

// Actions
fetchSettings(category?)    // Fetch settings
updateSetting(key, value)   // Queue update
saveChanges()               // Save all pending
discardChanges()            // Discard pending

API Service

Configuration (services/api.ts)

import axios from 'axios'

const api = axios.create({
  baseURL: '/api',
  timeout: 30000,
  headers: {
    'Content-Type': 'application/json'
  }
})

// Response interceptor for error handling
api.interceptors.response.use(
  response => response,
  error => {
    console.error('API Error:', error)
    return Promise.reject(error)
  }
)

export default api

Usage Example

import api from '@/services/api'

// GET request
const response = await api.get('/jobs', {
  params: { status_filter: 'queued', page: 1 }
})

// POST request
const job = await api.post('/jobs', {
  file_path: '/media/video.mkv',
  target_lang: 'spa'
})

// PUT request
await api.put('/settings/worker_cpu_count', {
  value: '2'
})

// DELETE request
await api.delete(`/jobs/${jobId}`)

Routing

Route Configuration

const routes = [
  { path: '/', name: 'Dashboard', component: DashboardView },
  { path: '/workers', name: 'Workers', component: WorkersView },
  { path: '/queue', name: 'Queue', component: QueueView },
  { path: '/scanner', name: 'Scanner', component: ScannerView },
  { path: '/rules', name: 'Rules', component: RulesView },
  { path: '/settings', name: 'Settings', component: SettingsView }
]

Navigation

Navigation is handled in App.vue with a sidebar menu.

<nav class="sidebar">
  <router-link to="/">Dashboard</router-link>
  <router-link to="/workers">Workers</router-link>
  <router-link to="/queue">Queue</router-link>
  <router-link to="/scanner">Scanner</router-link>
  <router-link to="/rules">Rules</router-link>
  <router-link to="/settings">Settings</router-link>
</nav>

<main class="content">
  <router-view />
</main>

Styling

Dark Theme

The application uses a Tdarr-inspired dark theme defined in assets/css/main.css.

Color Palette:

Variable Value Usage
--bg-primary #1a1a2e Main background
--bg-secondary #16213e Card background
--bg-tertiary #0f3460 Hover states
--text-primary #eaeaea Primary text
--text-secondary #a0a0a0 Secondary text
--accent-primary #e94560 Buttons, links
--accent-success #4ade80 Success states
--accent-warning #fbbf24 Warning states
--accent-error #ef4444 Error states

Component Styling

Components use scoped CSS with CSS variables:

<style scoped>
.card {
  background: var(--bg-secondary);
  border-radius: 8px;
  padding: 1.5rem;
}

.btn-primary {
  background: var(--accent-primary);
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  cursor: pointer;
}

.btn-primary:hover {
  opacity: 0.9;
}
</style>

Build and Deployment

Production Build

cd frontend
npm run build

This creates a dist/ folder with:

  • index.html - Entry HTML
  • assets/ - JS, CSS bundles (hashed filenames)

Deployment Options

The FastAPI backend automatically serves the frontend from frontend/dist/:

# backend/app.py
frontend_path = Path(__file__).parent.parent / "frontend" / "dist"

if frontend_path.exists():
    app.mount("/assets", StaticFiles(directory=str(frontend_path / "assets")))

    @app.get("/{full_path:path}")
    async def serve_frontend(full_path: str = ""):
        return FileResponse(str(frontend_path / "index.html"))

Access: http://localhost:8000

Option 2: Nginx Reverse Proxy

server {
    listen 80;
    server_name transcriptorio.local;

    # Frontend
    location / {
        root /var/www/transcriptorio/frontend/dist;
        try_files $uri $uri/ /index.html;
    }

    # Backend API
    location /api {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Option 3: Docker

# Build frontend
FROM node:18-alpine AS frontend-builder
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build

# Final image
FROM python:3.12-slim
COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist
# ... rest of backend setup

TypeScript Interfaces

Key Types (types/api.ts)

// Job
interface Job {
  id: string
  file_path: string
  file_name: string
  status: 'queued' | 'processing' | 'completed' | 'failed' | 'cancelled'
  priority: number
  progress: number
  // ... more fields
}

// Worker
interface Worker {
  worker_id: string
  worker_type: 'cpu' | 'gpu'
  device_id: number | null
  status: 'idle' | 'busy' | 'stopped' | 'error'
  current_job_id: string | null
  jobs_completed: number
  jobs_failed: number
}

// Setting
interface Setting {
  id: number
  key: string
  value: string | null
  description: string | null
  category: string | null
  value_type: string | null
}

// ScanRule
interface ScanRule {
  id: number
  name: string
  enabled: boolean
  priority: number
  conditions: ScanRuleConditions
  action: ScanRuleAction
}