@feralfile/cli 2.0.2 → 2.0.3

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.
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "defaultDuration": 10,
3
+ "generativeDuration": 60,
3
4
  "browser": {
4
5
  "timeout": 90000,
5
6
  "sanitizationLevel": "medium"
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feralfile/cli",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "CLI for building, signing, and playing DP-1 playlists of digital art on Feral File devices",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,6 +34,7 @@ function loadConfig() {
34
34
  const { localPath, userPath } = getConfigPaths();
35
35
  const defaultConfig = {
36
36
  defaultDuration: parseInt(process.env.DEFAULT_DURATION || '10', 10),
37
+ generativeDuration: parseInt(process.env.DEFAULT_GENERATIVE_DURATION || '60', 10),
37
38
  browser: {
38
39
  timeout: parseInt(process.env.BROWSER_TIMEOUT || '90000', 10),
39
40
  sanitizationLevel: process.env.SANITIZATION_LEVEL || 'medium',
@@ -48,16 +48,18 @@ function isInteractiveWeb(mimeType, sourceUrl) {
48
48
  * - a time-based source (video/audio) gets NO duration and
49
49
  * `display.loop: false`, so a conformant player MUST advance at
50
50
  * end-of-stream — the media plays its natural length;
51
- * - an interactive web page (HTML) gets NO duration either, but for the
52
- * opposite reason: it has no end-of-stream event, so a conformant player
53
- * parks on it indefinitely (the generative/interactive art keeps running);
51
+ * - an interactive web page (HTML) — generative art with no intrinsic
52
+ * runtime and no end-of-stream event — gets the configured
53
+ * `generativeDuration` (default 60s) so the playlist rotates. Set
54
+ * `generativeDuration` to 0 to omit it and have a conformant player park
55
+ * on the work open-ended instead;
54
56
  * - static and code-based sources fall back to the configured
55
57
  * `defaultDuration` because they have no intrinsic runtime to play out.
56
58
  *
57
- * Do not re-introduce an unconditional duration here: stamping a default on
58
- * video items silently truncates or pads them, and stamping one on an HTML
59
- * page cuts the artwork off and restarts it every `defaultDuration` seconds
60
- * (the bugs this guards against).
59
+ * Do not stamp a duration on video/audio: it silently truncates or pads them.
60
+ * (Earlier this also omitted duration for HTML works, which left them with no
61
+ * duration field. Players that require the field — notably FF1 — then rejected
62
+ * the entire playlist, so generative works now carry an explicit duration.)
61
63
  *
62
64
  * @param {Object} item - DP1 playlist item (mutated in place)
63
65
  * @param {Object} mediaHints - Media signals for the time-based check
@@ -76,8 +78,13 @@ function applyItemTiming(item, mediaHints = {}, duration) {
76
78
  return item;
77
79
  }
78
80
  if (isInteractiveWeb(mediaHints.mimeType, mediaHints.sourceUrl)) {
79
- // No duration: an open-ended interactive page should play until the
80
- // playlist advances for another reason, not on a fixed timer.
81
+ // Generative/interactive works have no intrinsic runtime, so stamp the
82
+ // configured generative duration to drive playlist rotation. A configured
83
+ // value of 0 means "omit" — a conformant player then parks open-ended.
84
+ const generativeDuration = getGenerativeDuration();
85
+ if (generativeDuration > 0) {
86
+ item.duration = generativeDuration;
87
+ }
81
88
  return item;
82
89
  }
83
90
  item.duration = getDefaultStaticDuration();
@@ -98,6 +105,23 @@ function getDefaultStaticDuration() {
98
105
  return 10;
99
106
  }
100
107
  }
108
+ /**
109
+ * getGenerativeDuration returns the configured display seconds for
110
+ * generative/interactive (HTML) works, which have no intrinsic runtime.
111
+ * Defaults to 60 when config is unavailable. A configured 0 is honored (the
112
+ * caller omits the duration so the work plays open-ended).
113
+ *
114
+ * @returns {number} Display duration in seconds (0 means "omit / open-ended")
115
+ */
116
+ function getGenerativeDuration() {
117
+ try {
118
+ const configured = getConfig().generativeDuration;
119
+ return typeof configured === 'number' ? configured : 60;
120
+ }
121
+ catch (_error) {
122
+ return 60;
123
+ }
124
+ }
101
125
  /**
102
126
  * Convert a string to a URL-friendly slug
103
127
  *
@@ -22,8 +22,11 @@ npm run dev -- config show
22
22
  ## Top‑level fields
23
23
 
24
24
  - **defaultDuration** (number, seconds)
25
- - Default per‑item display duration for media without an intrinsic runtime (images, code-based works). Falls back to 10s when unset.
25
+ - Default per‑item display duration for static media without an intrinsic runtime (images). Falls back to 10s when unset.
26
26
  - Does NOT apply to video/audio items: with no explicit duration those are emitted without `duration` and with `display.loop: false`, so a DP-1 player advances at end-of-stream (§4.1) — the media plays its natural length.
27
+ - **generativeDuration** (number, seconds)
28
+ - Display duration stamped on generative/interactive (HTML) works, which have no intrinsic runtime and no end-of-stream event. Defaults to **60s** (override via `DEFAULT_GENERATIVE_DURATION`).
29
+ - Set to **0** to omit the duration entirely, so a conformant player parks on the work open-ended instead of rotating. Note: some players (including FF1) require every item to carry a `duration` and will reject a playlist whose items omit it — keep this non-zero unless you know your player tolerates an absent duration.
27
30
 
28
31
  ## browser
29
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feralfile/cli",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "CLI for building, signing, and playing DP-1 playlists of digital art on Feral File devices",
5
5
  "repository": {
6
6
  "type": "git",