discstation 0.1.21 → 0.1.22

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/README.md CHANGED
@@ -1,12 +1,18 @@
1
1
  # DiscStation
2
2
 
3
3
  DiscStation is a physical-media appliance for DVD-Video, data DVDs, audio CDs,
4
- playback, ripping, phone uploads, and ESP32 remote control.
4
+ playback, ripping, phone uploads, and ESP32 remote control. The ESP32 remote
5
+ is optional — with none attached, the built-in web UI becomes a full
6
+ on-screen remote instead, so `npm install -g discstation` alone is enough to
7
+ use every mode.
5
8
 
6
9
  ## Hardware
7
10
 
8
11
  - **Brain:** Raspberry Pi 4/5 (or any Linux box)
9
- - **Remote:** ESP32-C6 with SSD1306 OLED, rotary encoder, and buttons
12
+ - **Remote (optional):** ESP32-C6 with SSD1306 OLED, rotary encoder, and
13
+ buttons — or none at all; the web UI's on-screen remote covers the same
14
+ controls when no ESP32 is detected, and steps aside (greyed out,
15
+ auto-collapsing) the moment one is plugged in
10
16
  - **Drive:** ATAPI DVD writer (e.g. iHAS124) over USB
11
17
 
12
18
  ## Features
@@ -58,7 +64,15 @@ get a standalone app window with a dock/taskbar icon on macOS, Linux, and
58
64
  Windows.
59
65
 
60
66
  `discstation-setup --help` lists the forwarded env vars. From a git clone,
61
- `npm run setup` does the same thing. Support by OS:
67
+ `npm run setup` does the same thing.
68
+
69
+ **Updating:** `npm install -g discstation` alone now redeploys and restarts
70
+ the already-running service automatically (a `postinstall` hook copies the
71
+ updated files over and restarts it) — no need to re-run `discstation-setup`
72
+ after every update. `discstation-setup` itself is still what performs the
73
+ first install (system packages, venv, cert, service registration).
74
+
75
+ Support by OS:
62
76
 
63
77
  | OS | What runs | Optical support |
64
78
  |----|-----------|-----------------|
@@ -95,11 +109,19 @@ mode but not full VIDEO_TS mirror or audio-CD ripping yet. See
95
109
 
96
110
  ## Web Interface
97
111
 
98
- Built-in web server on port 8080 (HTTPS with self-signed cert):
112
+ Built-in web server on port 8080 (HTTPS with self-signed cert) and a plain
113
+ HTTP mirror on 8081 for the mobile app / any browser that balks at the
114
+ self-signed cert. On startup the host prints the LAN URL to open
115
+ (`>>> Open http://<ip>:8081 ... <<<`).
99
116
 
100
117
  - Upload files from any device on the LAN for data DVD burning
101
118
  - Submit YouTube URLs for video DVD burning
102
119
  - Dark theme, mobile-responsive, PWA (installable on phone)
120
+ - **On-screen remote** — click the "REMOTE" tag next to the DiscStation
121
+ wordmark to reveal a full control surface (mode buttons, EJECT/CLOSE
122
+ toggle, live disc-status readout, playback transport). Fully functional
123
+ with no ESP32 attached; greys out and auto-collapses the instant a
124
+ physical remote is detected, so the two never fight for control.
103
125
 
104
126
  ## Disc Support
105
127
 
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "discstation",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "DiscStation optical-media appliance host — one cross-OS installer",
5
5
  "bin": {
6
6
  "discstation-setup": "scripts/setup.mjs",
7
7
  "discstation": "scripts/open.mjs"
8
8
  },
9
9
  "scripts": {
10
- "setup": "node scripts/setup.mjs"
10
+ "setup": "node scripts/setup.mjs",
11
+ "postinstall": "node scripts/postinstall.mjs"
11
12
  },
12
13
  "engines": {
13
14
  "node": ">=16"
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ // Runs automatically after `npm install -g discstation` (fresh install or an
3
+ // update). Closes the class of bug where `npm install -g` updates the
4
+ // package but an already-running background service (systemd/launchd/a
5
+ // Windows Scheduled Task) keeps serving the old code because nobody
6
+ // remembered to separately re-run `discstation-setup` afterward.
7
+ //
8
+ // Deliberately lightweight: only copies files + restarts the service if one
9
+ // is *already* installed. Never touches brew/apt/winget or does a first
10
+ // install - that stays an explicit, visible `discstation-setup` run, since
11
+ // it needs heavier system-package work a silent postinstall shouldn't do.
12
+ import { existsSync, cpSync } from 'node:fs';
13
+ import { homedir, platform } from 'node:os';
14
+ import { dirname, join, resolve } from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { spawnSync } from 'node:child_process';
17
+
18
+ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
19
+
20
+ // Never auto-run inside a dev checkout of this repo itself.
21
+ if (existsSync(join(ROOT, '.git'))) process.exit(0);
22
+ // Only for a real global install - a project merely depending on this
23
+ // package shouldn't have a background service silently touched.
24
+ if (!process.env.npm_config_global) process.exit(0);
25
+
26
+ function configDir() {
27
+ if (process.env.DISCSTATION_CONFIG_DIR) return process.env.DISCSTATION_CONFIG_DIR;
28
+ const p = platform();
29
+ if (p === 'win32') return join(process.env.APPDATA || homedir(), 'DiscStation');
30
+ if (p === 'darwin') return join(homedir(), 'Library', 'Application Support', 'DiscStation');
31
+ return join(homedir(), '.local', 'share', 'discstation');
32
+ }
33
+
34
+ const appDir = process.env.DISCSTATION_APP_DIR || join(configDir(), 'app');
35
+
36
+ function alreadyInstalled() {
37
+ const p = platform();
38
+ if (p === 'darwin') {
39
+ return existsSync(join(homedir(), 'Library', 'LaunchAgents', 'com.discstation.agent.plist'));
40
+ }
41
+ if (p === 'win32') {
42
+ const r = spawnSync('schtasks', ['/Query', '/TN', 'DiscStation'], { stdio: 'ignore' });
43
+ return r.status === 0;
44
+ }
45
+ return existsSync(join(homedir(), '.config', 'systemd', 'user', 'discstation.service'));
46
+ }
47
+
48
+ function restartService() {
49
+ const p = platform();
50
+ try {
51
+ if (p === 'darwin') {
52
+ spawnSync('launchctl', ['kickstart', '-k', `gui/${process.getuid()}/com.discstation.agent`], { stdio: 'ignore' });
53
+ } else if (p === 'win32') {
54
+ spawnSync(
55
+ 'powershell.exe',
56
+ [
57
+ '-NoProfile',
58
+ '-Command',
59
+ 'Stop-ScheduledTask -TaskName DiscStation -ErrorAction SilentlyContinue; Start-ScheduledTask -TaskName DiscStation',
60
+ ],
61
+ { stdio: 'ignore' }
62
+ );
63
+ } else {
64
+ spawnSync('systemctl', ['--user', 'restart', 'discstation.service'], { stdio: 'ignore' });
65
+ }
66
+ } catch {
67
+ /* best-effort - a failed restart here shouldn't fail the npm install */
68
+ }
69
+ }
70
+
71
+ if (!existsSync(appDir) || !alreadyInstalled()) {
72
+ console.log('\ndiscstation: first install detected - run `discstation-setup` to finish setting up the host.\n');
73
+ process.exit(0);
74
+ }
75
+
76
+ try {
77
+ cpSync(join(ROOT, 'src'), appDir, { recursive: true, force: true });
78
+ restartService();
79
+ console.log('\ndiscstation: redeployed the updated host and restarted the service.\n');
80
+ } catch (e) {
81
+ console.log(`\ndiscstation: could not auto-redeploy (${e.message}). Run \`discstation-setup\` manually.\n`);
82
+ }