retold-remote 0.0.3 → 0.0.5
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/package.json +2 -2
- package/source/Pict-Application-RetoldRemote.js +43 -1
- package/source/providers/Pict-Provider-GalleryFilterSort.js +13 -2
- package/source/providers/Pict-Provider-GalleryNavigation.js +208 -1
- package/source/server/RetoldRemote-MediaService.js +2 -2
- package/source/server/RetoldRemote-VideoFrameService.js +2 -1
- package/source/views/PictView-Remote-Layout.js +1 -0
- package/source/views/PictView-Remote-MediaViewer.js +231 -20
- package/source/views/PictView-Remote-SettingsPanel.js +154 -18
- package/source/views/PictView-Remote-VLCSetup.js +797 -0
- package/web-application/retold-remote.js +1606 -667
- package/web-application/retold-remote.js.map +1 -1
- package/web-application/retold-remote.min.js +5 -5
- package/web-application/retold-remote.min.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "retold-remote",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Retold Remote - NAS media browser with gallery views and keyboard navigation",
|
|
5
5
|
"main": "source/Pict-RetoldRemote-Bundle.js",
|
|
6
6
|
"bin": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"pict-section-filebrowser": "^0.0.2",
|
|
38
38
|
"pict-service-commandlineutility": "^1.0.19",
|
|
39
39
|
"pict-view": "^1.0.67",
|
|
40
|
-
"retold-content-system": "^1.0.
|
|
40
|
+
"retold-content-system": "^1.0.8",
|
|
41
41
|
"yauzl": "^3.2.0"
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
@@ -20,6 +20,7 @@ const libViewMediaViewer = require('./views/PictView-Remote-MediaViewer.js');
|
|
|
20
20
|
const libViewImageViewer = require('./views/PictView-Remote-ImageViewer.js');
|
|
21
21
|
const libViewVideoExplorer = require('./views/PictView-Remote-VideoExplorer.js');
|
|
22
22
|
const libViewAudioExplorer = require('./views/PictView-Remote-AudioExplorer.js');
|
|
23
|
+
const libViewVLCSetup = require('./views/PictView-Remote-VLCSetup.js');
|
|
23
24
|
|
|
24
25
|
// Application configuration
|
|
25
26
|
const _DefaultConfiguration = require('./Pict-Application-RetoldRemote-Configuration.json');
|
|
@@ -51,6 +52,7 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
51
52
|
this.pict.addView('RetoldRemote-SettingsPanel', libViewSettingsPanel.default_configuration, libViewSettingsPanel);
|
|
52
53
|
this.pict.addView('RetoldRemote-VideoExplorer', libViewVideoExplorer.default_configuration, libViewVideoExplorer);
|
|
53
54
|
this.pict.addView('RetoldRemote-AudioExplorer', libViewAudioExplorer.default_configuration, libViewAudioExplorer);
|
|
55
|
+
this.pict.addView('RetoldRemote-VLCSetup', libViewVLCSetup.default_configuration, libViewVLCSetup);
|
|
54
56
|
|
|
55
57
|
// Add new providers
|
|
56
58
|
this.pict.addProvider('RetoldRemote-Provider', libProviderRetoldRemote.default_configuration, libProviderRetoldRemote);
|
|
@@ -78,6 +80,7 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
78
80
|
RawFileList: [], // Unfiltered server response
|
|
79
81
|
GalleryItems: [], // Filtered+sorted file list (single source of truth)
|
|
80
82
|
GalleryCursorIndex: 0, // Currently highlighted item
|
|
83
|
+
FolderCursorHistory: {}, // Map of folder path -> last cursor index
|
|
81
84
|
GalleryFilter: 'all', // 'all', 'images', 'video', 'audio', 'documents'
|
|
82
85
|
SearchQuery: '',
|
|
83
86
|
SearchCaseSensitive: false,
|
|
@@ -356,6 +359,44 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
356
359
|
}
|
|
357
360
|
}
|
|
358
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Navigate to a file with an explicit media type override, bypassing
|
|
364
|
+
* extension-based detection.
|
|
365
|
+
*
|
|
366
|
+
* @param {string} pFilePath - Relative file path
|
|
367
|
+
* @param {string} pMediaType - 'image', 'video', 'audio', or 'text'
|
|
368
|
+
*/
|
|
369
|
+
navigateToFileAs(pFilePath, pMediaType)
|
|
370
|
+
{
|
|
371
|
+
if (!pFilePath)
|
|
372
|
+
{
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
377
|
+
|
|
378
|
+
// Update the hash
|
|
379
|
+
let tmpFragProvider = this.pict.providers['RetoldRemote-Provider'];
|
|
380
|
+
let tmpFragId = tmpFragProvider ? tmpFragProvider.getFragmentIdentifier(pFilePath) : pFilePath;
|
|
381
|
+
window.location.hash = '#/view/' + tmpFragId;
|
|
382
|
+
|
|
383
|
+
// Update parent state for compatibility
|
|
384
|
+
this.pict.AppData.ContentEditor.CurrentFile = pFilePath;
|
|
385
|
+
this.pict.AppData.ContentEditor.ActiveEditor = 'binary';
|
|
386
|
+
|
|
387
|
+
let tmpViewer = this.pict.views['RetoldRemote-MediaViewer'];
|
|
388
|
+
if (tmpViewer)
|
|
389
|
+
{
|
|
390
|
+
tmpViewer.showMedia(pFilePath, pMediaType);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
let tmpTopBar = this.pict.views['ContentEditor-TopBar'];
|
|
394
|
+
if (tmpTopBar)
|
|
395
|
+
{
|
|
396
|
+
tmpTopBar.updateInfo();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
359
400
|
/**
|
|
360
401
|
* Override loadFileList to also populate the gallery and fetch folder summary.
|
|
361
402
|
*/
|
|
@@ -467,7 +508,8 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
467
508
|
{
|
|
468
509
|
// Fallback if provider not ready
|
|
469
510
|
tmpRemote.GalleryItems = pFileList || [];
|
|
470
|
-
tmpRemote.
|
|
511
|
+
let tmpSavedIndex = tmpRemote.FolderCursorHistory && tmpRemote.FolderCursorHistory[tmpSelf.pict.AppData.PictFileBrowser.CurrentLocation || ''];
|
|
512
|
+
tmpRemote.GalleryCursorIndex = (typeof tmpSavedIndex === 'number' && tmpSavedIndex < (pFileList || []).length) ? tmpSavedIndex : 0;
|
|
471
513
|
let tmpGalleryView = tmpSelf.pict.views['RetoldRemote-Gallery'];
|
|
472
514
|
if (tmpGalleryView)
|
|
473
515
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const libPictProvider = require('pict-provider');
|
|
2
2
|
|
|
3
3
|
const _ImageExtensions = { 'png': true, 'jpg': true, 'jpeg': true, 'gif': true, 'webp': true, 'svg': true, 'bmp': true, 'ico': true, 'avif': true, 'tiff': true, 'tif': true };
|
|
4
|
-
const _VideoExtensions = { 'mp4': true, 'webm': true, 'mov': true, 'mkv': true, 'avi': true, 'wmv': true, 'flv': true, 'm4v': true };
|
|
4
|
+
const _VideoExtensions = { 'mp4': true, 'webm': true, 'mov': true, 'mkv': true, 'avi': true, 'wmv': true, 'flv': true, 'm4v': true, 'ogv': true, 'mpg': true, 'mpeg': true, 'mpe': true, 'mpv': true, 'm2v': true, 'ts': true, 'mts': true, 'm2ts': true, 'vob': true, '3gp': true, '3g2': true, 'f4v': true, 'rm': true, 'rmvb': true, 'divx': true, 'asf': true, 'mxf': true, 'dv': true, 'nsv': true, 'nuv': true, 'y4m': true, 'wtv': true, 'swf': true, 'dat': true };
|
|
5
5
|
const _AudioExtensions = { 'mp3': true, 'wav': true, 'ogg': true, 'flac': true, 'aac': true, 'm4a': true, 'wma': true };
|
|
6
6
|
const _DocumentExtensions = { 'pdf': true, 'epub': true, 'mobi': true };
|
|
7
7
|
|
|
@@ -60,7 +60,18 @@ class GalleryFilterSortProvider extends libPictProvider
|
|
|
60
60
|
|
|
61
61
|
// Write result
|
|
62
62
|
tmpRemote.GalleryItems = tmpItems;
|
|
63
|
-
|
|
63
|
+
|
|
64
|
+
// Restore cursor position if we have a saved one for this folder
|
|
65
|
+
let tmpCurrentLocation = (this.pict.AppData.PictFileBrowser && this.pict.AppData.PictFileBrowser.CurrentLocation) || '';
|
|
66
|
+
let tmpSavedIndex = tmpRemote.FolderCursorHistory && tmpRemote.FolderCursorHistory[tmpCurrentLocation];
|
|
67
|
+
if (typeof tmpSavedIndex === 'number' && tmpSavedIndex < tmpItems.length)
|
|
68
|
+
{
|
|
69
|
+
tmpRemote.GalleryCursorIndex = tmpSavedIndex;
|
|
70
|
+
}
|
|
71
|
+
else
|
|
72
|
+
{
|
|
73
|
+
tmpRemote.GalleryCursorIndex = 0;
|
|
74
|
+
}
|
|
64
75
|
|
|
65
76
|
// Re-render gallery
|
|
66
77
|
let tmpGalleryView = this.pict.views['RetoldRemote-Gallery'];
|
|
@@ -234,6 +234,26 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
234
234
|
this.moveCursor(tmpItems.length - 1);
|
|
235
235
|
break;
|
|
236
236
|
|
|
237
|
+
case '1':
|
|
238
|
+
pEvent.preventDefault();
|
|
239
|
+
this.openCurrentAs('image');
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
case '2':
|
|
243
|
+
pEvent.preventDefault();
|
|
244
|
+
this.openCurrentAs('video');
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case '3':
|
|
248
|
+
pEvent.preventDefault();
|
|
249
|
+
this.openCurrentAs('audio');
|
|
250
|
+
break;
|
|
251
|
+
|
|
252
|
+
case '4':
|
|
253
|
+
pEvent.preventDefault();
|
|
254
|
+
this.openCurrentAs('text');
|
|
255
|
+
break;
|
|
256
|
+
|
|
237
257
|
case 'f':
|
|
238
258
|
pEvent.preventDefault();
|
|
239
259
|
{
|
|
@@ -415,6 +435,66 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
415
435
|
*/
|
|
416
436
|
_handleViewerKey(pEvent)
|
|
417
437
|
{
|
|
438
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
439
|
+
|
|
440
|
+
// Video action menu mode — intercept keys for menu options
|
|
441
|
+
if (tmpRemote.VideoMenuActive && tmpRemote.CurrentViewerMediaType === 'video')
|
|
442
|
+
{
|
|
443
|
+
switch (pEvent.key)
|
|
444
|
+
{
|
|
445
|
+
case 'Escape':
|
|
446
|
+
pEvent.preventDefault();
|
|
447
|
+
this.closeViewer();
|
|
448
|
+
return;
|
|
449
|
+
|
|
450
|
+
case 'ArrowRight':
|
|
451
|
+
case 'j':
|
|
452
|
+
pEvent.preventDefault();
|
|
453
|
+
this.nextFile();
|
|
454
|
+
return;
|
|
455
|
+
|
|
456
|
+
case 'ArrowLeft':
|
|
457
|
+
case 'k':
|
|
458
|
+
pEvent.preventDefault();
|
|
459
|
+
this.prevFile();
|
|
460
|
+
return;
|
|
461
|
+
|
|
462
|
+
case 'e':
|
|
463
|
+
pEvent.preventDefault();
|
|
464
|
+
let tmpVEX = this.pict.views['RetoldRemote-VideoExplorer'];
|
|
465
|
+
if (tmpVEX)
|
|
466
|
+
{
|
|
467
|
+
tmpVEX.showExplorer(tmpRemote.CurrentViewerFile);
|
|
468
|
+
}
|
|
469
|
+
return;
|
|
470
|
+
|
|
471
|
+
case ' ':
|
|
472
|
+
case 'Enter':
|
|
473
|
+
pEvent.preventDefault();
|
|
474
|
+
let tmpViewer = this.pict.views['RetoldRemote-MediaViewer'];
|
|
475
|
+
if (tmpViewer)
|
|
476
|
+
{
|
|
477
|
+
tmpViewer.playVideo();
|
|
478
|
+
}
|
|
479
|
+
return;
|
|
480
|
+
|
|
481
|
+
case 't':
|
|
482
|
+
pEvent.preventDefault();
|
|
483
|
+
let tmpMediaViewer = this.pict.views['RetoldRemote-MediaViewer'];
|
|
484
|
+
if (tmpMediaViewer)
|
|
485
|
+
{
|
|
486
|
+
tmpMediaViewer.loadVideoMenuFrame();
|
|
487
|
+
}
|
|
488
|
+
return;
|
|
489
|
+
|
|
490
|
+
case 'v':
|
|
491
|
+
pEvent.preventDefault();
|
|
492
|
+
this._streamWithVLC();
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
418
498
|
switch (pEvent.key)
|
|
419
499
|
{
|
|
420
500
|
case 'Escape':
|
|
@@ -472,13 +552,38 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
472
552
|
|
|
473
553
|
case 'Enter':
|
|
474
554
|
pEvent.preventDefault();
|
|
475
|
-
this.
|
|
555
|
+
this._streamWithVLC();
|
|
556
|
+
break;
|
|
557
|
+
|
|
558
|
+
case 'v':
|
|
559
|
+
pEvent.preventDefault();
|
|
560
|
+
this._streamWithVLC();
|
|
476
561
|
break;
|
|
477
562
|
|
|
478
563
|
case 'd':
|
|
479
564
|
pEvent.preventDefault();
|
|
480
565
|
this._toggleDistractionFree();
|
|
481
566
|
break;
|
|
567
|
+
|
|
568
|
+
case '1':
|
|
569
|
+
pEvent.preventDefault();
|
|
570
|
+
this.switchViewerType('image');
|
|
571
|
+
break;
|
|
572
|
+
|
|
573
|
+
case '2':
|
|
574
|
+
pEvent.preventDefault();
|
|
575
|
+
this.switchViewerType('video');
|
|
576
|
+
break;
|
|
577
|
+
|
|
578
|
+
case '3':
|
|
579
|
+
pEvent.preventDefault();
|
|
580
|
+
this.switchViewerType('audio');
|
|
581
|
+
break;
|
|
582
|
+
|
|
583
|
+
case '4':
|
|
584
|
+
pEvent.preventDefault();
|
|
585
|
+
this.switchViewerType('text');
|
|
586
|
+
break;
|
|
482
587
|
}
|
|
483
588
|
}
|
|
484
589
|
|
|
@@ -601,6 +706,10 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
601
706
|
|
|
602
707
|
if (tmpItem.Type === 'folder' || tmpItem.Type === 'archive')
|
|
603
708
|
{
|
|
709
|
+
// Remember cursor position in the current folder before navigating away
|
|
710
|
+
let tmpCurrentLocation = (this.pict.AppData.PictFileBrowser && this.pict.AppData.PictFileBrowser.CurrentLocation) || '';
|
|
711
|
+
tmpRemote.FolderCursorHistory[tmpCurrentLocation] = tmpIndex;
|
|
712
|
+
|
|
604
713
|
// Navigate into the folder or archive
|
|
605
714
|
let tmpApp = this.pict.PictApplication;
|
|
606
715
|
if (tmpApp && tmpApp.loadFileList)
|
|
@@ -619,6 +728,59 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
619
728
|
}
|
|
620
729
|
}
|
|
621
730
|
|
|
731
|
+
/**
|
|
732
|
+
* Open the currently selected gallery item, forcing a specific media type
|
|
733
|
+
* regardless of its file extension.
|
|
734
|
+
*
|
|
735
|
+
* @param {string} pMediaType - 'image', 'video', 'audio', or 'text'
|
|
736
|
+
*/
|
|
737
|
+
openCurrentAs(pMediaType)
|
|
738
|
+
{
|
|
739
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
740
|
+
let tmpItems = tmpRemote.GalleryItems || [];
|
|
741
|
+
let tmpIndex = tmpRemote.GalleryCursorIndex || 0;
|
|
742
|
+
|
|
743
|
+
if (tmpIndex >= tmpItems.length)
|
|
744
|
+
{
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
let tmpItem = tmpItems[tmpIndex];
|
|
749
|
+
|
|
750
|
+
if (tmpItem.Type === 'folder' || tmpItem.Type === 'archive')
|
|
751
|
+
{
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
let tmpApp = this.pict.PictApplication;
|
|
756
|
+
if (tmpApp && tmpApp.navigateToFileAs)
|
|
757
|
+
{
|
|
758
|
+
tmpApp.navigateToFileAs(tmpItem.Path, pMediaType);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Re-open the currently viewed file with a different media type.
|
|
764
|
+
*
|
|
765
|
+
* @param {string} pMediaType - 'image', 'video', 'audio', or 'text'
|
|
766
|
+
*/
|
|
767
|
+
switchViewerType(pMediaType)
|
|
768
|
+
{
|
|
769
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
770
|
+
let tmpFilePath = tmpRemote.CurrentViewerFile;
|
|
771
|
+
|
|
772
|
+
if (!tmpFilePath)
|
|
773
|
+
{
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
let tmpViewer = this.pict.views['RetoldRemote-MediaViewer'];
|
|
778
|
+
if (tmpViewer)
|
|
779
|
+
{
|
|
780
|
+
tmpViewer.showMedia(tmpFilePath, pMediaType);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
622
784
|
/**
|
|
623
785
|
* Navigate up one directory level.
|
|
624
786
|
*/
|
|
@@ -631,6 +793,10 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
631
793
|
return;
|
|
632
794
|
}
|
|
633
795
|
|
|
796
|
+
// Remember cursor position in the current folder before navigating away
|
|
797
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
798
|
+
tmpRemote.FolderCursorHistory[tmpCurrentLocation] = tmpRemote.GalleryCursorIndex || 0;
|
|
799
|
+
|
|
634
800
|
let tmpParent = tmpCurrentLocation.indexOf('/') >= 0
|
|
635
801
|
? tmpCurrentLocation.replace(/\/[^/]+\/?$/, '')
|
|
636
802
|
: '';
|
|
@@ -1276,6 +1442,47 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1276
1442
|
});
|
|
1277
1443
|
}
|
|
1278
1444
|
|
|
1445
|
+
/**
|
|
1446
|
+
* Stream the current media file to VLC on the client device via vlc:// protocol link.
|
|
1447
|
+
*/
|
|
1448
|
+
_streamWithVLC()
|
|
1449
|
+
{
|
|
1450
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
1451
|
+
let tmpMediaType = tmpRemote.CurrentViewerMediaType;
|
|
1452
|
+
|
|
1453
|
+
if (tmpMediaType !== 'video' && tmpMediaType !== 'audio')
|
|
1454
|
+
{
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
let tmpFilePath = tmpRemote.CurrentViewerFile;
|
|
1459
|
+
if (!tmpFilePath)
|
|
1460
|
+
{
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
let tmpProvider = this.pict.providers['RetoldRemote-Provider'];
|
|
1465
|
+
let tmpContentPath = tmpProvider ? tmpProvider.getContentURL(tmpFilePath) : ('/content/' + encodeURIComponent(tmpFilePath));
|
|
1466
|
+
let tmpStreamURL = window.location.origin + tmpContentPath;
|
|
1467
|
+
// On Windows, VLC's native handler expects the raw URL.
|
|
1468
|
+
// On macOS/Linux our custom handlers URL-decode, so we encode.
|
|
1469
|
+
let tmpIsWindows = /Windows/.test(navigator.userAgent);
|
|
1470
|
+
let tmpVLCURL = tmpIsWindows
|
|
1471
|
+
? ('vlc://' + tmpStreamURL)
|
|
1472
|
+
: ('vlc://' + encodeURIComponent(tmpStreamURL));
|
|
1473
|
+
|
|
1474
|
+
this._showToast('Opening VLC...');
|
|
1475
|
+
|
|
1476
|
+
// Use a temporary anchor element to trigger the protocol handler
|
|
1477
|
+
// without navigating the current page away
|
|
1478
|
+
let tmpLink = document.createElement('a');
|
|
1479
|
+
tmpLink.href = tmpVLCURL;
|
|
1480
|
+
tmpLink.style.display = 'none';
|
|
1481
|
+
document.body.appendChild(tmpLink);
|
|
1482
|
+
tmpLink.click();
|
|
1483
|
+
document.body.removeChild(tmpLink);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1279
1486
|
/**
|
|
1280
1487
|
* Show a brief toast notification in the viewer.
|
|
1281
1488
|
*
|
|
@@ -19,7 +19,7 @@ const libToolDetector = require('./RetoldRemote-ToolDetector.js');
|
|
|
19
19
|
const libThumbnailCache = require('./RetoldRemote-ThumbnailCache.js');
|
|
20
20
|
|
|
21
21
|
const _ImageExtensions = { 'png': true, 'jpg': true, 'jpeg': true, 'gif': true, 'webp': true, 'svg': true, 'bmp': true, 'ico': true, 'avif': true, 'tiff': true, 'tif': true, 'heic': true, 'heif': true };
|
|
22
|
-
const _VideoExtensions = { 'mp4': true, 'webm': true, 'mov': true, 'mkv': true, 'avi': true, 'wmv': true, 'flv': true, 'm4v': true, 'ogv': true };
|
|
22
|
+
const _VideoExtensions = { 'mp4': true, 'webm': true, 'mov': true, 'mkv': true, 'avi': true, 'wmv': true, 'flv': true, 'm4v': true, 'ogv': true, 'mpg': true, 'mpeg': true, 'mpe': true, 'mpv': true, 'm2v': true, 'ts': true, 'mts': true, 'm2ts': true, 'vob': true, '3gp': true, '3g2': true, 'f4v': true, 'rm': true, 'rmvb': true, 'divx': true, 'asf': true, 'mxf': true, 'dv': true, 'nsv': true, 'nuv': true, 'y4m': true, 'wtv': true, 'swf': true, 'dat': true };
|
|
23
23
|
const _AudioExtensions = { 'mp3': true, 'wav': true, 'ogg': true, 'flac': true, 'aac': true, 'm4a': true, 'wma': true, 'oga': true };
|
|
24
24
|
const _DocumentExtensions = { 'pdf': true, 'epub': true, 'mobi': true, 'doc': true, 'docx': true };
|
|
25
25
|
|
|
@@ -475,7 +475,7 @@ class RetoldRemoteMediaService extends libFableServiceProviderBase
|
|
|
475
475
|
{
|
|
476
476
|
let tmpOutputFormat = pFormat === 'webp' ? 'webp' : 'mjpeg';
|
|
477
477
|
// Extract a frame at 10% into the video
|
|
478
|
-
let tmpCmd = `ffmpeg -ss 00:00:02 -i "${pFullPath}" -vframes 1 -vf "scale=${pWidth}:${pHeight}:force_original_aspect_ratio=decrease" -f
|
|
478
|
+
let tmpCmd = `ffmpeg -ss 00:00:02 -i "${pFullPath}" -vframes 1 -vf "scale=${pWidth}:${pHeight}:force_original_aspect_ratio=decrease" -f ${tmpOutputFormat} pipe:1`;
|
|
479
479
|
let tmpBuffer = libChildProcess.execSync(tmpCmd, { maxBuffer: 10 * 1024 * 1024, timeout: 30000 });
|
|
480
480
|
return fCallback(null, tmpBuffer);
|
|
481
481
|
}
|
|
@@ -147,7 +147,8 @@ class RetoldRemoteVideoFrameService extends libFableServiceProviderBase
|
|
|
147
147
|
|
|
148
148
|
let tmpCodec = (pFormat === 'png') ? 'png' : (pFormat === 'webp') ? 'webp' : 'mjpeg';
|
|
149
149
|
|
|
150
|
-
let
|
|
150
|
+
let tmpMuxer = (pFormat === 'png') ? 'image2' : (pFormat === 'webp') ? 'webp' : 'mjpeg';
|
|
151
|
+
let tmpCmd = `ffmpeg -ss ${tmpTimeStr} -i "${pAbsPath}" -vframes 1 -vf "scale=${pWidth}:${pHeight}:force_original_aspect_ratio=decrease" -f ${tmpMuxer} -y "${pOutputPath}"`;
|
|
151
152
|
libChildProcess.execSync(tmpCmd, { stdio: 'ignore', timeout: 30000 });
|
|
152
153
|
return libFs.existsSync(pOutputPath);
|
|
153
154
|
}
|