home-atom 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +25 -0
  2. package/bin/home-atom.js +135 -0
  3. package/dist/client/.vite/manifest.json +184 -0
  4. package/dist/client/_next/static/chunks/app-top-nav-Dh0jEt8u.js +1 -0
  5. package/dist/client/_next/static/chunks/chevron-left-Dn3noAqV.js +1 -0
  6. package/dist/client/_next/static/chunks/framework-BQUlGUOV.js +9 -0
  7. package/dist/client/_next/static/chunks/hls-CgexCc5Z.js +40 -0
  8. package/dist/client/_next/static/chunks/house-DQz6VeTk.js +1 -0
  9. package/dist/client/_next/static/chunks/index-hn3AiVww.js +33 -0
  10. package/dist/client/_next/static/chunks/layout-segment-context-DRM8YCgm.js +1 -0
  11. package/dist/client/_next/static/chunks/manage-profiles-modal-Doi6QM4e.js +1 -0
  12. package/dist/client/_next/static/chunks/navigation-errors-C_V42-00.js +1 -0
  13. package/dist/client/_next/static/chunks/netflix-browser-L_Vb9wXK.js +2 -0
  14. package/dist/client/_next/static/chunks/playback-activity-Dug3BRYr.js +1 -0
  15. package/dist/client/_next/static/chunks/playback-activity-track-switcher-CbO6kl8K.js +1 -0
  16. package/dist/client/_next/static/chunks/rolldown-runtime-QTnfLwEv.js +1 -0
  17. package/dist/client/_next/static/chunks/spotify-app-f93K4FG_.js +1 -0
  18. package/dist/client/_next/static/chunks/step-forward-CV8I5pPb.js +1 -0
  19. package/dist/client/_next/static/chunks/storefront-C_nmcuvH.js +1 -0
  20. package/dist/client/_next/static/chunks/streamed-icons-DwsKSgMb.js +1 -0
  21. package/dist/client/_next/static/css/index.BGXy8e6j.css +1 -0
  22. package/dist/client/_next/static/css/layout.AKSLtSR6.css +1 -0
  23. package/dist/client/_next/static/css/layout.D8T68cCD.css +1 -0
  24. package/dist/client/_next/static/css/manage-profiles-modal.BAVyxRRR.css +1 -0
  25. package/dist/client/_next/static/e1ce9af7-d770-43d6-845a-511443f19971/_buildManifest.js +1 -0
  26. package/dist/client/_next/static/e1ce9af7-d770-43d6-845a-511443f19971/_ssgManifest.js +1 -0
  27. package/dist/client/_next/static/media/roboto-cyrillic-ext-wght-normal.BCp_3N7R.woff2 +0 -0
  28. package/dist/client/_next/static/media/roboto-cyrillic-wght-normal.fOt7epNf.woff2 +0 -0
  29. package/dist/client/_next/static/media/roboto-greek-ext-wght-normal.Bgw4czJT.woff2 +0 -0
  30. package/dist/client/_next/static/media/roboto-greek-wght-normal.DlT9XTxE.woff2 +0 -0
  31. package/dist/client/_next/static/media/roboto-latin-ext-wght-normal.DYIxWhlt.woff2 +0 -0
  32. package/dist/client/_next/static/media/roboto-latin-wght-normal.ccAYIvAh.woff2 +0 -0
  33. package/dist/client/_next/static/media/roboto-math-wght-normal.4a3xuzGk.woff2 +0 -0
  34. package/dist/client/_next/static/media/roboto-symbols-wght-normal.B7iHSfaG.woff2 +0 -0
  35. package/dist/client/_next/static/media/roboto-vietnamese-wght-normal._f1XEJpo.woff2 +0 -0
  36. package/dist/client/icons/home.ico +0 -0
  37. package/dist/client/icons/keyboard.ico +0 -0
  38. package/dist/client/icons/netflix.ico +0 -0
  39. package/dist/client/icons/remote-screen.ico +0 -0
  40. package/dist/client/icons/spotify.ico +0 -0
  41. package/dist/client/vinext-client-entry-manifest.json +3 -0
  42. package/dist/server/BUILD_ID +1 -0
  43. package/dist/server/__vite_rsc_assets_manifest.js +162 -0
  44. package/dist/server/_next/static/app-page-cache-DcrtN14Y.js +1 -0
  45. package/dist/server/_next/static/app-page-cache-render-DoCquoNb.js +1 -0
  46. package/dist/server/_next/static/app-prerender-endpoints-rDLH8gfm.js +1 -0
  47. package/dist/server/_next/static/app-route-handler-dispatch-Be2YIqVp.js +1 -0
  48. package/dist/server/_next/static/app-static-generation-BX8cz43B.js +1 -0
  49. package/dist/server/_next/static/app-top-nav-aGPkB57A.js +1 -0
  50. package/dist/server/_next/static/automation-UGP78ei5.js +172 -0
  51. package/dist/server/_next/static/automation-tAN_1u1H.js +118 -0
  52. package/dist/server/_next/static/base-path-Cmiu3HvG.js +1 -0
  53. package/dist/server/_next/static/cache-headers-BQ-QuzWN.js +1 -0
  54. package/dist/server/_next/static/catalog-BS93IoWO.js +1 -0
  55. package/dist/server/_next/static/css/index.BGXy8e6j.css +1 -0
  56. package/dist/server/_next/static/css/layout.AKSLtSR6.css +1 -0
  57. package/dist/server/_next/static/css/layout.D8T68cCD.css +1 -0
  58. package/dist/server/_next/static/database-BNnYfHmT.js +114 -0
  59. package/dist/server/_next/static/db-BE1J0NNS.js +98 -0
  60. package/dist/server/_next/static/downloader-DgnjNL5T.js +21 -0
  61. package/dist/server/_next/static/framework~index-B2E_Di_-.js +18 -0
  62. package/dist/server/_next/static/framework~index~layout~layout~page~page~page~layout~page~app-page-cache-render~app-page-cac~nvd6h53m-ydvR7YSg.js +1 -0
  63. package/dist/server/_next/static/http-B2HyuWoI.js +1 -0
  64. package/dist/server/_next/static/input-platform-C0756QEt.js +1 -0
  65. package/dist/server/_next/static/instrumentation-BV3gg-L-.js +1 -0
  66. package/dist/server/_next/static/isr-decision-D1zHgPSI.js +1 -0
  67. package/dist/server/_next/static/layout-ClEWbCcj.js +1 -0
  68. package/dist/server/_next/static/layout-Ds3pFa6r.js +1 -0
  69. package/dist/server/_next/static/media/roboto-cyrillic-ext-wght-normal.BCp_3N7R.woff2 +0 -0
  70. package/dist/server/_next/static/media/roboto-cyrillic-wght-normal.fOt7epNf.woff2 +0 -0
  71. package/dist/server/_next/static/media/roboto-greek-ext-wght-normal.Bgw4czJT.woff2 +0 -0
  72. package/dist/server/_next/static/media/roboto-greek-wght-normal.DlT9XTxE.woff2 +0 -0
  73. package/dist/server/_next/static/media/roboto-latin-ext-wght-normal.DYIxWhlt.woff2 +0 -0
  74. package/dist/server/_next/static/media/roboto-latin-wght-normal.ccAYIvAh.woff2 +0 -0
  75. package/dist/server/_next/static/media/roboto-math-wght-normal.4a3xuzGk.woff2 +0 -0
  76. package/dist/server/_next/static/media/roboto-symbols-wght-normal.B7iHSfaG.woff2 +0 -0
  77. package/dist/server/_next/static/media/roboto-vietnamese-wght-normal._f1XEJpo.woff2 +0 -0
  78. package/dist/server/_next/static/netflix-page-BRbORN1F.js +1 -0
  79. package/dist/server/_next/static/page-C457kN_n.js +1 -0
  80. package/dist/server/_next/static/page-Cs-erYTy.js +1 -0
  81. package/dist/server/_next/static/page-DlBGATQp.js +1 -0
  82. package/dist/server/_next/static/page-DlRXyzSQ.js +1 -0
  83. package/dist/server/_next/static/providers-B3YELr_F.js +108 -0
  84. package/dist/server/_next/static/providers-CMBIwNGs.js +1 -0
  85. package/dist/server/_next/static/request-context-C1ZJzkex.js +1 -0
  86. package/dist/server/_next/static/request-profile-B1yGQ0l6.js +1 -0
  87. package/dist/server/_next/static/rolldown-runtime-DaDcL9Zw.js +1 -0
  88. package/dist/server/_next/static/route-96DdgJBs.js +1 -0
  89. package/dist/server/_next/static/route-B0WNLJZp.js +1 -0
  90. package/dist/server/_next/static/route-BBmE0xf12.js +1 -0
  91. package/dist/server/_next/static/route-BPcAdbz_.js +1 -0
  92. package/dist/server/_next/static/route-BWk9YK7r.js +1 -0
  93. package/dist/server/_next/static/route-BdG5ojMj2.js +1 -0
  94. package/dist/server/_next/static/route-BovNctFc.js +1 -0
  95. package/dist/server/_next/static/route-C2LuSjoh.js +1 -0
  96. package/dist/server/_next/static/route-C74RA7va.js +1 -0
  97. package/dist/server/_next/static/route-CCUsQ7AG.js +1 -0
  98. package/dist/server/_next/static/route-CHJzaQu2.js +73 -0
  99. package/dist/server/_next/static/route-CN6VdJwN.js +1 -0
  100. package/dist/server/_next/static/route-CNNiXZoM2.js +1 -0
  101. package/dist/server/_next/static/route-CVPPmUto.js +1 -0
  102. package/dist/server/_next/static/route-CeVsDcbj2.js +1 -0
  103. package/dist/server/_next/static/route-Cim7D4XQ.js +1 -0
  104. package/dist/server/_next/static/route-CjUCKwSj2.js +1 -0
  105. package/dist/server/_next/static/route-CkH7Cg-p.js +1 -0
  106. package/dist/server/_next/static/route-Cn40NftM.js +1 -0
  107. package/dist/server/_next/static/route-DAyF4BHS.js +1 -0
  108. package/dist/server/_next/static/route-DWAMZHLa.js +1 -0
  109. package/dist/server/_next/static/route-DXTJyFWD.js +1 -0
  110. package/dist/server/_next/static/route-D_G2SOlz.js +1 -0
  111. package/dist/server/_next/static/route-Dbs39TkC.js +1 -0
  112. package/dist/server/_next/static/route-DiT9UYUd.js +1 -0
  113. package/dist/server/_next/static/route-DslPFTAK.js +1 -0
  114. package/dist/server/_next/static/route-UWQN8uYr.js +1 -0
  115. package/dist/server/_next/static/route-ipFkK3iA.js +1 -0
  116. package/dist/server/_next/static/route-jMq6H_yh.js +1 -0
  117. package/dist/server/_next/static/route-mzLdjDCs2.js +1 -0
  118. package/dist/server/_next/static/route-vD15s2A8.js +1 -0
  119. package/dist/server/_next/static/route-xtz48fti.js +1 -0
  120. package/dist/server/_next/static/route-zazqen-A2.js +1 -0
  121. package/dist/server/_next/static/seed-cache-oCd9Lfdw.js +1 -0
  122. package/dist/server/_next/static/server-CS40XqUP.js +1 -0
  123. package/dist/server/_next/static/storage-D1zF0J_t.js +288 -0
  124. package/dist/server/_next/static/streamer-B1PRR-dj.js +2 -0
  125. package/dist/server/_next/static/subtitles-DYBlMnha.js +2 -0
  126. package/dist/server/_next/static/title-progress-CQBrSxvN.js +1 -0
  127. package/dist/server/_next/static/unified-request-context-CgIMz2dD.js +1 -0
  128. package/dist/server/index.js +17 -0
  129. package/dist/server/prerendered-routes/404.html +2 -0
  130. package/dist/server/prerendered-routes/index.html +2 -0
  131. package/dist/server/prerendered-routes/index.rsc +13 -0
  132. package/dist/server/ssr/__vite_rsc_assets_manifest.js +162 -0
  133. package/dist/server/ssr/_next/static/app-elements-Psr6bSdJ.js +1 -0
  134. package/dist/server/ssr/_next/static/app-prefetch-fetch-queue-Dm_lGOGz.js +1 -0
  135. package/dist/server/ssr/_next/static/app-router-context-QPmikWPX.js +1 -0
  136. package/dist/server/ssr/_next/static/app-router-scroll-C0MNOLz-.js +1 -0
  137. package/dist/server/ssr/_next/static/app-top-nav-a5vUAoEs.js +1 -0
  138. package/dist/server/ssr/_next/static/base-path-DtcGyvwb.js +1 -0
  139. package/dist/server/ssr/_next/static/chevron-left-CkBJG8E2.js +1 -0
  140. package/dist/server/ssr/_next/static/css/refresh-cw.BAVyxRRR.css +1 -0
  141. package/dist/server/ssr/_next/static/default-global-error-B62cHCkh.js +31 -0
  142. package/dist/server/ssr/_next/static/error-boundary-C28whqLB.js +1 -0
  143. package/dist/server/ssr/_next/static/hls-CgexCc5Z.js +40 -0
  144. package/dist/server/ssr/_next/static/house-l1u9dUbj.js +1 -0
  145. package/dist/server/ssr/_next/static/layout-segment-context-DK0O-NeY.js +1 -0
  146. package/dist/server/ssr/_next/static/minimize-C9JLgte-.js +1 -0
  147. package/dist/server/ssr/_next/static/navigation-BCDRWdBm.js +1 -0
  148. package/dist/server/ssr/_next/static/navigation-context-state-BnWorqxb.js +1 -0
  149. package/dist/server/ssr/_next/static/navigation-errors-BRIEcp79.js +1 -0
  150. package/dist/server/ssr/_next/static/navigation-server-CCLiCOcy.js +1 -0
  151. package/dist/server/ssr/_next/static/netflix-browser-CMAehdwi.js +2 -0
  152. package/dist/server/ssr/_next/static/pin-BQre-tQY.js +1 -0
  153. package/dist/server/ssr/_next/static/playback-activity-D5TYX2Ae.js +1 -0
  154. package/dist/server/ssr/_next/static/refresh-cw-CWdWHjqY.js +1 -0
  155. package/dist/server/ssr/_next/static/rolldown-runtime-DSjMdhoD.js +1 -0
  156. package/dist/server/ssr/_next/static/slot-CE_M6qTT.js +1 -0
  157. package/dist/server/ssr/_next/static/spotify-app-CE-RaTjZ.js +1 -0
  158. package/dist/server/ssr/_next/static/storefront-C5NNzPMa.js +1 -0
  159. package/dist/server/ssr/_next/static/streamed-icons-D2p1hUS_.js +1 -0
  160. package/dist/server/ssr/_next/static/url-safety-CnwxMXYX.js +1 -0
  161. package/dist/server/ssr/index.js +3 -0
  162. package/dist/server/ssr/vinext-client-assets.js +1 -0
  163. package/dist/server/ssr/vinext-server.json +1 -0
  164. package/dist/server/vinext-client-assets.js +1 -0
  165. package/dist/server/vinext-externals.json +5 -0
  166. package/dist/server/vinext-prerender-paths.json +7 -0
  167. package/dist/server/vinext-prerender.json +219 -0
  168. package/dist/server/vinext-server.json +1 -0
  169. package/package.json +66 -0
@@ -0,0 +1,288 @@
1
+ import e from"node:path";import{closeSync as t,existsSync as n,fsyncSync as r,lstatSync as i,mkdirSync as a,openSync as o,readFileSync as s,readdirSync as c,realpathSync as l,renameSync as u,rmSync as d,statSync as f,writeFileSync as p}from"node:fs";import{createHash as m,randomUUID as h}from"node:crypto";import g from"better-sqlite3";var _=process.env.HOME_ATOM_PROFILE_DATA_ROOT?.trim()||process.env.HOME_ATOM_PROFILE_DATA_DIR?.trim(),v=e.resolve(e.join(process.cwd(),[`da`,`ta`].join(``))),y=_?e.resolve(_):e.join(v,`home-atom-profile`),ee=process.env.HOME_ATOM_NETFLIX_DATA_DIR?.trim()?e.resolve(process.env.HOME_ATOM_NETFLIX_DATA_DIR.trim()):v,te=process.env.HOME_ATOM_SPOTIFY_DATA_DIR?.trim()?e.resolve(process.env.HOME_ATOM_SPOTIFY_DATA_DIR.trim()):v,ne=e.join(ee,`netflix-atom.sqlite`),re=e.join(te,`spotify-atom.sqlite`),b=e.join(y,`profiles.json`),x=e.join(y,`.profiles.lock`),S=e.join(x,`owner`),C=e.join(y,`.profiles-lock-recovery`),w=`database`,ie=`home-atom.sqlite`,T=1,ae=1,oe=6e4,se=6e4,ce=500,le=new Int32Array(new SharedArrayBuffer(4)),ue=[{id:`phimhdcss`,name:`PhimHDC`,baseUrl:`https://phimhdcss.com/`},{id:`fsharetv`,name:`FshareTV`,baseUrl:`https://fsharetv.com/`}],de=[{id:`zing-mp3`,name:`Zing MP3`,homeUrl:`https://zingmp3.vn/`},{id:`nhac-cua-tui`,name:`NhacCuaTui`,homeUrl:`https://www.nhaccuatui.com/`},{id:`tiktok`,name:`TikTok`,homeUrl:`https://www.tiktok.com/`},{id:`youtube`,name:`YouTube`,homeUrl:`https://www.youtube.com/`}],fe=globalThis,E=fe.homeAtomProfileStorageStateV1??{connections:new Map,initializedRoots:new Set};fe.homeAtomProfileStorageStateV1=E;var pe=`
2
+ CREATE TABLE IF NOT EXISTS schema_migrations (
3
+ id TEXT PRIMARY KEY,
4
+ applied_at TEXT NOT NULL
5
+ );
6
+
7
+ CREATE TABLE IF NOT EXISTS profiles (
8
+ id TEXT PRIMARY KEY,
9
+ name TEXT NOT NULL COLLATE NOCASE UNIQUE,
10
+ avatar_color TEXT NOT NULL,
11
+ created_at TEXT NOT NULL,
12
+ updated_at TEXT NOT NULL
13
+ );
14
+
15
+ CREATE TABLE IF NOT EXISTS netflix_providers (
16
+ id TEXT PRIMARY KEY,
17
+ name TEXT NOT NULL,
18
+ base_url TEXT NOT NULL,
19
+ last_synced_at TEXT,
20
+ sync_status TEXT NOT NULL DEFAULT 'idle',
21
+ last_error TEXT
22
+ );
23
+
24
+ CREATE TABLE IF NOT EXISTS netflix_movies (
25
+ id TEXT PRIMARY KEY,
26
+ provider_id TEXT NOT NULL,
27
+ source_url TEXT NOT NULL UNIQUE,
28
+ watch_url TEXT,
29
+ title TEXT NOT NULL,
30
+ original_title TEXT,
31
+ synopsis TEXT NOT NULL,
32
+ year INTEGER NOT NULL,
33
+ maturity_rating TEXT NOT NULL,
34
+ duration TEXT NOT NULL,
35
+ match_score INTEGER NOT NULL,
36
+ genres TEXT NOT NULL,
37
+ cast TEXT NOT NULL,
38
+ director TEXT,
39
+ subtitles TEXT NOT NULL DEFAULT '[]',
40
+ episodes TEXT NOT NULL DEFAULT '[]',
41
+ media_type TEXT NOT NULL,
42
+ poster_image TEXT NOT NULL,
43
+ backdrop_image TEXT NOT NULL,
44
+ accent_color TEXT NOT NULL,
45
+ playback_path TEXT,
46
+ playback_kind TEXT,
47
+ download_status TEXT NOT NULL DEFAULT 'metadata',
48
+ download_progress INTEGER NOT NULL DEFAULT 0,
49
+ download_error TEXT,
50
+ created_at TEXT NOT NULL,
51
+ updated_at TEXT NOT NULL
52
+ );
53
+
54
+ CREATE TABLE IF NOT EXISTS netflix_profile_title_state (
55
+ profile_id TEXT NOT NULL,
56
+ title_id TEXT NOT NULL,
57
+ in_my_list INTEGER NOT NULL DEFAULT 0,
58
+ liked INTEGER NOT NULL DEFAULT 0,
59
+ updated_at TEXT NOT NULL,
60
+ PRIMARY KEY (profile_id, title_id),
61
+ FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
62
+ );
63
+
64
+ CREATE TABLE IF NOT EXISTS netflix_watch_progress (
65
+ profile_id TEXT NOT NULL,
66
+ title_id TEXT NOT NULL,
67
+ season_number INTEGER NOT NULL DEFAULT 1,
68
+ episode_number INTEGER NOT NULL DEFAULT 1,
69
+ position_seconds REAL NOT NULL DEFAULT 0,
70
+ duration_seconds REAL NOT NULL DEFAULT 0,
71
+ completed INTEGER NOT NULL DEFAULT 0,
72
+ updated_at TEXT NOT NULL,
73
+ PRIMARY KEY (profile_id, title_id),
74
+ FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
75
+ );
76
+
77
+ CREATE TABLE IF NOT EXISTS netflix_subtitle_preferences (
78
+ profile_id TEXT PRIMARY KEY,
79
+ font_size INTEGER NOT NULL DEFAULT 32,
80
+ position_x REAL NOT NULL DEFAULT 50,
81
+ position_y REAL NOT NULL DEFAULT 78,
82
+ font_color TEXT NOT NULL DEFAULT '#ffffff',
83
+ background_color TEXT NOT NULL DEFAULT 'rgba(0,0,0,0.75)',
84
+ font_family TEXT NOT NULL DEFAULT 'default',
85
+ captions_on INTEGER NOT NULL DEFAULT 0,
86
+ updated_at TEXT NOT NULL,
87
+ FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
88
+ );
89
+
90
+ CREATE TABLE IF NOT EXISTS playback_activity_sessions (
91
+ session_id TEXT PRIMARY KEY,
92
+ profile_id TEXT NOT NULL,
93
+ title_id TEXT NOT NULL,
94
+ snapshot_json TEXT NOT NULL,
95
+ updated_at_ms INTEGER NOT NULL,
96
+ FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
97
+ );
98
+
99
+ CREATE TABLE IF NOT EXISTS playback_activity_commands (
100
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
101
+ session_id TEXT NOT NULL,
102
+ command_json TEXT NOT NULL,
103
+ created_at_ms INTEGER NOT NULL,
104
+ FOREIGN KEY (session_id)
105
+ REFERENCES playback_activity_sessions(session_id) ON DELETE CASCADE
106
+ );
107
+
108
+ CREATE TABLE IF NOT EXISTS spotify_providers (
109
+ id TEXT PRIMARY KEY,
110
+ name TEXT NOT NULL,
111
+ home_url TEXT NOT NULL,
112
+ sync_status TEXT NOT NULL DEFAULT 'idle',
113
+ last_synced_at TEXT,
114
+ last_error TEXT,
115
+ created_at TEXT NOT NULL,
116
+ updated_at TEXT NOT NULL
117
+ );
118
+
119
+ CREATE TABLE IF NOT EXISTS spotify_homepage_items (
120
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
121
+ provider_id TEXT NOT NULL
122
+ REFERENCES spotify_providers(id) ON DELETE CASCADE,
123
+ external_id TEXT NOT NULL,
124
+ kind TEXT NOT NULL,
125
+ title TEXT NOT NULL,
126
+ subtitle TEXT NOT NULL DEFAULT '',
127
+ image_url TEXT,
128
+ source_url TEXT,
129
+ position INTEGER NOT NULL,
130
+ payload_json TEXT,
131
+ synced_at TEXT NOT NULL,
132
+ UNIQUE(provider_id, external_id)
133
+ );
134
+
135
+ CREATE TABLE IF NOT EXISTS spotify_tracks (
136
+ id TEXT PRIMARY KEY,
137
+ provider_id TEXT NOT NULL REFERENCES spotify_providers(id),
138
+ external_id TEXT NOT NULL,
139
+ source_url TEXT NOT NULL UNIQUE,
140
+ title TEXT NOT NULL,
141
+ artist TEXT NOT NULL,
142
+ album TEXT NOT NULL DEFAULT '',
143
+ duration_sec INTEGER NOT NULL DEFAULT 0,
144
+ image_url TEXT,
145
+ audio_path TEXT,
146
+ audio_mime TEXT,
147
+ audio_bytes INTEGER,
148
+ download_status TEXT NOT NULL DEFAULT 'queued',
149
+ created_at TEXT NOT NULL,
150
+ updated_at TEXT NOT NULL,
151
+ UNIQUE(provider_id, external_id)
152
+ );
153
+
154
+ CREATE TABLE IF NOT EXISTS spotify_downloads (
155
+ id TEXT PRIMARY KEY,
156
+ source_url TEXT NOT NULL,
157
+ provider_id TEXT NOT NULL REFERENCES spotify_providers(id),
158
+ track_id TEXT REFERENCES spotify_tracks(id),
159
+ status TEXT NOT NULL,
160
+ progress INTEGER NOT NULL DEFAULT 0,
161
+ bytes_downloaded INTEGER NOT NULL DEFAULT 0,
162
+ total_bytes INTEGER,
163
+ error TEXT,
164
+ created_at TEXT NOT NULL,
165
+ updated_at TEXT NOT NULL
166
+ );
167
+
168
+ CREATE TABLE IF NOT EXISTS spotify_music_playlists (
169
+ id TEXT PRIMARY KEY,
170
+ provider_id TEXT NOT NULL REFERENCES spotify_providers(id),
171
+ external_id TEXT NOT NULL,
172
+ source_url TEXT NOT NULL UNIQUE,
173
+ title TEXT NOT NULL,
174
+ description TEXT NOT NULL DEFAULT '',
175
+ curator TEXT NOT NULL DEFAULT '',
176
+ image_url TEXT,
177
+ created_at TEXT NOT NULL,
178
+ updated_at TEXT NOT NULL,
179
+ UNIQUE(provider_id, external_id)
180
+ );
181
+
182
+ CREATE TABLE IF NOT EXISTS spotify_music_playlist_tracks (
183
+ playlist_id TEXT NOT NULL
184
+ REFERENCES spotify_music_playlists(id) ON DELETE CASCADE,
185
+ track_id TEXT NOT NULL REFERENCES spotify_tracks(id) ON DELETE CASCADE,
186
+ position INTEGER NOT NULL,
187
+ PRIMARY KEY(playlist_id, position)
188
+ );
189
+
190
+ CREATE TABLE IF NOT EXISTS spotify_recently_played (
191
+ kind TEXT NOT NULL CHECK(kind IN ('track', 'playlist')),
192
+ item_id TEXT NOT NULL,
193
+ item_json TEXT NOT NULL,
194
+ played_at_ms INTEGER NOT NULL,
195
+ PRIMARY KEY(kind, item_id)
196
+ );
197
+
198
+ CREATE TABLE IF NOT EXISTS netflix_title_markers (
199
+ title_id TEXT NOT NULL,
200
+ marker_type TEXT NOT NULL CHECK(marker_type IN ('intro', 'outro')),
201
+ time_seconds REAL NOT NULL DEFAULT 0,
202
+ updated_at TEXT NOT NULL,
203
+ PRIMARY KEY (title_id, marker_type)
204
+ );
205
+
206
+ CREATE INDEX IF NOT EXISTS netflix_watch_progress_profile_updated
207
+ ON netflix_watch_progress (profile_id, updated_at DESC);
208
+ CREATE INDEX IF NOT EXISTS playback_activity_profile_updated
209
+ ON playback_activity_sessions (profile_id, updated_at_ms DESC);
210
+ CREATE INDEX IF NOT EXISTS playback_activity_commands_session_id
211
+ ON playback_activity_commands (session_id, id);
212
+ CREATE INDEX IF NOT EXISTS playback_activity_commands_created
213
+ ON playback_activity_commands (created_at_ms);
214
+ CREATE INDEX IF NOT EXISTS spotify_homepage_items_provider_position
215
+ ON spotify_homepage_items(provider_id, position);
216
+ CREATE INDEX IF NOT EXISTS spotify_downloads_created_at
217
+ ON spotify_downloads(created_at DESC);
218
+ CREATE INDEX IF NOT EXISTS spotify_music_playlist_tracks_track
219
+ ON spotify_music_playlist_tracks(track_id);
220
+ CREATE INDEX IF NOT EXISTS spotify_recently_played_at
221
+ ON spotify_recently_played(played_at_ms DESC);
222
+ `,me=[[`id`,``],[`name`,``],[`base_url`,``],[`last_synced_at`,null],[`sync_status`,`idle`],[`last_error`,null]],he=[[`id`,``],[`provider_id`,``],[`source_url`,``],[`watch_url`,null],[`title`,``],[`original_title`,null],[`synopsis`,``],[`year`,0],[`maturity_rating`,`NR`],[`duration`,`Available`],[`match_score`,0],[`genres`,`[]`],[`cast`,`[]`],[`director`,null],[`subtitles`,`[]`],[`episodes`,`[]`],[`media_type`,`movie`],[`poster_image`,``],[`backdrop_image`,``],[`accent_color`,`#111111`],[`playback_path`,null],[`playback_kind`,null],[`download_status`,`metadata`],[`download_progress`,0],[`download_error`,null],[`created_at`,``],[`updated_at`,``]],ge=[[`profile_id`,``],[`title_id`,``],[`in_my_list`,0],[`liked`,0],[`updated_at`,``]],_e=[[`profile_id`,``],[`title_id`,``],[`season_number`,1],[`episode_number`,1],[`position_seconds`,0],[`duration_seconds`,0],[`completed`,0],[`updated_at`,``]],ve=[[`profile_id`,``],[`font_size`,32],[`position_x`,50],[`position_y`,78],[`font_color`,`#ffffff`],[`background_color`,`rgba(0,0,0,0.75)`],[`font_family`,`default`],[`captions_on`,0],[`updated_at`,``]],ye=[[`session_id`,``],[`profile_id`,``],[`title_id`,``],[`snapshot_json`,`{}`],[`updated_at_ms`,0]],be=[[`id`,0],[`session_id`,``],[`command_json`,`{}`],[`created_at_ms`,0]],xe=[[`id`,``],[`name`,``],[`home_url`,``],[`sync_status`,`idle`],[`last_synced_at`,null],[`last_error`,null],[`created_at`,``],[`updated_at`,``]],Se=[[`id`,0],[`provider_id`,``],[`external_id`,``],[`kind`,``],[`title`,``],[`subtitle`,``],[`image_url`,null],[`source_url`,null],[`position`,0],[`payload_json`,null],[`synced_at`,``]],Ce=[[`id`,``],[`provider_id`,``],[`external_id`,``],[`source_url`,``],[`title`,``],[`artist`,``],[`album`,``],[`duration_sec`,0],[`image_url`,null],[`audio_path`,null],[`audio_mime`,null],[`audio_bytes`,null],[`download_status`,`queued`],[`created_at`,``],[`updated_at`,``]],we=[[`id`,``],[`source_url`,``],[`provider_id`,``],[`track_id`,null],[`status`,`queued`],[`progress`,0],[`bytes_downloaded`,0],[`total_bytes`,null],[`error`,null],[`created_at`,``],[`updated_at`,``]],Te=[[`id`,``],[`provider_id`,``],[`external_id`,``],[`source_url`,``],[`title`,``],[`description`,``],[`curator`,``],[`image_url`,null],[`created_at`,``],[`updated_at`,``]],Ee=[[`playlist_id`,``],[`track_id`,``],[`position`,0]];function D(e){return`${y}\u0000${e}`}function O(e){return{id:e.id,name:e.name,avatarColor:e.avatarColor,createdAt:e.createdAt,updatedAt:e.updatedAt}}function k(e){return e.normalize(`NFKC`).trim().toLocaleLowerCase()}function A(e,t){let n=e.trim(),r=t.trim();if(!n||n.length>30)throw Error(`Profile name must be between 1 and 30 characters.`);if(!/^#[0-9a-f]{6}$/i.test(r))throw Error(`Choose a valid avatar color.`);return{name:n,avatarColor:r}}function De(e){return e.normalize(`NFKD`).replace(/\p{Mark}/gu,``).toLocaleLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,48).replace(/-+$/g,``)||`profile`}function j(e,t,r){let i=De(e),a=e=>r.has(e)||n(N(e));if(!a(i))return i;let o=m(`sha256`).update(t).digest(`hex`).slice(0,8),s=`${i.slice(0,39).replace(/-+$/g,``)}-${o}`;if(!a(s))return s;let c=2;for(;a(`${s}-${c}`);)c+=1;return`${s}-${c}`}function M(e){if(!/^[a-z0-9](?:[a-z0-9-]{0,62})$/.test(e)||e===`.`||e===`..`)throw Error(`Profile storage key is invalid.`)}function N(...t){let n=e.resolve(y),r=e.resolve(n,...t);if(r===n||!r.startsWith(`${n}${e.sep}`))throw Error(`Profile storage path escapes the profile data root.`);return r}function P(e){return M(e.storageKey),N(e.storageKey)}function F(e){return N(e.storageKey,w)}function I(e){return N(e.storageKey,w,ie)}function L(e){if(!n(e))return;let t=i(e);if(t.isSymbolicLink()||!t.isDirectory())throw Error(`Unsafe profile storage directory: ${e}`)}function Oe(e){a(y,{recursive:!0});let t=P(e),r=F(e);L(t),n(t)||a(t,{recursive:!1}),L(r),n(r)||a(r,{recursive:!1});let o=I(e);if(n(o)&&i(o).isSymbolicLink())throw Error(`Profile database cannot be a symbolic link.`)}function ke(e){a(y,{recursive:!0});let t=P(e),n=F(e);a(t,{recursive:!1});try{a(n,{recursive:!1})}catch(e){throw d(t,{recursive:!0,force:!0}),e}}function R(e){if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Profile manifest is invalid.`);let t=e;if(t.version!==T||typeof t.activeProfileId!=`string`||!Array.isArray(t.profiles)||t.profiles.length===0)throw Error(`Profile manifest is invalid.`);let n=new Set,r=new Set,i=new Set,a=t.profiles.map(e=>{if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Profile manifest contains an invalid profile.`);let t=e;if(typeof t.id!=`string`||!t.id||typeof t.name!=`string`||!t.name.trim()||typeof t.avatarColor!=`string`||!/^#[0-9a-f]{6}$/i.test(t.avatarColor)||typeof t.createdAt!=`string`||!t.createdAt||typeof t.updatedAt!=`string`||!t.updatedAt||typeof t.storageKey!=`string`||typeof t.legacyBootstrap!=`boolean`)throw Error(`Profile manifest contains an invalid profile.`);M(t.storageKey);let a=k(t.name);if(n.has(t.id)||r.has(a)||i.has(t.storageKey))throw Error(`Profile manifest contains duplicate profiles.`);return n.add(t.id),r.add(a),i.add(t.storageKey),t});if(!n.has(t.activeProfileId))throw Error(`Profile manifest active profile is invalid.`);return{version:T,activeProfileId:t.activeProfileId,profiles:a}}function Ae(e){let t=e.indexOf(`:`),n=Number(t===-1?e:e.slice(0,t));return Number.isSafeInteger(n)&&n>0?n:void 0}function je(e){try{return process.kill(e,0),!0}catch(e){return e instanceof Error&&`code`in e&&e.code===`EPERM`}}function Me(){a(C,{recursive:!0});let t=`${String(process.pid).padStart(10,`0`)}-${h()}`,r=e.join(C,t);p(r,``,{flag:`wx`,mode:384});try{let r=Date.now();for(let t of c(C,{withFileTypes:!0})){if(!t.isFile())continue;let n=e.join(C,t.name),i=Number(t.name.slice(0,10));try{(!Number.isSafeInteger(i)||!je(i)||r-f(n).mtimeMs>se)&&d(n,{force:!0})}catch{}}if(c(C).filter(e=>/^\d{10}-/.test(e)).sort()[0]!==t)return!1;let i=``,a=0;try{i=s(S,`utf8`),a=r-f(x).mtimeMs}catch{try{a=r-f(x).mtimeMs}catch{return!0}}let o=Ae(i);if(o!==void 0&&je(o)||o===void 0&&a<ce)return!1;let l=``;try{l=s(S,`utf8`)}catch{}if(l!==i)return!1;let p=N(`.profiles-lock-abandoned-${h()}`);try{u(x,p)}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!0;throw e}let m=``;try{m=s(e.join(p,`owner`),`utf8`)}catch{}return m===i?(d(p,{recursive:!0,force:!0}),!0):(n(x)||u(p,x),!1)}finally{d(r,{force:!0})}}function z(e){a(y,{recursive:!0});let t=Date.now()+oe,n=`${process.pid}:${h()}`;for(;;)try{a(x,{recursive:!1});try{p(S,n,{encoding:`utf8`,flag:`wx`,mode:384})}catch(e){throw d(x,{recursive:!0,force:!0}),e}break}catch(e){if(!(e instanceof Error)||!(`code`in e)||e.code!==`EEXIST`)throw e;if(Me())continue;if(Date.now()>=t)throw Error(`Timed out waiting for the profile registry lock.`);Atomics.wait(le,0,0,25)}try{return e()}finally{try{s(S,`utf8`)===n&&d(x,{recursive:!0,force:!0})}catch{}}}function B(e){let n=R(e);a(y,{recursive:!0});let i=N(`.profiles-${process.pid}-${h()}.tmp`),s;try{s=o(i,`wx`,384),p(s,`${JSON.stringify(n,null,2)}\n`,`utf8`),r(s),t(s),s=void 0,u(i,b);let e;try{e=o(y,`r`),r(e)}catch{}finally{e!==void 0&&t(e)}}catch(e){throw s!==void 0&&t(s),d(i,{force:!0}),e}}function V(){return R(JSON.parse(s(b,`utf8`)))}function H(e,t){return!!e.prepare(`SELECT 1 FROM sqlite_master
223
+ WHERE type = 'table' AND name = ?`).get(t)}function U(e){if(!n(e))return;let t=new g(e,{readonly:!0,fileMustExist:!0});return t.pragma(`query_only = ON`),t.pragma(`busy_timeout = 5000`),t}function W(e,t,n=``){return typeof e[t]==`string`?e[t]:n}function Ne(){let e=new Date().toISOString(),t=[{id:`adam`,name:`Adam`,avatarColor:`#1565c0`,createdAt:e,updatedAt:e},{id:`eva`,name:`Eva`,avatarColor:`#f05b47`,createdAt:e,updatedAt:e}],n=U(ne);if(!n)return{profiles:t,activeProfileId:t[0].id};try{if(!H(n,`profiles`))return{profiles:t,activeProfileId:t[0].id};let r=n.prepare(`SELECT * FROM profiles`).all().flatMap(t=>{let n=W(t,`id`).trim(),r=W(t,`name`).trim();if(!n||!r)return[];let i=W(t,`avatar_color`),a=W(t,`created_at`).trim()||e,o=W(t,`updated_at`).trim()||a;return[{id:n,name:r,avatarColor:/^#[0-9a-f]{6}$/i.test(i)?i:`#1565c0`,createdAt:a,updatedAt:o}]}).sort((e,t)=>e.createdAt.localeCompare(t.createdAt)||e.name.localeCompare(t.name));if(!r.length)return{profiles:t,activeProfileId:t[0].id};let i=r[0].id;if(H(n,`app_settings`)){let e=n.prepare(`SELECT value FROM app_settings
224
+ WHERE key = 'active_profile_id'`).get(),t=e?W(e,`value`).trim():``;r.some(e=>e.id===t)&&(i=t)}return{profiles:r,activeProfileId:i}}finally{n.close()}}function Pe(){let e=Ne(),t=new Set,n=e.profiles.map(e=>{let n=j(e.name,e.id,t);return t.add(n),{...e,storageKey:n,legacyBootstrap:!0}});return{version:T,activeProfileId:e.activeProfileId,profiles:n}}function Fe(e,t,n){let r=e[t];return r===null||typeof r==`string`||typeof r==`number`||typeof r==`bigint`||Buffer.isBuffer(r)?r:n}function G(e,t,n,r,i,a=[]){let o=e.prepare(n).all(...a);if(!o.length)return;let s=i.map(([e])=>e),c=s.map(()=>`?`).join(`, `),l=t.prepare(`INSERT OR IGNORE INTO ${r}
225
+ (${s.join(`, `)}) VALUES (${c})`);for(let e of o)l.run(...i.map(([t,n])=>Fe(e,t,n)))}function K(e,t){return!!e.prepare(`SELECT 1 FROM schema_migrations WHERE id = ?`).get(t)}function q(e,t){e.prepare(`INSERT OR IGNORE INTO schema_migrations (id, applied_at)
226
+ VALUES (?, ?)`).run(t,new Date().toISOString())}function Ie(e,t){let n=`legacy-netflix-v1`;if(K(e,n))return;let r=U(ne);if(r){r.exec(`BEGIN`);try{if(!H(r,`providers`)||!H(r,`movies`))return;e.transaction(()=>{K(e,n)||(H(r,`providers`)&&G(r,e,`SELECT * FROM providers`,`netflix_providers`,me),H(r,`movies`)&&G(r,e,`SELECT * FROM movies`,`netflix_movies`,he),H(r,`profile_title_state`)&&G(r,e,`SELECT * FROM profile_title_state WHERE profile_id = ?`,`netflix_profile_title_state`,ge,[t]),H(r,`watch_progress`)&&G(r,e,`SELECT * FROM watch_progress WHERE profile_id = ?`,`netflix_watch_progress`,_e,[t]),H(r,`subtitle_preferences`)&&G(r,e,`SELECT * FROM subtitle_preferences WHERE profile_id = ?`,`netflix_subtitle_preferences`,ve,[t]),H(r,`playback_activity_sessions`)&&G(r,e,`SELECT * FROM playback_activity_sessions WHERE profile_id = ?`,`playback_activity_sessions`,ye,[t]),H(r,`playback_activity_sessions`)&&H(r,`playback_activity_commands`)&&G(r,e,`SELECT commands.*
227
+ FROM playback_activity_commands commands
228
+ JOIN playback_activity_sessions sessions
229
+ ON sessions.session_id = commands.session_id
230
+ WHERE sessions.profile_id = ?`,`playback_activity_commands`,be,[t]),q(e,n))}).immediate()}finally{r.inTransaction&&r.exec(`ROLLBACK`),r.close()}}}function Le(e){let t=`legacy-spotify-v1`;if(K(e,t))return;let n=U(re);if(n){n.exec(`BEGIN`);try{if(!H(n,`providers`)||!H(n,`tracks`)||!H(n,`downloads`))return;e.transaction(()=>{K(e,t)||(H(n,`providers`)&&G(n,e,`SELECT * FROM providers`,`spotify_providers`,xe),H(n,`homepage_items`)&&G(n,e,`SELECT * FROM homepage_items`,`spotify_homepage_items`,Se),H(n,`tracks`)&&G(n,e,`SELECT * FROM tracks`,`spotify_tracks`,Ce),H(n,`music_playlists`)&&G(n,e,`SELECT * FROM music_playlists`,`spotify_music_playlists`,Te),H(n,`music_playlist_tracks`)&&G(n,e,`SELECT * FROM music_playlist_tracks`,`spotify_music_playlist_tracks`,Ee),H(n,`downloads`)&&G(n,e,`SELECT * FROM downloads`,`spotify_downloads`,we),q(e,t))}).immediate()}finally{n.inTransaction&&n.exec(`ROLLBACK`),n.close()}}}function Re(e){let t=e.prepare(`
231
+ INSERT INTO netflix_providers (id, name, base_url)
232
+ VALUES (?, ?, ?)
233
+ ON CONFLICT(id) DO UPDATE SET
234
+ name = excluded.name,
235
+ base_url = excluded.base_url
236
+ `);for(let e of ue)t.run(e.id,e.name,e.baseUrl);let n=new Date().toISOString(),r=e.prepare(`
237
+ INSERT INTO spotify_providers (
238
+ id, name, home_url, created_at, updated_at
239
+ ) VALUES (?, ?, ?, ?, ?)
240
+ ON CONFLICT(id) DO UPDATE SET
241
+ name = excluded.name,
242
+ home_url = excluded.home_url
243
+ `);for(let e of de)r.run(e.id,e.name,e.homeUrl,n,n)}function ze(e){let t=new Date().toISOString();e.prepare(`UPDATE netflix_movies
244
+ SET download_status = 'failed',
245
+ download_progress = 0,
246
+ download_error = COALESCE(
247
+ download_error,
248
+ 'Download was interrupted by a server restart.'
249
+ ),
250
+ updated_at = ?
251
+ WHERE download_status IN ('queued', 'downloading')`).run(t),e.prepare(`UPDATE netflix_providers
252
+ SET sync_status = 'error',
253
+ last_error = COALESCE(
254
+ last_error,
255
+ 'Synchronization was interrupted by a server restart.'
256
+ )
257
+ WHERE sync_status = 'syncing'`).run()}function Be(e){let t=new Date().toISOString();e.prepare(`UPDATE spotify_downloads
258
+ SET status = 'failed',
259
+ error = COALESCE(
260
+ error,
261
+ 'Download was interrupted by a server restart.'
262
+ ),
263
+ updated_at = ?
264
+ WHERE status IN ('queued', 'fetching', 'downloading')`).run(t),e.prepare(`UPDATE spotify_tracks
265
+ SET download_status = 'failed', updated_at = ?
266
+ WHERE download_status IN ('queued', 'fetching', 'downloading')`).run(t),e.prepare(`UPDATE spotify_providers
267
+ SET sync_status = 'error',
268
+ last_error = COALESCE(
269
+ last_error,
270
+ 'Synchronization was interrupted by a server restart.'
271
+ ),
272
+ updated_at = ?
273
+ WHERE sync_status = 'syncing'`).run(t)}function Ve(e,t){e.pragma(`busy_timeout = 5000`),e.pragma(`foreign_keys = ON`),e.pragma(`journal_mode = WAL`),e.exec(pe),e.pragma(`user_version = ${ae}`),e.prepare(`INSERT INTO profiles (
274
+ id, name, avatar_color, created_at, updated_at
275
+ ) VALUES (?, ?, ?, ?, ?)
276
+ ON CONFLICT(id) DO UPDATE SET
277
+ name = excluded.name,
278
+ avatar_color = excluded.avatar_color,
279
+ created_at = excluded.created_at,
280
+ updated_at = excluded.updated_at`).run(t.id,t.name,t.avatarColor,t.createdAt,t.updatedAt),Re(e),q(e,`merged-schema-v1`),t.legacyBootstrap&&(Ie(e,t.id),Le(e)),ze(e),Be(e),e.exec(`
281
+ CREATE UNIQUE INDEX IF NOT EXISTS spotify_downloads_one_active_source
282
+ ON spotify_downloads(provider_id, source_url)
283
+ WHERE status IN ('queued', 'fetching', 'downloading');
284
+ `)}function J(e){let t=D(e.id),n=I(e),r=E.connections.get(t);if(r?.database.open){if(r.databasePath!==n)throw Error(`Profile database path changed unexpectedly.`);return r.database}E.connections.delete(t),Oe(e);let i=new g(n);try{Ve(i,e)}catch(e){throw i.close(),e}return E.connections.set(t,{database:i,databasePath:n}),i}function He(){E.initializedRoots.has(y)||(a(y,{recursive:!0}),z(()=>{n(b)||B(Pe());let e=V();for(let t of e.profiles)J(t)}),E.initializedRoots.add(y))}function Y(){return He(),V()}function X(e){return He(),z(()=>e(V()))}function Z(e){return Y().profiles.find(t=>t.id===e)}function Q(e){return[...e].sort((e,t)=>e.createdAt.localeCompare(t.createdAt)||e.name.localeCompare(t.name))}function Ue(){return Q(Y().profiles).map(O)}function We(e){let t=Z(e);return t?O(t):void 0}function $(){let e=Y(),t=e.profiles.find(t=>t.id===e.activeProfileId);if(!t)throw Error(`At least one profile is required.`);return O(t)}function Ge(e){return X(t=>{let n=t.profiles.find(t=>t.id===e);if(!n)throw Error(`Profile not found.`);return B({...t,activeProfileId:e}),O(n)})}function Ke(e,t){let r=A(e,t);return X(e=>{if(e.profiles.some(e=>k(e.name)===k(r.name)))throw Error(`UNIQUE constraint failed: profiles.name`);let t=new Date().toISOString(),i=h(),a=new Set(e.profiles.map(e=>e.storageKey)),o={id:i,name:r.name,avatarColor:r.avatarColor,createdAt:t,updatedAt:t,storageKey:j(r.name,i,a),legacyBootstrap:!1},s=!1;try{return ke(o),s=!0,J(o),B({...e,profiles:[...e.profiles,o]}),O(o)}catch(e){let t=D(o.id),r=E.connections.get(t);r?.database.open&&r.database.close(),E.connections.delete(t);let i=P(o);throw s&&n(i)&&(L(i),d(i,{recursive:!0,force:!1})),e}})}function qe(e,t){let n=A(t.name,t.avatarColor);return X(t=>{let r=t.profiles.findIndex(t=>t.id===e);if(r<0)throw Error(`Profile not found.`);if(t.profiles.some(t=>t.id!==e&&k(t.name)===k(n.name)))throw Error(`UNIQUE constraint failed: profiles.name`);let i=t.profiles[r],a={...i,name:n.name,avatarColor:n.avatarColor,updatedAt:new Date().toISOString()},o=J(i);o.prepare(`UPDATE profiles
285
+ SET name = ?, avatar_color = ?, updated_at = ?
286
+ WHERE id = ?`).run(a.name,a.avatarColor,a.updatedAt,e);let s=[...t.profiles];s[r]=a;try{B({...t,profiles:s})}catch(t){throw o.prepare(`UPDATE profiles
287
+ SET name = ?, avatar_color = ?, updated_at = ?
288
+ WHERE id = ?`).run(i.name,i.avatarColor,i.updatedAt,e),t}return O(a)})}function Je(e){if(!Z(e))throw Error(`Profile not found.`);let t=D(e),n=E.connections.get(t);if(!n||(E.connections.delete(t),!n.database.open))return;let r;try{n.database.pragma(`wal_checkpoint(TRUNCATE)`)}catch(e){r=e}finally{n.database.close()}if(r)throw r}function Ye(t){L(t);let n=l(y),r=l(t);if(e.dirname(r)!==n)throw Error(`Refusing to delete a profile outside the data root.`)}function Xe(e){return X(t=>{let r=t.profiles.find(t=>t.id===e);if(!r)throw Error(`Profile not found.`);if(t.profiles.length===1)throw Error(`You must keep at least one profile.`);Je(e);let i=P(r),a=t.profiles.filter(t=>t.id!==e),o=t.activeProfileId===e?Q(a)[0].id:t.activeProfileId;B({...t,activeProfileId:o,profiles:a});let s;try{n(i)&&(Ye(i),s=N(`.deleted-${r.storageKey}-${h()}`),u(i,s))}catch(e){try{B(t)}catch(t){throw AggregateError([e,t],`Profile data could not be deleted and its registry could not be restored.`)}throw e}if(s)try{d(s,{recursive:!0,force:!1})}catch{}})}function Ze(e){let t=e??$().id,n=Z(t);if(!n)throw Error(`Profile not found.`);return E.connections.get(D(t))?.database.open?J(n):z(()=>{let e=V().profiles.find(e=>e.id===t);if(!e)throw Error(`Profile not found.`);return J(e)})}export{Ze as a,qe as c,We as i,Xe as n,Ue as o,$ as r,Ge as s,Ke as t};
@@ -0,0 +1,2 @@
1
+ import{c as e,s as t,t as n}from"./database-BNnYfHmT.js";import{i as r,n as i,r as a}from"./providers-B3YELr_F.js";import{i as o}from"./downloader-DgnjNL5T.js";import{r as s}from"./subtitles-DYBlMnha.js";import{randomUUID as c}from"node:crypto";var l=globalThis,u=l.homeAtomNetflixStreams??={sessions:new Map},d=7200*1e3,f=32;function p(){let e=Date.now();for(let[t,n]of u.sessions)e-n.lastAccessedAt>d&&u.sessions.delete(t);if(u.sessions.size<=f)return;let t=[...u.sessions.values()].sort((e,t)=>e.lastAccessedAt-t.lastAccessedAt);for(let e of t.slice(0,u.sessions.size-f))u.sessions.delete(e.id)}function m(e,t){return`/api/netflix/stream/${encodeURIComponent(e.movieId)}/${encodeURIComponent(e.id)}/${t}`}function h(e,t,n){let r=new URL(t,n).toString(),i=e.resourceTokens.get(r);if(i)return m(e,`resource/${i}`);let a=c();return e.resources.set(a,r),e.resourceTokens.set(r,a),m(e,`resource/${a}`)}function g(e,t,n){return e.split(/\r?\n/).map(e=>{let r=e.trim();return r?r.startsWith(`#`)?e.replace(/\bURI=(["'])(.*?)\1/g,(e,r,i)=>`URI=${r}${h(n,i,t)}${r}`):h(n,r,t):e}).join(`
2
+ `)}function _(e,t){p();let n=u.sessions.get(t);return!n||n.movieId!==e?null:(n.lastAccessedAt=Date.now(),n)}function v(e,t,n){return new Response(g(e,t,n),{headers:{"cache-control":`no-store`,"content-type":`application/vnd.apple.mpegurl`}})}function y(e){let t=new Headers;for(let n of[`accept-ranges`,`content-length`,`content-range`,`content-type`,`etag`,`last-modified`]){let r=e.headers.get(n);r&&t.set(n,r)}return t.set(`cache-control`,`private, max-age=3600`),t}async function b(e,t){let n;for(let r=0;r<3;r+=1){if(n=await fetch(e,{headers:t,redirect:`follow`,signal:AbortSignal.timeout(6e4)}),n.ok||n.status===206||!(n.status===404||n.status===408||n.status===425||n.status===429||n.status>=500)||r===2)return n;await n.body?.cancel(),await new Promise(e=>setTimeout(e,300*2**r))}return n}async function x(r,l,d){p();let f=d??t().id,h=e(r,f);if(!h?.watch_url)throw Error(`This title does not have a playable provider page.`);let g=h.watch_url,_;if(l){let e=[];try{e=JSON.parse(h.episodes)}catch{}if(_=e.find(e=>e.episode===l),!_?.watchUrl)throw Error(`Episode ${l} is not available for this title.`);_.id||(_={..._,id:`episode-${l}`}),g=_.watchUrl}let v=[];if(!_)try{v=JSON.parse(h.subtitles)}catch{}let y=await i(g,h.source_url),b=_?.id||`movie`,x=_?.subtitles?.length?[..._.subtitles]:v,[S,C]=await Promise.all([o(g,y),x.length?Promise.resolve(x):s(r,b,g,y,f)]),w=_&&h.provider_id===`phimhdcss`?{...a(_,y),subtitles:C}:_?{..._,subtitles:C}:void 0;try{n(r,w?{episode:w}:{subtitles:C},f)}catch{}let T=Date.now(),E={id:c(),profileId:f,movieId:r,kind:S.kind,referer:S.referer,resources:new Map,resourceTokens:new Map,createdAt:T,lastAccessedAt:T,...S.kind===`file`?{rootUrl:S.url}:{initialPlaylist:S.playlist,initialBaseUrl:S.baseUrl}};return u.sessions.set(E.id,E),{kind:E.kind,episode:l,episodeMetadata:w,subtitles:C,path:m(E,E.kind===`hls`?`index.m3u8`:`media`)}}async function S(e,t,n){let[i,a,o]=n,s=_(t,i);if(!s)return Response.json({error:`This stream expired. Close the player and try again.`},{status:404});if(a===`index.m3u8`&&s.kind===`hls`&&s.initialPlaylist&&s.initialBaseUrl)return v(s.initialPlaylist,s.initialBaseUrl,s);let c=a===`media`&&s.kind===`file`?s.rootUrl:a===`resource`&&o?s.resources.get(o):void 0;if(!c)return Response.json({error:`Stream resource not found.`},{status:404});let l={...r(s.referer,c)},u=e.headers.get(`range`);u&&(l.range=u);let d=await b(c,l);if(!d.ok&&d.status!==206)return Response.json({error:`The provider stream returned HTTP ${d.status}.`},{status:502});let f=d.headers.get(`content-type`)?.toLowerCase()||``,p=new URL(c).pathname.toLowerCase();if(f.includes(`mpegurl`)||p.endsWith(`.m3u8`)||p.endsWith(`.txt`)){let e=await d.text();if(e.trimStart().startsWith(`#EXTM3U`))return v(e,d.url||c,s);let t=y(d);return t.delete(`content-length`),new Response(e,{status:d.status,headers:t})}return new Response(d.body,{status:d.status,headers:y(d)})}export{S as n,x as t};
@@ -0,0 +1,2 @@
1
+ import{c as e}from"./database-BNnYfHmT.js";import{i as t,n,u as r}from"./providers-B3YELr_F.js";import{t as i}from"./downloader-DgnjNL5T.js";import a from"node:path";import{mkdirSync as o,writeFileSync as s}from"node:fs";var c=process.env.HOME_ATOM_NETFLIX_DATA_DIR||process.env.NETFLIX_ATOM_DATA_DIR||a.join(process.cwd(),[`da`,`ta`].join(``));function l(e){let t=``;for(let n=0;n<e.length;n+=1){if(e[n]!==`\\`){t+=e[n];continue}let r=e[++n];r===`n`?t+=`
2
+ `:r===`r`?t+=`\r`:r===`t`?t+=` `:r===`x`?(t+=String.fromCharCode(Number.parseInt(e.slice(n+1,n+3),16)),n+=2):r===`u`?(t+=String.fromCharCode(Number.parseInt(e.slice(n+1,n+5),16)),n+=4):t+=r}return t}function u(e,t){let n=e<10?String(e):e<36?String.fromCharCode(e+87):String.fromCharCode(e+29);return e<t?n:`${u(Math.floor(e/t),t)}${n}`}function d(e){let t=e.match(/eval\(function\(p,a,c,k,e,d\)[\s\S]*?\}\('((?:\\.|[^'])*)',(\d+),(\d+),'((?:\\.|[^'])*)'\.split\('\|'\),0,\{\}\)\)/);if(!t)return``;let n=l(t[1]),r=Number(t[2]),i=Number(t[3]),a=l(t[4]).split(`|`);for(let e=i-1;e>=0;--e)a[e]&&(n=n.replace(RegExp(`\\b${u(e,r)}\\b`,`g`),a[e]));return n}function f(e){let t=e.trim().toLowerCase();return/^(vi|vie|vietnamese|tiếng việt)$/.test(t)?`vi`:/^(en|eng|english)$/.test(t)?`en`:/^(zh|zho|chi|chinese)$/.test(t)?`zh`:`und`}function p(e,t){let n=[],r=e.match(/playerjsSubtitle\s*=\s*["']([^"']*)["']/)?.[1];if(r)for(let e of r.matchAll(/\[([^\]]+)](https?:\/\/.*?)(?=,\[[^\]]+]|$)/g)){let r=e[1].trim();n.push({label:r,language:f(r),url:e[2].replace(/\\\//g,`/`),referer:t})}if(n.length)return n;let i=``;try{i=d(e)}catch{return n}let a=i.match(/"tracks"\s*:\s*(\[[\s\S]*?])\s*,\s*"captions"/)?.[1];if(a)try{let e=JSON.parse(a);for(let r of e){if(!r.file||r.kind===`thumbnails`||r.kind&&r.kind!==`captions`&&r.kind!==`subtitles`)continue;let e=r.label||r.language||`Subtitles`;n.push({label:e,language:r.language||f(e),url:new URL(r.file,t).toString(),referer:t})}}catch{}return[...new Map(n.map(e=>[e.url,e])).values()]}async function m(e,t){let a=t||await n(e),o=r(a),s=i(a)||o(`iframe[src]`).first().attr(`src`)||``;return!s||(s=new URL(s,e).toString(),/\.(?:m3u8|mp4|mkv|webm)(?:$|\?)/i.test(s))?[]:p(await n(s,e),s)}function h(e){let t=e.replace(/^\uFEFF/,``).trim();return t.startsWith(`WEBVTT`)?`${t}\n`:t.includes(`-->`)?`WEBVTT\n\n${t.replace(/(\d{2}:\d{2}:\d{2}),(\d{3})(?=\s+-->)/g,`$1.$2`).replace(/(?<=-->\s+\d{2}:\d{2}:\d{2}),(\d{3})/g,`.$1`)}\n`:``}function g(e,t,n,r){let i=`/api/netflix/subtitles/${encodeURIComponent(e)}/${encodeURIComponent(t)}/${n}.vtt`;return r?`${i}?profileId=${encodeURIComponent(r)}`:i}function _(e){return e.normalize(`NFKD`).replace(/[^\w.-]+/g,`-`).replace(/^-+|-+$/g,``).toLowerCase()||`track`}async function v(e,n,r,i){let l=[];try{l=await m(r,i)}catch{return[]}if(!l.length)return[];let u=a.join(`subtitles`,_(n)),d=a.join(c,`movies`,e,u);o(d,{recursive:!0});let f=[];for(let[n,r]of l.entries())try{let i=await fetch(r.url,{headers:t(r.referer,r.url),signal:AbortSignal.timeout(3e4)});if(!i.ok)continue;let o=h(await i.text());if(!o)continue;let c=`${String(n+1).padStart(2,`0`)}-${_(r.language)}-${_(r.label)}.vtt`;s(a.join(d,c),o,`utf8`),f.push({label:r.label,language:r.language,src:`/api/netflix/media/${encodeURIComponent(e)}/${u.split(a.sep).map(encodeURIComponent).join(`/`)}/${encodeURIComponent(c)}`})}catch{}return f}async function y(e,t,n,r,i){let a=[];try{a=await m(n,r)}catch{return[]}return a.map((n,r)=>({label:n.label,language:n.language,src:g(e,t,r,i)}))}async function b(e){if(e.episodes?.length){let t=e.episodes.find(t=>t.watchUrl===e.watchUrl)||e.episodes[0],n=await v(e.id,t.id,t.watchUrl);return{...e,episodes:e.episodes.map(e=>e.id===t.id?{...e,subtitles:n}:e)}}return e.watchUrl?{...e,subtitles:await v(e.id,`movie`,e.watchUrl)}:e}function x(t,n,r){let i=e(t,r);if(!i)throw Error(`Movie not found.`);if(n===`movie`){if(!i.watch_url)throw Error(`This movie has no watch page.`);return i.watch_url}let a=[];try{a=JSON.parse(i.episodes)}catch{throw Error(`The stored episode metadata is invalid.`)}let o=a.find(e=>e.id===n);if(!o?.watchUrl)throw Error(`Episode not found.`);return o.watchUrl}async function S(e,n,r,i){let a=(await m(x(e,n,i)))[r];if(!a)throw Error(`Subtitle track not found.`);let o=await fetch(a.url,{headers:t(a.referer,a.url),signal:AbortSignal.timeout(3e4)});if(!o.ok)throw Error(`The subtitle provider returned HTTP ${o.status}.`);let s=h(await o.text());if(!s)throw Error(`The subtitle track is not valid WebVTT.`);return s}export{S as n,y as r,b as t};
@@ -0,0 +1 @@
1
+ import{c as e,g as t}from"./database-BNnYfHmT.js";import{i as n}from"./catalog-BS93IoWO.js";function r(r,i){let a=n(r);if(a)return a;let o=e(r,i);return o?t(o):void 0}function i(e){return!!(e&&!e.completed&&e.positionSeconds>=1)}function a(e,t){let n={...e,progress:void 0,resumeSeason:void 0,resumeEpisode:void 0,resumePositionSeconds:void 0,resumeDurationSeconds:void 0,resumeUpdatedAt:void 0};return!i(t)||!t?n:{...n,progress:t.durationSeconds>0?Math.min(100,Math.max(0,t.positionSeconds/t.durationSeconds*100)):0,resumeSeason:t.seasonNumber,resumeEpisode:t.episodeNumber,resumePositionSeconds:t.positionSeconds,resumeDurationSeconds:t.durationSeconds,resumeUpdatedAt:t.updatedAt}}export{r as n,a as t};
@@ -0,0 +1 @@
1
+ import*as e from"node:async_hooks";import{AsyncLocalStorage as t}from"node:async_hooks";globalThis.AsyncLocalStorage=e.AsyncLocalStorage;var n=globalThis,r=class{getStore(){}run(e,t,...n){return t(...n)}exit(e,...t){return e(...t)}enterWith(e){}disable(){}};function i(e){let i=Symbol.for(e);return n[i]??=typeof t==`function`?new t:new r}var a=Symbol.for(`vinext.requestContext.als`),o=globalThis,s=i(`vinext.unifiedRequestContext.als`);function c(){let e=s.getStore();return e?e.executionContext:o[a]?.getStore()??null}function l(e){return{headersContext:null,actionRevalidationKind:0,pendingRevalidatedTags:new Set,pendingRevalidations:new Set,dynamicUsageDetected:!1,renderRequestApiUsage:new Set,connectionProbe:null,invalidDynamicUsageError:null,pendingSetCookies:[],draftModeCookieHeader:null,phase:`render`,i18nContext:null,serverContext:null,serverInsertedHTMLCallbacks:[],requestScopedCacheLife:null,unstableCacheObservations:new Map,unstableCacheRevalidation:`foreground`,_privateCache:null,cacheableFetchUrls:new Set,currentRequestTags:[],currentFetchSoftTags:[],currentFetchCacheMode:null,currentForceDynamicFetchDefault:!1,dynamicFetchUrls:new Set,refreshStaleFetchesInForeground:!1,isFetchDedupeActive:!1,currentFetchDedupeEntries:new Map,executionContext:c(),requestCache:new WeakMap,afterContext:{callbacks:[],responseClosed:!1,pendingCallbacks:0,completion:null,resolveCompletion:null},ssrContext:null,ssrHeadChildren:[],documentInitialHead:[],rootParams:null,...e}}function u(e){let t=e.afterContext;if(t.resolveCompletion&&t.completion)return;let n,r=new Promise(e=>{n=e});t.completion=r,t.resolveCompletion=n,e.executionContext?.waitUntil(r)}function d(e){let t=e.afterContext;if(!t.responseClosed||t.pendingCallbacks!==0||t.callbacks.length!==0||!t.resolveCompletion)return;let n=t.resolveCompletion;t.resolveCompletion=null,n()}function f(e,t){let n=e.afterContext;u(e),n.pendingCallbacks+=1,Promise.resolve().then(t).catch(e=>{console.error(`[vinext] after() task failed:`,e)}).finally(()=>{--n.pendingCallbacks,d(e)})}async function p(e){let t=e.afterContext;if(!t.responseClosed){t.responseClosed=!0;let n=t.callbacks.splice(0);for(let t of n)f(e,t);d(e)}return t.completion??Promise.resolve()}function m(e,t){if(!e.body)return queueMicrotask(()=>void p(t)),e;let n=new TransformStream;e.body.pipeTo(n.writable).then(()=>void p(t),()=>void p(t));let r=new Response(n.readable,{status:e.status,statusText:e.statusText,headers:e.headers});return r.__vinextStreamedHtmlResponse=e.__vinextStreamedHtmlResponse,r}function h(e,t){return s.run(e,t)}function g(e,t){let n=s.getStore();if(!n)return t();let r={...n};return e(r),s.run(r,t)}function _(){return s.getStore()??l()}function v(){return s.getStore()!=null}export{v as a,i as c,_ as i,m as n,h as o,l as r,g as s,p as t};