Skip to content

Feat/csv import export#12

Merged
Glory42 merged 9 commits into
masterfrom
feat/csv-import-export
May 4, 2026
Merged

Feat/csv import export#12
Glory42 merged 9 commits into
masterfrom
feat/csv-import-export

Conversation

@Glory42

@Glory42 Glory42 commented May 4, 2026

Copy link
Copy Markdown
Owner

No description provided.

Glory42 added 9 commits May 4, 2026 21:11
secrets context is invalid in job-level if conditions, causing
a workflow parse error. The backend job also requires a Neon
test DB that won't be available in CI, so remove it entirely
rather than leave a permanently-broken job in the pipeline.
Add searchMovieByTitleAndYear to the TMDB client so the import
pipeline can resolve a film to a tmdbId without a URI or a local
record.

Add existsByUserAndMovie (any date) and existsByUserMovieAndDate
to DiaryRepository for deduplication during CSV import.
Export: GET /api/data/export returns the user's full diary as a
CSV (WatchedDate, Title, Year, TmdbId, Rating, Rewatch, Review,
Spoilers). TmdbId in the export makes re-import instant.

Import: POST /api/data/import accepts text/csv and streams ndjson
back — one event per processed row so the client can render live
progress. Auto-detects Letterboxd diary.csv, reviews.csv,
watched.csv, and the Interis export format.

Letterboxd watched.csv uses any-date deduplication (skip if the
film exists in the diary at all), while diary/reviews use exact
userId+movieId+watchedDate matching.
Frontend reads the ndjson stream via an async generator and feeds
it into useImportStream, which accumulates lines and summary state
in the parent page — not in the dialog — so the modal can be
dismissed while import continues in the background.

ImportDialog renders a scrolling terminal log (✓ imported,
→ skipped, ✗ failed) with a live progress bar and summary stat
grid. A "Work in background" button closes the dialog; a banner
in the settings page shows live progress and lets the user reopen
the log when done.

Export is a single button that triggers a browser file download.

Wired into /settings/data with a Download tab in SettingsTabs.
watchlist.csv shares identical headers with watched.csv so the
format is resolved from the filename passed as a query param.
Watchlist rows upsert movie_interaction.watchlisted=true via a
new InteractionsService.setWatchlisted helper that takes movieId
directly — avoids a redundant findOrCreate call.

Reviews were already being saved via upsertReview when the Review
column is present (reviews.csv). No additional changes needed
for that path.
Send file.name as ?filename= so the backend can distinguish
watchlist.csv from watched.csv. Update the import section copy
to mention watchlist.csv and clarify that reviews are imported
automatically from reviews.csv.
Year fallback: if a title+year search returns nothing, retry
without the year. Letterboxd and TMDB frequently disagree on
release year for late-December films, causing well-known movies
to fail unnecessarily.

Parallel processing: replace the sequential row loop with a
worker pool (concurrency=5). Each worker pulls the next row from
a shared queue, so 5 TMDB lookups run simultaneously. Expected
import time drops from ~100s to ~20s for a 163-entry diary.
…port

Add searchSeriesByTitleAndYear to the TMDB serials client using
first_air_date_year (the correct TV param, not year).

Add to SerialsInteractionsRepository: existsByUserAndSeries,
existsByUserSeriesAndDate, insertSerialDiaryEntry, setWatchlisted
to mirror the movie-side DiaryRepository methods needed by the
CSV import pipeline.
Replace resolveTmdbId with resolveMedia which returns a typed
{ mediaType, tmdbId } discriminated union. Resolution order:
  1. movie + year
  2. movie without year (year mismatch fallback)
  3. TV series + year
  4. TV series without year

The import loop branches on mediaType — series entries go into
serial_diary_entry via SerialsInteractionsRepository, movie
entries go into diary_entry via DiaryRepository. Watchlist import
also branches so TV shows land in serial_interaction instead of
movie_interaction.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying interis with  Cloudflare Pages  Cloudflare Pages

Latest commit: ff8c914
Status: ✅  Deploy successful!
Preview URL: https://20fe1a29.this-is-cinema.pages.dev
Branch Preview URL: https://feat-csv-import-export.this-is-cinema.pages.dev

View logs

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying interis-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: ff8c914
Status: ✅  Deploy successful!
Preview URL: https://76933b29.interis-docs.pages.dev
Branch Preview URL: https://feat-csv-import-export.interis-docs.pages.dev

View logs

@Glory42 Glory42 merged commit 5e15b9d into master May 4, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

csv file importing and exporting for easy history inserting or taking for user

1 participant