gitoriaLog in with ident

tracker

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit3251488a3251488atracker#7: /my/shows (followed shows, newest follow first, poster, title, last watched SxxEyy); gate can take screenshots (TRACKER_GATE_SHOTS)mre3251488a/shows.hl

3.6 KB

  1. // shows.hl — READ-ONLY data access for a show's page (tracker.worldapi.org#4): shows, seasons,
  2. // episodes, persons, genres (storage/mpackdb/, migrated by tools/migrate.hl, tracker.worldapi.org#2).
  3. // Kept apart from watches.hl (the per-user watch state) and from components/show.hl (the page itself)
  4. // so /unwatched and /schedule (tickets #5/#6, "reuse, don't copy") can import the same functions.
  5. //
  6. // No declared index beyond the migration's own ['!oldId'] (opening a table with indexes it wasn't
  7. // created with is untested territory, hybriel#21 "opening a table rewrites its files") — a slug
  8. // lookup is a linear scan of ~9500 shows, cheap for one page load.
  9. import { MPackDB } from 'hl:mpackdb'
  10. import { env } from 'hl:proc'
  11. static envOr = (name, fallback) => {
  12. let v = env(name)
  13. return v != null && v.trim() != '' ? v.trim() : fallback
  14. }
  15. static storageDir = envOr('TRACKER_STORAGE', './storage/mpackdb')
  16. static genresTable = new MPackDB(file = storageDir + '/genres.db', primaryKey = '@id', indexes = ['!oldId'])
  17. static personsTable = new MPackDB(file = storageDir + '/persons.db', primaryKey = '@id', indexes = ['!oldId'])
  18. static showsTable = new MPackDB(file = storageDir + '/shows.db', primaryKey = '@id', indexes = ['!oldId'])
  19. static seasonsTable = new MPackDB(file = storageDir + '/seasons.db', primaryKey = '@id', indexes = ['!oldId'])
  20. static episodesTable = new MPackDB(file = storageDir + '/episodes.db', primaryKey = '@id', indexes = ['!oldId'])
  21. // the show with this urlSegment (slug), or null
  22. static showBySlug = (slug) => {
  23. if (slug == null || hlTypeName(slug) != 'String' || slug == '') { return null }
  24. for (s of showsTable.find(null, null)) { if (s.urlSegment == slug) { return s } }
  25. return null
  26. }
  27. static genreById = (id) => { return id == null ? null : genresTable.fetch(id) }
  28. static personById = (id) => { return id == null ? null : personsTable.fetch(id) }
  29. static seasonById = (id) => { return id == null ? null : seasonsTable.fetch(id) }
  30. static showById = (id) => { return id == null ? null : showsTable.fetch(id) }
  31. static episodeById = (id) => { return id == null ? null : episodesTable.fetch(id) }
  32. // insertion sort, hand-written (no list sort(), hybriel#1) — a show has a handful of seasons at most
  33. static insertDesc = (sorted, s) => {
  34. let out = []
  35. let inserted = false
  36. for (x of sorted) {
  37. if (!inserted && s.seasonNumber >= x.seasonNumber) { out.push(s) inserted = true }
  38. out.push(x)
  39. }
  40. if (!inserted) { out.push(s) }
  41. return out
  42. }
  43. static insertAsc = (sorted, e) => {
  44. let out = []
  45. let inserted = false
  46. for (x of sorted) {
  47. if (!inserted && e.episodeNumber <= x.episodeNumber) { out.push(e) inserted = true }
  48. out.push(x)
  49. }
  50. if (!inserted) { out.push(e) }
  51. return out
  52. }
  53. // a show's seasons, latest (highest seasonNumber) first — creator: "all seasons in reverse order,
  54. // latest on top"
  55. static seasonsOfShow = (show) => {
  56. let sorted = []
  57. for (id of show.seasons) {
  58. let s = seasonsTable.fetch(id)
  59. if (s != null) { sorted = insertDesc(sorted, s) }
  60. }
  61. return sorted
  62. }
  63. // a season's episodes, in episode-number order
  64. static episodesOfSeason = (season) => {
  65. let sorted = []
  66. for (id of season.episodes) {
  67. let e = episodesTable.fetch(id)
  68. if (e != null) { sorted = insertAsc(sorted, e) }
  69. }
  70. return sorted
  71. }
  72. // the poster file name (oldId + the migrated extension) — null when either is missing; served at
  73. // /posters/<name> (project.hl), which falls back to a placeholder when the file itself isn't there
  74. // yet (the real poster files are not part of the mongo export, see the report's `open`)
  75. static posterName = (show) => {
  76. if (show.oldId == null || show.image == null || show.image == '') { return null }
  77. return show.oldId + '.' + show.image
  78. }

Branches

Latest commits

  • 3251488atracker#7: /my/shows (followed shows, newest follow first, poster, title, last watched SxxEyy); gate can take screenshots (TRACKER_GATE_SHOTS)mre
  • 44b7d9f9tracker#6: /schedule — upcoming episodes of followed shows, soonest firstmre
  • 91c9fc8ctracker#5: /unwatched — unwatched released episodes of followed shows, newest firstmre
  • a97c0295tracker#4: show page /shows/:slug (header, seasons, episodes, watch checks) + tools/relink-episode-seasons.hlmre
  • 05f407c5tracker: no border on any button except inverted ones (Log out, ident status and identities too); header brand weight 100mre
  • 17375427tracker#2: tools/migrate.hl + tools/verify.hl — old MongoDB data into mpackdb with new idsmre
  • 31aac936tracker#3: no border on the header and on filled buttons; inverted buttons keep theirsmre
  • 2ad9d29ctracker#1: login exactly like calendar (identity selector in the header, empty homepage)mre
  • 3691e176tracker#1: empty tracker with the ident login (state of 2026-09-27)mre