Grassvalley AMPP job management application
Find a file
Claude c29245758c Handle unlinked high-res proxy blocks with fallback lookup
When proxyLinks is empty (no explicit .gves→hires linkage), now also process unlinked high-res proxy blocks that have:
- IsProxy: true
- FilePath: null
- Title metadata (e.g. 'File 1', 'File 2')

Uses fallback lookup to find matching files in hiresMediaFolder by Title, then injects FilePath into the XML Media block. This handles the case where FramelightX creates proxies but doesn't establish explicit linkages.
2026-03-31 19:53:22 -04:00
public Add high-res media folder fallback lookup for unmapped proxy files 2026-03-31 19:39:42 -04:00
.dockerignore Add .dockerignore 2026-03-31 15:29:48 -04:00
.env.example Add .env.example 2026-03-31 15:29:48 -04:00
ame-log-parser.js feat: Support macOS Adobe Media Encoder log filenames 2026-03-31 17:22:18 -04:00
CHANGES.md feat: Add SMB network share auto-mounting support 2026-03-31 16:03:40 -04:00
docker-compose.yml Fix: Bind SMB subdirectories correctly in docker-compose.yml 2026-03-31 16:55:55 -04:00
Dockerfile feat: Add SMB network share auto-mounting support 2026-03-31 16:03:40 -04:00
entrypoint.sh simplify: Remove unnecessary bind-mounts in entrypoint 2026-03-31 16:28:44 -04:00
LICENSE Initial commit 2026-03-31 15:26:09 -04:00
package-lock.json Add package-lock.json 2026-03-31 15:29:49 -04:00
package.json Add package.json 2026-03-31 15:29:49 -04:00
prproj-remapper.js Handle unlinked high-res proxy blocks with fallback lookup 2026-03-31 19:53:22 -04:00
README.md docs: Add detailed architecture and troubleshooting guide 2026-03-31 17:00:21 -04:00
server.js Add high-res media folder fallback lookup for unmapped proxy files 2026-03-31 19:39:42 -04:00
test-remap.js Add test-remap.js 2026-03-31 15:29:50 -04:00

AME Remote Job Manager

A web-based job submission and monitoring tool for Adobe Media Encoder (AME) in a Grassvalley AMPP environment. Editors upload .prproj files through the browser, the server remaps .gves proxy paths to high-resolution UNC paths, then delivers the remapped project to AME's watch folder for automated rendering.

How It Works

  1. Editor uploads a Premiere Pro .prproj file via the web UI
  2. The server parses and remaps any .gves proxy media paths to their high-res UNC equivalents
  3. The remapped project file is written to AME's watch folder
  4. AME picks up the file, renders it, and writes output to the output folder
  5. The job manager polls both folders and updates job status automatically: queued → encoding → complete

Features

  • .prproj path remapping — replaces .gves proxy references with full-resolution UNC paths
  • Dry-run analysis mode — inspect what paths would be remapped before submitting
  • Real-time job status tracking via watch folder and output folder polling
  • AME log parsing — reads AMEEncodingLog.txt and error logs for encoding stats
  • SMB/UNC path configuration via the settings UI
  • Session-based authentication
  • Docker-ready with volume-backed persistent storage

Prerequisites

  • Docker and Docker Compose (with SYS_ADMIN capability enabled for SMB mounting)
  • Adobe Media Encoder running on a Windows machine with a configured watch folder
  • SMB network share accessible at //172.18.210.5/ame containing:
    • watch — folder where .prproj files are picked up by AME
    • output — folder where AME writes encoded files
    • logs — folder where AME writes AMEEncodingLog.txt
  • Network connectivity from Docker host to SMB server
  • .prproj files that reference .gves proxy media on the AMPP platform

Quick Start

  1. Clone the repo and copy the example env file:
cp .env.example .env
  1. Edit .env:
PORT=3100
AUTH_USER=admin
AUTH_PASS=changeme

# Docker volume mount paths
WATCH_FOLDER=/watch
OUTPUT_FOLDER=/output
AME_LOG_DIR=/ame-logs

# Polling interval (ms)
POLL_INTERVAL_MS=5000

# Job timeout — mark as error if AME hasn't produced output after this long (ms)
JOB_TIMEOUT_MS=3600000
  1. Mount the SMB share on the Docker host (one-time setup):
# Create mount point
sudo mkdir -p /mnt/smb-ame

# Mount the SMB share (adjust credentials/IP as needed)
sudo mount -t cifs //172.18.210.5/ame /mnt/smb-ame \
  -o username=smb,password=Production2020!,uid=1000,gid=1000,file_mode=0755,dir_mode=0755,vers=3.0

# Verify mount succeeded
mount | grep cifs
  1. Configure docker-compose.yml with bind-mounts to subdirectories, then start:
docker compose up -d
  1. Open http://localhost:3100 in your browser and log in.

Docker Compose Configuration

The key to this architecture is binding SMB subdirectories from the host into the container paths the app expects:

services:
  ame-job-manager:
    build: .
    ports:
      - "3100:3100"
    volumes:
      # Bind SMB subdirectories from host into container
      # The host must have these mounted (e.g., at /mnt/smb-ame) from the SMB server
      - /mnt/smb-ame/Watch:/watch              # Where AME watches for new .prproj files
      - /mnt/smb-ame/Output:/output            # Where AME writes encoded output
      - /mnt/smb-ame/Logs:/ame-logs            # Where AME writes AMEEncodingLog.txt

      # Persistent storage for job records and session data
      - app_data:/data
      - upload_tmp:/tmp/uploads
    env_file: .env
    restart: unless-stopped

volumes:
  app_data:
  upload_tmp:

Important: The paths /mnt/smb-ame/Watch, /mnt/smb-ame/Output, and /mnt/smb-ame/Logs must exist on the Docker host after mounting. Create them if they don't exist:

mkdir -p /mnt/smb-ame/{Watch,Output,Logs}

Architecture: Docker Container with Host-Level SMB Mounting

The job manager runs in Docker but needs access to SMB network shares for the watch folder, output folder, and AME logs. The architecture accomplishes this by:

  1. Host-level SMB mount: The Docker host mounts the SMB share using native Linux mount -t cifs, making it available at a path like /mnt/smb-ame
  2. Bind-mounts from host to container: docker-compose.yml binds specific subdirectories from the host into the container:
    • /mnt/smb-ame/Watch/watch (inside container)
    • /mnt/smb-ame/Output/output (inside container)
    • /mnt/smb-ame/Logs/ame-logs (inside container)
  3. Application accesses local paths: The Node.js app reads/writes to /watch, /output, /ame-logs as if they were local, unaware of the SMB infrastructure

Why This Approach?

We chose host-level SMB mounting over container-level mounting for several reasons:

  • Capability constraints: Docker requires SYS_ADMIN capability and apparmor=unconfined to mount CIFS from within a container. This introduces security surface and may fail depending on host kernel/Docker daemon version.
  • Reliability: SMB mounts are more stable and persistent when managed by the host OS (systemd, /etc/fstab) rather than ephemeral container entrypoint scripts.
  • Separation of concerns: The container doesn't need to know or care about SMB credentials—the host handles authentication, the container just accesses mounted paths.
  • Volume flexibility: If the SMB share is ever replaced with local storage or a different protocol, only the host mount needs to change; the container remains unaware.

SMB Network Share Configuration

The watch folder, output folder, and logs are expected to be on a shared network location (SMB/CIFS). The Docker host must mount this share at the path specified in docker-compose.yml volumes.

Configuration Steps

  1. Start the container (it will run with fallback Docker volumes if no SMB credentials yet)

  2. Open the Settings panel in the web UI (⚙️ icon in header)

  3. Fill in SMB credentials:

    • SMB Username: e.g., encoder or DOMAIN\encoder
    • SMB Password: Network password (stored server-side, never exposed to browser)
    • SMB Domain/Workgroup: Optional, e.g., WORKGROUP or BMG
    • Notes: Optional reference, e.g., \\172.18.210.5\ame
  4. Create subdirectories on the SMB share (if they don't exist):

    \\172.18.210.5\ame\
    ├── watch\        (AME's watch folder)
    ├── output\       (AME's output folder)
    └── logs\         (Contains AMEEncodingLog.txt)
    
  5. Restart the container — it will mount the SMB share on next startup

Troubleshooting SMB Mount Issues

  • Mount failed: Check that credentials are correct and the SMB server is reachable
  • Permission denied: Verify the SMB user has read/write access to the share
  • Container falls back to local volumes: Check Docker logs for mount errors: docker logs ame-job-manager
  • Shares already mounted locally: If the watch/output/logs folders are already mounted on the Docker host via /etc/fstab, use bind-mounts in docker-compose.yml instead:
volumes:
  - /mnt/host-smb-share/watch:/watch
  - /mnt/host-smb-share/output:/output
  - /mnt/host-smb-share/logs:/ame-logs

In this case, you don't need to configure SMB credentials in the app settings.

Job Lifecycle

Status Meaning
queued File written to watch folder, waiting for AME to pick it up
encoding File disappeared from watch folder — AME is actively rendering
complete Output file detected in the output folder
error Job timed out or AME log reported an error

The server polls both folders every POLL_INTERVAL_MS milliseconds to detect status transitions.

Path Remapping

The core function of this tool is to make .prproj files renderable on a high-res render machine. Premiere projects created on AMPP workstations often reference .gves proxy files. This server rewrites those references to the corresponding UNC/high-res paths before handing the project to AME.

The remapping logic lives in prproj-remapper.js. You can test a remap without submitting a job using the analyze endpoint or the dry-run button in the UI.

API Reference

Method Endpoint Description
POST /api/login Authenticate and get a session ID
POST /api/logout End session
POST /api/jobs Submit a .prproj file and create a job
POST /api/jobs/analyze Dry-run analysis of a .prproj (no submission)
GET /api/jobs List all jobs
GET /api/jobs/:id Get a single job by ID
DELETE /api/jobs/:id Delete a job record
GET /api/status System status — folder health, job counts, AME log stats
GET /api/ame/logs Full AME log data with recent entries
GET /api/settings Get current settings
POST /api/settings Update settings (watch folder, SMB config, etc.)

All endpoints except /api/login require an x-session-id header from a valid login.

Environment Variables

Variable Default Description
PORT 3100 HTTP port
AUTH_USER admin Login username
AUTH_PASS password Login password
WATCH_FOLDER /watch Path AME watches for new projects
OUTPUT_FOLDER /output Path AME writes rendered output to
DATA_DIR /data Persistent storage for job records and sessions
UPLOAD_TEMP /tmp/uploads Temp dir for incoming file uploads
POLL_INTERVAL_MS 5000 How often to poll watch/output folders (ms)
JOB_TIMEOUT_MS 3600000 Time before a stuck job is marked as error (ms)
AME_LOG_DIR /ame-logs Directory containing AMEEncodingLog.txt

Architecture Decisions

Host-Level SMB Mounting (Final Approach)

Decision: Mount the SMB share on the Docker host at /mnt/smb-ame, then bind-mount individual subdirectories into the container.

Why not container-level mounting?

  • Container-level CIFS mounting requires Docker SYS_ADMIN capability and disabling AppArmor, introducing security risks
  • Host kernel version or Docker daemon configuration may not support container-level mounts
  • SMB credentials in container entrypoint scripts are harder to manage and rotate

Why not a single mount point?

  • Initial implementation mounted /mnt/smb-ame as /smb-share in the container, then created separate Docker volumes for /watch, /output, /ame-logs
  • This caused uploaded files to go to ephemeral Docker volumes instead of the SMB share
  • Fixed by binding each SMB subdirectory directly to its container path

Final solution:

  • Host mounts SMB at /mnt/smb-ame (persists in /etc/fstab or systemd automount)
  • docker-compose.yml specifies three bind-mounts:
    - /mnt/smb-ame/Watch:/watch
    - /mnt/smb-ame/Output:/output
    - /mnt/smb-ame/Logs:/ame-logs
    
  • App reads/writes to /watch, /output, /ame-logs as local paths
  • Files automatically appear on the SMB share where AME can access them

Separation of Concerns

The Docker container intentionally does NOT handle SMB mounting. This separation ensures:

  • Security: SMB credentials live on the host, never in container code or .env files
  • Reliability: Host OS manages mount persistence; container restart doesn't affect SMB access
  • Portability: Container works with any mounted filesystem (SMB, NFS, local, etc.) — no protocol assumptions
  • Operations: Infrastructure team manages storage layer; application team manages app container

Why .prproj Path Remapping?

The core value of this tool is translating proxy-resolution Premiere projects to high-resolution paths:

  • AMPP Workflow: Editors on Grassvalley AMPP workstations edit with .gves proxy media for responsiveness
  • Problem: AME on a dedicated render machine can't resolve .gves paths — they're relative to AMPP infrastructure
  • Solution: Before handing the project to AME, rewrite all .gves references to their corresponding high-resolution UNC paths
  • Result: AME can render the full-resolution media without the editor needing to manage two versions of the project

Why Not Auto-Generate Remappings?

The remapping rules are configured manually in the app settings because:

  • .gves files and their high-res equivalents may not follow a consistent naming pattern
  • Different projects may use different proxy strategies
  • Manual configuration is explicit and auditable — you can see what gets remapped and why

Troubleshooting

Files Don't Appear on SMB Share After Upload

Symptom: File appears in job queue but isn't visible at //172.18.210.5/ame/Watch on the network.

Check 1: Verify SMB is mounted on the host

mount | grep cifs

Check 2: Verify subdirectories exist and are accessible

ls -la /mnt/smb-ame/Watch
ls -la /mnt/smb-ame/Output
ls -la /mnt/smb-ame/Logs

Check 3: Verify container has the bind-mounts

docker inspect ame-job-manager | jq '.Mounts[] | {Source, Destination}'

Check 4: Verify file is actually written to /watch inside the container

docker exec ame-job-manager ls -la /watch/

If file is in /watch inside container but not on host SMB share: The bind-mount isn't working. Verify:

  • /mnt/smb-ame/Watch exists on the host
  • SMB is mounted (check mount | grep cifs)
  • File permissions allow writing (ls -la /mnt/smb-ame)

SMB Mount Fails on Host

Symptom: mount -t cifs command fails with "Permission denied" or "Connection refused"

Check credentials:

# Test with correct username/password/domain
sudo mount -t cifs //172.18.210.5/ame /mnt/smb-ame \
  -o username=smb,password=YourPassword,domain=WORKGROUP

Check network connectivity:

# Can we reach the SMB server?
ping 172.18.210.5
# Can we list the share?
smbclient -L //172.18.210.5 -U smb -p YourPassword

Check subdirectories exist on the share:

smbclient //172.18.210.5/ame -U smb -p YourPassword
> ls

Container Starts but No Files Appear

Symptom: Container is running, upload succeeds, but files don't show up anywhere.

Check 1: Are there any uploads at all?

docker exec ame-job-manager find /tmp/uploads -type f

Check 2: Are files being written to /watch?

docker exec ame-job-manager ls -la /watch/

Check 3: Check Docker logs for errors during startup

docker logs ame-job-manager

Look for mount errors or permission issues in the logs.