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.
- package/LICENSE +21 -0
- package/css/retold-remote.css +83 -0
- package/html/codejar.js +511 -0
- package/html/index.html +23 -0
- package/package.json +68 -0
- package/server.js +43 -0
- package/source/Pict-Application-RetoldRemote-Configuration.json +7 -0
- package/source/Pict-Application-RetoldRemote.js +622 -0
- package/source/Pict-RetoldRemote-Bundle.js +14 -0
- package/source/cli/RetoldRemote-CLI-Program.js +15 -0
- package/source/cli/RetoldRemote-CLI-Run.js +3 -0
- package/source/cli/RetoldRemote-Server-Setup.js +257 -0
- package/source/cli/commands/RetoldRemote-Command-Serve.js +87 -0
- package/source/providers/Pict-Provider-GalleryFilterSort.js +597 -0
- package/source/providers/Pict-Provider-GalleryNavigation.js +819 -0
- package/source/providers/Pict-Provider-RetoldRemote.js +273 -0
- package/source/providers/Pict-Provider-RetoldRemoteIcons.js +640 -0
- package/source/providers/Pict-Provider-RetoldRemoteTheme.js +879 -0
- package/source/server/RetoldRemote-MediaService.js +536 -0
- package/source/server/RetoldRemote-PathRegistry.js +121 -0
- package/source/server/RetoldRemote-ThumbnailCache.js +89 -0
- package/source/server/RetoldRemote-ToolDetector.js +78 -0
- package/source/views/PictView-Remote-Gallery.js +1437 -0
- package/source/views/PictView-Remote-ImageViewer.js +363 -0
- package/source/views/PictView-Remote-Layout.js +420 -0
- package/source/views/PictView-Remote-MediaViewer.js +530 -0
- package/source/views/PictView-Remote-SettingsPanel.js +318 -0
- package/source/views/PictView-Remote-TopBar.js +206 -0
- package/web-application/codejar.js +511 -0
- package/web-application/css/retold-remote.css +83 -0
- package/web-application/index.html +23 -0
- package/web-application/js/pict.min.js +12 -0
- package/web-application/js/pict.min.js.map +1 -0
- package/web-application/retold-remote.compatible.js +5764 -0
- package/web-application/retold-remote.compatible.js.map +1 -0
- package/web-application/retold-remote.compatible.min.js +120 -0
- package/web-application/retold-remote.compatible.min.js.map +1 -0
- package/web-application/retold-remote.js +5763 -0
- package/web-application/retold-remote.js.map +1 -0
- package/web-application/retold-remote.min.js +120 -0
- 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;
|