retold-remote 0.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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/css/retold-remote.css +83 -0
  3. package/html/codejar.js +511 -0
  4. package/html/index.html +23 -0
  5. package/package.json +68 -0
  6. package/server.js +43 -0
  7. package/source/Pict-Application-RetoldRemote-Configuration.json +7 -0
  8. package/source/Pict-Application-RetoldRemote.js +622 -0
  9. package/source/Pict-RetoldRemote-Bundle.js +14 -0
  10. package/source/cli/RetoldRemote-CLI-Program.js +15 -0
  11. package/source/cli/RetoldRemote-CLI-Run.js +3 -0
  12. package/source/cli/RetoldRemote-Server-Setup.js +257 -0
  13. package/source/cli/commands/RetoldRemote-Command-Serve.js +87 -0
  14. package/source/providers/Pict-Provider-GalleryFilterSort.js +597 -0
  15. package/source/providers/Pict-Provider-GalleryNavigation.js +819 -0
  16. package/source/providers/Pict-Provider-RetoldRemote.js +273 -0
  17. package/source/providers/Pict-Provider-RetoldRemoteIcons.js +640 -0
  18. package/source/providers/Pict-Provider-RetoldRemoteTheme.js +879 -0
  19. package/source/server/RetoldRemote-MediaService.js +536 -0
  20. package/source/server/RetoldRemote-PathRegistry.js +121 -0
  21. package/source/server/RetoldRemote-ThumbnailCache.js +89 -0
  22. package/source/server/RetoldRemote-ToolDetector.js +78 -0
  23. package/source/views/PictView-Remote-Gallery.js +1437 -0
  24. package/source/views/PictView-Remote-ImageViewer.js +363 -0
  25. package/source/views/PictView-Remote-Layout.js +420 -0
  26. package/source/views/PictView-Remote-MediaViewer.js +530 -0
  27. package/source/views/PictView-Remote-SettingsPanel.js +318 -0
  28. package/source/views/PictView-Remote-TopBar.js +206 -0
  29. package/web-application/codejar.js +511 -0
  30. package/web-application/css/retold-remote.css +83 -0
  31. package/web-application/index.html +23 -0
  32. package/web-application/js/pict.min.js +12 -0
  33. package/web-application/js/pict.min.js.map +1 -0
  34. package/web-application/retold-remote.compatible.js +5764 -0
  35. package/web-application/retold-remote.compatible.js.map +1 -0
  36. package/web-application/retold-remote.compatible.min.js +120 -0
  37. package/web-application/retold-remote.compatible.min.js.map +1 -0
  38. package/web-application/retold-remote.js +5763 -0
  39. package/web-application/retold-remote.js.map +1 -0
  40. package/web-application/retold-remote.min.js +120 -0
  41. package/web-application/retold-remote.min.js.map +1 -0
@@ -0,0 +1,273 @@
1
+ const libPictProvider = require('pict-provider');
2
+
3
+ const _DefaultProviderConfiguration =
4
+ {
5
+ ProviderIdentifier: 'RetoldRemote-Provider',
6
+ AutoInitialize: true,
7
+ AutoSolveWithApp: false
8
+ };
9
+
10
+ class RetoldRemoteProvider extends libPictProvider
11
+ {
12
+ constructor(pFable, pOptions, pServiceHash)
13
+ {
14
+ super(pFable, pOptions, pServiceHash);
15
+
16
+ // Client-side cache: path -> hash and hash -> path
17
+ this._pathToHash = {};
18
+ this._hashToPath = {};
19
+ }
20
+
21
+ /**
22
+ * Fetch the server's media processing capabilities.
23
+ *
24
+ * @param {Function} fCallback - Callback(pError, pCapabilities)
25
+ */
26
+ fetchCapabilities(fCallback)
27
+ {
28
+ fetch('/api/media/capabilities')
29
+ .then((pResponse) => pResponse.json())
30
+ .then((pData) =>
31
+ {
32
+ if (pData && pData.Capabilities)
33
+ {
34
+ return fCallback(null, pData.Capabilities);
35
+ }
36
+ return fCallback(null, {});
37
+ })
38
+ .catch((pError) =>
39
+ {
40
+ return fCallback(pError);
41
+ });
42
+ }
43
+
44
+ /**
45
+ * Fetch the server's remote settings (e.g. hashed filenames mode).
46
+ *
47
+ * @param {Function} fCallback - Callback(pError, pSettings)
48
+ */
49
+ fetchRemoteSettings(fCallback)
50
+ {
51
+ fetch('/api/remote/settings')
52
+ .then((pResponse) => pResponse.json())
53
+ .then((pData) =>
54
+ {
55
+ if (pData)
56
+ {
57
+ return fCallback(null, pData);
58
+ }
59
+ return fCallback(null, {});
60
+ })
61
+ .catch((pError) =>
62
+ {
63
+ return fCallback(pError);
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Register a path<->hash mapping in the client-side cache.
69
+ *
70
+ * @param {string} pPath - Relative file/folder path
71
+ * @param {string} pHash - 10-char hex hash
72
+ */
73
+ registerHash(pPath, pHash)
74
+ {
75
+ if (pPath && pHash)
76
+ {
77
+ this._pathToHash[pPath] = pHash;
78
+ this._hashToPath[pHash] = pPath;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Get the hash for a known path.
84
+ *
85
+ * @param {string} pPath
86
+ * @returns {string|null}
87
+ */
88
+ getHashForPath(pPath)
89
+ {
90
+ return this._pathToHash[pPath] || null;
91
+ }
92
+
93
+ /**
94
+ * Get the path for a known hash.
95
+ *
96
+ * @param {string} pHash
97
+ * @returns {string|null}
98
+ */
99
+ getPathForHash(pHash)
100
+ {
101
+ return this._hashToPath[pHash] || null;
102
+ }
103
+
104
+ /**
105
+ * Get the best path identifier for use in URL query params.
106
+ * Returns the hash if hashed filenames is enabled and hash is cached,
107
+ * otherwise returns the full path.
108
+ *
109
+ * @param {string} pPath - Relative file/folder path
110
+ * @returns {string} Hash or encoded path
111
+ */
112
+ _getPathParam(pPath)
113
+ {
114
+ if (this.pict.AppData.RetoldRemote.HashedFilenames)
115
+ {
116
+ let tmpHash = this.getHashForPath(pPath);
117
+ if (tmpHash)
118
+ {
119
+ return tmpHash;
120
+ }
121
+ }
122
+ return encodeURIComponent(pPath);
123
+ }
124
+
125
+ /**
126
+ * Get a short identifier for a path, suitable for use in browser hash fragments.
127
+ * Returns the hash token if hashed filenames is on and hash is cached,
128
+ * otherwise returns the raw path.
129
+ *
130
+ * @param {string} pPath - Relative file/folder path
131
+ * @returns {string} Hash token or raw path
132
+ */
133
+ getFragmentIdentifier(pPath)
134
+ {
135
+ if (this.pict.AppData.RetoldRemote.HashedFilenames)
136
+ {
137
+ let tmpHash = this.getHashForPath(pPath);
138
+ if (tmpHash)
139
+ {
140
+ return tmpHash;
141
+ }
142
+ }
143
+ return pPath;
144
+ }
145
+
146
+ /**
147
+ * Resolve a fragment identifier back to a path.
148
+ * If it looks like a 10-char hex hash and we have a mapping, return the path.
149
+ * Otherwise return the identifier as-is (it's already a path).
150
+ *
151
+ * @param {string} pIdentifier - Hash token or raw path from fragment
152
+ * @returns {string} Resolved path
153
+ */
154
+ resolveFragmentIdentifier(pIdentifier)
155
+ {
156
+ if (pIdentifier && /^[a-f0-9]{10}$/.test(pIdentifier))
157
+ {
158
+ let tmpPath = this.getPathForHash(pIdentifier);
159
+ if (tmpPath !== null)
160
+ {
161
+ return tmpPath;
162
+ }
163
+ }
164
+ return pIdentifier;
165
+ }
166
+
167
+ /**
168
+ * Get a content URL for a file.
169
+ * Uses /content-hashed/<hash> when hashed filenames is on and hash is cached,
170
+ * otherwise uses /content/<encoded-path>.
171
+ *
172
+ * @param {string} pPath - Relative file path
173
+ * @returns {string} URL
174
+ */
175
+ getContentURL(pPath)
176
+ {
177
+ // Always use the /content/ static route for actual file serving.
178
+ // Encode each path segment individually to preserve directory separators.
179
+ let tmpSegments = pPath.split('/').map((pSeg) => encodeURIComponent(pSeg));
180
+ return '/content/' + tmpSegments.join('/');
181
+ }
182
+
183
+ /**
184
+ * Build a thumbnail URL for a file.
185
+ * Returns the direct content URL if the server has no thumbnail capability.
186
+ *
187
+ * @param {string} pPath - Relative file path
188
+ * @param {number} pWidth - Desired width
189
+ * @param {number} pHeight - Desired height
190
+ * @returns {string} URL
191
+ */
192
+ getThumbnailURL(pPath, pWidth, pHeight)
193
+ {
194
+ let tmpCapabilities = this.pict.AppData.RetoldRemote.ServerCapabilities;
195
+ let tmpExtension = pPath.replace(/^.*\./, '').toLowerCase();
196
+ let tmpIsImage = this._isImageExtension(tmpExtension);
197
+
198
+ // If server has thumbnail tools, use the thumbnail API
199
+ if (tmpCapabilities && (tmpCapabilities.sharp || tmpCapabilities.imagemagick))
200
+ {
201
+ return '/api/media/thumbnail?path=' + this._getPathParam(pPath) +
202
+ '&width=' + (pWidth || 200) +
203
+ '&height=' + (pHeight || 200);
204
+ }
205
+
206
+ // For images without server-side tools, use the direct file URL
207
+ if (tmpIsImage)
208
+ {
209
+ return this.getContentURL(pPath);
210
+ }
211
+
212
+ // For non-images without tools, return null (use icon instead)
213
+ return null;
214
+ }
215
+
216
+ /**
217
+ * Fetch media metadata for a file.
218
+ *
219
+ * @param {string} pPath - Relative file path
220
+ * @param {Function} fCallback - Callback(pError, pProbeData)
221
+ */
222
+ fetchMediaProbe(pPath, fCallback)
223
+ {
224
+ fetch('/api/media/probe?path=' + this._getPathParam(pPath))
225
+ .then((pResponse) => pResponse.json())
226
+ .then((pData) =>
227
+ {
228
+ // Cache hash if returned in probe data
229
+ if (pData && pData.Hash && pData.Path)
230
+ {
231
+ this.registerHash(pData.Path, pData.Hash);
232
+ }
233
+ fCallback(null, pData);
234
+ })
235
+ .catch((pError) => fCallback(pError));
236
+ }
237
+
238
+ /**
239
+ * Fetch folder media summary.
240
+ *
241
+ * @param {string} pPath - Relative folder path
242
+ * @param {Function} fCallback - Callback(pError, pSummaryData)
243
+ */
244
+ fetchFolderSummary(pPath, fCallback)
245
+ {
246
+ let tmpURL = '/api/media/folder-summary';
247
+ if (pPath)
248
+ {
249
+ tmpURL += '?path=' + this._getPathParam(pPath);
250
+ }
251
+
252
+ fetch(tmpURL)
253
+ .then((pResponse) => pResponse.json())
254
+ .then((pData) => fCallback(null, pData))
255
+ .catch((pError) => fCallback(pError));
256
+ }
257
+
258
+ /**
259
+ * Check if a file extension is a known image type.
260
+ *
261
+ * @param {string} pExtension - Lowercase extension
262
+ * @returns {boolean}
263
+ */
264
+ _isImageExtension(pExtension)
265
+ {
266
+ let tmpImageExtensions = { 'png': true, 'jpg': true, 'jpeg': true, 'gif': true, 'webp': true, 'svg': true, 'bmp': true, 'ico': true, 'avif': true, 'tiff': true, 'tif': true };
267
+ return !!tmpImageExtensions[pExtension];
268
+ }
269
+ }
270
+
271
+ RetoldRemoteProvider.default_configuration = _DefaultProviderConfiguration;
272
+
273
+ module.exports = RetoldRemoteProvider;