@@ -891,6 +891,7 @@ function addVideoPreview(nodeType) {
891891 previewWidget . parentEl . appendChild ( previewWidget . imgEl )
892892 } ) ;
893893}
894+ let copiedPath = undefined
894895function addPreviewOptions ( nodeType ) {
895896 chainCallback ( nodeType . prototype , "getExtraMenuOptions" , function ( _ , options ) {
896897 // The intended way of appending options is returning a list of extra options,
@@ -901,10 +902,12 @@ function addPreviewOptions(nodeType) {
901902
902903 let url = null
903904 if ( previewWidget . videoEl ?. hidden == false && previewWidget . videoEl . src ) {
904- //Use full quality video
905- url = api . apiURL ( '/view?' + new URLSearchParams ( previewWidget . value . params ) ) ;
906- //Workaround for 16bit png: Just do first frame
907- url = url . replace ( '%2503d' , '001' )
905+ if ( [ 'input' , 'output' , 'temp' ] . includes ( previewWidget . value . params . type ) ) {
906+ //Use full quality video
907+ url = api . apiURL ( '/view?' + new URLSearchParams ( previewWidget . value . params ) ) ;
908+ //Workaround for 16bit png: Just do first frame
909+ url = url . replace ( '%2503d' , '001' )
910+ }
908911 } else if ( previewWidget . imgEl ?. hidden == false && previewWidget . imgEl . src ) {
909912 url = previewWidget . imgEl . src ;
910913 url = new URL ( url ) ;
@@ -922,13 +925,42 @@ function addPreviewOptions(nodeType) {
922925 callback : ( ) => {
923926 const a = document . createElement ( "a" ) ;
924927 a . href = url ;
925- a . setAttribute ( "download" , new URLSearchParams ( previewWidget . value . params ) . get ( " filename" ) ) ;
928+ a . setAttribute ( "download" , previewWidget . value . params . filename ) ;
926929 document . body . append ( a ) ;
927930 a . click ( ) ;
928931 requestAnimationFrame ( ( ) => a . remove ( ) ) ;
929932 } ,
930933 }
931934 ) ;
935+ if ( previewWidget . value . params . fullpath ) {
936+ copiedPath = previewWidget . value . params . fullpath
937+ const blob = new Blob ( [ previewWidget . value . params . fullpath ] ,
938+ { type : 'text/plain' } )
939+ optNew . push ( {
940+ content : "Copy output filepath" ,
941+ callback : async ( ) => {
942+ await navigator . clipboard . write ( [
943+ new ClipboardItem ( {
944+ 'text/plain' : blob
945+ } ) ] ) }
946+ } ) ;
947+ }
948+ if ( previewWidget . value . params . workflow ) {
949+ let wParams = { ...previewWidget . value . params ,
950+ filename : previewWidget . value . params . workflow }
951+ let wUrl = api . apiURL ( '/view?' + new URLSearchParams ( wParams ) ) ;
952+ optNew . push ( {
953+ content : "Save workflow image" ,
954+ callback : ( ) => {
955+ const a = document . createElement ( "a" ) ;
956+ a . href = wUrl ;
957+ a . setAttribute ( "download" , previewWidget . value . params . workflow ) ;
958+ document . body . append ( a ) ;
959+ a . click ( ) ;
960+ requestAnimationFrame ( ( ) => a . remove ( ) ) ;
961+ }
962+ } ) ;
963+ }
932964 }
933965 const PauseDesc = ( previewWidget . value . paused ? "Resume" : "Pause" ) + " preview" ;
934966 if ( previewWidget . videoEl . hidden == false ) {
@@ -1043,7 +1075,7 @@ function addFormatWidgets(nodeType) {
10431075 w . options . values = w . type ;
10441076 w . type = "combo" ;
10451077 }
1046- if ( inputData [ 1 ] ?. default ) {
1078+ if ( inputData [ 1 ] ?. default != undefined ) {
10471079 w . value = inputData [ 1 ] . default ;
10481080 }
10491081 if ( w . type == "INT" ) {
@@ -1633,6 +1665,55 @@ app.registerExtension({
16331665 return res
16341666 }
16351667 app . graphToPrompt = graphToPrompt
1668+ //Add a handler for pasting video data
1669+ document . addEventListener ( 'paste' , async ( e ) => {
1670+ if ( ! e . target . classList . contains ( 'litegraph' ) &&
1671+ ! e . target . classList . contains ( 'graph-canvas-container' ) ) {
1672+ return
1673+ }
1674+ let data = e . clipboardData || window . clipboardData
1675+ let filepath = data . getData ( 'text/plain' )
1676+ let video
1677+ for ( const item of data . items ) {
1678+ if ( item . type . startsWith ( 'video/' ) ) {
1679+ video = item
1680+ break
1681+ }
1682+ }
1683+ if ( filepath && copiedPath == filepath ) {
1684+ //Add a Load Video (Path) and populate filepath
1685+ const pastedNode = LiteGraph . createNode ( 'VHS_LoadVideoPath' )
1686+ app . graph . add ( pastedNode )
1687+ pastedNode . pos [ 0 ] = app . canvas . graph_mouse [ 0 ]
1688+ pastedNode . pos [ 1 ] = app . canvas . graph_mouse [ 1 ]
1689+ pastedNode . widgets [ 0 ] . value = filepath
1690+ pastedNode . widgets [ 0 ] . callback ?. ( filepath )
1691+ } else if ( video && false ) {
1692+ //Disabled due to lack of testing
1693+ //Add a Load Video (Upload), then upload the file, then select the file
1694+ const pastedNode = LiteGraph . createNode ( 'VHS_LoadVideo' )
1695+ app . graph . add ( pastedNode )
1696+ pastedNode . pos [ 0 ] = app . canvas . graph_mouse [ 0 ]
1697+ pastedNode . pos [ 1 ] = app . canvas . graph_mouse [ 1 ]
1698+ const pathWidget = pastedNode . widgets [ 0 ]
1699+ //TODO: upload to pasted dir?
1700+ const blob = video . getAsFile ( )
1701+ const resp = await uploadFile ( blob )
1702+ if ( resp . status != 200 ) {
1703+ //upload failed and file can not be added to options
1704+ return ;
1705+ }
1706+ const filename = ( await resp . json ( ) ) . name ;
1707+ pathWidget . options . values . push ( filename ) ;
1708+ pathWidget . value = filename ;
1709+ pathWidget . callback ?. ( filename )
1710+ } else {
1711+ return
1712+ }
1713+ e . preventDefault ( )
1714+ e . stopImmediatePropagation ( )
1715+ return false
1716+ } , true )
16361717 } ,
16371718 async init ( ) {
16381719 if ( app . VHSHelp != helpDOM ) {
0 commit comments