home-atom 1.0.1 → 1.0.2
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.
- package/dist/client/.vite/manifest.json +33 -33
- package/dist/client/_next/static/chunks/{app-top-nav-Dh0jEt8u.js → app-top-nav-Co7RIixU.js} +1 -1
- package/dist/client/_next/static/chunks/{index-hn3AiVww.js → index-BUxxgh38.js} +3 -3
- package/dist/client/_next/static/chunks/{layout-segment-context-DRM8YCgm.js → layout-segment-context-D_cL7vkY.js} +1 -1
- package/dist/client/_next/static/chunks/manage-profiles-modal-CWi3qgd4.js +1 -0
- package/dist/client/_next/static/chunks/netflix-browser-BrXQctI5.js +2 -0
- package/dist/client/_next/static/chunks/playback-activity-66FeikYZ.js +1 -0
- package/dist/client/_next/static/chunks/playback-activity-track-switcher-BzvHiv7c.js +1 -0
- package/dist/client/_next/static/chunks/{step-forward-CV8I5pPb.js → provider-registry-CWHfJn3B.js} +1 -1
- package/dist/client/_next/static/chunks/spotify-app-CJimeMR0.js +1 -0
- package/dist/client/_next/static/chunks/storefront-CCeanBPs.js +1 -0
- package/dist/client/_next/static/css/index.8o3Y-jts.css +1 -0
- package/dist/client/_next/static/css/layout.D2NsWbhP.css +1 -0
- package/dist/client/_next/static/css/layout.D4D5fsiD.css +1 -0
- package/dist/client/_next/static/css/manage-profiles-modal.BOKESpxI.css +1 -0
- package/dist/client/vinext-client-entry-manifest.json +1 -1
- package/dist/server/BUILD_ID +1 -1
- package/dist/server/__vite_rsc_assets_manifest.js +35 -35
- package/dist/server/_next/static/{app-page-cache-DcrtN14Y.js → app-page-cache-CQaSjBaF.js} +1 -1
- package/dist/server/_next/static/{app-page-cache-render-DoCquoNb.js → app-page-cache-render-Dty3aMiv.js} +1 -1
- package/dist/server/_next/static/{app-route-handler-dispatch-Be2YIqVp.js → app-route-handler-dispatch-DCSrhGNV.js} +1 -1
- package/dist/server/_next/static/{cache-headers-BQ-QuzWN.js → cache-headers-lY59goLh.js} +1 -1
- package/dist/server/_next/static/css/index.8o3Y-jts.css +1 -0
- package/dist/server/_next/static/css/layout.D2NsWbhP.css +1 -0
- package/dist/server/_next/static/css/layout.D4D5fsiD.css +1 -0
- package/dist/server/_next/static/database-D79CeUrn.js +161 -0
- package/dist/server/_next/static/{db-BE1J0NNS.js → db-x8J3Vuoa.js} +1 -1
- package/dist/server/_next/static/{downloader-DgnjNL5T.js → downloader-0G-gq1D-.js} +3 -3
- package/dist/server/_next/static/episode-sync-Ct5CeY25.js +1 -0
- package/dist/server/_next/static/{isr-decision-D1zHgPSI.js → isr-decision-BOA4L6nr.js} +1 -1
- package/dist/server/_next/static/netflix-page-Cpp2PgD1.js +1 -0
- package/dist/server/_next/static/{page-Cs-erYTy.js → page-BvzzZVuW.js} +1 -1
- package/dist/server/_next/static/{page-DlBGATQp.js → page-C27wjUYW.js} +1 -1
- package/dist/server/_next/static/{page-DlRXyzSQ.js → page-Rnpx_OjW.js} +1 -1
- package/dist/server/_next/static/{providers-B3YELr_F.js → providers-DwLUa_U_.js} +13 -13
- package/dist/server/_next/static/{providers-CMBIwNGs.js → providers-_uOQh6hB.js} +1 -1
- package/dist/server/_next/static/{request-profile-B1yGQ0l6.js → request-profile-ClcH0i0T.js} +1 -1
- package/dist/server/_next/static/route-4xkmbpc4.js +1 -0
- package/dist/server/_next/static/{route-CeVsDcbj2.js → route-B0L_rsrg2.js} +1 -1
- package/dist/server/_next/static/{route-xtz48fti.js → route-B6q-wR2F.js} +1 -1
- package/dist/server/_next/static/{route-DXTJyFWD.js → route-B6zrlXB5.js} +1 -1
- package/dist/server/_next/static/{route-CNNiXZoM2.js → route-B99Pvusz2.js} +1 -1
- package/dist/server/_next/static/route-B9YeBtG6.js +1 -0
- package/dist/server/_next/static/route-BHbJ9hFI2.js +1 -0
- package/dist/server/_next/static/{route-mzLdjDCs2.js → route-BR3Y_0V42.js} +1 -1
- package/dist/server/_next/static/{route-Cim7D4XQ.js → route-BaGo5TEW.js} +1 -1
- package/dist/server/_next/static/{route-DslPFTAK.js → route-BczKcyvV.js} +1 -1
- package/dist/server/_next/static/{route-B0WNLJZp.js → route-BiSvZr_s.js} +1 -1
- package/dist/server/_next/static/{route-Dbs39TkC.js → route-Bj7s6WXI.js} +1 -1
- package/dist/server/_next/static/{route-vD15s2A8.js → route-CGH08Y8C.js} +1 -1
- package/dist/server/_next/static/{route-CkH7Cg-p.js → route-CJmc17Dq.js} +1 -1
- package/dist/server/_next/static/{route-C74RA7va.js → route-CLvr0Ue3.js} +1 -1
- package/dist/server/_next/static/route-CUaIuP8a.js +1 -0
- package/dist/server/_next/static/{route-BPcAdbz_.js → route-CcrNbScT.js} +1 -1
- package/dist/server/_next/static/route-CiOFTfyw2.js +1 -0
- package/dist/server/_next/static/route-Cn7l9eYT.js +1 -0
- package/dist/server/_next/static/route-CnKXQb5r2.js +1 -0
- package/dist/server/_next/static/{route-CVPPmUto.js → route-D93PQ3NI2.js} +1 -1
- package/dist/server/_next/static/{route-D_G2SOlz.js → route-D9ZD1_5T.js} +1 -1
- package/dist/server/_next/static/{route-DAyF4BHS.js → route-DWPhX9kT2.js} +1 -1
- package/dist/server/_next/static/route-DihT2xS82.js +1 -0
- package/dist/server/_next/static/route-Dn-sYYEs.js +1 -0
- package/dist/server/_next/static/route-DpzLCx1V.js +1 -0
- package/dist/server/_next/static/route-Dswz7MvP2.js +1 -0
- package/dist/server/_next/static/route-Dwk0kt8B.js +1 -0
- package/dist/server/_next/static/route-DxLQx9eD.js +1 -0
- package/dist/server/_next/static/{route-zazqen-A2.js → route-FwNLcs2r2.js} +1 -1
- package/dist/server/_next/static/route-GmbSJZjk2.js +1 -0
- package/dist/server/_next/static/route-iUxcDN2r.js +1 -0
- package/dist/server/_next/static/{route-UWQN8uYr.js → route-j61HPczh.js} +1 -1
- package/dist/server/_next/static/{route-CHJzaQu2.js → route-qJ1EJhxJ.js} +1 -1
- package/dist/server/_next/static/{route-BWk9YK7r.js → route-sNW0pw7C.js} +1 -1
- package/dist/server/_next/static/{seed-cache-oCd9Lfdw.js → seed-cache-DsYPqBld.js} +1 -1
- package/dist/server/_next/static/{server-CS40XqUP.js → server-DwVU6UyP.js} +1 -1
- package/dist/server/_next/static/storage-fxaUbjG1.js +322 -0
- package/dist/server/_next/static/streamer-DUAWx_Tm.js +2 -0
- package/dist/server/_next/static/{subtitles-DYBlMnha.js → subtitles-4c0GFlaW.js} +1 -1
- package/dist/server/_next/static/{title-progress-CQBrSxvN.js → title-progress-BTypVYBz.js} +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/prerendered-routes/404.html +2 -2
- package/dist/server/prerendered-routes/index.html +2 -2
- package/dist/server/prerendered-routes/index.rsc +2 -2
- package/dist/server/ssr/__vite_rsc_assets_manifest.js +35 -35
- package/dist/server/ssr/_next/static/css/refresh-cw.BOKESpxI.css +1 -0
- package/dist/server/ssr/_next/static/minimize-gPI5ytrc.js +1 -0
- package/dist/server/ssr/_next/static/netflix-browser-CXJEizJ6.js +2 -0
- package/dist/server/ssr/_next/static/{pin-BQre-tQY.js → pin-DiZl7z2n.js} +1 -1
- package/dist/server/ssr/_next/static/playback-activity-AIsC-eO5.js +1 -0
- package/dist/server/ssr/_next/static/refresh-cw-DNvHlOAy.js +1 -0
- package/dist/server/ssr/_next/static/spotify-app-xyDH0rho.js +1 -0
- package/dist/server/ssr/_next/static/storefront-DMeL08z3.js +1 -0
- package/dist/server/ssr/index.js +1 -1
- package/dist/server/ssr/vinext-client-assets.js +1 -1
- package/dist/server/ssr/vinext-server.json +1 -1
- package/dist/server/vinext-client-assets.js +1 -1
- package/dist/server/vinext-prerender-paths.json +1 -1
- package/dist/server/vinext-prerender.json +16 -1
- package/dist/server/vinext-server.json +1 -1
- package/package.json +4 -2
- package/dist/client/_next/static/chunks/manage-profiles-modal-Doi6QM4e.js +0 -1
- package/dist/client/_next/static/chunks/netflix-browser-L_Vb9wXK.js +0 -2
- package/dist/client/_next/static/chunks/playback-activity-Dug3BRYr.js +0 -1
- package/dist/client/_next/static/chunks/playback-activity-track-switcher-CbO6kl8K.js +0 -1
- package/dist/client/_next/static/chunks/spotify-app-f93K4FG_.js +0 -1
- package/dist/client/_next/static/chunks/storefront-C_nmcuvH.js +0 -1
- package/dist/client/_next/static/css/index.BGXy8e6j.css +0 -1
- package/dist/client/_next/static/css/layout.AKSLtSR6.css +0 -1
- package/dist/client/_next/static/css/layout.D8T68cCD.css +0 -1
- package/dist/client/_next/static/css/manage-profiles-modal.BAVyxRRR.css +0 -1
- package/dist/server/_next/static/css/index.BGXy8e6j.css +0 -1
- package/dist/server/_next/static/css/layout.AKSLtSR6.css +0 -1
- package/dist/server/_next/static/css/layout.D8T68cCD.css +0 -1
- package/dist/server/_next/static/database-BNnYfHmT.js +0 -114
- package/dist/server/_next/static/netflix-page-BRbORN1F.js +0 -1
- package/dist/server/_next/static/route-96DdgJBs.js +0 -1
- package/dist/server/_next/static/route-BBmE0xf12.js +0 -1
- package/dist/server/_next/static/route-BdG5ojMj2.js +0 -1
- package/dist/server/_next/static/route-BovNctFc.js +0 -1
- package/dist/server/_next/static/route-C2LuSjoh.js +0 -1
- package/dist/server/_next/static/route-CCUsQ7AG.js +0 -1
- package/dist/server/_next/static/route-CN6VdJwN.js +0 -1
- package/dist/server/_next/static/route-CjUCKwSj2.js +0 -1
- package/dist/server/_next/static/route-Cn40NftM.js +0 -1
- package/dist/server/_next/static/route-DWAMZHLa.js +0 -1
- package/dist/server/_next/static/route-DiT9UYUd.js +0 -1
- package/dist/server/_next/static/route-jMq6H_yh.js +0 -1
- package/dist/server/_next/static/storage-D1zF0J_t.js +0 -288
- package/dist/server/_next/static/streamer-B1PRR-dj.js +0 -2
- package/dist/server/ssr/_next/static/css/refresh-cw.BAVyxRRR.css +0 -1
- package/dist/server/ssr/_next/static/minimize-C9JLgte-.js +0 -1
- package/dist/server/ssr/_next/static/netflix-browser-CMAehdwi.js +0 -2
- package/dist/server/ssr/_next/static/playback-activity-D5TYX2Ae.js +0 -1
- package/dist/server/ssr/_next/static/refresh-cw-CWdWHjqY.js +0 -1
- package/dist/server/ssr/_next/static/spotify-app-CE-RaTjZ.js +0 -1
- package/dist/server/ssr/_next/static/storefront-C5NNzPMa.js +0 -1
- /package/dist/client/_next/static/{e1ce9af7-d770-43d6-845a-511443f19971 → 7c810a6d-59fd-41d6-b465-cf61efaea04f}/_buildManifest.js +0 -0
- /package/dist/client/_next/static/{e1ce9af7-d770-43d6-845a-511443f19971 → 7c810a6d-59fd-41d6-b465-cf61efaea04f}/_ssgManifest.js +0 -0
- /package/dist/server/_next/static/{layout-Ds3pFa6r.js → layout-D1iejeee.js} +0 -0
- /package/dist/server/_next/static/{layout-ClEWbCcj.js → layout-DpOw0OgX.js} +0 -0
|
@@ -0,0 +1,322 @@
|
|
|
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,b=e.join(ee,`netflix-atom.sqlite`),ne=e.join(te,`spotify-atom.sqlite`),x=e.join(y,`profiles.json`),S=e.join(y,`.profiles.lock`),C=e.join(S,`owner`),w=e.join(y,`.profiles-lock-recovery`),T=`database`,re=`home-atom.sqlite`,E=1,ie=2,ae=6e4,oe=6e4,se=500,ce=new Int32Array(new SharedArrayBuffer(4)),le=[{id:`phimhdcss`,name:`PhimHDC`,baseUrl:`https://phimhdcss.com/`},{id:`fsharetv`,name:`FshareTV`,baseUrl:`https://fsharetv.com/`},{id:`hh3d`,name:`HH3D`,baseUrl:`https://hh3d.com/`},{id:`hhpanda`,name:`HHPanda`,baseUrl:`https://hhpanda.st/`}],ue=[{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/`}],de=globalThis,D=de.homeAtomProfileStorageStateV1??{connections:new Map,initializedRoots:new Set};de.homeAtomProfileStorageStateV1=D;var fe=`
|
|
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
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
20
|
+
last_synced_at TEXT,
|
|
21
|
+
sync_status TEXT NOT NULL DEFAULT 'idle',
|
|
22
|
+
last_error TEXT
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
CREATE TABLE IF NOT EXISTS netflix_movies (
|
|
26
|
+
id TEXT PRIMARY KEY,
|
|
27
|
+
provider_id TEXT NOT NULL,
|
|
28
|
+
source_url TEXT NOT NULL UNIQUE,
|
|
29
|
+
watch_url TEXT,
|
|
30
|
+
title TEXT NOT NULL,
|
|
31
|
+
original_title TEXT,
|
|
32
|
+
synopsis TEXT NOT NULL,
|
|
33
|
+
year INTEGER NOT NULL,
|
|
34
|
+
maturity_rating TEXT NOT NULL,
|
|
35
|
+
duration TEXT NOT NULL,
|
|
36
|
+
match_score INTEGER NOT NULL,
|
|
37
|
+
genres TEXT NOT NULL,
|
|
38
|
+
cast TEXT NOT NULL,
|
|
39
|
+
director TEXT,
|
|
40
|
+
subtitles TEXT NOT NULL DEFAULT '[]',
|
|
41
|
+
episodes TEXT NOT NULL DEFAULT '[]',
|
|
42
|
+
media_type TEXT NOT NULL,
|
|
43
|
+
poster_image TEXT NOT NULL,
|
|
44
|
+
backdrop_image TEXT NOT NULL,
|
|
45
|
+
accent_color TEXT NOT NULL,
|
|
46
|
+
playback_path TEXT,
|
|
47
|
+
playback_kind TEXT,
|
|
48
|
+
download_status TEXT NOT NULL DEFAULT 'metadata',
|
|
49
|
+
download_progress INTEGER NOT NULL DEFAULT 0,
|
|
50
|
+
download_error TEXT,
|
|
51
|
+
created_at TEXT NOT NULL,
|
|
52
|
+
updated_at TEXT NOT NULL
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
CREATE TABLE IF NOT EXISTS netflix_profile_title_state (
|
|
56
|
+
profile_id TEXT NOT NULL,
|
|
57
|
+
title_id TEXT NOT NULL,
|
|
58
|
+
in_my_list INTEGER NOT NULL DEFAULT 0,
|
|
59
|
+
liked INTEGER NOT NULL DEFAULT 0,
|
|
60
|
+
episode_sort TEXT NOT NULL DEFAULT 'asc',
|
|
61
|
+
updated_at TEXT NOT NULL,
|
|
62
|
+
PRIMARY KEY (profile_id, title_id),
|
|
63
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
CREATE TABLE IF NOT EXISTS netflix_watch_progress (
|
|
67
|
+
profile_id TEXT NOT NULL,
|
|
68
|
+
title_id TEXT NOT NULL,
|
|
69
|
+
season_number INTEGER NOT NULL DEFAULT 1,
|
|
70
|
+
episode_number INTEGER NOT NULL DEFAULT 1,
|
|
71
|
+
position_seconds REAL NOT NULL DEFAULT 0,
|
|
72
|
+
duration_seconds REAL NOT NULL DEFAULT 0,
|
|
73
|
+
completed INTEGER NOT NULL DEFAULT 0,
|
|
74
|
+
updated_at TEXT NOT NULL,
|
|
75
|
+
PRIMARY KEY (profile_id, title_id),
|
|
76
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
CREATE TABLE IF NOT EXISTS netflix_episode_tracking (
|
|
80
|
+
profile_id TEXT NOT NULL,
|
|
81
|
+
title_id TEXT NOT NULL,
|
|
82
|
+
tracking_enabled INTEGER NOT NULL DEFAULT 1,
|
|
83
|
+
tracked_at TEXT NOT NULL,
|
|
84
|
+
last_episode_number INTEGER NOT NULL DEFAULT 0,
|
|
85
|
+
last_episode_count INTEGER NOT NULL DEFAULT 0,
|
|
86
|
+
last_checked_at TEXT,
|
|
87
|
+
last_new_episode_at TEXT,
|
|
88
|
+
updated_at TEXT NOT NULL,
|
|
89
|
+
PRIMARY KEY (profile_id, title_id),
|
|
90
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
CREATE TABLE IF NOT EXISTS netflix_new_episodes (
|
|
94
|
+
profile_id TEXT NOT NULL,
|
|
95
|
+
title_id TEXT NOT NULL,
|
|
96
|
+
season_number INTEGER NOT NULL DEFAULT 1,
|
|
97
|
+
episode_number INTEGER NOT NULL,
|
|
98
|
+
episode_title TEXT NOT NULL DEFAULT '',
|
|
99
|
+
discovered_at TEXT NOT NULL,
|
|
100
|
+
seen INTEGER NOT NULL DEFAULT 0,
|
|
101
|
+
PRIMARY KEY (profile_id, title_id, season_number, episode_number),
|
|
102
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
CREATE TABLE IF NOT EXISTS netflix_subtitle_preferences (
|
|
106
|
+
profile_id TEXT PRIMARY KEY,
|
|
107
|
+
font_size INTEGER NOT NULL DEFAULT 32,
|
|
108
|
+
position_x REAL NOT NULL DEFAULT 50,
|
|
109
|
+
position_y REAL NOT NULL DEFAULT 78,
|
|
110
|
+
font_color TEXT NOT NULL DEFAULT '#ffffff',
|
|
111
|
+
background_color TEXT NOT NULL DEFAULT 'rgba(0,0,0,0.75)',
|
|
112
|
+
font_family TEXT NOT NULL DEFAULT 'default',
|
|
113
|
+
captions_on INTEGER NOT NULL DEFAULT 0,
|
|
114
|
+
updated_at TEXT NOT NULL,
|
|
115
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
CREATE TABLE IF NOT EXISTS playback_activity_sessions (
|
|
119
|
+
session_id TEXT PRIMARY KEY,
|
|
120
|
+
profile_id TEXT NOT NULL,
|
|
121
|
+
title_id TEXT NOT NULL,
|
|
122
|
+
snapshot_json TEXT NOT NULL,
|
|
123
|
+
updated_at_ms INTEGER NOT NULL,
|
|
124
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
CREATE TABLE IF NOT EXISTS playback_activity_commands (
|
|
128
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
129
|
+
session_id TEXT NOT NULL,
|
|
130
|
+
command_json TEXT NOT NULL,
|
|
131
|
+
created_at_ms INTEGER NOT NULL,
|
|
132
|
+
FOREIGN KEY (session_id)
|
|
133
|
+
REFERENCES playback_activity_sessions(session_id) ON DELETE CASCADE
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
CREATE TABLE IF NOT EXISTS spotify_providers (
|
|
137
|
+
id TEXT PRIMARY KEY,
|
|
138
|
+
name TEXT NOT NULL,
|
|
139
|
+
home_url TEXT NOT NULL,
|
|
140
|
+
sync_status TEXT NOT NULL DEFAULT 'idle',
|
|
141
|
+
last_synced_at TEXT,
|
|
142
|
+
last_error TEXT,
|
|
143
|
+
created_at TEXT NOT NULL,
|
|
144
|
+
updated_at TEXT NOT NULL
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
CREATE TABLE IF NOT EXISTS spotify_homepage_items (
|
|
148
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
149
|
+
provider_id TEXT NOT NULL
|
|
150
|
+
REFERENCES spotify_providers(id) ON DELETE CASCADE,
|
|
151
|
+
external_id TEXT NOT NULL,
|
|
152
|
+
kind TEXT NOT NULL,
|
|
153
|
+
title TEXT NOT NULL,
|
|
154
|
+
subtitle TEXT NOT NULL DEFAULT '',
|
|
155
|
+
image_url TEXT,
|
|
156
|
+
source_url TEXT,
|
|
157
|
+
position INTEGER NOT NULL,
|
|
158
|
+
payload_json TEXT,
|
|
159
|
+
synced_at TEXT NOT NULL,
|
|
160
|
+
UNIQUE(provider_id, external_id)
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
CREATE TABLE IF NOT EXISTS spotify_tracks (
|
|
164
|
+
id TEXT PRIMARY KEY,
|
|
165
|
+
provider_id TEXT NOT NULL REFERENCES spotify_providers(id),
|
|
166
|
+
external_id TEXT NOT NULL,
|
|
167
|
+
source_url TEXT NOT NULL UNIQUE,
|
|
168
|
+
title TEXT NOT NULL,
|
|
169
|
+
artist TEXT NOT NULL,
|
|
170
|
+
album TEXT NOT NULL DEFAULT '',
|
|
171
|
+
duration_sec INTEGER NOT NULL DEFAULT 0,
|
|
172
|
+
image_url TEXT,
|
|
173
|
+
audio_path TEXT,
|
|
174
|
+
audio_mime TEXT,
|
|
175
|
+
audio_bytes INTEGER,
|
|
176
|
+
download_status TEXT NOT NULL DEFAULT 'queued',
|
|
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_downloads (
|
|
183
|
+
id TEXT PRIMARY KEY,
|
|
184
|
+
source_url TEXT NOT NULL,
|
|
185
|
+
provider_id TEXT NOT NULL REFERENCES spotify_providers(id),
|
|
186
|
+
track_id TEXT REFERENCES spotify_tracks(id),
|
|
187
|
+
status TEXT NOT NULL,
|
|
188
|
+
progress INTEGER NOT NULL DEFAULT 0,
|
|
189
|
+
bytes_downloaded INTEGER NOT NULL DEFAULT 0,
|
|
190
|
+
total_bytes INTEGER,
|
|
191
|
+
error TEXT,
|
|
192
|
+
created_at TEXT NOT NULL,
|
|
193
|
+
updated_at TEXT NOT NULL
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
CREATE TABLE IF NOT EXISTS spotify_music_playlists (
|
|
197
|
+
id TEXT PRIMARY KEY,
|
|
198
|
+
provider_id TEXT NOT NULL REFERENCES spotify_providers(id),
|
|
199
|
+
external_id TEXT NOT NULL,
|
|
200
|
+
source_url TEXT NOT NULL UNIQUE,
|
|
201
|
+
title TEXT NOT NULL,
|
|
202
|
+
description TEXT NOT NULL DEFAULT '',
|
|
203
|
+
curator TEXT NOT NULL DEFAULT '',
|
|
204
|
+
image_url TEXT,
|
|
205
|
+
created_at TEXT NOT NULL,
|
|
206
|
+
updated_at TEXT NOT NULL,
|
|
207
|
+
UNIQUE(provider_id, external_id)
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
CREATE TABLE IF NOT EXISTS spotify_music_playlist_tracks (
|
|
211
|
+
playlist_id TEXT NOT NULL
|
|
212
|
+
REFERENCES spotify_music_playlists(id) ON DELETE CASCADE,
|
|
213
|
+
track_id TEXT NOT NULL REFERENCES spotify_tracks(id) ON DELETE CASCADE,
|
|
214
|
+
position INTEGER NOT NULL,
|
|
215
|
+
PRIMARY KEY(playlist_id, position)
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
CREATE TABLE IF NOT EXISTS spotify_recently_played (
|
|
219
|
+
kind TEXT NOT NULL CHECK(kind IN ('track', 'playlist')),
|
|
220
|
+
item_id TEXT NOT NULL,
|
|
221
|
+
item_json TEXT NOT NULL,
|
|
222
|
+
played_at_ms INTEGER NOT NULL,
|
|
223
|
+
PRIMARY KEY(kind, item_id)
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
CREATE TABLE IF NOT EXISTS netflix_title_markers (
|
|
227
|
+
title_id TEXT NOT NULL,
|
|
228
|
+
marker_type TEXT NOT NULL CHECK(marker_type IN ('intro', 'outro')),
|
|
229
|
+
time_seconds REAL NOT NULL DEFAULT 0,
|
|
230
|
+
updated_at TEXT NOT NULL,
|
|
231
|
+
PRIMARY KEY (title_id, marker_type)
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
CREATE INDEX IF NOT EXISTS netflix_watch_progress_profile_updated
|
|
235
|
+
ON netflix_watch_progress (profile_id, updated_at DESC);
|
|
236
|
+
CREATE INDEX IF NOT EXISTS netflix_new_episodes_profile_discovered
|
|
237
|
+
ON netflix_new_episodes (profile_id, discovered_at DESC);
|
|
238
|
+
CREATE INDEX IF NOT EXISTS playback_activity_profile_updated
|
|
239
|
+
ON playback_activity_sessions (profile_id, updated_at_ms DESC);
|
|
240
|
+
CREATE INDEX IF NOT EXISTS playback_activity_commands_session_id
|
|
241
|
+
ON playback_activity_commands (session_id, id);
|
|
242
|
+
CREATE INDEX IF NOT EXISTS playback_activity_commands_created
|
|
243
|
+
ON playback_activity_commands (created_at_ms);
|
|
244
|
+
CREATE INDEX IF NOT EXISTS spotify_homepage_items_provider_position
|
|
245
|
+
ON spotify_homepage_items(provider_id, position);
|
|
246
|
+
CREATE INDEX IF NOT EXISTS spotify_downloads_created_at
|
|
247
|
+
ON spotify_downloads(created_at DESC);
|
|
248
|
+
CREATE INDEX IF NOT EXISTS spotify_music_playlist_tracks_track
|
|
249
|
+
ON spotify_music_playlist_tracks(track_id);
|
|
250
|
+
CREATE INDEX IF NOT EXISTS spotify_recently_played_at
|
|
251
|
+
ON spotify_recently_played(played_at_ms DESC);
|
|
252
|
+
`,pe=[[`id`,``],[`name`,``],[`base_url`,``],[`priority`,999999],[`last_synced_at`,null],[`sync_status`,`idle`],[`last_error`,null]],me=[[`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`,``]],he=[[`profile_id`,``],[`title_id`,``],[`in_my_list`,0],[`liked`,0],[`updated_at`,``]],ge=[[`profile_id`,``],[`title_id`,``],[`season_number`,1],[`episode_number`,1],[`position_seconds`,0],[`duration_seconds`,0],[`completed`,0],[`updated_at`,``]],_e=[[`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`,``]],ve=[[`session_id`,``],[`profile_id`,``],[`title_id`,``],[`snapshot_json`,`{}`],[`updated_at_ms`,0]],ye=[[`id`,0],[`session_id`,``],[`command_json`,`{}`],[`created_at_ms`,0]],be=[[`id`,``],[`name`,``],[`home_url`,``],[`sync_status`,`idle`],[`last_synced_at`,null],[`last_error`,null],[`created_at`,``],[`updated_at`,``]],xe=[[`id`,0],[`provider_id`,``],[`external_id`,``],[`kind`,``],[`title`,``],[`subtitle`,``],[`image_url`,null],[`source_url`,null],[`position`,0],[`payload_json`,null],[`synced_at`,``]],Se=[[`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`,``]],Ce=[[`id`,``],[`source_url`,``],[`provider_id`,``],[`track_id`,null],[`status`,`queued`],[`progress`,0],[`bytes_downloaded`,0],[`total_bytes`,null],[`error`,null],[`created_at`,``],[`updated_at`,``]],we=[[`id`,``],[`provider_id`,``],[`external_id`,``],[`source_url`,``],[`title`,``],[`description`,``],[`curator`,``],[`image_url`,null],[`created_at`,``],[`updated_at`,``]],Te=[[`playlist_id`,``],[`track_id`,``],[`position`,0]];function O(e){return`${y}\u0000${e}`}function k(e){return{id:e.id,name:e.name,avatarColor:e.avatarColor,createdAt:e.createdAt,updatedAt:e.updatedAt}}function A(e){return e.normalize(`NFKC`).trim().toLocaleLowerCase()}function j(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 Ee(e){return e.normalize(`NFKD`).replace(/\p{Mark}/gu,``).toLocaleLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,48).replace(/-+$/g,``)||`profile`}function M(e,t,r){let i=Ee(e),a=e=>r.has(e)||n(P(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 N(e){if(!/^[a-z0-9](?:[a-z0-9-]{0,62})$/.test(e)||e===`.`||e===`..`)throw Error(`Profile storage key is invalid.`)}function P(...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 F(e){return N(e.storageKey),P(e.storageKey)}function I(e){return P(e.storageKey,T)}function L(e){return P(e.storageKey,T,re)}function R(e){if(!n(e))return;let t=i(e);if(t.isSymbolicLink()||!t.isDirectory())throw Error(`Unsafe profile storage directory: ${e}`)}function De(e){a(y,{recursive:!0});let t=F(e),r=I(e);R(t),n(t)||a(t,{recursive:!1}),R(r),n(r)||a(r,{recursive:!1});let o=L(e);if(n(o)&&i(o).isSymbolicLink())throw Error(`Profile database cannot be a symbolic link.`)}function Oe(e){a(y,{recursive:!0});let t=F(e),n=I(e);a(t,{recursive:!1});try{a(n,{recursive:!1})}catch(e){throw d(t,{recursive:!0,force:!0}),e}}function z(e){if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Profile manifest is invalid.`);let t=e;if(t.version!==E||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.`);N(t.storageKey);let a=A(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:E,activeProfileId:t.activeProfileId,profiles:a}}function ke(e){let t=e.indexOf(`:`),n=Number(t===-1?e:e.slice(0,t));return Number.isSafeInteger(n)&&n>0?n:void 0}function Ae(e){try{return process.kill(e,0),!0}catch(e){return e instanceof Error&&`code`in e&&e.code===`EPERM`}}function je(){a(w,{recursive:!0});let t=`${String(process.pid).padStart(10,`0`)}-${h()}`,r=e.join(w,t);p(r,``,{flag:`wx`,mode:384});try{let r=Date.now();for(let t of c(w,{withFileTypes:!0})){if(!t.isFile())continue;let n=e.join(w,t.name),i=Number(t.name.slice(0,10));try{(!Number.isSafeInteger(i)||!Ae(i)||r-f(n).mtimeMs>oe)&&d(n,{force:!0})}catch{}}if(c(w).filter(e=>/^\d{10}-/.test(e)).sort()[0]!==t)return!1;let i=``,a=0;try{i=s(C,`utf8`),a=r-f(S).mtimeMs}catch{try{a=r-f(S).mtimeMs}catch{return!0}}let o=ke(i);if(o!==void 0&&Ae(o)||o===void 0&&a<se)return!1;let l=``;try{l=s(C,`utf8`)}catch{}if(l!==i)return!1;let p=P(`.profiles-lock-abandoned-${h()}`);try{u(S,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(S)||u(p,S),!1)}finally{d(r,{force:!0})}}function B(e){a(y,{recursive:!0});let t=Date.now()+ae,n=`${process.pid}:${h()}`;for(;;)try{a(S,{recursive:!1});try{p(C,n,{encoding:`utf8`,flag:`wx`,mode:384})}catch(e){throw d(S,{recursive:!0,force:!0}),e}break}catch(e){if(!(e instanceof Error)||!(`code`in e)||e.code!==`EEXIST`)throw e;if(je())continue;if(Date.now()>=t)throw Error(`Timed out waiting for the profile registry lock.`);Atomics.wait(ce,0,0,25)}try{return e()}finally{try{s(C,`utf8`)===n&&d(S,{recursive:!0,force:!0})}catch{}}}function V(e){let n=z(e);a(y,{recursive:!0});let i=P(`.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,x);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 H(){return z(JSON.parse(s(x,`utf8`)))}function U(e,t){return!!e.prepare(`SELECT 1 FROM sqlite_master
|
|
253
|
+
WHERE type = 'table' AND name = ?`).get(t)}function W(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 G(e,t,n=``){return typeof e[t]==`string`?e[t]:n}function Me(){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=W(b);if(!n)return{profiles:t,activeProfileId:t[0].id};try{if(!U(n,`profiles`))return{profiles:t,activeProfileId:t[0].id};let r=n.prepare(`SELECT * FROM profiles`).all().flatMap(t=>{let n=G(t,`id`).trim(),r=G(t,`name`).trim();if(!n||!r)return[];let i=G(t,`avatar_color`),a=G(t,`created_at`).trim()||e,o=G(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(U(n,`app_settings`)){let e=n.prepare(`SELECT value FROM app_settings
|
|
254
|
+
WHERE key = 'active_profile_id'`).get(),t=e?G(e,`value`).trim():``;r.some(e=>e.id===t)&&(i=t)}return{profiles:r,activeProfileId:i}}finally{n.close()}}function Ne(){let e=Me(),t=new Set,n=e.profiles.map(e=>{let n=M(e.name,e.id,t);return t.add(n),{...e,storageKey:n,legacyBootstrap:!0}});return{version:E,activeProfileId:e.activeProfileId,profiles:n}}function Pe(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 K(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}
|
|
255
|
+
(${s.join(`, `)}) VALUES (${c})`);for(let e of o)l.run(...i.map(([t,n])=>Pe(e,t,n)))}function q(e,t){return!!e.prepare(`SELECT 1 FROM schema_migrations WHERE id = ?`).get(t)}function J(e,t){e.prepare(`INSERT OR IGNORE INTO schema_migrations (id, applied_at)
|
|
256
|
+
VALUES (?, ?)`).run(t,new Date().toISOString())}function Fe(e){let t=`netflix-provider-priority-v1`;if(e.pragma(`table_info(netflix_providers)`).some(e=>e.name===`priority`)){J(e,t);return}e.transaction(()=>{e.exec(`ALTER TABLE netflix_providers ADD COLUMN priority INTEGER NOT NULL DEFAULT 0`);let n=e.prepare(`SELECT id FROM netflix_providers ORDER BY rowid`).all(),r=e.prepare(`UPDATE netflix_providers SET priority = ? WHERE id = ?`);n.forEach((e,t)=>r.run(t,e.id)),J(e,t)}).immediate()}function Ie(e){let t=`netflix-title-episode-sort-v1`;if(e.pragma(`table_info(netflix_profile_title_state)`).some(e=>e.name===`episode_sort`)){J(e,t);return}e.transaction(()=>{e.exec(`ALTER TABLE netflix_profile_title_state
|
|
257
|
+
ADD COLUMN episode_sort TEXT NOT NULL DEFAULT 'asc'`),J(e,t)}).immediate()}function Le(e,t){let n=`legacy-netflix-v1`;if(q(e,n))return;let r=W(b);if(r){r.exec(`BEGIN`);try{if(!U(r,`providers`)||!U(r,`movies`))return;e.transaction(()=>{q(e,n)||(U(r,`providers`)&&K(r,e,`SELECT * FROM providers`,`netflix_providers`,pe),U(r,`movies`)&&K(r,e,`SELECT * FROM movies`,`netflix_movies`,me),U(r,`profile_title_state`)&&K(r,e,`SELECT * FROM profile_title_state WHERE profile_id = ?`,`netflix_profile_title_state`,he,[t]),U(r,`watch_progress`)&&K(r,e,`SELECT * FROM watch_progress WHERE profile_id = ?`,`netflix_watch_progress`,ge,[t]),U(r,`subtitle_preferences`)&&K(r,e,`SELECT * FROM subtitle_preferences WHERE profile_id = ?`,`netflix_subtitle_preferences`,_e,[t]),U(r,`playback_activity_sessions`)&&K(r,e,`SELECT * FROM playback_activity_sessions WHERE profile_id = ?`,`playback_activity_sessions`,ve,[t]),U(r,`playback_activity_sessions`)&&U(r,`playback_activity_commands`)&&K(r,e,`SELECT commands.*
|
|
258
|
+
FROM playback_activity_commands commands
|
|
259
|
+
JOIN playback_activity_sessions sessions
|
|
260
|
+
ON sessions.session_id = commands.session_id
|
|
261
|
+
WHERE sessions.profile_id = ?`,`playback_activity_commands`,ye,[t]),J(e,n))}).immediate()}finally{r.inTransaction&&r.exec(`ROLLBACK`),r.close()}}}function Re(e){let t=`legacy-spotify-v1`;if(q(e,t))return;let n=W(ne);if(n){n.exec(`BEGIN`);try{if(!U(n,`providers`)||!U(n,`tracks`)||!U(n,`downloads`))return;e.transaction(()=>{q(e,t)||(U(n,`providers`)&&K(n,e,`SELECT * FROM providers`,`spotify_providers`,be),U(n,`homepage_items`)&&K(n,e,`SELECT * FROM homepage_items`,`spotify_homepage_items`,xe),U(n,`tracks`)&&K(n,e,`SELECT * FROM tracks`,`spotify_tracks`,Se),U(n,`music_playlists`)&&K(n,e,`SELECT * FROM music_playlists`,`spotify_music_playlists`,we),U(n,`music_playlist_tracks`)&&K(n,e,`SELECT * FROM music_playlist_tracks`,`spotify_music_playlist_tracks`,Te),U(n,`downloads`)&&K(n,e,`SELECT * FROM downloads`,`spotify_downloads`,Ce),J(e,t))}).immediate()}finally{n.inTransaction&&n.exec(`ROLLBACK`),n.close()}}}function ze(e){let t=e.prepare(`
|
|
262
|
+
INSERT INTO netflix_providers (id, name, base_url, priority)
|
|
263
|
+
VALUES (
|
|
264
|
+
?, ?, ?,
|
|
265
|
+
COALESCE((SELECT MAX(priority) + 1 FROM netflix_providers), ?)
|
|
266
|
+
)
|
|
267
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
268
|
+
name = excluded.name,
|
|
269
|
+
base_url = excluded.base_url
|
|
270
|
+
`);for(let[e,n]of le.entries())t.run(n.id,n.name,n.baseUrl,e);let n=new Date().toISOString(),r=e.prepare(`
|
|
271
|
+
INSERT INTO spotify_providers (
|
|
272
|
+
id, name, home_url, created_at, updated_at
|
|
273
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
274
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
275
|
+
name = excluded.name,
|
|
276
|
+
home_url = excluded.home_url
|
|
277
|
+
`);for(let e of ue)r.run(e.id,e.name,e.homeUrl,n,n)}function Be(e){let t=new Date().toISOString();e.prepare(`UPDATE netflix_movies
|
|
278
|
+
SET download_status = 'failed',
|
|
279
|
+
download_progress = 0,
|
|
280
|
+
download_error = COALESCE(
|
|
281
|
+
download_error,
|
|
282
|
+
'Download was interrupted by a server restart.'
|
|
283
|
+
),
|
|
284
|
+
updated_at = ?
|
|
285
|
+
WHERE download_status IN ('queued', 'downloading')`).run(t),e.prepare(`UPDATE netflix_providers
|
|
286
|
+
SET sync_status = 'error',
|
|
287
|
+
last_error = COALESCE(
|
|
288
|
+
last_error,
|
|
289
|
+
'Synchronization was interrupted by a server restart.'
|
|
290
|
+
)
|
|
291
|
+
WHERE sync_status = 'syncing'`).run()}function Ve(e){let t=new Date().toISOString();e.prepare(`UPDATE spotify_downloads
|
|
292
|
+
SET status = 'failed',
|
|
293
|
+
error = COALESCE(
|
|
294
|
+
error,
|
|
295
|
+
'Download was interrupted by a server restart.'
|
|
296
|
+
),
|
|
297
|
+
updated_at = ?
|
|
298
|
+
WHERE status IN ('queued', 'fetching', 'downloading')`).run(t),e.prepare(`UPDATE spotify_tracks
|
|
299
|
+
SET download_status = 'failed', updated_at = ?
|
|
300
|
+
WHERE download_status IN ('queued', 'fetching', 'downloading')`).run(t),e.prepare(`UPDATE spotify_providers
|
|
301
|
+
SET sync_status = 'error',
|
|
302
|
+
last_error = COALESCE(
|
|
303
|
+
last_error,
|
|
304
|
+
'Synchronization was interrupted by a server restart.'
|
|
305
|
+
),
|
|
306
|
+
updated_at = ?
|
|
307
|
+
WHERE sync_status = 'syncing'`).run(t)}function He(e,t){e.pragma(`busy_timeout = 5000`),e.pragma(`foreign_keys = ON`),e.pragma(`journal_mode = WAL`),e.exec(fe),Fe(e),Ie(e),e.pragma(`user_version = ${ie}`),e.prepare(`INSERT INTO profiles (
|
|
308
|
+
id, name, avatar_color, created_at, updated_at
|
|
309
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
310
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
311
|
+
name = excluded.name,
|
|
312
|
+
avatar_color = excluded.avatar_color,
|
|
313
|
+
created_at = excluded.created_at,
|
|
314
|
+
updated_at = excluded.updated_at`).run(t.id,t.name,t.avatarColor,t.createdAt,t.updatedAt),ze(e),J(e,`merged-schema-v1`),t.legacyBootstrap&&(Le(e,t.id),Re(e)),Be(e),Ve(e),e.exec(`
|
|
315
|
+
CREATE UNIQUE INDEX IF NOT EXISTS spotify_downloads_one_active_source
|
|
316
|
+
ON spotify_downloads(provider_id, source_url)
|
|
317
|
+
WHERE status IN ('queued', 'fetching', 'downloading');
|
|
318
|
+
`)}function Y(e){let t=O(e.id),n=L(e),r=D.connections.get(t);if(r?.database.open){if(r.databasePath!==n)throw Error(`Profile database path changed unexpectedly.`);return r.database}D.connections.delete(t),De(e);let i=new g(n);try{He(i,e)}catch(e){throw i.close(),e}return D.connections.set(t,{database:i,databasePath:n}),i}function Ue(){D.initializedRoots.has(y)||(a(y,{recursive:!0}),B(()=>{n(x)||V(Ne());let e=H();for(let t of e.profiles)Y(t)}),D.initializedRoots.add(y))}function X(){return Ue(),H()}function Z(e){return Ue(),B(()=>e(H()))}function Q(e){return X().profiles.find(t=>t.id===e)}function We(e){return[...e].sort((e,t)=>e.createdAt.localeCompare(t.createdAt)||e.name.localeCompare(t.name))}function Ge(){return We(X().profiles).map(k)}function Ke(e){let t=Q(e);return t?k(t):void 0}function $(){let e=X(),t=e.profiles.find(t=>t.id===e.activeProfileId);if(!t)throw Error(`At least one profile is required.`);return k(t)}function qe(e){return Z(t=>{let n=t.profiles.find(t=>t.id===e);if(!n)throw Error(`Profile not found.`);return V({...t,activeProfileId:e}),k(n)})}function Je(e,t){let r=j(e,t);return Z(e=>{if(e.profiles.some(e=>A(e.name)===A(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:M(r.name,i,a),legacyBootstrap:!1},s=!1;try{return Oe(o),s=!0,Y(o),V({...e,profiles:[...e.profiles,o]}),k(o)}catch(e){let t=O(o.id),r=D.connections.get(t);r?.database.open&&r.database.close(),D.connections.delete(t);let i=F(o);throw s&&n(i)&&(R(i),d(i,{recursive:!0,force:!1})),e}})}function Ye(e,t){let n=j(t.name,t.avatarColor);return Z(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&&A(t.name)===A(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=Y(i);o.prepare(`UPDATE profiles
|
|
319
|
+
SET name = ?, avatar_color = ?, updated_at = ?
|
|
320
|
+
WHERE id = ?`).run(a.name,a.avatarColor,a.updatedAt,e);let s=[...t.profiles];s[r]=a;try{V({...t,profiles:s})}catch(t){throw o.prepare(`UPDATE profiles
|
|
321
|
+
SET name = ?, avatar_color = ?, updated_at = ?
|
|
322
|
+
WHERE id = ?`).run(i.name,i.avatarColor,i.updatedAt,e),t}return k(a)})}function Xe(e){if(!Q(e))throw Error(`Profile not found.`);let t=O(e),n=D.connections.get(t);if(!n||(D.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 Ze(t){R(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 Qe(e){return Z(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.`);Xe(e);let i=F(r),a=t.profiles.filter(t=>t.id!==e),o=t.activeProfileId===e?We(a)[0].id:t.activeProfileId;V({...t,activeProfileId:o,profiles:a});let s;try{n(i)&&(Ze(i),s=P(`.deleted-${r.storageKey}-${h()}`),u(i,s))}catch(e){try{V(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 $e(e){let t=e??$().id,n=Q(t);if(!n)throw Error(`Profile not found.`);return D.connections.get(O(t))?.database.open?Y(n):B(()=>{let e=H().profiles.find(e=>e.id===t);if(!e)throw Error(`Profile not found.`);return Y(e)})}export{$e as a,Ye as c,Ke as i,Qe as n,Ge as o,$ as r,qe as s,Je as t};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{d as e,n as t,u as n}from"./database-D79CeUrn.js";import{i as r,n as i,r as a}from"./providers-DwLUa_U_.js";import{i as o}from"./downloader-0G-gq1D-.js";import{r as s}from"./subtitles-4c0GFlaW.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,n){let r;for(let i=0;i<3;i+=1){let a=AbortSignal.timeout(6e4);if(r=await fetch(e,{headers:t,redirect:`follow`,signal:n?AbortSignal.any([n,a]):a}),r.ok||r.status===206||!(r.status===404||r.status===408||r.status===425||r.status===429||r.status>=500)||i===2||(await r.body?.cancel(),n?.aborted))return r;await new Promise(e=>setTimeout(e,300*2**i))}return r}async function x(r,l,d,f){p();let h=d??n().id,g=e(r,h);if(!g?.watch_url)throw Error(`This title does not have a playable provider page.`);let _=g.watch_url,v;if(l){let e=[];try{e=JSON.parse(g.episodes)}catch{}if(v=e.find(e=>e.episode===l),!v?.watchUrl)throw Error(`Episode ${l} is not available for this title.`);v.id||(v={...v,id:`episode-${l}`}),_=v.watchUrl}let y=[];if(!v)try{y=JSON.parse(g.subtitles)}catch{}let b=await i(_,g.source_url),x=v?.id||`movie`,S=v?.subtitles?.length?[...v.subtitles]:y,[C,w]=await Promise.all([o(_,b,f),S.length?Promise.resolve(S):s(r,x,_,b,h)]),T=C.servers??[],E=v&&g.provider_id===`phimhdcss`?{...a(v,b),subtitles:w}:v?{...v,subtitles:w}:void 0;try{t(r,E?{episode:E}:{subtitles:w},h)}catch{}if(C.kind===`embed`)return{kind:`embed`,episode:l,episodeMetadata:E,subtitles:w,servers:T,server:C.server,path:C.url};let D=Date.now(),O={id:c(),profileId:h,movieId:r,kind:C.kind,referer:C.referer,resources:new Map,resourceTokens:new Map,createdAt:D,lastAccessedAt:D,...C.kind===`file`?{rootUrl:C.url}:{initialPlaylist:C.playlist,initialBaseUrl:C.baseUrl}};return u.sessions.set(O.id,O),{kind:O.kind,episode:l,episodeMetadata:E,subtitles:w,servers:T,server:C.server,path:m(O,O.kind===`hls`?`index.m3u8`:`media`)}}var S=499;function C(){return new Response(null,{status:S})}async function w(e,t,n){if(e.signal.aborted)return C();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;try{d=await b(c,l,e.signal)}catch(t){return e.signal.aborted?C():Response.json({error:t instanceof Error?t.message:`The provider stream could not be reached.`},{status:502})}if(!d.ok&&d.status!==206)return await d.body?.cancel(),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 t;try{t=await d.text()}catch{return e.signal.aborted?C():Response.json({error:`The provider stream ended before it could be read.`},{status:502})}if(e.signal.aborted)return C();if(t.trimStart().startsWith(`#EXTM3U`))return v(t,d.url||c,s);let n=y(d);return n.delete(`content-length`),new Response(t,{status:d.status,headers:n})}return e.signal.aborted?(await d.body?.cancel(),C()):new Response(d.body,{status:d.status,headers:y(d)})}export{w as n,x as t};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{d as e}from"./database-D79CeUrn.js";import{i as t,n,u as r}from"./providers-DwLUa_U_.js";import{t as i}from"./downloader-0G-gq1D-.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
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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{T as e,d as t}from"./database-D79CeUrn.js";import{i as n}from"./catalog-BS93IoWO.js";function r(r,i){let a=n(r);if(a)return a;let o=t(r,i);return o?e(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};
|