@feralfile/cli 2.10.1 → 3.0.0
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 +16 -11
- package/config.json.example +5 -0
- package/dist/package.json +1 -1
- package/dist/src/commands/build.js +40 -0
- package/dist/src/commands/config.js +1 -0
- package/dist/src/commands/device.js +1 -1
- package/dist/src/commands/enrich.js +16 -21
- package/dist/src/commands/find.js +83 -79
- package/dist/src/commands/helpers/prompt.js +85 -10
- package/dist/src/commands/play.js +138 -73
- package/dist/src/commands/setup.js +81 -6
- package/dist/src/commands/status.js +4 -1
- package/dist/src/config.js +94 -0
- package/dist/src/main.js +14 -5
- package/dist/src/utilities/device-lookup.js +36 -11
- package/dist/src/utilities/feral-file-artwork.js +2 -2
- package/dist/src/utilities/index.js +270 -259
- package/dist/src/utilities/media-url.js +117 -0
- package/dist/src/utilities/nft-indexer.js +12 -376
- package/dist/src/utilities/playlist-builder.js +10 -1
- package/dist/src/utilities/playlist-source.js +222 -33
- package/dist/src/utilities/raster-client.js +100 -13
- package/dist/src/utilities/token-item.js +428 -0
- package/dist/src/utilities/token-source-contract.js +55 -0
- package/dist/src/utilities/token-source-indexer.js +99 -0
- package/dist/src/utilities/token-source-raster.js +378 -0
- package/dist/src/utilities/token-source.js +48 -0
- package/docs/CONFIGURATION.md +63 -2
- package/docs/EXAMPLES.md +2 -2
- package/docs/PROJECT_SPEC.md +23 -17
- package/docs/README.md +40 -15
- package/package.json +1 -1
|
@@ -11,8 +11,9 @@ const device_normalize_1 = require("./device-normalize");
|
|
|
11
11
|
* so a stale row that happens to share the same IP cannot shadow the correct
|
|
12
12
|
* entry. Requires the stored entry to have been written with an id field.
|
|
13
13
|
* 2. Exact URL match — normal case after ID (same host format, no migration).
|
|
14
|
-
* 3.
|
|
15
|
-
*
|
|
14
|
+
* 3. Hostname component match — both URLs are parsed and the hostname compared.
|
|
15
|
+
* For a `.local` name, port and protocol are ignored (same device, stored a
|
|
16
|
+
* different way); for any other host the port must match too.
|
|
16
17
|
* 4a. Discovered IP → stored IP: the mDNS discovery reports a resolved IP that
|
|
17
18
|
* matches the IP in a stored entry. Bridges .local ↔ stored-IP migration for
|
|
18
19
|
* pre-id configs with no stored id.
|
|
@@ -41,18 +42,28 @@ function findExistingDeviceEntry(existingDevices, newHost, discoveredName, disco
|
|
|
41
42
|
if (byHost) {
|
|
42
43
|
return byHost;
|
|
43
44
|
}
|
|
44
|
-
// 3.
|
|
45
|
-
|
|
45
|
+
// 3. Hostname component match. A `.local` name is the device itself, so port
|
|
46
|
+
// and protocol differences are ignored. Any other host (an IP, a tunnel or
|
|
47
|
+
// port-forwarding front) can serve several devices on different ports, so
|
|
48
|
+
// the port must match as well: matching on the address alone made
|
|
49
|
+
// `device add --host <same-ip>:<other-port> --name Kitchen` overwrite the
|
|
50
|
+
// existing device while reporting "Added device: Kitchen".
|
|
51
|
+
let newUrl = null;
|
|
46
52
|
try {
|
|
47
|
-
|
|
53
|
+
newUrl = new URL(newHost);
|
|
48
54
|
}
|
|
49
55
|
catch {
|
|
50
56
|
// not a valid URL — skip hostname matching
|
|
51
57
|
}
|
|
52
|
-
if (
|
|
58
|
+
if (newUrl?.hostname) {
|
|
59
|
+
const candidate = newUrl;
|
|
53
60
|
const byHostname = existingDevices.find((d) => {
|
|
54
61
|
try {
|
|
55
|
-
|
|
62
|
+
const stored = new URL(d.host || '');
|
|
63
|
+
if (stored.hostname !== candidate.hostname) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return (candidate.hostname.toLowerCase().endsWith('.local') || stored.port === candidate.port);
|
|
56
67
|
}
|
|
57
68
|
catch {
|
|
58
69
|
return false;
|
|
@@ -76,8 +87,12 @@ function findExistingDeviceEntry(existingDevices, newHost, discoveredName, disco
|
|
|
76
87
|
return false; // different physical devices — do not conflate
|
|
77
88
|
}
|
|
78
89
|
try {
|
|
79
|
-
const
|
|
80
|
-
|
|
90
|
+
const stored = new URL(d.host || '');
|
|
91
|
+
const storedIp = stripBrackets(stored.hostname);
|
|
92
|
+
// Same port rule as steps 3 and 4b: an address match on another port is
|
|
93
|
+
// another device. (A matching stable id already returned in step 1.)
|
|
94
|
+
const samePort = stored.port === (newUrl?.port ?? stored.port);
|
|
95
|
+
return discoveredAddresses.includes(storedIp) && samePort;
|
|
81
96
|
}
|
|
82
97
|
catch {
|
|
83
98
|
return false;
|
|
@@ -91,9 +106,19 @@ function findExistingDeviceEntry(existingDevices, newHost, discoveredName, disco
|
|
|
91
106
|
// and a stored entry has that IP in its stored addresses list (populated from
|
|
92
107
|
// prior mDNS discoveries). This bridges --host <ip/ipv6> → existing .local entry.
|
|
93
108
|
// Strip IPv6 brackets before comparing (Node URL.hostname returns '[fe80::1]').
|
|
94
|
-
|
|
109
|
+
// The port must match the stored entry's port, for the same reason as step 3:
|
|
110
|
+
// another port at a remembered address is another device, not this one.
|
|
111
|
+
const rawHostname = stripBrackets(newUrl?.hostname ?? '');
|
|
95
112
|
if (rawHostname && (/^[0-9.]+$/.test(rawHostname) || rawHostname.includes(':'))) {
|
|
96
|
-
const
|
|
113
|
+
const samePort = (stored) => {
|
|
114
|
+
try {
|
|
115
|
+
return new URL(stored || '').port === newUrl?.port;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const byStoredAddress = existingDevices.find((d) => d.addresses?.includes(rawHostname) && samePort(d.host));
|
|
97
122
|
if (byStoredAddress) {
|
|
98
123
|
return byStoredAddress;
|
|
99
124
|
}
|
|
@@ -13,12 +13,12 @@ exports.parseFeralFileArtworkId = parseFeralFileArtworkId;
|
|
|
13
13
|
const http_1 = require("./http");
|
|
14
14
|
const user_agent_1 = require("./user-agent");
|
|
15
15
|
/**
|
|
16
|
-
* resolveFeralFileArtwork resolves a public Feral File artwork id into
|
|
16
|
+
* resolveFeralFileArtwork resolves a public Feral File artwork id into token
|
|
17
17
|
* token coordinates using the self-describing artwork API response.
|
|
18
18
|
*
|
|
19
19
|
* @param artworkId - Public artwork id from Feral File.
|
|
20
20
|
* @param fetchImpl - Fetch implementation, injected by tests.
|
|
21
|
-
* @returns Token coordinates
|
|
21
|
+
* @returns Token coordinates for the configured token source.
|
|
22
22
|
*/
|
|
23
23
|
async function resolveFeralFileArtwork(artworkId, fetchImpl = http_1.defaultDeadlineFetch) {
|
|
24
24
|
const normalizedArtworkId = artworkId.trim();
|