Windows Adobe Media Encoder writes AMEEncodingLog.txt as UTF-16 LE with BOM. The parser was reading files as UTF-8, producing garbled output where every character had a null byte between it — causing all regex matching to fail silently and return zero parsed entries. Added readFileAutoEncoding() that detects UTF-16 LE/BE BOM and converts to UTF-8 before parsing. Also handles BOM-less UTF-16 by checking for null byte patterns. Additionally improved parser to handle Windows AME log format: - "File Encoded with warning" status lines (not just "Status: Done") - "Queue Started/Stopped" lines are now skipped - "Log File Created" header lines are skipped - Separator lines (dashes) are skipped - Offline media warnings and missing asset lines are captured - "warning" status counts as success in stats Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| public | ||
| .dockerignore | ||
| .env.example | ||
| ame-log-parser.js | ||
| CHANGES.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| entrypoint.sh | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| prproj-remapper.js | ||
| README.md | ||
| server.js | ||
| test-remap.js | ||
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
- Editor uploads a Premiere Pro
.prprojfile via the web UI - The server parses and remaps any
.gvesproxy media paths to their high-res UNC equivalents - The remapped project file is written to AME's watch folder
- AME picks up the file, renders it, and writes output to the output folder
- The job manager polls both folders and updates job status automatically:
queued → encoding → complete
Features
.prprojpath remapping — replaces.gvesproxy 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.txtand 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_ADMINcapability 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/amecontaining:watch— folder where .prproj files are picked up by AMEoutput— folder where AME writes encoded fileslogs— folder where AME writesAMEEncodingLog.txt
- Network connectivity from Docker host to SMB server
.prprojfiles that reference.gvesproxy media on the AMPP platform
Quick Start
- Clone the repo and copy the example env file:
cp .env.example .env
- 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
- 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
- Configure
docker-compose.ymlwith bind-mounts to subdirectories, then start:
docker compose up -d
- Open
http://localhost:3100in 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:
- 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 - Bind-mounts from host to container:
docker-compose.ymlbinds 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)
- Application accesses local paths: The Node.js app reads/writes to
/watch,/output,/ame-logsas 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_ADMINcapability andapparmor=unconfinedto 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
-
Start the container (it will run with fallback Docker volumes if no SMB credentials yet)
-
Open the Settings panel in the web UI (⚙️ icon in header)
-
Fill in SMB credentials:
- SMB Username: e.g.,
encoderorDOMAIN\encoder - SMB Password: Network password (stored server-side, never exposed to browser)
- SMB Domain/Workgroup: Optional, e.g.,
WORKGROUPorBMG - Notes: Optional reference, e.g.,
\\172.18.210.5\ame
- SMB Username: e.g.,
-
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) -
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 indocker-compose.ymlinstead:
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_ADMINcapability 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-ameas/smb-sharein 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/fstabor systemd automount) docker-compose.ymlspecifies 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-logsas 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
.envfiles - 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
.gvesproxy media for responsiveness - Problem: AME on a dedicated render machine can't resolve
.gvespaths — they're relative to AMPP infrastructure - Solution: Before handing the project to AME, rewrite all
.gvesreferences 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:
.gvesfiles 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/Watchexists 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.