gitoriaLog in with ident

tracker

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commite7305014e7305014tracker: report 032 (art + photos)mree7305014/tests/seed-dupes.hl

10.8 KB

  1. // tests/seed-dupes.hl — GATE FIXTURE for mission 026 (tracker#31, duplicate titles): written by tests/browser.mjs into the gate's
  2. // storage while the app is STOPPED (one process per storage, hybriel#21), late in the gate. Self-assigned ids, one put() each.
  3. // Shaped like the live data's migrated duplicates (Mongo-like oldIds in creation order, shared slugs):
  4. // Dupe Gate Series (tv 1301) ×3:
  5. // A oldest, slug Dupe-Gate-Series, short id dupa1: S1 (E1, E2) + S3 (E1 — B has no S3 → moved), cast Dupe Cast A
  6. // B slug Dupe-Gate-Series-2, short id dupb1: alice FOLLOWS it, watched S1E1 → the keeper; S1 (E1, E2) + S2 (E1), cast Dupe Cast B
  7. // C slug Dupe-Gate-Series-2 (shared, like Archive 81), short id dupc1: S1 (E1, E2, E3 — B has no E3 → moved), alice watched
  8. // S1E2 there (→ B's E2); a person's credit (Dupe Credit Person) and a timeline (Dupe Gate Line) name C
  9. // Dupe Gate Movie (movie 1303) ×2: M1 older (slug Dupe-Gate-Movie, dupm1), M2 (slug Dupe-Gate-Movie-2, dupm2) alice watched → keeper
  10. // Stray Gate Series (tv 1302), one record: listed S1 (E1, E2, the sync's kind: tmdb-season oldId); stray seasons naming it but
  11. // not listed — S1' (E1, E2: alice watched both + checked the season) → merged into S1; S2' (E1: alice watched) → linked;
  12. // S3' (E1, nobody watched) → stays as it is
  13. // Double Episode Series (tv 1305, tracker#39), one record, alice follows it: listed S1 holds DOUBLE EPISODES like the live data —
  14. // migrated E1 (TMDB id 9001, TMDB has replaced it; alice watched it) + E2 (9002), and the sync's E1 (tmdb-episode:9101) + E2
  15. // (9102); listed S2 a migrated E1 with a stale TMDB id (9201; the fake TMDB lists 9301) for the sync's number match
  16. // (tests/paths-m039.hl)
  17. import { MPackDB } from 'hl:mpackdb'
  18. import { env } from 'hl:proc'
  19. import { now } from 'hl:time'
  20. storageDir = env('TRACKER_STORAGE')
  21. if (storageDir == null || !storageDir.startsWith('/')) { hlError('set TRACKER_STORAGE, absolute') }
  22. userId = env('TRACKER_SEED_USER')
  23. if (userId == null || userId == '') { hlError('set TRACKER_SEED_USER') }
  24. showsTable = new MPackDB(file = storageDir + '/shows.db', primaryKey = '@id', indexes = ['!oldId'])
  25. seasonsTable = new MPackDB(file = storageDir + '/seasons.db', primaryKey = '@id', indexes = ['!oldId'])
  26. episodesTable = new MPackDB(file = storageDir + '/episodes.db', primaryKey = '@id', indexes = ['!oldId'])
  27. followsTable = new MPackDB(file = storageDir + '/follows.db', primaryKey = '@id', indexes = ['!oldId'])
  28. watchesTable = new MPackDB(file = storageDir + '/watches.db', primaryKey = '@id', indexes = ['!oldId'])
  29. personsTable = new MPackDB(file = storageDir + '/persons.db', primaryKey = '@id', indexes = ['!oldId'])
  30. timelinesTable = new MPackDB(file = storageDir + '/timelines.db', primaryKey = '@id', indexes = ['!slug'])
  31. title = (id, tmdbId, type, name, slug, sid, oldId, seasons, cast) => {
  32. showsTable.put({ adult = false id = id title = name summary = null tmdbSummary = 'a duplicate' tmdbId = tmdbId
  33. language = 'en' type = type release = '2019-04-04' year = 2019 image = null urlSegment = slug
  34. episodesCount = null homepage = null imdbId = null seasonsCount = null status = 'Ended' tagline = null
  35. tvdbId = null tvmzId = null genres = [] cast = cast seasons = seasons oldId = oldId shortId = sid kind = type == 'series' ? 'series' : null
  36. creditsAt = 1 detailsAt = 1 })
  37. return true
  38. }
  39. season = (id, show, n, eps, oldId) => {
  40. seasonsTable.put({ id = id seasonNumber = n title = 'Season ' + n release = '2019-04-04' summary = '' show = show episodes = eps oldId = oldId })
  41. return true
  42. }
  43. episode = (id, show, seasonId, n, e, name) => {
  44. episodesTable.put({ id = id episodeNumber = e seasonNumber = n title = name summary = '' release = '2019-04-0' + e show = show season = seasonId oldId = 'o' + id })
  45. return true
  46. }
  47. watch = (id, type, target) => {
  48. watchesTable.put({ id = id targetType = type target = target user = userId at = now() oldId = 'gw-' + id })
  49. return true
  50. }
  51. // Dupe Gate Series
  52. episode('dpae000000000a11', 'dpa000000000000a', 'dpas00000000000a', 1, 1, 'Pilot A')
  53. episode('dpae000000000a12', 'dpa000000000000a', 'dpas00000000000a', 1, 2, 'Second A')
  54. season('dpas00000000000a', 'dpa000000000000a', 1, ['dpae000000000a11', 'dpae000000000a12'], '5f00000000000000000000a1')
  55. episode('dpae000000000a31', 'dpa000000000000a', 'dpas0000000000a3', 3, 1, 'Third Season Start')
  56. season('dpas0000000000a3', 'dpa000000000000a', 3, ['dpae000000000a31'], '5f00000000000000000000a3')
  57. title('dpa000000000000a', 1301, 'series', 'Dupe Gate Series', 'Dupe-Gate-Series', 'dupa1', '5f0000000000000000000001', ['dpas00000000000a', 'dpas0000000000a3'],
  58. [{ person = 'dpp000000000000a' character = 'Role A' actor = 'Dupe Cast A' }])
  59. episode('dpbe000000000b11', 'dpb000000000000b', 'dpbs00000000000b', 1, 1, 'Pilot')
  60. episode('dpbe000000000b12', 'dpb000000000000b', 'dpbs00000000000b', 1, 2, 'Second')
  61. season('dpbs00000000000b', 'dpb000000000000b', 1, ['dpbe000000000b11', 'dpbe000000000b12'], '5f00000000000000000000b1')
  62. episode('dpbe000000000b21', 'dpb000000000000b', 'dpbs0000000000b2', 2, 1, 'Second Season Start')
  63. season('dpbs0000000000b2', 'dpb000000000000b', 2, ['dpbe000000000b21'], '5f00000000000000000000b2')
  64. title('dpb000000000000b', 1301, 'series', 'Dupe Gate Series', 'Dupe-Gate-Series-2', 'dupb1', '5f0000000000000000000002', ['dpbs00000000000b', 'dpbs0000000000b2'],
  65. [{ person = 'dpp000000000000b' character = 'Role B' actor = 'Dupe Cast B' }])
  66. episode('dpce000000000c11', 'dpc000000000000c', 'dpcs00000000000c', 1, 1, 'Pilot C')
  67. episode('dpce000000000c12', 'dpc000000000000c', 'dpcs00000000000c', 1, 2, 'Second C')
  68. episode('dpce000000000c13', 'dpc000000000000c', 'dpcs00000000000c', 1, 3, 'Only In C')
  69. season('dpcs00000000000c', 'dpc000000000000c', 1, ['dpce000000000c11', 'dpce000000000c12', 'dpce000000000c13'], '5f00000000000000000000c1')
  70. title('dpc000000000000c', 1301, 'series', 'Dupe Gate Series', 'Dupe-Gate-Series-2', 'dupc1', '5f0000000000000000000003', ['dpcs00000000000c'], [])
  71. personsTable.put({ id = 'dpp000000000000a' name = 'Dupe Cast A' urlSegment = 'Dupe-Cast-A' tmdbId = null oldId = 'dpp-a' shortId = 'dupp1' filmographyAt = 1
  72. shows = [{ show = 'dpa000000000000a' character = 'Role A' }] })
  73. personsTable.put({ id = 'dpp000000000000b' name = 'Dupe Cast B' urlSegment = 'Dupe-Cast-B' tmdbId = null oldId = 'dpp-b' shortId = 'dupp2' filmographyAt = 1
  74. shows = [{ show = 'dpb000000000000b' character = 'Role B' }] })
  75. personsTable.put({ id = 'dpp000000000000c' name = 'Dupe Credit Person' urlSegment = 'Dupe-Credit-Person' tmdbId = null oldId = 'dpp-c' shortId = 'dupp3' filmographyAt = 1
  76. shows = [{ show = 'dpc000000000000c' character = 'Guest In C' }] })
  77. // tests/paths-m026.hl: a person whose filmography is not fetched yet (fake TMDB person 3301: tv 1301, movie 1303, NEW movie 1304)
  78. personsTable.put({ id = 'dpp000000000000d' name = 'Dupe Fill Person' urlSegment = 'Dupe-Fill-Person' tmdbId = 3301 oldId = 'dpp-d' shortId = 'dupp4' })
  79. followsTable.put({ id = 'dpf00000000000f1' show = 'dpb000000000000b' user = userId at = now() oldId = 'gate-follow-dupe' })
  80. watch('dpw00000000000w1', 'episode', 'dpbe000000000b11')
  81. watch('dpw00000000000w2', 'episode', 'dpce000000000c12')
  82. timelinesTable.put({ id = 'dpt00000000000t1' name = 'Dupe Gate Line' slug = 'dupe-gate-line' franchise = null order = ['dpc000000000000c', 'dpm00000000000m2'] source = 'editor'
  83. tmdbCollection = null created = now() })
  84. // Dupe Gate Movie
  85. title('dpm00000000000m1', 1303, 'movie', 'Dupe Gate Movie', 'Dupe-Gate-Movie', 'dupm1', '5f0000000000000000000011', [], [])
  86. title('dpm00000000000m2', 1303, 'movie', 'Dupe Gate Movie', 'Dupe-Gate-Movie-2', 'dupm2', '5f0000000000000000000012', [], [])
  87. watch('dpw00000000000w3', 'movie', 'dpm00000000000m2')
  88. // Stray Gate Series
  89. episode('dpse000000000s11', 'dps000000000000s', 'dpss00000000000s', 1, 1, 'Stray Pilot')
  90. episode('dpse000000000s12', 'dps000000000000s', 'dpss00000000000s', 1, 2, 'Stray Second')
  91. season('dpss00000000000s', 'dps000000000000s', 1, ['dpse000000000s11', 'dpse000000000s12'], 'tmdb-season:1302:1')
  92. episode('dpse00000000x111', 'dps000000000000s', 'dpss0000000000x1', 1, 1, 'Old Stray Pilot')
  93. episode('dpse00000000x112', 'dps000000000000s', 'dpss0000000000x1', 1, 2, 'Old Stray Second')
  94. season('dpss0000000000x1', 'dps000000000000s', 1, ['dpse00000000x111', 'dpse00000000x112'], '5f00000000000000000000x1')
  95. episode('dpse00000000x211', 'dps000000000000s', 'dpss0000000000x2', 2, 1, 'Watched Special')
  96. season('dpss0000000000x2', 'dps000000000000s', 2, ['dpse00000000x211'], '5f00000000000000000000x2')
  97. episode('dpse00000000x311', 'dps000000000000s', 'dpss0000000000x3', 3, 1, 'Nobody Watched')
  98. season('dpss0000000000x3', 'dps000000000000s', 3, ['dpse00000000x311'], '5f00000000000000000000x3')
  99. title('dps000000000000s', 1302, 'series', 'Stray Gate Series', 'Stray-Gate-Series', 'dups1', '5f0000000000000000000021', ['dpss00000000000s'], [])
  100. followsTable.put({ id = 'dpf00000000000f2' show = 'dps000000000000s' user = userId at = now() - 1000 oldId = 'gate-follow-stray' })
  101. watch('dpw00000000000w4', 'episode', 'dpse00000000x111')
  102. watch('dpw00000000000w5', 'episode', 'dpse00000000x112')
  103. watch('dpw00000000000w6', 'season', 'dpss0000000000x1')
  104. watch('dpw00000000000w7', 'episode', 'dpse00000000x211')
  105. // Double Episode Series (tracker#39)
  106. ep39 = (id, n, e, tmdbId, oldId, name) => {
  107. episodesTable.put({ id = id episodeNumber = e seasonNumber = n title = name summary = '' release = '2018-0' + n + '-0' + e show = 'dde000000000000d'
  108. season = n == 1 ? 'dds00000000000s1' : 'dds00000000000s2' tmdbId = tmdbId oldId = oldId })
  109. return true
  110. }
  111. ep39('dde0000000000e11', 1, 1, 9001, '5f00000000000000000000e1', 'Old Pilot')
  112. ep39('dde0000000000e12', 1, 2, 9002, '5f00000000000000000000e2', 'Old Second')
  113. ep39('dde0000000000t11', 1, 1, 9101, 'tmdb-episode:9101', 'Pilot')
  114. ep39('dde0000000000t12', 1, 2, 9102, 'tmdb-episode:9102', 'Second')
  115. ep39('dde0000000000e21', 2, 1, 9201, '5f00000000000000000000e3', 'Old Two One')
  116. season('dds00000000000s1', 'dde000000000000d', 1, ['dde0000000000e11', 'dde0000000000e12', 'dde0000000000t11', 'dde0000000000t12'], '5f00000000000000000000d1')
  117. season('dds00000000000s2', 'dde000000000000d', 2, ['dde0000000000e21'], '5f00000000000000000000d2')
  118. title('dde000000000000d', 1305, 'series', 'Double Episode Series', 'Double-Episode-Series', 'dupd1', '5f0000000000000000000031', ['dds00000000000s1', 'dds00000000000s2'], [])
  119. followsTable.put({ id = 'dpf00000000000f3' show = 'dde000000000000d' user = userId at = now() - 2000 oldId = 'gate-follow-double' })
  120. watch('dpw00000000000w8', 'episode', 'dde0000000000e11')
  121. showsTable.persist()
  122. seasonsTable.persist()
  123. episodesTable.persist()
  124. followsTable.persist()
  125. watchesTable.persist()
  126. personsTable.persist()
  127. timelinesTable.persist()
  128. console.log('seed-dupes: Dupe Gate Series ×3, Dupe Gate Movie ×2, Stray Gate Series with 3 stray seasons, Double Episode Series; ' + userId + ' follows 3, 8 watches')

Branches

Latest commits

  • e7305014tracker: report 032 (art + photos)mre
  • fbb903cctracker#33/#37 (mission 032): a title without a TMDB poster gets its backdrop (w780, posterFromBackdrop, shown 2:3 centre-cropped); movies store runtime, the page shows release date + runtime; art backfill (public titles without poster file / movies without runtime) and person photo backfill (tmdbProfile / photoCheck) as the last start jobs (TRACKER_ART, TRACKER_PHOTOS; off in every gate start); gates 379/0, 32/0, 53/0, 229/0, 26/0, check-theme 0; live copy: art 4977 titles in 42 min (115 posters, 15 backdrops, 4609 runtimes), The Remaining shows its backdrop + 7 minmre
  • 4ecc67b2tracker: STATUS/LOG for the t38 + t40 merge (gates 374/0, 32/0, 53/0, 229/0, 26/0, check-theme 0; live copy checks)mre
  • e5945d2fMerge t40 (tracker#40 curated franchises, Franchises menu, superseded collections) into main: deploy.sh lists all six gates (browser, kinds, franchises, pager, franchiseseed, check-theme); search.hl keeps #37's personPhotoOf + #40's importWithCredits; pager gate runs with TRACKER_FRANCHISE_SEED=0; gates 374/0, 32/0, 53/0, 229/0, 26/0, check-theme 0mre
  • b638e99dMerge t38 (tracker#38 pagination, #35 Returning/Airing label) into main: LOG/STATUS keep both sides; pager gate follows #37's /people (everyone, last updated first: seed updatedAt); gates pager 229/0, browser 374/0mre
  • f8ffa4dbtracker: report 032 + The Remaining + #39mre
  • 7565a863tracker: LOG timemre
  • b10f00c8tracker#39: double episodes — migrated episodes whose TMDB id TMDB replaced are adopted by their number in the sync (old id -> migratedTmdbId); merge.hl step 3 merges each season's doubles at start (keeper: most watches > synced > first; watches moved/parked; tombstones into mergedEpisodes, nothing deleted); tools/count-duplicate-episodes.hl; gate fixture + paths-m039; live copy 850 -> 0 in 64 s; gates 373/0, 32/0, 52/0mre
  • 8f1d4542tracker#40: superseded collections — a TMDB collection timeline whose titles are all in one curated timeline is hidden (supersededBy; kept: own page + editor finder), set by the collection seed when it makes one and by the curated build (lifted when the cover is gone); partly covered ones join that franchise; no second widget (First Contact: only Star Trek — Prime); gate franchiseseed 26/0, browser 365/0, kinds 32/0, franchises 53/0, check-theme 0; real copy 24 supersededmre
  • 9448d643tracker#35 follow-up (mission 033): TVmaze 'Running' is labelled 'Returning', or 'Airing' while a non-special episode of the two newest seasons is released within today +-7 days (data unchanged); gate fixtures Running/Airing/Aired + a special; browser 366/0, kinds 32/0, franchises 52/0, pager 229/0, check-theme 0mre
  • 7d4b293dtracker#40: "Franchises" in the main menu (desktop header after People, phone sidebar) → /franchises, marked on franchise and timeline pages; gates 365/0, 32/0, 53/0, 24/0, check-theme 0mre
  • 8751adb8tracker: report 032mre
  • 9bce1f65tracker mission 032: STATUS gate files + the hour-boundary flakemre
  • 718bfb89tracker#37 (mission 032): /people = everyone, last updated first (updatedAt stamped by the person fill; view built at boot, touched people first at once), photo + name tiles (person colour) with the /movies pagination, /people/<letter> removed; photo = our file, tmdbProfile, a cast/crew entry's profile (in-memory map at boot), else the new 'no photo' placeholder; new cast/crew/created_by people keep tmdbProfile; search people rows with the photo; /settings = the heading only; util.hl sortDesc starts from sorted runs (same result, 105k: 1.6 s -> 0.15 s); gates 369/0, 32/0, 52/0, check-theme 0; README/STATUS/LOGmre
  • 93dfb0batracker#40 (mission 034): the curated franchises — data/franchises.json (17 franchises, 31 timelines, 285 TMDB titles, movies + series, in-universe/release order, 12 TMDB collections attached); lib/franchiseseed.hl + jobs.hl franchiseSeedTick (last start job, imports missing titles via details.hl importWithCredits = the search's Add, one per step paced, then one build; franchiseseed.db: editor changes win, the creator's same-name franchise adopted / timeline left alone, 404 remembered, resumable, idempotent); timeline heads 'N titles · in-universe order' (orderKind) and wrap on a phone; series pages show the widget; new gate tests/franchiseseed.mjs (5th in deploy.sh), the others run with TRACKER_FRANCHISE_SEED=0; gates 365/0, 32/0, 52/0, 24/0, check-theme 0; real copy 196 imported, 0 failed, 7 min, restart unchanged=31mre
  • 03ec792ftracker#38 (mission 033): pagination goes exactly to the clicked page — tilelist read the clicked button's text after pagination.hl's own handler had rebuilt the buttons (real clicks only); now li.current, else the button's own text; new gate tests/pager.mjs (5 lists x 11 pages, 390/1280, real + script clicks, Back/Forward) 229/0; browser 365/0, kinds 32/0, franchises 52/0, check-theme 0mre
  • 96ba683adeploy.sh: a gate without a 'passed,' line (check-theme) no longer ends the scriptmre
  • eb3b9205tracker: report 031mre
  • 9b5d2e89tracker mission 031: README (What it does, Test: four gates + the #32 checks, Files: theme/, new pages), STATUS (real copy, A/B load, how to repeat, open points), LOGmre
  • 39950e4ctracker#32 (mission 031): the WorldAPI theme (theme/ vendored verbatim from layouts.worldapi.org 85b5654; styles.hl inherits it: accent green-dark, type colours 1-6; own base/header rules, row lines, genre-pill and inverted-button frames removed, the season foldable keeps its line; check-theme 21 -> 0, 4th deploy gate; main actions class primary) and the #32 header (theme AppHeader/MainMenu/UserMenu/Sidebar/ContentFirst: desktop brand, search, Series|Shows|Movies|Genres|People, user icon with Unwatched..Settings, Logout; signed out the ident selector, phone the iD icon dropdown; phone menu in the sidebar overlay; marked entry by :has); /find -> /search/<q>, /genres, /people(/<letter>), /settings; main { ContentFirst { slot } } works around the hl:web one-line slot bug; gates 365/0, 32/0, 52/0, check-theme 0mre