retold-remote 0.0.4 → 0.0.6

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 (63) hide show
  1. package/docs/README.md +181 -0
  2. package/docs/_cover.md +14 -0
  3. package/docs/_sidebar.md +10 -0
  4. package/docs/_topbar.md +3 -0
  5. package/docs/audio-viewer.md +133 -0
  6. package/docs/ebook-reader.md +90 -0
  7. package/docs/image-viewer.md +90 -0
  8. package/docs/server-setup.md +262 -0
  9. package/docs/video-viewer.md +134 -0
  10. package/html/docs.html +59 -0
  11. package/package.json +21 -7
  12. package/source/Pict-Application-RetoldRemote.js +143 -2
  13. package/source/RetoldRemote-ExtensionMaps.js +33 -0
  14. package/source/cli/RetoldRemote-Server-Setup.js +82 -67
  15. package/source/cli/commands/RetoldRemote-Command-Serve.js +5 -26
  16. package/source/providers/Pict-Provider-CollectionManager.js +934 -0
  17. package/source/providers/Pict-Provider-FormattingUtilities.js +109 -0
  18. package/source/providers/Pict-Provider-GalleryFilterSort.js +2 -11
  19. package/source/providers/Pict-Provider-GalleryNavigation.js +270 -353
  20. package/source/providers/Pict-Provider-RetoldRemoteIcons.js +52 -0
  21. package/source/providers/Pict-Provider-ToastNotification.js +96 -0
  22. package/source/providers/keyboard-handlers/KeyHandler-AudioExplorer.js +88 -0
  23. package/source/providers/keyboard-handlers/KeyHandler-Gallery.js +190 -0
  24. package/source/providers/keyboard-handlers/KeyHandler-Sidebar.js +65 -0
  25. package/source/providers/keyboard-handlers/KeyHandler-VideoExplorer.js +57 -0
  26. package/source/providers/keyboard-handlers/KeyHandler-Viewer.js +197 -0
  27. package/source/server/RetoldRemote-ArchiveService.js +2 -12
  28. package/source/server/RetoldRemote-AudioWaveformService.js +7 -16
  29. package/source/server/RetoldRemote-CollectionService.js +684 -0
  30. package/source/server/RetoldRemote-EbookService.js +7 -16
  31. package/source/server/RetoldRemote-MediaService.js +3 -14
  32. package/source/server/RetoldRemote-ParimeCache.js +349 -0
  33. package/source/server/RetoldRemote-ThumbnailCache.js +52 -20
  34. package/source/server/RetoldRemote-VideoFrameService.js +7 -15
  35. package/source/views/PictView-Remote-AudioExplorer.js +10 -43
  36. package/source/views/PictView-Remote-CollectionsPanel.js +1087 -0
  37. package/source/views/PictView-Remote-Gallery.js +237 -44
  38. package/source/views/PictView-Remote-ImageViewer.js +1 -34
  39. package/source/views/PictView-Remote-Layout.js +410 -20
  40. package/source/views/PictView-Remote-MediaViewer.js +338 -51
  41. package/source/views/PictView-Remote-SettingsPanel.js +155 -138
  42. package/source/views/PictView-Remote-TopBar.js +615 -14
  43. package/source/views/PictView-Remote-VLCSetup.js +766 -0
  44. package/source/views/PictView-Remote-VideoExplorer.js +20 -54
  45. package/web-application/css/docuserve.css +73 -0
  46. package/web-application/docs/README.md +181 -0
  47. package/web-application/docs/_cover.md +14 -0
  48. package/web-application/docs/_sidebar.md +10 -0
  49. package/web-application/docs/_topbar.md +3 -0
  50. package/web-application/docs/audio-viewer.md +133 -0
  51. package/web-application/docs/ebook-reader.md +90 -0
  52. package/web-application/docs/image-viewer.md +90 -0
  53. package/web-application/docs/server-setup.md +262 -0
  54. package/web-application/docs/video-viewer.md +134 -0
  55. package/web-application/docs.html +59 -0
  56. package/web-application/js/pict-docuserve.min.js +58 -0
  57. package/web-application/js/pict.min.js +2 -2
  58. package/web-application/js/pict.min.js.map +1 -1
  59. package/web-application/retold-remote.js +2558 -439
  60. package/web-application/retold-remote.js.map +1 -1
  61. package/web-application/retold-remote.min.js +41 -11
  62. package/web-application/retold-remote.min.js.map +1 -1
  63. package/server.js +0 -43
@@ -0,0 +1,766 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "RetoldRemote-VLCSetup",
6
+ DefaultRenderable: "RetoldRemote-VLCSetup",
7
+ DefaultDestinationAddress: "#ContentEditor-Application-Container",
8
+ AutoRender: false,
9
+
10
+ CSS: /*css*/`
11
+ .retold-remote-vlc-modal-backdrop
12
+ {
13
+ position: fixed;
14
+ top: 0;
15
+ left: 0;
16
+ right: 0;
17
+ bottom: 0;
18
+ background: rgba(0, 0, 0, 0.6);
19
+ z-index: 9000;
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ }
24
+ .retold-remote-vlc-modal
25
+ {
26
+ background: var(--retold-bg-tertiary);
27
+ border: 1px solid var(--retold-border);
28
+ border-radius: 8px;
29
+ width: 600px;
30
+ max-width: 90vw;
31
+ max-height: 85vh;
32
+ display: flex;
33
+ flex-direction: column;
34
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
35
+ }
36
+ .retold-remote-vlc-modal-header
37
+ {
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: space-between;
41
+ padding: 14px 18px;
42
+ border-bottom: 1px solid var(--retold-border);
43
+ flex-shrink: 0;
44
+ }
45
+ .retold-remote-vlc-modal-title
46
+ {
47
+ font-size: 0.85rem;
48
+ font-weight: 700;
49
+ color: var(--retold-text-primary);
50
+ }
51
+ .retold-remote-vlc-modal-close
52
+ {
53
+ border: none;
54
+ background: transparent;
55
+ color: var(--retold-text-muted);
56
+ font-size: 1.1rem;
57
+ cursor: pointer;
58
+ padding: 2px 6px;
59
+ border-radius: 3px;
60
+ font-family: inherit;
61
+ line-height: 1;
62
+ }
63
+ .retold-remote-vlc-modal-close:hover
64
+ {
65
+ background: var(--retold-bg-hover);
66
+ color: var(--retold-text-primary);
67
+ }
68
+ .retold-remote-vlc-modal-body
69
+ {
70
+ flex: 1;
71
+ overflow-y: auto;
72
+ padding: 18px;
73
+ }
74
+ .retold-remote-vlc-setup-section
75
+ {
76
+ margin-bottom: 18px;
77
+ }
78
+ .retold-remote-vlc-setup-section-title
79
+ {
80
+ font-size: 0.7rem;
81
+ font-weight: 700;
82
+ text-transform: uppercase;
83
+ letter-spacing: 0.5px;
84
+ color: var(--retold-text-dim);
85
+ margin-bottom: 8px;
86
+ }
87
+ .retold-remote-vlc-setup-desc
88
+ {
89
+ font-size: 0.75rem;
90
+ color: var(--retold-text-secondary);
91
+ line-height: 1.5;
92
+ margin-bottom: 8px;
93
+ }
94
+ .retold-remote-vlc-setup-status
95
+ {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 8px;
99
+ padding: 8px;
100
+ border-radius: 4px;
101
+ background: var(--retold-bg-secondary);
102
+ margin-bottom: 12px;
103
+ font-size: 0.75rem;
104
+ color: var(--retold-text-secondary);
105
+ }
106
+ .retold-remote-vlc-setup-status-dot
107
+ {
108
+ width: 8px;
109
+ height: 8px;
110
+ border-radius: 50%;
111
+ flex-shrink: 0;
112
+ }
113
+ .retold-remote-vlc-setup-status-dot.detected
114
+ {
115
+ background: var(--retold-accent);
116
+ }
117
+ .retold-remote-vlc-setup-status-dot.unknown
118
+ {
119
+ background: var(--retold-text-dim);
120
+ }
121
+ .retold-remote-vlc-setup-platform
122
+ {
123
+ display: none;
124
+ }
125
+ .retold-remote-vlc-setup-platform.active
126
+ {
127
+ display: block;
128
+ }
129
+ .retold-remote-vlc-setup-platform-tabs
130
+ {
131
+ display: flex;
132
+ gap: 0;
133
+ margin-bottom: 12px;
134
+ border-bottom: 1px solid var(--retold-border);
135
+ }
136
+ .retold-remote-vlc-setup-platform-tab
137
+ {
138
+ padding: 6px 12px;
139
+ border: none;
140
+ background: transparent;
141
+ font-size: 0.72rem;
142
+ font-weight: 600;
143
+ color: var(--retold-text-muted);
144
+ cursor: pointer;
145
+ border-bottom: 2px solid transparent;
146
+ font-family: inherit;
147
+ }
148
+ .retold-remote-vlc-setup-platform-tab:hover
149
+ {
150
+ color: var(--retold-text-secondary);
151
+ }
152
+ .retold-remote-vlc-setup-platform-tab.active
153
+ {
154
+ color: var(--retold-accent);
155
+ border-bottom-color: var(--retold-accent);
156
+ }
157
+ .retold-remote-vlc-setup-code
158
+ {
159
+ background: var(--retold-bg-primary);
160
+ border: 1px solid var(--retold-border);
161
+ border-radius: 4px;
162
+ padding: 10px;
163
+ font-family: "SF Mono", "Fira Code", "Consolas", monospace;
164
+ font-size: 0.68rem;
165
+ color: var(--retold-text-secondary);
166
+ line-height: 1.6;
167
+ overflow-x: auto;
168
+ white-space: pre;
169
+ margin-bottom: 8px;
170
+ tab-size: 4;
171
+ }
172
+ .retold-remote-vlc-setup-btn
173
+ {
174
+ display: inline-block;
175
+ padding: 6px 14px;
176
+ border: 1px solid var(--retold-border);
177
+ border-radius: 4px;
178
+ background: var(--retold-bg-secondary);
179
+ color: var(--retold-text-secondary);
180
+ font-size: 0.72rem;
181
+ font-family: inherit;
182
+ cursor: pointer;
183
+ transition: background 0.15s, color 0.15s;
184
+ margin-right: 6px;
185
+ margin-bottom: 6px;
186
+ }
187
+ .retold-remote-vlc-setup-btn:hover
188
+ {
189
+ background: var(--retold-bg-hover);
190
+ color: var(--retold-text-primary);
191
+ }
192
+ .retold-remote-vlc-setup-btn.primary
193
+ {
194
+ background: var(--retold-accent);
195
+ border-color: var(--retold-accent);
196
+ color: #fff;
197
+ }
198
+ .retold-remote-vlc-setup-btn.primary:hover
199
+ {
200
+ opacity: 0.85;
201
+ }
202
+ .retold-remote-vlc-setup-step
203
+ {
204
+ display: flex;
205
+ gap: 10px;
206
+ margin-bottom: 10px;
207
+ }
208
+ .retold-remote-vlc-setup-step-num
209
+ {
210
+ flex-shrink: 0;
211
+ width: 20px;
212
+ height: 20px;
213
+ border-radius: 50%;
214
+ background: var(--retold-accent);
215
+ color: #fff;
216
+ font-size: 0.65rem;
217
+ font-weight: 700;
218
+ display: flex;
219
+ align-items: center;
220
+ justify-content: center;
221
+ }
222
+ .retold-remote-vlc-setup-step-content
223
+ {
224
+ flex: 1;
225
+ font-size: 0.75rem;
226
+ color: var(--retold-text-secondary);
227
+ line-height: 1.5;
228
+ }
229
+ .retold-remote-vlc-setup-note
230
+ {
231
+ font-size: 0.7rem;
232
+ color: var(--retold-text-dim);
233
+ font-style: italic;
234
+ margin-top: 4px;
235
+ }
236
+ .retold-remote-toast
237
+ {
238
+ position: fixed;
239
+ bottom: 20px;
240
+ left: 50%;
241
+ transform: translateX(-50%);
242
+ background: var(--retold-bg-secondary);
243
+ color: var(--retold-accent);
244
+ padding: 8px 16px;
245
+ border-radius: 4px;
246
+ font-size: 0.75rem;
247
+ z-index: 10000;
248
+ pointer-events: none;
249
+ border: 1px solid var(--retold-border);
250
+ }
251
+ `
252
+ };
253
+
254
+ class RetoldRemoteVLCSetupView extends libPictView
255
+ {
256
+ constructor(pFable, pOptions, pServiceHash)
257
+ {
258
+ super(pFable, pOptions, pServiceHash);
259
+
260
+ this._activePlatformTab = this._detectPlatform();
261
+ this._modalVisible = false;
262
+ this._boundKeyHandler = null;
263
+ }
264
+
265
+ _detectPlatform()
266
+ {
267
+ let tmpUA = (typeof navigator !== 'undefined') ? navigator.userAgent : '';
268
+ if (/Macintosh|Mac OS X/.test(tmpUA))
269
+ {
270
+ return 'macos';
271
+ }
272
+ if (/Windows/.test(tmpUA))
273
+ {
274
+ return 'windows';
275
+ }
276
+ return 'linux';
277
+ }
278
+
279
+ openModal()
280
+ {
281
+ if (this._modalVisible)
282
+ {
283
+ return;
284
+ }
285
+ this._modalVisible = true;
286
+
287
+ // Create the backdrop
288
+ let tmpBackdrop = document.createElement('div');
289
+ tmpBackdrop.className = 'retold-remote-vlc-modal-backdrop';
290
+ tmpBackdrop.id = 'RetoldRemote-VLCSetup-Backdrop';
291
+ tmpBackdrop.onclick = (pEvent) =>
292
+ {
293
+ if (pEvent.target === tmpBackdrop)
294
+ {
295
+ this.closeModal();
296
+ }
297
+ };
298
+
299
+ // Create the modal
300
+ let tmpModal = document.createElement('div');
301
+ tmpModal.className = 'retold-remote-vlc-modal';
302
+
303
+ // Header
304
+ let tmpHeader = document.createElement('div');
305
+ tmpHeader.className = 'retold-remote-vlc-modal-header';
306
+ tmpHeader.innerHTML = '<span class="retold-remote-vlc-modal-title">VLC Protocol Setup</span>'
307
+ + '<button class="retold-remote-vlc-modal-close" onclick="pict.views[\'RetoldRemote-VLCSetup\'].closeModal()">X</button>';
308
+
309
+ // Body
310
+ let tmpBody = document.createElement('div');
311
+ tmpBody.className = 'retold-remote-vlc-modal-body';
312
+ tmpBody.id = 'RetoldRemote-VLCSetup-Container';
313
+
314
+ tmpModal.appendChild(tmpHeader);
315
+ tmpModal.appendChild(tmpBody);
316
+ tmpBackdrop.appendChild(tmpModal);
317
+ document.body.appendChild(tmpBackdrop);
318
+
319
+ // Render content into the body
320
+ this._renderVLCSetupContent();
321
+
322
+ // Escape key handler
323
+ this._boundKeyHandler = (pEvent) =>
324
+ {
325
+ if (pEvent.key === 'Escape')
326
+ {
327
+ pEvent.preventDefault();
328
+ pEvent.stopPropagation();
329
+ this.closeModal();
330
+ }
331
+ };
332
+ document.addEventListener('keydown', this._boundKeyHandler, true);
333
+ }
334
+
335
+ closeModal()
336
+ {
337
+ if (!this._modalVisible)
338
+ {
339
+ return;
340
+ }
341
+ this._modalVisible = false;
342
+
343
+ let tmpBackdrop = document.getElementById('RetoldRemote-VLCSetup-Backdrop');
344
+ if (tmpBackdrop)
345
+ {
346
+ tmpBackdrop.remove();
347
+ }
348
+
349
+ if (this._boundKeyHandler)
350
+ {
351
+ document.removeEventListener('keydown', this._boundKeyHandler, true);
352
+ this._boundKeyHandler = null;
353
+ }
354
+ }
355
+
356
+ switchPlatformTab(pTab)
357
+ {
358
+ this._activePlatformTab = pTab;
359
+ this._renderVLCSetupContent();
360
+ }
361
+
362
+ _renderVLCSetupContent()
363
+ {
364
+ let tmpContainer = document.getElementById('RetoldRemote-VLCSetup-Container');
365
+ if (!tmpContainer)
366
+ {
367
+ return;
368
+ }
369
+
370
+ let tmpPlatform = this._activePlatformTab;
371
+ let tmpHTML = '';
372
+
373
+ // Description
374
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
375
+ tmpHTML += '<div class="retold-remote-vlc-setup-desc">';
376
+ tmpHTML += 'Stream media directly in VLC from the browser. Press <b>v</b> in the media viewer to launch VLC with the current file.';
377
+ tmpHTML += '</div>';
378
+ tmpHTML += '</div>';
379
+
380
+ // Platform status
381
+ tmpHTML += '<div class="retold-remote-vlc-setup-status">';
382
+ tmpHTML += '<div class="retold-remote-vlc-setup-status-dot ' + (this._detectPlatform() === tmpPlatform ? 'detected' : 'unknown') + '"></div>';
383
+ tmpHTML += '<span>Detected platform: <b>' + this._getPlatformLabel(this._detectPlatform()) + '</b></span>';
384
+ tmpHTML += '</div>';
385
+
386
+ // Platform tabs
387
+ tmpHTML += '<div class="retold-remote-vlc-setup-platform-tabs">';
388
+ tmpHTML += this._buildPlatformTab('macos', 'macOS', tmpPlatform);
389
+ tmpHTML += this._buildPlatformTab('windows', 'Windows', tmpPlatform);
390
+ tmpHTML += this._buildPlatformTab('linux', 'Linux', tmpPlatform);
391
+ tmpHTML += '</div>';
392
+
393
+ // Platform-specific content
394
+ tmpHTML += this._buildMacOSContent(tmpPlatform);
395
+ tmpHTML += this._buildWindowsContent(tmpPlatform);
396
+ tmpHTML += this._buildLinuxContent(tmpPlatform);
397
+
398
+ // Test section
399
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
400
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">Test</div>';
401
+ tmpHTML += '<div class="retold-remote-vlc-setup-desc">';
402
+ tmpHTML += 'Click below to test whether the vlc:// protocol handler is registered. VLC should open.';
403
+ tmpHTML += '</div>';
404
+ tmpHTML += '<button class="retold-remote-vlc-setup-btn" onclick="pict.views[\'RetoldRemote-VLCSetup\'].testProtocol()">Test VLC Protocol</button>';
405
+ tmpHTML += '</div>';
406
+
407
+ tmpContainer.innerHTML = tmpHTML;
408
+ }
409
+
410
+ _buildPlatformTab(pKey, pLabel, pActive)
411
+ {
412
+ let tmpClass = 'retold-remote-vlc-setup-platform-tab';
413
+ if (pKey === pActive)
414
+ {
415
+ tmpClass += ' active';
416
+ }
417
+ return '<button class="' + tmpClass + '" onclick="pict.views[\'RetoldRemote-VLCSetup\'].switchPlatformTab(\'' + pKey + '\')">' + pLabel + '</button>';
418
+ }
419
+
420
+ _getPlatformLabel(pKey)
421
+ {
422
+ if (pKey === 'macos') return 'macOS';
423
+ if (pKey === 'windows') return 'Windows';
424
+ return 'Linux';
425
+ }
426
+
427
+ _buildMacOSContent(pActive)
428
+ {
429
+ let tmpClass = 'retold-remote-vlc-setup-platform' + (pActive === 'macos' ? ' active' : '');
430
+ let tmpHTML = '<div class="' + tmpClass + '" data-platform="macos">';
431
+
432
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
433
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">Setup (macOS)</div>';
434
+ tmpHTML += '<div class="retold-remote-vlc-setup-desc">';
435
+ tmpHTML += 'VLC on macOS does not register a vlc:// protocol handler by default. ';
436
+ tmpHTML += 'An AppleScript app bundle is needed to bridge vlc:// links to VLC. ';
437
+ tmpHTML += 'Run the command below in Terminal to create and register the handler automatically.';
438
+ tmpHTML += '</div>';
439
+ tmpHTML += '</div>';
440
+
441
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
442
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">Automatic Setup</div>';
443
+ tmpHTML += '<div class="retold-remote-vlc-setup-desc">';
444
+ tmpHTML += 'Copy and paste this into Terminal:';
445
+ tmpHTML += '</div>';
446
+
447
+ let tmpScript = this._getMacSetupScript();
448
+ tmpHTML += '<div class="retold-remote-vlc-setup-code">' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpScript) + '</div>';
449
+ tmpHTML += '<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyMacSetup()">Copy to Clipboard</button>';
450
+ tmpHTML += '</div>';
451
+
452
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
453
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">What This Does</div>';
454
+
455
+ tmpHTML += '<div class="retold-remote-vlc-setup-step">';
456
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-num">1</div>';
457
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-content">Creates an AppleScript at <code>/tmp/VLCProtocol.applescript</code> that handles vlc:// URLs</div>';
458
+ tmpHTML += '</div>';
459
+
460
+ tmpHTML += '<div class="retold-remote-vlc-setup-step">';
461
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-num">2</div>';
462
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-content">Compiles it into an app bundle at <code>/Applications/VLCProtocol.app</code></div>';
463
+ tmpHTML += '</div>';
464
+
465
+ tmpHTML += '<div class="retold-remote-vlc-setup-step">';
466
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-num">3</div>';
467
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-content">Adds the vlc:// URL scheme to the app\'s Info.plist</div>';
468
+ tmpHTML += '</div>';
469
+
470
+ tmpHTML += '<div class="retold-remote-vlc-setup-step">';
471
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-num">4</div>';
472
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-content">Registers the protocol handler with macOS Launch Services</div>';
473
+ tmpHTML += '</div>';
474
+
475
+ tmpHTML += '<div class="retold-remote-vlc-setup-note">Requires VLC installed at /Applications/VLC.app and Python 3 (included with macOS).</div>';
476
+ tmpHTML += '</div>';
477
+
478
+ tmpHTML += '</div>';
479
+ return tmpHTML;
480
+ }
481
+
482
+ _buildWindowsContent(pActive)
483
+ {
484
+ let tmpClass = 'retold-remote-vlc-setup-platform' + (pActive === 'windows' ? ' active' : '');
485
+ let tmpHTML = '<div class="' + tmpClass + '" data-platform="windows">';
486
+
487
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
488
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">Setup (Windows)</div>';
489
+ tmpHTML += '<div class="retold-remote-vlc-setup-desc">';
490
+ tmpHTML += 'VLC on Windows registers the vlc:// protocol handler during installation. ';
491
+ tmpHTML += 'If it is not working, you can re-register it by saving and running the registry file below.';
492
+ tmpHTML += '</div>';
493
+ tmpHTML += '</div>';
494
+
495
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
496
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">Option A: Reinstall VLC</div>';
497
+
498
+ tmpHTML += '<div class="retold-remote-vlc-setup-step">';
499
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-num">1</div>';
500
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-content">Reinstall VLC and ensure "Register VLC as handler for vlc:// protocol" is checked during installation.</div>';
501
+ tmpHTML += '</div>';
502
+ tmpHTML += '</div>';
503
+
504
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
505
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">Option B: Registry File</div>';
506
+ tmpHTML += '<div class="retold-remote-vlc-setup-desc">';
507
+ tmpHTML += 'Save this as <code>vlc-protocol.reg</code> and double-click to import. ';
508
+ tmpHTML += 'Adjust the VLC path if yours differs.';
509
+ tmpHTML += '</div>';
510
+
511
+ let tmpRegFile = this._getWindowsRegFile();
512
+ tmpHTML += '<div class="retold-remote-vlc-setup-code">' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpRegFile) + '</div>';
513
+ tmpHTML += '<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyWindowsReg()">Copy to Clipboard</button>';
514
+ tmpHTML += '</div>';
515
+
516
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
517
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">Option C: Batch Script</div>';
518
+ tmpHTML += '<div class="retold-remote-vlc-setup-desc">';
519
+ tmpHTML += 'Alternatively, save this as <code>vlc-protocol-setup.bat</code> and run as Administrator. ';
520
+ tmpHTML += 'This creates a wrapper script that URL-decodes the vlc:// link before passing it to VLC.';
521
+ tmpHTML += '</div>';
522
+
523
+ let tmpBatchScript = this._getWindowsBatchScript();
524
+ tmpHTML += '<div class="retold-remote-vlc-setup-code">' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpBatchScript) + '</div>';
525
+ tmpHTML += '<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyWindowsBatch()">Copy to Clipboard</button>';
526
+ tmpHTML += '</div>';
527
+
528
+ tmpHTML += '</div>';
529
+ return tmpHTML;
530
+ }
531
+
532
+ _buildLinuxContent(pActive)
533
+ {
534
+ let tmpClass = 'retold-remote-vlc-setup-platform' + (pActive === 'linux' ? ' active' : '');
535
+ let tmpHTML = '<div class="' + tmpClass + '" data-platform="linux">';
536
+
537
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
538
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">Setup (Linux)</div>';
539
+ tmpHTML += '<div class="retold-remote-vlc-setup-desc">';
540
+ tmpHTML += 'Register a vlc:// protocol handler using a .desktop file and xdg-mime. ';
541
+ tmpHTML += 'Run the command below in a terminal.';
542
+ tmpHTML += '</div>';
543
+ tmpHTML += '</div>';
544
+
545
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
546
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">Setup Command</div>';
547
+
548
+ let tmpScript = this._getLinuxSetupScript();
549
+ tmpHTML += '<div class="retold-remote-vlc-setup-code">' + this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpScript) + '</div>';
550
+ tmpHTML += '<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyLinuxSetup()">Copy to Clipboard</button>';
551
+ tmpHTML += '</div>';
552
+
553
+ tmpHTML += '<div class="retold-remote-vlc-setup-section">';
554
+ tmpHTML += '<div class="retold-remote-vlc-setup-section-title">What This Does</div>';
555
+
556
+ tmpHTML += '<div class="retold-remote-vlc-setup-step">';
557
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-num">1</div>';
558
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-content">Creates a handler script at <code>~/.local/bin/vlc-protocol</code> that URL-decodes and opens VLC</div>';
559
+ tmpHTML += '</div>';
560
+
561
+ tmpHTML += '<div class="retold-remote-vlc-setup-step">';
562
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-num">2</div>';
563
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-content">Creates a .desktop file at <code>~/.local/share/applications/vlc-protocol.desktop</code></div>';
564
+ tmpHTML += '</div>';
565
+
566
+ tmpHTML += '<div class="retold-remote-vlc-setup-step">';
567
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-num">3</div>';
568
+ tmpHTML += '<div class="retold-remote-vlc-setup-step-content">Registers vlc:// as a URL scheme via <code>xdg-mime</code></div>';
569
+ tmpHTML += '</div>';
570
+
571
+ tmpHTML += '<div class="retold-remote-vlc-setup-note">Requires VLC and Python 3 installed.</div>';
572
+ tmpHTML += '</div>';
573
+
574
+ tmpHTML += '</div>';
575
+ return tmpHTML;
576
+ }
577
+
578
+ _getMacSetupScript()
579
+ {
580
+ return [
581
+ "# Create the AppleScript handler",
582
+ "cat > /tmp/VLCProtocol.applescript << 'EOF'",
583
+ "on open location theURL",
584
+ "\tset theURL to text 7 thru -1 of theURL",
585
+ "\tset theURL to do shell script \"python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))' \" & quoted form of theURL",
586
+ "\tdo shell script \"open -a VLC \" & quoted form of theURL",
587
+ "end open location",
588
+ "EOF",
589
+ "",
590
+ "# Compile into app bundle",
591
+ "osacompile -o /Applications/VLCProtocol.app /tmp/VLCProtocol.applescript",
592
+ "",
593
+ "# Add vlc:// URL scheme to Info.plist",
594
+ "/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes array\" \\",
595
+ " /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null",
596
+ "/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0 dict\" \\",
597
+ " /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null",
598
+ "/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLName string 'VLC Protocol'\" \\",
599
+ " /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null",
600
+ "/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes array\" \\",
601
+ " /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null",
602
+ "/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string vlc\" \\",
603
+ " /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null",
604
+ "",
605
+ "# Register with Launch Services",
606
+ "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \\",
607
+ " -f /Applications/VLCProtocol.app",
608
+ "",
609
+ "echo \"VLC protocol handler installed successfully.\""
610
+ ].join('\n');
611
+ }
612
+
613
+ _getWindowsRegFile()
614
+ {
615
+ return [
616
+ "Windows Registry Editor Version 5.00",
617
+ "",
618
+ "[HKEY_CLASSES_ROOT\\vlc]",
619
+ "@=\"URL:VLC Protocol\"",
620
+ "\"URL Protocol\"=\"\"",
621
+ "",
622
+ "[HKEY_CLASSES_ROOT\\vlc\\shell]",
623
+ "",
624
+ "[HKEY_CLASSES_ROOT\\vlc\\shell\\open]",
625
+ "",
626
+ "[HKEY_CLASSES_ROOT\\vlc\\shell\\open\\command]",
627
+ "@=\"\\\"C:\\\\Program Files\\\\VideoLAN\\\\VLC\\\\vlc.exe\\\" \\\"%1\\\"\""
628
+ ].join('\n');
629
+ }
630
+
631
+ _getWindowsBatchScript()
632
+ {
633
+ return [
634
+ "@echo off",
635
+ "REM VLC Protocol Handler Setup for Windows",
636
+ "REM Run this as Administrator",
637
+ "",
638
+ "REM Create the handler script",
639
+ "mkdir \"%APPDATA%\\VLCProtocol\" 2>nul",
640
+ "(",
641
+ "echo import sys, urllib.parse, subprocess",
642
+ "echo url = sys.argv[1] if len(sys.argv^) ^> 1 else ''",
643
+ "echo if url.startswith('vlc://'^): url = url[6:]",
644
+ "echo url = urllib.parse.unquote(url^)",
645
+ "echo subprocess.Popen(['C:\\\\Program Files\\\\VideoLAN\\\\VLC\\\\vlc.exe', url]^)",
646
+ ") > \"%APPDATA%\\VLCProtocol\\handler.py\"",
647
+ "",
648
+ "REM Register the protocol in the registry",
649
+ "reg add \"HKCU\\Software\\Classes\\vlc\" /ve /d \"URL:VLC Protocol\" /f",
650
+ "reg add \"HKCU\\Software\\Classes\\vlc\" /v \"URL Protocol\" /d \"\" /f",
651
+ "reg add \"HKCU\\Software\\Classes\\vlc\\shell\\open\\command\" /ve /d \"pythonw \\\"%APPDATA%\\VLCProtocol\\handler.py\\\" \\\"%%1\\\"\" /f",
652
+ "",
653
+ "echo VLC protocol handler installed successfully.",
654
+ "pause"
655
+ ].join('\n');
656
+ }
657
+
658
+ _getLinuxSetupScript()
659
+ {
660
+ return [
661
+ "# Create handler script",
662
+ "mkdir -p ~/.local/bin",
663
+ "cat > ~/.local/bin/vlc-protocol << 'EOF'",
664
+ "#!/bin/bash",
665
+ "URL=\"$1\"",
666
+ "URL=\"${URL#vlc://}\"",
667
+ "URL=$(python3 -c \"import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))\" \"$URL\")",
668
+ "exec vlc \"$URL\" &",
669
+ "EOF",
670
+ "chmod +x ~/.local/bin/vlc-protocol",
671
+ "",
672
+ "# Create .desktop file",
673
+ "cat > ~/.local/share/applications/vlc-protocol.desktop << 'EOF'",
674
+ "[Desktop Entry]",
675
+ "Name=VLC Protocol Handler",
676
+ "Exec=bash -c '~/.local/bin/vlc-protocol %u'",
677
+ "Type=Application",
678
+ "NoDisplay=true",
679
+ "MimeType=x-scheme-handler/vlc;",
680
+ "EOF",
681
+ "",
682
+ "# Register the handler",
683
+ "xdg-mime default vlc-protocol.desktop x-scheme-handler/vlc",
684
+ "update-desktop-database ~/.local/share/applications/",
685
+ "",
686
+ "echo \"VLC protocol handler installed successfully.\""
687
+ ].join('\n');
688
+ }
689
+
690
+ _copyToClipboard(pText, pLabel)
691
+ {
692
+ if (navigator.clipboard && navigator.clipboard.writeText)
693
+ {
694
+ navigator.clipboard.writeText(pText).then(() =>
695
+ {
696
+ this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel + ' copied to clipboard');
697
+ }).catch(() =>
698
+ {
699
+ this._fallbackCopy(pText, pLabel);
700
+ });
701
+ }
702
+ else
703
+ {
704
+ this._fallbackCopy(pText, pLabel);
705
+ }
706
+ }
707
+
708
+ _fallbackCopy(pText, pLabel)
709
+ {
710
+ let tmpTextarea = document.createElement('textarea');
711
+ tmpTextarea.value = pText;
712
+ tmpTextarea.style.position = 'fixed';
713
+ tmpTextarea.style.left = '-9999px';
714
+ document.body.appendChild(tmpTextarea);
715
+ tmpTextarea.select();
716
+ try
717
+ {
718
+ document.execCommand('copy');
719
+ this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel + ' copied to clipboard');
720
+ }
721
+ catch (pErr)
722
+ {
723
+ this.pict.providers['RetoldRemote-ToastNotification'].showToast('Failed to copy - please select and copy manually');
724
+ }
725
+ document.body.removeChild(tmpTextarea);
726
+ }
727
+
728
+ copyMacSetup()
729
+ {
730
+ this._copyToClipboard(this._getMacSetupScript(), 'macOS setup script');
731
+ }
732
+
733
+ copyWindowsReg()
734
+ {
735
+ this._copyToClipboard(this._getWindowsRegFile(), 'Registry file');
736
+ }
737
+
738
+ copyWindowsBatch()
739
+ {
740
+ this._copyToClipboard(this._getWindowsBatchScript(), 'Batch script');
741
+ }
742
+
743
+ copyLinuxSetup()
744
+ {
745
+ this._copyToClipboard(this._getLinuxSetupScript(), 'Linux setup script');
746
+ }
747
+
748
+ testProtocol()
749
+ {
750
+ let tmpIsWindows = /Windows/.test(navigator.userAgent);
751
+ let tmpSampleURL = 'https://www.sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4';
752
+ let tmpTestURL = tmpIsWindows
753
+ ? ('vlc://' + tmpSampleURL)
754
+ : ('vlc://' + encodeURIComponent(tmpSampleURL));
755
+ let tmpLink = document.createElement('a');
756
+ tmpLink.href = tmpTestURL;
757
+ tmpLink.style.display = 'none';
758
+ document.body.appendChild(tmpLink);
759
+ tmpLink.click();
760
+ document.body.removeChild(tmpLink);
761
+ }
762
+ }
763
+
764
+ RetoldRemoteVLCSetupView.default_configuration = _ViewConfiguration;
765
+
766
+ module.exports = RetoldRemoteVLCSetupView;