Skip to content
screenjson

screenjson-db-importer

Load a folder of scripts into PostgreSQL

Convert a folder of Final Draft and Fountain files with screenjson-export, then load them into PostgreSQL with the free screenjson-db-importer.

Last updated September 2026

Two free tools, two commands: screenjson-export turns each script into ScreenJSON, and screenjson-db-importer validates the results and writes them to the database.

1. Convert

mkdir -p converted
for f in scripts/*.fdx scripts/*.fountain; do
  screenjson-export -i "$f" -o "converted/$(basename "${f%.*}").json"
done

2. Start a database

Any PostgreSQL will do. For a local one with pgvector:

docker run -d --name screenplays -p 127.0.0.1:5432:5432 \
  -e POSTGRES_USER=screenjson -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=screenplays \
  pgvector/pgvector:pg17

3. Configure the importer

Save this as importer.yaml:

data_dir: ./.screenjson-importer

storage:
  driver: postgres
  url: postgres://screenjson:${PGPASSWORD:-secret}@127.0.0.1:5432/screenplays?sslmode=disable
  layout: scenes

blob:
  driver: fs
  root: .

layout: scenes keeps one record per screenplay plus one per scene. Use whole for one record per script, or elements (the default) to split every line out.

4. Import

screenjson-db-importer --config importer.yaml --workers 8 ./converted/
DONE converted/the-heist.json 3f64b20e-40b6-4a84-a6f2-da9ae05c11f3
DONE converted/night-shift.json 9fea5fc5-f8f9-4c59-85d2-9ec5dcd452c1
imported 2 document(s); manifest screenjson-import.jsonl

Every file is checked against the ScreenJSON schema before anything is written. A file that fails is reported as INVALID with the reason, and the run exits non-zero.

5. Run it again

Add more scripts to converted/ and run the same command. Files already imported are skipped (SKIP … already imported), because the manifest remembers each file’s SHA-256. To retry files that failed last time, add --retry-failed.

Other databases

Change storage.driver and storage.url:

DriverExample storage.url
mongomongodb://127.0.0.1:27017 (and set storage.database)
elastichttp://127.0.0.1:9200
chromahttp://127.0.0.1:8000
weaviatehttp://127.0.0.1:8080
pineconeyour index host, with storage.pinecone.api_key

To import straight from a bucket, set the blob section to s3 or azure and pass an s3://bucket/prefix/ or azure://container/prefix/ URI instead of a folder.

Next