retold-remote 0.0.13 → 0.0.15
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/css/retold-remote.css +75 -0
- package/docs/_sidebar.md +2 -0
- package/docs/ultravisor-configuration.md +212 -0
- package/docs/ultravisor-integration.md +140 -0
- package/package.json +121 -96
- package/source/cli/RetoldRemote-Server-Setup.js +26 -0
- package/source/providers/Pict-Provider-GalleryNavigation.js +11 -3
- package/source/providers/keyboard-handlers/KeyHandler-AudioExplorer.js +5 -0
- package/source/providers/keyboard-handlers/KeyHandler-VideoExplorer.js +16 -0
- package/source/server/RetoldRemote-AudioWaveformService.js +101 -23
- package/source/server/RetoldRemote-EbookService.js +119 -6
- package/source/server/RetoldRemote-ImageService.js +144 -0
- package/source/server/RetoldRemote-MediaService.js +208 -34
- package/source/server/RetoldRemote-ToolDetector.js +27 -3
- package/source/server/RetoldRemote-UltravisorDispatcher.js +288 -0
- package/source/server/RetoldRemote-VideoFrameService.js +309 -77
- package/source/views/PictView-Remote-AudioExplorer.js +28 -14
- package/source/views/PictView-Remote-ImageExplorer.js +31 -11
- package/source/views/PictView-Remote-VLCSetup.js +22 -12
- package/source/views/PictView-Remote-VideoExplorer.js +29 -14
- package/web-application/css/retold-remote.css +75 -0
- package/web-application/docs/_sidebar.md +2 -0
- package/web-application/docs/ultravisor-configuration.md +212 -0
- package/web-application/docs/ultravisor-integration.md +140 -0
- package/web-application/retold-remote.js +58 -22
- package/web-application/retold-remote.js.map +1 -1
- package/web-application/retold-remote.min.js +6 -6
- package/web-application/retold-remote.min.js.map +1 -1
|
@@ -3332,7 +3332,7 @@ pView._updateImagePreviews(pSegmentIndex);};/**
|
|
|
3332
3332
|
* @param {number} pSegmentIndex - The internal segment index
|
|
3333
3333
|
*/pView._updateImagePreviews=function _updateImagePreviews(pSegmentIndex){let tmpPreviewEl=document.getElementById(`PictMDE-ImagePreview-${pSegmentIndex}`);if(!tmpPreviewEl){return;}let tmpEditor=pView._segmentEditors[pSegmentIndex];if(!tmpEditor){tmpPreviewEl.innerHTML='';tmpPreviewEl.classList.remove('pict-mde-has-images');return;}let tmpContent=tmpEditor.state.doc.toString();// Match markdown image syntax: 
|
|
3334
3334
|
let tmpImageRegex=/!\[([^\]]*)\]\(([^)]+)\)/g;let tmpMatches=[];let tmpMatch;while((tmpMatch=tmpImageRegex.exec(tmpContent))!==null){tmpMatches.push({alt:tmpMatch[1]||'image',url:tmpMatch[2]});}if(tmpMatches.length===0){tmpPreviewEl.innerHTML='';tmpPreviewEl.classList.remove('pict-mde-has-images');return;}// Build preview HTML
|
|
3335
|
-
let tmpHTML='';for(let i=0;i<tmpMatches.length;i++){let tmpAlt=tmpMatches[i].alt.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');let
|
|
3335
|
+
let tmpHTML='';for(let i=0;i<tmpMatches.length;i++){let tmpAlt=tmpMatches[i].alt.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');let tmpResolvedURL=pView._resolveImageURL(tmpMatches[i].url);let tmpURL=tmpResolvedURL.replace(/&/g,'&').replace(/"/g,'"');tmpHTML+=`<div class="pict-mde-image-preview-item"><img src="${tmpURL}" alt="${tmpAlt}" /><span class="pict-mde-image-preview-label">${tmpAlt}</span></div>`;}tmpPreviewEl.innerHTML=tmpHTML;tmpPreviewEl.classList.add('pict-mde-has-images');};/**
|
|
3336
3336
|
* Wire drag-and-drop events for image files on a segment editor container.
|
|
3337
3337
|
*
|
|
3338
3338
|
* These events are separate from the segment-reorder drag events.
|
|
@@ -3374,7 +3374,8 @@ if(pView._dragSourceIndex>=0){return;}if(!pEvent.dataTransfer||!pEvent.dataTrans
|
|
|
3374
3374
|
*/pView._updateRichPreviews=function _updateRichPreviews(pSegmentIndex){if(!pView.options.EnableRichPreview){return;}let tmpPreviewEl=document.getElementById(`PictMDE-RichPreview-${pSegmentIndex}`);if(!tmpPreviewEl){return;}let tmpEditor=pView._segmentEditors[pSegmentIndex];if(!tmpEditor){tmpPreviewEl.innerHTML='';tmpPreviewEl.classList.remove('pict-mde-has-rich-preview');return;}let tmpContent=tmpEditor.state.doc.toString();if(!tmpContent||tmpContent.trim().length===0){tmpPreviewEl.innerHTML='';tmpPreviewEl.classList.remove('pict-mde-has-rich-preview');return;}// Use pict-section-content's provider to parse the raw markdown into HTML
|
|
3375
3375
|
let tmpProvider=pView._getContentProvider();let tmpRenderedHTML=tmpProvider.parseMarkdown(tmpContent);if(!tmpRenderedHTML||tmpRenderedHTML.trim().length===0){tmpPreviewEl.innerHTML='';tmpPreviewEl.classList.remove('pict-mde-has-rich-preview');return;}// Wrap the rendered HTML in a pict-content container so that
|
|
3376
3376
|
// pict-section-content's CSS classes take effect
|
|
3377
|
-
let tmpPreviewID=`PictMDE-RichPreviewBody-${pSegmentIndex}`;tmpPreviewEl.innerHTML=`<div class="pict-content" id="${tmpPreviewID}">${tmpRenderedHTML}</div>`;tmpPreviewEl.classList.add('pict-mde-has-rich-preview');//
|
|
3377
|
+
let tmpPreviewID=`PictMDE-RichPreviewBody-${pSegmentIndex}`;tmpPreviewEl.innerHTML=`<div class="pict-content" id="${tmpPreviewID}">${tmpRenderedHTML}</div>`;tmpPreviewEl.classList.add('pict-mde-has-rich-preview');// Resolve relative image URLs in the rendered HTML using ImageBaseURL
|
|
3378
|
+
if(pView.options.ImageBaseURL){let tmpImages=tmpPreviewEl.querySelectorAll('img');for(let i=0;i<tmpImages.length;i++){let tmpSrc=tmpImages[i].getAttribute('src');if(tmpSrc){let tmpResolved=pView._resolveImageURL(tmpSrc);if(tmpResolved!==tmpSrc){tmpImages[i].setAttribute('src',tmpResolved);}}}}// Bump generation counter for stale-render protection (mermaid is async)
|
|
3378
3379
|
let tmpGeneration=(pView._richPreviewGenerations[pSegmentIndex]||0)+1;pView._richPreviewGenerations[pSegmentIndex]=tmpGeneration;// Post-render: call mermaid.run() for mermaid diagram elements
|
|
3379
3380
|
pView._postRenderMermaid(tmpPreviewID,pSegmentIndex,tmpGeneration);// Post-render: call katex.render() for KaTeX math elements
|
|
3380
3381
|
pView._postRenderKaTeX(tmpPreviewID);};/**
|
|
@@ -3414,7 +3415,8 @@ pView._marshalAllEditorsToData();// Combine all segments into a single markdown
|
|
|
3414
3415
|
let tmpSegments=pView._getSegmentsFromData();let tmpCombinedContent='';if(tmpSegments&&tmpSegments.length>0){let tmpParts=[];for(let i=0;i<tmpSegments.length;i++){tmpParts.push(tmpSegments[i].Content||'');}tmpCombinedContent=tmpParts.join('\n\n');}// Destroy existing CodeMirror editors
|
|
3415
3416
|
for(let tmpIdx in pView._segmentEditors){if(pView._segmentEditors[tmpIdx]){pView._segmentEditors[tmpIdx].destroy();}}pView._segmentEditors={};// Render the combined markdown via pict-section-content
|
|
3416
3417
|
let tmpProvider=pView._getContentProvider();let tmpRenderedHTML=tmpProvider.parseMarkdown(tmpCombinedContent);// Build the rendered view container
|
|
3417
|
-
let tmpRenderedViewID='PictMDE-RenderedView';tmpContainer.innerHTML=`<div class="pict-mde-rendered-view" id="${tmpRenderedViewID}"><div class="pict-content">${tmpRenderedHTML||''}</div></div>`;tmpContainer.classList.add('pict-mde-rendered-mode');//
|
|
3418
|
+
let tmpRenderedViewID='PictMDE-RenderedView';tmpContainer.innerHTML=`<div class="pict-mde-rendered-view" id="${tmpRenderedViewID}"><div class="pict-content">${tmpRenderedHTML||''}</div></div>`;tmpContainer.classList.add('pict-mde-rendered-mode');// Resolve relative image URLs in the rendered HTML using ImageBaseURL
|
|
3419
|
+
if(pView.options.ImageBaseURL){let tmpImages=tmpContainer.querySelectorAll('.pict-mde-rendered-view img');for(let i=0;i<tmpImages.length;i++){let tmpSrc=tmpImages[i].getAttribute('src');if(tmpSrc){let tmpResolved=pView._resolveImageURL(tmpSrc);if(tmpResolved!==tmpSrc){tmpImages[i].setAttribute('src',tmpResolved);}}}}// Bump generation for stale-render protection
|
|
3418
3420
|
pView._renderedViewGeneration++;let tmpGeneration=pView._renderedViewGeneration;// Post-render hooks for mermaid diagrams and KaTeX equations
|
|
3419
3421
|
let tmpContentContainer=tmpContainer.querySelector(`#${tmpRenderedViewID} .pict-content`);if(tmpContentContainer){let tmpContentID='PictMDE-RenderedViewContent';tmpContentContainer.id=tmpContentID;pView._postRenderMermaid(tmpContentID,-1,tmpGeneration);pView._postRenderKaTeX(tmpContentID);}};/**
|
|
3420
3422
|
* Switch back from rendered view to the editing view: rebuild the
|
|
@@ -3445,7 +3447,11 @@ let tmpContentContainer=tmpContainer.querySelector(`#${tmpRenderedViewID} .pict-
|
|
|
3445
3447
|
// code, mermaid diagrams, KaTeX equations, tables, etc. via pict-section-content).
|
|
3446
3448
|
// Requires the consumer to load the mermaid and/or katex libraries via CDN
|
|
3447
3449
|
// for diagram/equation rendering; code highlighting works without CDN scripts.
|
|
3448
|
-
"EnableRichPreview":true,//
|
|
3450
|
+
"EnableRichPreview":true,// Base URL prepended to relative image URLs in image and rich previews.
|
|
3451
|
+
// Set this to the directory-level path (e.g. "/content/") so that images
|
|
3452
|
+
// referenced by filename in the markdown resolve correctly.
|
|
3453
|
+
// Absolute URLs (starting with /, http://, https://, data:) are left as-is.
|
|
3454
|
+
"ImageBaseURL":"",// ---- Quadrant button definitions ----
|
|
3449
3455
|
// Each quadrant is an array of button objects:
|
|
3450
3456
|
// HTML — innerHTML for the button
|
|
3451
3457
|
// Action — method name, optionally "method:arg" (receives segment index as first param)
|
|
@@ -4160,6 +4166,16 @@ this._buildEditorUI();}/**
|
|
|
4160
4166
|
*
|
|
4161
4167
|
* @returns {HTMLElement|null}
|
|
4162
4168
|
*/_getContainerElement(){if(this.targetElement){let tmpContainer=this.targetElement.querySelector('.pict-mde');if(tmpContainer){return tmpContainer;}}return this.targetElement||null;}/**
|
|
4169
|
+
* Resolve a URL relative to the configured ImageBaseURL.
|
|
4170
|
+
*
|
|
4171
|
+
* Absolute URLs (starting with /, http://, https://, data:) are returned
|
|
4172
|
+
* unchanged. Relative URLs are prepended with this.options.ImageBaseURL.
|
|
4173
|
+
*
|
|
4174
|
+
* @param {string} pURL - The URL to resolve
|
|
4175
|
+
* @returns {string} The resolved URL
|
|
4176
|
+
*/_resolveImageURL(pURL){if(!pURL||!this.options.ImageBaseURL){return pURL;}// Leave absolute URLs alone
|
|
4177
|
+
if(pURL.startsWith('/')||pURL.startsWith('http://')||pURL.startsWith('https://')||pURL.startsWith('data:')){return pURL;}let tmpBase=this.options.ImageBaseURL;// Ensure base ends with /
|
|
4178
|
+
if(tmpBase&&!tmpBase.endsWith('/')){tmpBase+='/';}return tmpBase+pURL;}/**
|
|
4163
4179
|
* Build the full editor UI: render existing segments from data and the add-segment button.
|
|
4164
4180
|
*/_buildEditorUI(){let tmpContainer=this._getContainerElement();// Ensure the container has the pict-mde class (the template's wrapper
|
|
4165
4181
|
// may have been replaced by pict's async render pipeline)
|
|
@@ -9872,7 +9888,8 @@ let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.FolderCursorHistory[tmpCu
|
|
|
9872
9888
|
*/closeViewer(){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='gallery';// Clear viewer file state so collection/favorites resolution
|
|
9873
9889
|
// falls through to the gallery cursor instead of a stale path
|
|
9874
9890
|
tmpRemote.CurrentViewerFile='';tmpRemote.CurrentViewerMediaType='';// Exit collection browsing mode
|
|
9875
|
-
tmpRemote.BrowsingCollection=false;tmpRemote.BrowsingCollectionIndex=-1;let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container')
|
|
9891
|
+
tmpRemote.BrowsingCollection=false;tmpRemote.BrowsingCollectionIndex=-1;let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container');// Stop any playing video/audio and release resources before hiding the viewer
|
|
9892
|
+
let tmpVideo=document.querySelector('#RetoldRemote-Viewer-Container video');let tmpAudio=document.querySelector('#RetoldRemote-Viewer-Container audio');if(tmpVideo){tmpVideo.pause();tmpVideo.removeAttribute('src');tmpVideo.load();}if(tmpAudio){tmpAudio.pause();tmpAudio.removeAttribute('src');tmpAudio.load();}if(tmpGalleryContainer)tmpGalleryContainer.style.display='';if(tmpViewerContainer)tmpViewerContainer.style.display='none';// Clean up swipe and DF exit listeners
|
|
9876
9893
|
let tmpMediaViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpMediaViewer){if(tmpMediaViewer._cleanupSwipe){tmpMediaViewer._cleanupSwipe();}if(tmpMediaViewer._cleanupDFExitHotspot){tmpMediaViewer._cleanupDFExitHotspot();}}// Restore the hash to the browse route (use hashed identifier when available)
|
|
9877
9894
|
let tmpCurrentLocation=this.pict.AppData.PictFileBrowser&&this.pict.AppData.PictFileBrowser.CurrentLocation||'';let tmpFragProvider=this.pict.providers['RetoldRemote-Provider'];let tmpFragId=tmpFragProvider&&tmpCurrentLocation?tmpFragProvider.getFragmentIdentifier(tmpCurrentLocation):tmpCurrentLocation;window.location.hash=tmpFragId?'#/browse/'+tmpFragId:'#/browse/';// Re-render gallery to ensure cursor is visible
|
|
9878
9895
|
let tmpGalleryView=this.pict.views['RetoldRemote-Gallery'];if(tmpGalleryView){tmpGalleryView.renderGallery();}}/**
|
|
@@ -9968,8 +9985,10 @@ fetch('/api/media/open',{method:'POST',headers:{'Content-Type':'application/json
|
|
|
9968
9985
|
*/_streamWithVLC(){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpMediaType=tmpRemote.CurrentViewerMediaType;if(tmpMediaType!=='video'&&tmpMediaType!=='audio'){return;}let tmpFilePath=tmpRemote.CurrentViewerFile;if(!tmpFilePath){return;}let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpContentPath=tmpProvider?tmpProvider.getContentURL(tmpFilePath):'/content/'+encodeURIComponent(tmpFilePath);let tmpStreamURL=window.location.origin+tmpContentPath;// Detect platform for VLC URL format.
|
|
9969
9986
|
// iPadOS 13+ sends a macOS user agent — detect it via maxTouchPoints.
|
|
9970
9987
|
let tmpIsWindows=/Windows/.test(navigator.userAgent);let tmpIsIOS=/iPhone|iPad|iPod/i.test(navigator.userAgent)||/Macintosh/i.test(navigator.userAgent)&&navigator.maxTouchPoints>1;let tmpIsAndroid=/Android/i.test(navigator.userAgent);let tmpIsMobile=tmpIsIOS||tmpIsAndroid;let tmpVLCURL;if(tmpIsIOS){// iOS/iPadOS: use vlc-x-callback for reliable app launching
|
|
9971
|
-
tmpVLCURL='vlc-x-callback://x-callback-url/stream?url='+encodeURIComponent(tmpStreamURL);}else if(
|
|
9972
|
-
tmpVLCURL='vlc://'+tmpStreamURL;}else{// macOS
|
|
9988
|
+
tmpVLCURL='vlc-x-callback://x-callback-url/stream?url='+encodeURIComponent(tmpStreamURL);}else if(tmpIsAndroid){// Android: VLC app handles the raw URL natively via intent
|
|
9989
|
+
tmpVLCURL='vlc://'+tmpStreamURL;}else{// Windows, macOS, Linux: encode the URL so the custom protocol
|
|
9990
|
+
// handler can decode it. On Windows this is required because the
|
|
9991
|
+
// shell strips the colon from nested http:// URLs.
|
|
9973
9992
|
tmpVLCURL='vlc://'+encodeURIComponent(tmpStreamURL);}this.pict.providers['RetoldRemote-ToastNotification'].showOverlayIndicator('Opening VLC...');// Use window.location for iOS (more reliable for custom URL schemes
|
|
9974
9993
|
// on iOS Safari than a programmatic anchor click), anchor for others.
|
|
9975
9994
|
if(tmpIsIOS){window.location.href=tmpVLCURL;}else{let tmpLink=document.createElement('a');tmpLink.href=tmpVLCURL;tmpLink.style.display='none';document.body.appendChild(tmpLink);tmpLink.click();document.body.removeChild(tmpLink);}}}GalleryNavigationProvider.default_configuration=_DefaultProviderConfiguration;module.exports=GalleryNavigationProvider;},{"./keyboard-handlers/KeyHandler-AudioExplorer.js":128,"./keyboard-handlers/KeyHandler-Gallery.js":129,"./keyboard-handlers/KeyHandler-ImageExplorer.js":130,"./keyboard-handlers/KeyHandler-Sidebar.js":131,"./keyboard-handlers/KeyHandler-VideoExplorer.js":132,"./keyboard-handlers/KeyHandler-Viewer.js":133,"pict-provider":46}],123:[function(require,module,exports){const libPictProvider=require('pict-provider');const libExtensionMaps=require('../RetoldRemote-ExtensionMaps.js');const _DefaultProviderConfiguration={ProviderIdentifier:'RetoldRemote-Provider',AutoInitialize:true,AutoSolveWithApp:false};class RetoldRemoteProvider extends libPictProvider{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);// Client-side cache: path -> hash and hash -> path
|
|
@@ -10277,7 +10296,7 @@ let tmpRemote=this.pict.AppData.RetoldRemote;if(tmpRemote){tmpRemote.Theme=pThem
|
|
|
10277
10296
|
*
|
|
10278
10297
|
* @param {GalleryNavigationProvider} pGalleryNav - The provider instance
|
|
10279
10298
|
* @param {KeyboardEvent} pEvent - The keyboard event
|
|
10280
|
-
*/function handleAudioExplorerKey(pGalleryNav,pEvent){let tmpAEX=pGalleryNav.pict.views['RetoldRemote-AudioExplorer'];if(!tmpAEX){return;}switch(pEvent.key){case'Escape':pEvent.preventDefault();if(tmpAEX._selectionStart>=0){tmpAEX.clearSelection();}else{tmpAEX.goBack();}break;case'+':case'=':pEvent.preventDefault();tmpAEX.zoomIn();break;case'-':case'_':pEvent.preventDefault();tmpAEX.zoomOut();break;case'0':pEvent.preventDefault();tmpAEX.zoomToFit();break;case'z':case'Z':pEvent.preventDefault();tmpAEX.zoomToSelection();break;case' ':pEvent.preventDefault();tmpAEX.playSelection();break;case'a':case's':pEvent.preventDefault();{let tmpCollMgr=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollMgr){let tmpQuickGUID=tmpCollMgr.getQuickAddTargetGUID();if(tmpQuickGUID){tmpCollMgr.addAudioSnippetToCollection(tmpQuickGUID);}else{let tmpTopBar=pGalleryNav.pict.views['ContentEditor-TopBar'];if(tmpTopBar&&typeof tmpTopBar.showAddToCollectionDropdown==='function'){tmpTopBar.showAddToCollectionDropdown();}}}}break;case'b':pEvent.preventDefault();{let tmpCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollManager){tmpCollManager.togglePanel();}}break;case'h':pEvent.preventDefault();{let tmpFavCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpFavCollManager){tmpFavCollManager.toggleFavorite();}}break;}}module.exports=handleAudioExplorerKey;},{}],129:[function(require,module,exports){/**
|
|
10299
|
+
*/function handleAudioExplorerKey(pGalleryNav,pEvent){let tmpAEX=pGalleryNav.pict.views['RetoldRemote-AudioExplorer'];if(!tmpAEX){return;}switch(pEvent.key){case'Escape':pEvent.preventDefault();if(tmpAEX._selectionStart>=0){tmpAEX.clearSelection();}else{tmpAEX.goBack();}break;case'+':case'=':pEvent.preventDefault();tmpAEX.zoomIn();break;case'-':case'_':pEvent.preventDefault();tmpAEX.zoomOut();break;case'0':pEvent.preventDefault();tmpAEX.zoomToFit();break;case'z':case'Z':pEvent.preventDefault();tmpAEX.zoomToSelection();break;case' ':pEvent.preventDefault();tmpAEX.playSelection();break;case'a':case's':pEvent.preventDefault();{let tmpCollMgr=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollMgr){let tmpQuickGUID=tmpCollMgr.getQuickAddTargetGUID();if(tmpQuickGUID){tmpCollMgr.addAudioSnippetToCollection(tmpQuickGUID);}else{let tmpTopBar=pGalleryNav.pict.views['ContentEditor-TopBar'];if(tmpTopBar&&typeof tmpTopBar.showAddToCollectionDropdown==='function'){tmpTopBar.showAddToCollectionDropdown();}}}}break;case'b':pEvent.preventDefault();{let tmpCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollManager){tmpCollManager.togglePanel();}}break;case'h':pEvent.preventDefault();{let tmpFavCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpFavCollManager){tmpFavCollManager.toggleFavorite();}}break;case'v':pEvent.preventDefault();pGalleryNav._streamWithVLC();break;}}module.exports=handleAudioExplorerKey;},{}],129:[function(require,module,exports){/**
|
|
10281
10300
|
* Gallery mode keyboard handler.
|
|
10282
10301
|
*
|
|
10283
10302
|
* @param {GalleryNavigationProvider} pGalleryNav - The provider instance
|
|
@@ -10310,7 +10329,7 @@ if(tmpVEX._previewKeyHandler){tmpVEX.closeFramePreview();}else{tmpVEX.goBack();}
|
|
|
10310
10329
|
let tmpSelVEX=pGalleryNav.pict.views['RetoldRemote-VideoExplorer'];if(!tmpSelVEX||tmpSelVEX._selectionStartTime<0||tmpSelVEX._selectionEndTime<0){// No selection active \u2014 do nothing
|
|
10311
10330
|
break;}let tmpSelCollMgr=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpSelCollMgr){let tmpStart=Math.min(tmpSelVEX._selectionStartTime,tmpSelVEX._selectionEndTime);let tmpEnd=Math.max(tmpSelVEX._selectionStartTime,tmpSelVEX._selectionEndTime);let tmpSelQuickGUID=tmpSelCollMgr.getQuickAddTargetGUID();if(tmpSelQuickGUID){tmpSelCollMgr.addVideoClipToCollection(tmpSelQuickGUID,tmpStart,tmpEnd);}else{let tmpSelTopBar=pGalleryNav.pict.views['ContentEditor-TopBar'];if(tmpSelTopBar&&typeof tmpSelTopBar.showAddToCollectionDropdown==='function'){tmpSelTopBar.showAddToCollectionDropdown();}}}}break;case'[':pEvent.preventDefault();{// Set selection start marker at currently selected frame's timestamp
|
|
10312
10331
|
let tmpStartVEX=pGalleryNav.pict.views['RetoldRemote-VideoExplorer'];if(tmpStartVEX&&tmpStartVEX._frameData&&tmpStartVEX._frameData.Frames&&tmpStartVEX._selectedFrameIndex>=0&&tmpStartVEX._frameData.Frames[tmpStartVEX._selectedFrameIndex]){let tmpTimestamp=tmpStartVEX._frameData.Frames[tmpStartVEX._selectedFrameIndex].Timestamp;tmpStartVEX.setSelectionStart(tmpTimestamp);}}break;case']':pEvent.preventDefault();{// Set selection end marker at currently selected frame's timestamp
|
|
10313
|
-
let tmpEndVEX=pGalleryNav.pict.views['RetoldRemote-VideoExplorer'];if(tmpEndVEX&&tmpEndVEX._frameData&&tmpEndVEX._frameData.Frames&&tmpEndVEX._selectedFrameIndex>=0&&tmpEndVEX._frameData.Frames[tmpEndVEX._selectedFrameIndex]){let tmpTimestamp=tmpEndVEX._frameData.Frames[tmpEndVEX._selectedFrameIndex].Timestamp;tmpEndVEX.setSelectionEnd(tmpTimestamp);}}break;case'b':pEvent.preventDefault();{let tmpCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollManager){tmpCollManager.togglePanel();}}break;case'h':pEvent.preventDefault();{let tmpFavCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpFavCollManager){tmpFavCollManager.toggleFavorite();}}break;}}module.exports=handleVideoExplorerKey;},{}],133:[function(require,module,exports){/**
|
|
10332
|
+
let tmpEndVEX=pGalleryNav.pict.views['RetoldRemote-VideoExplorer'];if(tmpEndVEX&&tmpEndVEX._frameData&&tmpEndVEX._frameData.Frames&&tmpEndVEX._selectedFrameIndex>=0&&tmpEndVEX._frameData.Frames[tmpEndVEX._selectedFrameIndex]){let tmpTimestamp=tmpEndVEX._frameData.Frames[tmpEndVEX._selectedFrameIndex].Timestamp;tmpEndVEX.setSelectionEnd(tmpTimestamp);}}break;case'b':pEvent.preventDefault();{let tmpCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollManager){tmpCollManager.togglePanel();}}break;case'h':pEvent.preventDefault();{let tmpFavCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpFavCollManager){tmpFavCollManager.toggleFavorite();}}break;case'v':pEvent.preventDefault();pGalleryNav._streamWithVLC();break;case' ':pEvent.preventDefault();{let tmpPlayVEX=pGalleryNav.pict.views['RetoldRemote-VideoExplorer'];if(tmpPlayVEX){tmpPlayVEX.playInBrowser();}}break;}}module.exports=handleVideoExplorerKey;},{}],133:[function(require,module,exports){/**
|
|
10314
10333
|
* Viewer mode keyboard handler.
|
|
10315
10334
|
*
|
|
10316
10335
|
* @param {GalleryNavigationProvider} pGalleryNav - The provider instance
|
|
@@ -10414,13 +10433,13 @@ this._mainCanvas=null;this._overviewCanvas=null;this._resizeObserver=null;}/**
|
|
|
10414
10433
|
* @param {string} pFilePath - Relative file path
|
|
10415
10434
|
* @param {number} [pSelectionStart] - Optional selection start in seconds
|
|
10416
10435
|
* @param {number} [pSelectionEnd] - Optional selection end in seconds
|
|
10417
|
-
*/showExplorer(pFilePath,pSelectionStart,pSelectionEnd){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='audio-explorer';this._currentPath=pFilePath;this._waveformData=null;this._peaks=[];this._viewStart=0;this._viewEnd=1;this._segmentURL=null;// Store passed-in selection times (in seconds) to apply after waveform loads
|
|
10436
|
+
*/showExplorer(pFilePath,pSelectionStart,pSelectionEnd){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='audio-explorer';tmpRemote.CurrentViewerFile=pFilePath;tmpRemote.CurrentViewerMediaType='audio';this._currentPath=pFilePath;this._waveformData=null;this._peaks=[];this._viewStart=0;this._viewEnd=1;this._segmentURL=null;// Store passed-in selection times (in seconds) to apply after waveform loads
|
|
10418
10437
|
this._pendingSelectionStartSec=typeof pSelectionStart==='number'&&pSelectionStart>=0?pSelectionStart:-1;this._pendingSelectionEndSec=typeof pSelectionEnd==='number'&&pSelectionEnd>=0?pSelectionEnd:-1;this._selectionStart=-1;this._selectionEnd=-1;// Update the hash. Replace (not push) when coming from #/view/ to
|
|
10419
10438
|
// prevent back-button loops when auto-launched from the media viewer.
|
|
10420
10439
|
let tmpFragProvider=this.pict.providers['RetoldRemote-Provider'];let tmpFragId=tmpFragProvider?tmpFragProvider.getFragmentIdentifier(pFilePath):pFilePath;let tmpNewHash='#/explore-audio/'+tmpFragId;let tmpCurrentHash=window.location.hash||'';if(tmpCurrentHash.indexOf('#/view/')===0){history.replaceState(null,'',tmpNewHash);}else{window.location.hash=tmpNewHash;}// Show viewer container, hide gallery
|
|
10421
10440
|
let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container');if(tmpGalleryContainer)tmpGalleryContainer.style.display='none';if(tmpViewerContainer)tmpViewerContainer.style.display='block';let tmpFileName=pFilePath.replace(/^.*\//,'');// Build initial UI
|
|
10422
10441
|
let tmpHTML='<div class="retold-remote-aex">';// Header
|
|
10423
|
-
tmpHTML+='<div class="retold-remote-aex-header">';tmpHTML+='<button class="retold-remote-aex-nav-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].goBack()" title="Back
|
|
10442
|
+
tmpHTML+='<div class="retold-remote-aex-header">';tmpHTML+='<button class="retold-remote-aex-nav-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].goBack()" title="Back (Esc)">← Back</button>';tmpHTML+='<div class="retold-remote-aex-title">Audio Explorer — '+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpFileName)+'</div>';tmpHTML+='<div class="retold-remote-aex-actions">';tmpHTML+='<button class="retold-remote-aex-action-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].playInBrowser()" title="Play in browser">▶ Play</button>';tmpHTML+='<button class="retold-remote-aex-action-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._streamWithVLC()" title="Stream with VLC (v)">▶ VLC</button>';tmpHTML+='</div>';tmpHTML+='</div>';// Info bar (populated after waveform loads)
|
|
10424
10443
|
tmpHTML+='<div class="retold-remote-aex-info" id="RetoldRemote-AEX-Info" style="display:none;"></div>';// Controls bar
|
|
10425
10444
|
tmpHTML+='<div class="retold-remote-aex-controls" id="RetoldRemote-AEX-Controls" style="display:none;">';tmpHTML+='<button class="retold-remote-aex-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].zoomIn()" title="Zoom In (+)">+ Zoom In</button>';tmpHTML+='<button class="retold-remote-aex-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].zoomOut()" title="Zoom Out (-)">- Zoom Out</button>';tmpHTML+='<button class="retold-remote-aex-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].zoomToFit()" title="Zoom to Fit (0)">Fit All</button>';tmpHTML+='<button class="retold-remote-aex-btn" id="RetoldRemote-AEX-ZoomSelBtn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].zoomToSelection()" title="Zoom to Selection (Z)" disabled>Zoom to Selection</button>';tmpHTML+='<button class="retold-remote-aex-btn" id="RetoldRemote-AEX-PlaySelBtn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].playSelection()" title="Play Selection (Space)" disabled>▶ Play Selection</button>';tmpHTML+='<button class="retold-remote-aex-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].clearSelection()" title="Clear Selection (Esc)">Clear Selection</button>';tmpHTML+='<span style="border-left:1px solid var(--retold-border);height:20px;margin:0 4px;"></span>';tmpHTML+='<button class="retold-remote-aex-save-btn" id="RetoldRemote-AEX-SaveSelBtn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].saveSelectionToCollection()" title="Save segment to collection (s)" disabled>Save Segment</button>';tmpHTML+='</div>';// Body (loading initially)
|
|
10426
10445
|
tmpHTML+='<div class="retold-remote-aex-body" id="RetoldRemote-AEX-Body">';tmpHTML+='<div class="retold-remote-aex-loading">';tmpHTML+='<div class="retold-remote-aex-loading-spinner"></div>';tmpHTML+='Analyzing audio waveform...';tmpHTML+='</div>';tmpHTML+='</div>';// Time display bar
|
|
@@ -10506,9 +10525,12 @@ let tmpDuration=this._waveformData&&this._waveformData.Duration?this._waveformDa
|
|
|
10506
10525
|
*/playSelection(){if(this._selectionStart<0||this._selectionEnd<0||!this._waveformData){return;}let tmpSelf=this;let tmpDuration=this._waveformData.Duration;let tmpStart=Math.min(this._selectionStart,this._selectionEnd)*tmpDuration;let tmpEnd=Math.max(this._selectionStart,this._selectionEnd)*tmpDuration;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(this._currentPath):encodeURIComponent(this._currentPath);let tmpURL='/api/media/audio-segment?path='+tmpPathParam+'&start='+tmpStart.toFixed(3)+'&end='+tmpEnd.toFixed(3)+'&format=mp3';// Show playback bar with loading state
|
|
10507
10526
|
let tmpPlaybackBar=document.getElementById('RetoldRemote-AEX-Playback');if(tmpPlaybackBar){tmpPlaybackBar.style.display='';}let tmpAudioEl=document.getElementById('RetoldRemote-AEX-Audio');if(tmpAudioEl){tmpAudioEl.src=tmpURL;tmpAudioEl.load();tmpAudioEl.play().catch(()=>{// Autoplay may be blocked; user can click play
|
|
10508
10527
|
});}this._segmentURL=tmpURL;}/**
|
|
10509
|
-
* Navigate back to the
|
|
10528
|
+
* Navigate back to the gallery / file listing.
|
|
10510
10529
|
*/goBack(){// Clean up resize observer
|
|
10511
|
-
if(this._resizeObserver){this._resizeObserver.disconnect();this._resizeObserver=null;}
|
|
10530
|
+
if(this._resizeObserver){this._resizeObserver.disconnect();this._resizeObserver=null;}let tmpNav=this.pict.providers['RetoldRemote-GalleryNavigation'];if(tmpNav){tmpNav.closeViewer();}}/**
|
|
10531
|
+
* Leave the audio explorer and play the file in the browser viewer.
|
|
10532
|
+
*/playInBrowser(){// Clean up resize observer
|
|
10533
|
+
if(this._resizeObserver){this._resizeObserver.disconnect();this._resizeObserver=null;}let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showMedia(this._currentPath,'audio');}}// --- Explorer State Persistence ---
|
|
10512
10534
|
/**
|
|
10513
10535
|
* Fire-and-forget save of the current selection and view state.
|
|
10514
10536
|
*/_saveState(){if(!this._currentPath||!this._waveformData){return;}let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpSelections=[];if(this._selectionStart>=0&&this._selectionEnd>=0&&Math.abs(this._selectionEnd-this._selectionStart)>=0.001){let tmpDuration=this._waveformData.Duration||0;let tmpStartNorm=Math.min(this._selectionStart,this._selectionEnd);let tmpEndNorm=Math.max(this._selectionStart,this._selectionEnd);let tmpStartSec=Math.round(tmpStartNorm*tmpDuration*100)/100;let tmpEndSec=Math.round(tmpEndNorm*tmpDuration*100)/100;let tmpFmt=this.pict.providers['RetoldRemote-FormattingUtilities'];tmpSelections.push({Start:tmpStartNorm,End:tmpEndNorm,StartSeconds:tmpStartSec,EndSeconds:tmpEndSec,Label:tmpFmt.formatTimestamp(tmpStartSec,true)+' - '+tmpFmt.formatTimestamp(tmpEndSec,true)});}let tmpBody={Path:this._currentPath,Selections:tmpSelections,ViewStart:this._viewStart,ViewEnd:this._viewEnd};fetch('/api/media/audio-explorer-state',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(tmpBody)}).catch(()=>{// Non-critical — explorer works without persistence
|
|
@@ -10782,7 +10804,7 @@ let tmpExt=(pExtension||'').replace(/^\./,'').toLowerCase();if(tmpExt==='png'||t
|
|
|
10782
10804
|
* Show the image explorer for a given image file.
|
|
10783
10805
|
*
|
|
10784
10806
|
* @param {string} pFilePath - Relative file path
|
|
10785
|
-
*/showExplorer(pFilePath){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='image-explorer';this._currentPath=pFilePath;this._dziData=null;this._loading=false;// Clean up existing viewer
|
|
10807
|
+
*/showExplorer(pFilePath){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='image-explorer';tmpRemote.CurrentViewerFile=pFilePath;tmpRemote.CurrentViewerMediaType='image';this._currentPath=pFilePath;this._dziData=null;this._loading=false;// Clean up existing viewer
|
|
10786
10808
|
if(this._osdViewer){try{this._osdViewer.destroy();}catch(pErr){// ignore
|
|
10787
10809
|
}this._osdViewer=null;}// Update URL hash.
|
|
10788
10810
|
// When the current hash is #/view/ for the same file (i.e. the media
|
|
@@ -10791,7 +10813,7 @@ if(this._osdViewer){try{this._osdViewer.destroy();}catch(pErr){// ignore
|
|
|
10791
10813
|
let tmpFragProvider=this.pict.providers['RetoldRemote-Provider'];let tmpFragId=tmpFragProvider?tmpFragProvider.getFragmentIdentifier(pFilePath):pFilePath;let tmpNewHash='#/explore-image/'+tmpFragId;let tmpCurrentHash=window.location.hash||'';if(tmpCurrentHash.indexOf('#/view/')===0){history.replaceState(null,'',tmpNewHash);}else{window.location.hash=tmpNewHash;}// Show viewer, hide gallery
|
|
10792
10814
|
let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container');if(tmpGalleryContainer)tmpGalleryContainer.style.display='none';if(tmpViewerContainer)tmpViewerContainer.style.display='block';let tmpFileName=pFilePath.replace(/^.*\//,'');let tmpFmt=this.pict.providers['RetoldRemote-FormattingUtilities'];// Build the explorer UI
|
|
10793
10815
|
let tmpHTML='<div class="retold-remote-iex">';// Header
|
|
10794
|
-
tmpHTML+='<div class="retold-remote-iex-header">';tmpHTML+='<button class="retold-remote-iex-nav-btn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].goBack()" title="Back (Esc)">← Back</button>';tmpHTML+='<div class="retold-remote-iex-title">Image Explorer — '+tmpFmt.escapeHTML(tmpFileName)+'</div>';tmpHTML+='</div>';// Info bar
|
|
10816
|
+
tmpHTML+='<div class="retold-remote-iex-header">';tmpHTML+='<button class="retold-remote-iex-nav-btn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].goBack()" title="Back (Esc)">← Back</button>';tmpHTML+='<div class="retold-remote-iex-title">Image Explorer — '+tmpFmt.escapeHTML(tmpFileName)+'</div>';tmpHTML+='<div class="retold-remote-iex-actions">';tmpHTML+='<button class="retold-remote-iex-action-btn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].viewInBrowser()" title="View in standard viewer">🖼 View</button>';tmpHTML+='</div>';tmpHTML+='</div>';// Info bar
|
|
10795
10817
|
tmpHTML+='<div class="retold-remote-iex-info" id="RetoldRemote-IEX-Info" style="display:none;"></div>';// Body
|
|
10796
10818
|
tmpHTML+='<div class="retold-remote-iex-body" id="RetoldRemote-IEX-Body">';tmpHTML+='<div class="retold-remote-iex-loading" id="RetoldRemote-IEX-Loading">';tmpHTML+='<div>Loading image\u2026</div>';tmpHTML+='</div>';tmpHTML+='<div id="RetoldRemote-IEX-Viewer" style="display:none;"></div>';tmpHTML+='</div>';// Controls bar
|
|
10797
10819
|
tmpHTML+='<div class="retold-remote-iex-controls" id="RetoldRemote-IEX-Controls" style="display:none;">';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].zoomIn()" title="Zoom In (+)">+ Zoom In</button>';tmpHTML+='<span class="retold-remote-iex-zoom-label" id="RetoldRemote-IEX-ZoomLabel">100%</span>';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].zoomOut()" title="Zoom Out (-)">- Zoom Out</button>';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].zoomHome()" title="Fit to view (0)">Fit</button>';tmpHTML+='<span style="flex:1;"></span>';tmpHTML+='<span id="RetoldRemote-IEX-Coords" style="color:var(--retold-text-dim);font-size:0.72rem;"></span>';tmpHTML+='</div>';tmpHTML+='</div>';if(tmpViewerContainer){tmpViewerContainer.innerHTML=tmpHTML;}// Update topbar
|
|
@@ -10891,10 +10913,14 @@ let tmpHomeZoom=this._osdViewer.viewport.getHomeZoom();let tmpPercent=Math.round
|
|
|
10891
10913
|
*/zoomOut(){if(this._osdViewer){let tmpCurrentZoom=this._osdViewer.viewport.getZoom();this._osdViewer.viewport.zoomTo(tmpCurrentZoom/1.5);}}/**
|
|
10892
10914
|
* Reset to home (fit to view).
|
|
10893
10915
|
*/zoomHome(){if(this._osdViewer){this._osdViewer.viewport.goHome();}}/**
|
|
10894
|
-
* Navigate back to the
|
|
10916
|
+
* Navigate back to the gallery / file listing.
|
|
10895
10917
|
*/goBack(){// Destroy the OSD viewer
|
|
10896
10918
|
if(this._osdViewer){try{this._osdViewer.destroy();}catch(pErr){// ignore
|
|
10897
|
-
}this._osdViewer=null;}
|
|
10919
|
+
}this._osdViewer=null;}let tmpNav=this.pict.providers['RetoldRemote-GalleryNavigation'];if(tmpNav){tmpNav.closeViewer();}}/**
|
|
10920
|
+
* Leave the image explorer and view the image in the standard viewer.
|
|
10921
|
+
*/viewInBrowser(){// Destroy the OSD viewer
|
|
10922
|
+
if(this._osdViewer){try{this._osdViewer.destroy();}catch(pErr){// ignore
|
|
10923
|
+
}this._osdViewer=null;}let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showMedia(this._currentPath,'image');}}/**
|
|
10898
10924
|
* Show an error message.
|
|
10899
10925
|
*
|
|
10900
10926
|
* @param {string} pMessage - Error message
|
|
@@ -11323,7 +11349,15 @@ tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="r
|
|
|
11323
11349
|
tmpHTML+='<div class="retold-remote-vlc-setup-status">';tmpHTML+='<div class="retold-remote-vlc-setup-status-dot '+(this._detectPlatform()===tmpPlatform?'detected':'unknown')+'"></div>';tmpHTML+='<span>Detected platform: <b>'+this._getPlatformLabel(this._detectPlatform())+'</b></span>';tmpHTML+='</div>';// Platform tabs
|
|
11324
11350
|
tmpHTML+='<div class="retold-remote-vlc-setup-platform-tabs">';tmpHTML+=this._buildPlatformTab('ios','iOS',tmpPlatform);tmpHTML+=this._buildPlatformTab('android','Android',tmpPlatform);tmpHTML+=this._buildPlatformTab('macos','macOS',tmpPlatform);tmpHTML+=this._buildPlatformTab('windows','Windows',tmpPlatform);tmpHTML+=this._buildPlatformTab('linux','Linux',tmpPlatform);tmpHTML+='</div>';// Platform-specific content
|
|
11325
11351
|
tmpHTML+=this._buildIOSContent(tmpPlatform);tmpHTML+=this._buildAndroidContent(tmpPlatform);tmpHTML+=this._buildMacOSContent(tmpPlatform);tmpHTML+=this._buildWindowsContent(tmpPlatform);tmpHTML+=this._buildLinuxContent(tmpPlatform);// Test section
|
|
11326
|
-
tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Test</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Click below to test whether the vlc:// protocol handler is registered. VLC should open.';tmpHTML+='</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn" onclick="pict.views[\'RetoldRemote-VLCSetup\'].testProtocol()">Test VLC Protocol</button>';tmpHTML+='</div>';tmpContainer.innerHTML=tmpHTML;}_buildPlatformTab(pKey,pLabel,pActive){let tmpClass='retold-remote-vlc-setup-platform-tab';if(pKey===pActive){tmpClass+=' active';}return'<button class="'+tmpClass+'" onclick="pict.views[\'RetoldRemote-VLCSetup\'].switchPlatformTab(\''+pKey+'\')">'+pLabel+'</button>';}_getPlatformLabel(pKey){if(pKey==='ios')return'iOS';if(pKey==='android')return'Android';if(pKey==='macos')return'macOS';if(pKey==='windows')return'Windows';return'Linux';}_buildIOSContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='ios'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="ios">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (iOS)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='VLC for iOS registers the vlc:// protocol handler automatically when installed. No additional setup is needed.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Installation</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Install <b>VLC for Mobile</b> from the App Store if you haven\'t already.</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Tap the <b>Stream with VLC</b> button on any video or audio file. Safari will ask to open VLC — tap <b>Open</b>.</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">If Safari shows "Cannot Open Page", VLC may not be installed or may need to be updated.</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildAndroidContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='android'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="android">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (Android)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='VLC for Android registers the vlc:// protocol handler automatically when installed. No additional setup is needed.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Installation</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Install <b>VLC for Android</b> from the Google Play Store if you haven\'t already.</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Tap the <b>Stream with VLC</b> button on any video or audio file. Your browser will ask to open VLC — tap <b>Open</b>.</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">If your browser shows an error, VLC may not be installed or may need to be updated.</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildMacOSContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='macos'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="macos">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (macOS)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='VLC on macOS does not register a vlc:// protocol handler by default. ';tmpHTML+='An AppleScript app bundle is needed to bridge vlc:// links to VLC. ';tmpHTML+='Run the command below in Terminal to create and register the handler automatically.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Automatic Setup</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Copy and paste this into Terminal:';tmpHTML+='</div>';let tmpScript=this._getMacSetupScript();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpScript)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyMacSetup()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">What This Does</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Creates an AppleScript at <code>/tmp/VLCProtocol.applescript</code> that handles vlc:// URLs</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Compiles it into an app bundle at <code>/Applications/VLCProtocol.app</code></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">3</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Adds the vlc:// URL scheme to the app\'s Info.plist</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">4</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Registers the protocol handler with macOS Launch Services</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">Requires VLC installed at /Applications/VLC.app and Python 3 (included with macOS).</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildWindowsContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='windows'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="windows">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (Windows)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='VLC on Windows registers the vlc:// protocol handler during installation. ';tmpHTML+='If it is not working, you can re-register it by saving and running the registry file below.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Option A: Reinstall VLC</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';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>';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Option B: Registry File</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Save this as <code>vlc-protocol.reg</code> and double-click to import. ';tmpHTML+='Adjust the VLC path if yours differs.';tmpHTML+='</div>';let tmpRegFile=this._getWindowsRegFile();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpRegFile)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyWindowsReg()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Option C: Batch Script</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Alternatively, save this as <code>vlc-protocol-setup.bat</code> and run as Administrator. ';tmpHTML+='This creates a wrapper script that URL-decodes the vlc:// link before passing it to VLC.';tmpHTML+='</div>';let tmpBatchScript=this._getWindowsBatchScript();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpBatchScript)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyWindowsBatch()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildLinuxContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='linux'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="linux">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (Linux)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Register a vlc:// protocol handler using a .desktop file and xdg-mime. ';tmpHTML+='Run the command below in a terminal.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup Command</div>';let tmpScript=this._getLinuxSetupScript();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpScript)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyLinuxSetup()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">What This Does</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';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>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Creates a .desktop file at <code>~/.local/share/applications/vlc-protocol.desktop</code></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">3</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Registers vlc:// as a URL scheme via <code>xdg-mime</code></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">Requires VLC and Python 3 installed.</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_getMacSetupScript(){return["# Create the AppleScript handler","cat > /tmp/VLCProtocol.applescript << 'EOF'","on open location theURL","\tset theURL to text 7 thru -1 of theURL","\tset theURL to do shell script \"python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))' \" & quoted form of theURL","\tdo shell script \"open -a VLC \" & quoted form of theURL","end open location","EOF","","# Compile into app bundle","osacompile -o /Applications/VLCProtocol.app /tmp/VLCProtocol.applescript","","# Add vlc:// URL scheme to Info.plist","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes array\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0 dict\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLName string 'VLC Protocol'\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes array\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string vlc\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","","# Register with Launch Services","/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \\"," -f /Applications/VLCProtocol.app","","echo \"VLC protocol handler installed successfully.\""].join('\n');}_getWindowsRegFile(){return["Windows Registry Editor Version 5.00","","[HKEY_CLASSES_ROOT\\vlc]","@=\"URL:VLC Protocol\"","\"URL Protocol\"=\"\"","","[HKEY_CLASSES_ROOT\\vlc\\shell]","","[HKEY_CLASSES_ROOT\\vlc\\shell\\open]","","[HKEY_CLASSES_ROOT\\vlc\\shell\\open\\command]","@=\"\\\"C:\\\\Program Files\\\\VideoLAN\\\\VLC\\\\vlc.exe\\\" \\\"%1\\\"\""].join('\n');}_getWindowsBatchScript(){return["@echo off","REM VLC Protocol Handler Setup for Windows","REM Run this as Administrator","","REM Create the handler script","mkdir \"%APPDATA%\\VLCProtocol\" 2>nul","(","echo import sys, urllib.parse, subprocess","echo url = sys.argv[1] if len(sys.argv^) ^> 1 else ''","echo if url.startswith('vlc://'^): url = url[6:]","echo url = urllib.parse.unquote(url^)","echo subprocess.Popen(['C:\\\\Program Files\\\\VideoLAN\\\\VLC\\\\vlc.exe', url]^)",") > \"%APPDATA%\\VLCProtocol\\handler.py\"","","REM Register the protocol in the registry","reg add \"HKCU\\Software\\Classes\\vlc\" /ve /d \"URL:VLC Protocol\" /f","reg add \"HKCU\\Software\\Classes\\vlc\" /v \"URL Protocol\" /d \"\" /f","reg add \"HKCU\\Software\\Classes\\vlc\\shell\\open\\command\" /ve /d \"pythonw \\\"%APPDATA%\\VLCProtocol\\handler.py\\\" \\\"%%1\\\"\" /f","","echo VLC protocol handler installed successfully.","pause"].join('\n');}_getLinuxSetupScript(){return["# Create handler script","mkdir -p ~/.local/bin","cat > ~/.local/bin/vlc-protocol << 'EOF'","#!/bin/bash","URL=\"$1\"","URL=\"${URL#vlc://}\"","URL=$(python3 -c \"import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))\" \"$URL\")","exec vlc \"$URL\" &","EOF","chmod +x ~/.local/bin/vlc-protocol","","# Create .desktop file","cat > ~/.local/share/applications/vlc-protocol.desktop << 'EOF'","[Desktop Entry]","Name=VLC Protocol Handler","Exec=bash -c '~/.local/bin/vlc-protocol %u'","Type=Application","NoDisplay=true","MimeType=x-scheme-handler/vlc;","EOF","","# Register the handler","xdg-mime default vlc-protocol.desktop x-scheme-handler/vlc","update-desktop-database ~/.local/share/applications/","","echo \"VLC protocol handler installed successfully.\""].join('\n');}_copyToClipboard(pText,pLabel){if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(pText).then(()=>{this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel+' copied to clipboard');}).catch(()=>{this._fallbackCopy(pText,pLabel);});}else{this._fallbackCopy(pText,pLabel);}}_fallbackCopy(pText,pLabel){let tmpTextarea=document.createElement('textarea');tmpTextarea.value=pText;tmpTextarea.style.position='fixed';tmpTextarea.style.left='-9999px';document.body.appendChild(tmpTextarea);tmpTextarea.select();try{document.execCommand('copy');this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel+' copied to clipboard');}catch(pErr){this.pict.providers['RetoldRemote-ToastNotification'].showToast('Failed to copy - please select and copy manually');}document.body.removeChild(tmpTextarea);}copyMacSetup(){this._copyToClipboard(this._getMacSetupScript(),'macOS setup script');}copyWindowsReg(){this._copyToClipboard(this._getWindowsRegFile(),'Registry file');}copyWindowsBatch(){this._copyToClipboard(this._getWindowsBatchScript(),'Batch script');}copyLinuxSetup(){this._copyToClipboard(this._getLinuxSetupScript(),'Linux setup script');}testProtocol(){let tmpIsWindows=/Windows/.test(navigator.userAgent);let tmpIsMobile=/iPhone|iPad|iPod|Android/i.test(navigator.userAgent);let tmpSampleURL='https://www.sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4';let tmpTestURL=tmpIsWindows||tmpIsMobile?'vlc://'+tmpSampleURL:'vlc://'+encodeURIComponent(tmpSampleURL);let tmpLink=document.createElement('a');tmpLink.href=tmpTestURL;tmpLink.style.display='none';document.body.appendChild(tmpLink);tmpLink.click();document.body.removeChild(tmpLink);}}RetoldRemoteVLCSetupView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteVLCSetupView;},{"pict-view":76}],147:[function(require,module,exports){const libPictView=require('pict-view');const _VideoExplorerSelection=require('./VideoExplorer-Selection');const _VideoExplorerCustomFrames=require('./VideoExplorer-CustomFrames');const _VideoExplorerPreview=require('./VideoExplorer-Preview');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-VideoExplorer",DefaultRenderable:"RetoldRemote-VideoExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteVideoExplorerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath='';this._frameData=null;this._selectedFrameIndex=-1;this._frameCount=20;this._fullResFrames=true;this._customFrames=[];// Selection mode and state for timeline range selection
|
|
11352
|
+
tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Test</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Click below to test whether the vlc:// protocol handler is registered. VLC should open.';tmpHTML+='</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn" onclick="pict.views[\'RetoldRemote-VLCSetup\'].testProtocol()">Test VLC Protocol</button>';tmpHTML+='</div>';tmpContainer.innerHTML=tmpHTML;}_buildPlatformTab(pKey,pLabel,pActive){let tmpClass='retold-remote-vlc-setup-platform-tab';if(pKey===pActive){tmpClass+=' active';}return'<button class="'+tmpClass+'" onclick="pict.views[\'RetoldRemote-VLCSetup\'].switchPlatformTab(\''+pKey+'\')">'+pLabel+'</button>';}_getPlatformLabel(pKey){if(pKey==='ios')return'iOS';if(pKey==='android')return'Android';if(pKey==='macos')return'macOS';if(pKey==='windows')return'Windows';return'Linux';}_buildIOSContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='ios'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="ios">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (iOS)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='VLC for iOS registers the vlc:// protocol handler automatically when installed. No additional setup is needed.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Installation</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Install <b>VLC for Mobile</b> from the App Store if you haven\'t already.</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Tap the <b>Stream with VLC</b> button on any video or audio file. Safari will ask to open VLC — tap <b>Open</b>.</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">If Safari shows "Cannot Open Page", VLC may not be installed or may need to be updated.</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildAndroidContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='android'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="android">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (Android)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='VLC for Android registers the vlc:// protocol handler automatically when installed. No additional setup is needed.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Installation</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Install <b>VLC for Android</b> from the Google Play Store if you haven\'t already.</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Tap the <b>Stream with VLC</b> button on any video or audio file. Your browser will ask to open VLC — tap <b>Open</b>.</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">If your browser shows an error, VLC may not be installed or may need to be updated.</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildMacOSContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='macos'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="macos">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (macOS)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='VLC on macOS does not register a vlc:// protocol handler by default. ';tmpHTML+='An AppleScript app bundle is needed to bridge vlc:// links to VLC. ';tmpHTML+='Run the command below in Terminal to create and register the handler automatically.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Automatic Setup</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Copy and paste this into Terminal:';tmpHTML+='</div>';let tmpScript=this._getMacSetupScript();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpScript)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyMacSetup()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">What This Does</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Creates an AppleScript at <code>/tmp/VLCProtocol.applescript</code> that handles vlc:// URLs</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Compiles it into an app bundle at <code>/Applications/VLCProtocol.app</code></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">3</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Adds the vlc:// URL scheme to the app\'s Info.plist</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">4</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Registers the protocol handler with macOS Launch Services</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">Requires VLC installed at /Applications/VLC.app and Python 3 (included with macOS).</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildWindowsContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='windows'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="windows">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (Windows)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Windows requires a protocol handler to open vlc:// links. ';tmpHTML+='Choose one of the options below to register the handler.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Option A: Reinstall VLC</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';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>';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Option B: Registry File</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Save this as <code>vlc-protocol.reg</code> and double-click to import. ';tmpHTML+='Adjust the VLC path if yours differs.';tmpHTML+='</div>';let tmpRegFile=this._getWindowsRegFile();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpRegFile)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyWindowsReg()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Option C: Batch Script</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Alternatively, save this as <code>vlc-protocol-setup.bat</code> and run as Administrator. ';tmpHTML+='This creates a handler script and registers the vlc:// protocol automatically.';tmpHTML+='</div>';let tmpBatchScript=this._getWindowsBatchScript();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpBatchScript)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyWindowsBatch()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildLinuxContent(pActive){let tmpClass='retold-remote-vlc-setup-platform'+(pActive==='linux'?' active':'');let tmpHTML='<div class="'+tmpClass+'" data-platform="linux">';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup (Linux)</div>';tmpHTML+='<div class="retold-remote-vlc-setup-desc">';tmpHTML+='Register a vlc:// protocol handler using a .desktop file and xdg-mime. ';tmpHTML+='Run the command below in a terminal.';tmpHTML+='</div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">Setup Command</div>';let tmpScript=this._getLinuxSetupScript();tmpHTML+='<div class="retold-remote-vlc-setup-code">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpScript)+'</div>';tmpHTML+='<button class="retold-remote-vlc-setup-btn primary" onclick="pict.views[\'RetoldRemote-VLCSetup\'].copyLinuxSetup()">Copy to Clipboard</button>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-section">';tmpHTML+='<div class="retold-remote-vlc-setup-section-title">What This Does</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">1</div>';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>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">2</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Creates a .desktop file at <code>~/.local/share/applications/vlc-protocol.desktop</code></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step">';tmpHTML+='<div class="retold-remote-vlc-setup-step-num">3</div>';tmpHTML+='<div class="retold-remote-vlc-setup-step-content">Registers vlc:// as a URL scheme via <code>xdg-mime</code></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-vlc-setup-note">Requires VLC and Python 3 installed.</div>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_getMacSetupScript(){return["# Create the AppleScript handler","cat > /tmp/VLCProtocol.applescript << 'EOF'","on open location theURL","\tset theURL to text 7 thru -1 of theURL","\tset theURL to do shell script \"python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))' \" & quoted form of theURL","\tdo shell script \"open -a VLC \" & quoted form of theURL","end open location","EOF","","# Compile into app bundle","osacompile -o /Applications/VLCProtocol.app /tmp/VLCProtocol.applescript","","# Add vlc:// URL scheme to Info.plist","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes array\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0 dict\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLName string 'VLC Protocol'\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes array\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","/usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string vlc\" \\"," /Applications/VLCProtocol.app/Contents/Info.plist 2>/dev/null","","# Register with Launch Services","/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \\"," -f /Applications/VLCProtocol.app","","echo \"VLC protocol handler installed successfully.\""].join('\n');}_getWindowsRegFile(){// Use an inline PowerShell command to strip the vlc:// prefix and
|
|
11353
|
+
// URL-decode before launching VLC. The URL is percent-encoded by
|
|
11354
|
+
// the client to prevent Windows from stripping the colon in nested
|
|
11355
|
+
// http:// URLs (a known Windows protocol handler issue).
|
|
11356
|
+
return["Windows Registry Editor Version 5.00","","[HKEY_CLASSES_ROOT\\vlc]","@=\"URL:VLC Protocol\"","\"URL Protocol\"=\"\"","","[HKEY_CLASSES_ROOT\\vlc\\shell]","","[HKEY_CLASSES_ROOT\\vlc\\shell\\open]","","[HKEY_CLASSES_ROOT\\vlc\\shell\\open\\command]","@=\"powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -Command \\\"$u='%1'; if($u.StartsWith('vlc://')){$u=$u.Substring(6)}; $u=[System.Uri]::UnescapeDataString($u); $u=$u.TrimEnd('/'); Start-Process -FilePath 'C:\\\\Program Files\\\\VideoLAN\\\\VLC\\\\vlc.exe' -ArgumentList $u\\\"\""].join('\n');}_getWindowsBatchScript(){// Creates a PowerShell handler script and registers the vlc://
|
|
11357
|
+
// protocol to use it. The handler URL-decodes the argument because
|
|
11358
|
+
// the client percent-encodes the URL to prevent Windows from
|
|
11359
|
+
// stripping colons in nested http:// URLs.
|
|
11360
|
+
return["@echo off","REM VLC Protocol Handler Setup for Windows","REM Run this as Administrator","","REM Create the handler directory","mkdir \"%APPDATA%\\VLCProtocol\" 2>nul","","REM Write the PowerShell handler script","(","echo $url = $args[0]","echo if ^($url -and $url.StartsWith^('vlc://'^)^) { $url = $url.Substring^(6^) }","echo $url = [System.Uri]::UnescapeDataString^($url^)","echo $url = $url.TrimEnd^('/'^)","echo if ^($url^) { Start-Process 'C:\\Program Files\\VideoLAN\\VLC\\vlc.exe' -ArgumentList $url }",") > \"%APPDATA%\\VLCProtocol\\handler.ps1\"","","REM Register the protocol in the registry","reg add \"HKCU\\Software\\Classes\\vlc\" /ve /d \"URL:VLC Protocol\" /f","reg add \"HKCU\\Software\\Classes\\vlc\" /v \"URL Protocol\" /d \"\" /f","reg add \"HKCU\\Software\\Classes\\vlc\\shell\\open\\command\" /ve /d \"powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File \\\"%APPDATA%\\VLCProtocol\\handler.ps1\\\" \\\"%%1\\\"\" /f","","echo VLC protocol handler installed successfully.","pause"].join('\n');}_getLinuxSetupScript(){return["# Create handler script","mkdir -p ~/.local/bin","cat > ~/.local/bin/vlc-protocol << 'EOF'","#!/bin/bash","URL=\"$1\"","URL=\"${URL#vlc://}\"","URL=$(python3 -c \"import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))\" \"$URL\")","exec vlc \"$URL\" &","EOF","chmod +x ~/.local/bin/vlc-protocol","","# Create .desktop file","cat > ~/.local/share/applications/vlc-protocol.desktop << 'EOF'","[Desktop Entry]","Name=VLC Protocol Handler","Exec=bash -c '~/.local/bin/vlc-protocol %u'","Type=Application","NoDisplay=true","MimeType=x-scheme-handler/vlc;","EOF","","# Register the handler","xdg-mime default vlc-protocol.desktop x-scheme-handler/vlc","update-desktop-database ~/.local/share/applications/","","echo \"VLC protocol handler installed successfully.\""].join('\n');}_copyToClipboard(pText,pLabel){if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(pText).then(()=>{this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel+' copied to clipboard');}).catch(()=>{this._fallbackCopy(pText,pLabel);});}else{this._fallbackCopy(pText,pLabel);}}_fallbackCopy(pText,pLabel){let tmpTextarea=document.createElement('textarea');tmpTextarea.value=pText;tmpTextarea.style.position='fixed';tmpTextarea.style.left='-9999px';document.body.appendChild(tmpTextarea);tmpTextarea.select();try{document.execCommand('copy');this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel+' copied to clipboard');}catch(pErr){this.pict.providers['RetoldRemote-ToastNotification'].showToast('Failed to copy - please select and copy manually');}document.body.removeChild(tmpTextarea);}copyMacSetup(){this._copyToClipboard(this._getMacSetupScript(),'macOS setup script');}copyWindowsReg(){this._copyToClipboard(this._getWindowsRegFile(),'Registry file');}copyWindowsBatch(){this._copyToClipboard(this._getWindowsBatchScript(),'Batch script');}copyLinuxSetup(){this._copyToClipboard(this._getLinuxSetupScript(),'Linux setup script');}testProtocol(){let tmpIsWindows=/Windows/.test(navigator.userAgent);let tmpIsMobile=/iPhone|iPad|iPod|Android/i.test(navigator.userAgent);let tmpSampleURL='https://www.sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4';let tmpTestURL=tmpIsWindows||tmpIsMobile?'vlc://'+tmpSampleURL:'vlc://'+encodeURIComponent(tmpSampleURL);let tmpLink=document.createElement('a');tmpLink.href=tmpTestURL;tmpLink.style.display='none';document.body.appendChild(tmpLink);tmpLink.click();document.body.removeChild(tmpLink);}}RetoldRemoteVLCSetupView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteVLCSetupView;},{"pict-view":76}],147:[function(require,module,exports){const libPictView=require('pict-view');const _VideoExplorerSelection=require('./VideoExplorer-Selection');const _VideoExplorerCustomFrames=require('./VideoExplorer-CustomFrames');const _VideoExplorerPreview=require('./VideoExplorer-Preview');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-VideoExplorer",DefaultRenderable:"RetoldRemote-VideoExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteVideoExplorerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath='';this._frameData=null;this._selectedFrameIndex=-1;this._frameCount=20;this._fullResFrames=true;this._customFrames=[];// Selection mode and state for timeline range selection
|
|
11327
11361
|
this._selectionModeActive=false;this._selectionStartTime=-1;this._selectionEndTime=-1;this._isSelectingRange=false;this._isDraggingTimeline=false;this._draggingHandle=null;// 'start', 'end', or null
|
|
11328
11362
|
// Cached provider references (resolved lazily)
|
|
11329
11363
|
this._fmt=null;this._provider=null;}// -----------------------------------------------------------------
|
|
@@ -11374,7 +11408,7 @@ tmpCollMgr.setPendingClipContext({Type:'video-clip',Start:tmpStart,End:tmpEnd});
|
|
|
11374
11408
|
* @param {string} pFilePath - Relative file path
|
|
11375
11409
|
* @param {number} [pSelectionStart] - Optional selection start time (seconds)
|
|
11376
11410
|
* @param {number} [pSelectionEnd] - Optional selection end time (seconds)
|
|
11377
|
-
*/showExplorer(pFilePath,pSelectionStart,pSelectionEnd){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='video-explorer';this._currentPath=pFilePath;this._frameData=null;this._selectedFrameIndex=-1;this._customFrames=[];this._selectionModeActive=false;this._isSelectingRange=false;this._isDraggingTimeline=false;this._draggingHandle=null;// Apply passed-in selection range, or reset
|
|
11411
|
+
*/showExplorer(pFilePath,pSelectionStart,pSelectionEnd){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='video-explorer';tmpRemote.CurrentViewerFile=pFilePath;tmpRemote.CurrentViewerMediaType='video';this._currentPath=pFilePath;this._frameData=null;this._selectedFrameIndex=-1;this._customFrames=[];this._selectionModeActive=false;this._isSelectingRange=false;this._isDraggingTimeline=false;this._draggingHandle=null;// Apply passed-in selection range, or reset
|
|
11378
11412
|
// _selectionFromCaller prevents _loadSavedCustomFrames from
|
|
11379
11413
|
// overwriting an explicit selection (e.g. when opening a saved clip)
|
|
11380
11414
|
if(typeof pSelectionStart==='number'&&pSelectionStart>=0&&typeof pSelectionEnd==='number'&&pSelectionEnd>=0){this._selectionStartTime=pSelectionStart;this._selectionEndTime=pSelectionEnd;this._selectionFromCaller=true;}else{this._selectionStartTime=-1;this._selectionEndTime=-1;this._selectionFromCaller=false;}// Clean up any window-level event listeners from previous session
|
|
@@ -11383,7 +11417,7 @@ this._cleanupWindowListeners();// Update the hash. Replace (not push) when comi
|
|
|
11383
11417
|
let tmpFragProvider=this._getProvider();let tmpFragId=tmpFragProvider?tmpFragProvider.getFragmentIdentifier(pFilePath):pFilePath;let tmpNewHash='#/explore/'+tmpFragId;let tmpCurrentHash=window.location.hash||'';if(tmpCurrentHash.indexOf('#/view/')===0){history.replaceState(null,'',tmpNewHash);}else{window.location.hash=tmpNewHash;}// Show viewer container, hide gallery
|
|
11384
11418
|
let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container');if(tmpGalleryContainer)tmpGalleryContainer.style.display='none';if(tmpViewerContainer)tmpViewerContainer.style.display='block';let tmpFileName=pFilePath.replace(/^.*\//,'');// Build initial UI with loading state
|
|
11385
11419
|
let tmpHTML='<div class="retold-remote-vex">';// Header
|
|
11386
|
-
tmpHTML+='<div class="retold-remote-vex-header">';tmpHTML+='<button class="retold-remote-vex-nav-btn" onclick="pict.views[\'RetoldRemote-VideoExplorer\'].goBack()" title="Back
|
|
11420
|
+
let tmpCapabilities=tmpRemote.ServerCapabilities||{};tmpHTML+='<div class="retold-remote-vex-header">';tmpHTML+='<button class="retold-remote-vex-nav-btn" onclick="pict.views[\'RetoldRemote-VideoExplorer\'].goBack()" title="Back (Esc)">← Back</button>';tmpHTML+='<div class="retold-remote-vex-title">Video Explorer — '+this._getFmt().escapeHTML(tmpFileName)+'</div>';tmpHTML+='<div class="retold-remote-vex-actions">';tmpHTML+='<button class="retold-remote-vex-action-btn" onclick="pict.views[\'RetoldRemote-VideoExplorer\'].playInBrowser()" title="Play in browser (Space)">▶ Play</button>';tmpHTML+='<button class="retold-remote-vex-action-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._streamWithVLC()" title="Stream with VLC (v)">▶ VLC</button>';if(tmpCapabilities.ffmpeg||tmpCapabilities.ffprobe){tmpHTML+='<button class="retold-remote-vex-action-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].showExplorer(pict.views[\'RetoldRemote-VideoExplorer\']._currentPath)" title="Explore audio track">♫ Audio</button>';}tmpHTML+='</div>';tmpHTML+='</div>';// Info bar (populated after frames load)
|
|
11387
11421
|
tmpHTML+='<div class="retold-remote-vex-info" id="RetoldRemote-VEX-Info" style="display:none;"></div>';// Controls bar
|
|
11388
11422
|
tmpHTML+='<div class="retold-remote-vex-controls" id="RetoldRemote-VEX-Controls" style="display:none;">';tmpHTML+='<label>Frames:</label>';tmpHTML+='<select id="RetoldRemote-VEX-FrameCount" onchange="pict.views[\'RetoldRemote-VideoExplorer\'].onFrameCountChange(this.value)">';tmpHTML+='<option value="10"'+(this._frameCount===10?' selected':'')+'>10</option>';tmpHTML+='<option value="20"'+(this._frameCount===20?' selected':'')+'>20</option>';tmpHTML+='<option value="40"'+(this._frameCount===40?' selected':'')+'>40</option>';tmpHTML+='<option value="60"'+(this._frameCount===60?' selected':'')+'>60</option>';tmpHTML+='<option value="100"'+(this._frameCount===100?' selected':'')+'>100</option>';tmpHTML+='</select>';tmpHTML+='<label style="display:inline-flex;align-items:center;gap:4px;cursor:pointer;">';tmpHTML+='<input type="checkbox" id="RetoldRemote-VEX-FullRes"'+(this._fullResFrames?' checked':'')+' onchange="pict.views[\'RetoldRemote-VideoExplorer\'].onFullResChange(this.checked)">';tmpHTML+='Full Res Frames</label>';tmpHTML+='<button class="retold-remote-vex-refresh-btn" onclick="pict.views[\'RetoldRemote-VideoExplorer\'].refresh()">Refresh</button>';tmpHTML+='<span style="border-left:1px solid var(--retold-border);height:20px;margin:0 4px;"></span>';tmpHTML+='<button class="retold-remote-vex-select-btn" id="RetoldRemote-VEX-SelectBtn" onclick="pict.views[\'RetoldRemote-VideoExplorer\'].toggleSelectionMode()">Select Range</button>';tmpHTML+='<span class="retold-remote-vex-selection-info" id="RetoldRemote-VEX-SelectionInfo" style="display:none;"></span>';tmpHTML+='<button class="retold-remote-vex-clear-btn" id="RetoldRemote-VEX-ClearBtn" style="display:none;" onclick="pict.views[\'RetoldRemote-VideoExplorer\'].clearSelection()">Clear</button>';tmpHTML+='<span id="RetoldRemote-VEX-GenerateControls" style="display:none;">';tmpHTML+='<span style="border-left:1px solid var(--retold-border);height:20px;margin:0 2px;"></span>';tmpHTML+='<select class="retold-remote-vex-range-frame-select" id="RetoldRemote-VEX-RangeFrameCount">';tmpHTML+='<option value="3">3</option>';tmpHTML+='<option value="5" selected>5</option>';tmpHTML+='<option value="10">10</option>';tmpHTML+='<option value="20">20</option>';tmpHTML+='</select>';tmpHTML+='<button class="retold-remote-vex-generate-btn" onclick="pict.views[\'RetoldRemote-VideoExplorer\'].generateSelectionFrames()">Generate Frames</button>';tmpHTML+='<span style="border-left:1px solid var(--retold-border);height:20px;margin:0 2px;"></span>';tmpHTML+='<button class="retold-remote-vex-save-btn" onclick="pict.views[\'RetoldRemote-VideoExplorer\'].saveSelectionToCollection()" title="Save segment to collection (s)">Save Segment</button>';tmpHTML+='</span>';tmpHTML+='</div>';// Body (loading initially)
|
|
11389
11423
|
tmpHTML+='<div class="retold-remote-vex-body" id="RetoldRemote-VEX-Body">';tmpHTML+='<div class="retold-remote-vex-loading">';tmpHTML+='<div class="retold-remote-vex-loading-spinner"></div>';tmpHTML+='Extracting frames from video...';tmpHTML+='</div>';tmpHTML+='</div>';// Timeline bar (populated after frames load)
|
|
@@ -11450,8 +11484,10 @@ let tmpTimeline=document.getElementById('RetoldRemote-VEX-Timeline');if(tmpTimel
|
|
|
11450
11484
|
// Navigation
|
|
11451
11485
|
// -----------------------------------------------------------------
|
|
11452
11486
|
/**
|
|
11453
|
-
* Navigate back to the
|
|
11454
|
-
*/goBack(){this._cleanupWindowListeners();
|
|
11487
|
+
* Navigate back to the gallery / file listing.
|
|
11488
|
+
*/goBack(){this._cleanupWindowListeners();let tmpNav=this.pict.providers['RetoldRemote-GalleryNavigation'];if(tmpNav){tmpNav.closeViewer();}}/**
|
|
11489
|
+
* Leave the video explorer and play the video in the browser viewer.
|
|
11490
|
+
*/playInBrowser(){this._cleanupWindowListeners();let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showMedia(this._currentPath,'video');tmpViewer.playVideo();}}/**
|
|
11455
11491
|
* Show an error message.
|
|
11456
11492
|
*
|
|
11457
11493
|
* @param {string} pMessage - Error message
|