@@ -11,7 +11,7 @@ export default async function ({ feature, console }) {
1111 } )
1212
1313 let openPopup = document . createElement ( "button" ) ;
14-
14+
1515 ScratchTools . waitForElements ( ".preview .inner .flex-row.action-buttons" , async function ( row ) {
1616 if ( row . querySelector ( ".ste-video-recorder-open" ) ) return ;
1717 openPopup = document . createElement ( "button" ) ;
@@ -20,8 +20,9 @@ export default async function ({ feature, console }) {
2020 row . insertAdjacentElement ( "afterbegin" , openPopup ) ;
2121 openPopup . addEventListener ( 'click' , ( ) => {
2222 document . body . append ( popup )
23- } )
23+ } )
2424 } )
25+
2526 ScratchTools . waitForElements ( ".menu-bar_account-info-group_MeJZP" , async function ( row ) {
2627 if ( row . querySelector ( ".ste-video-recorder-open" ) ) return ;
2728 openPopup = document . createElement ( "div" ) ;
@@ -32,7 +33,7 @@ export default async function ({ feature, console }) {
3233 row . insertAdjacentElement ( "afterbegin" , openPopup ) ;
3334 openPopup . addEventListener ( 'click' , ( ) => {
3435 document . body . append ( popup )
35- } )
36+ } )
3637 } )
3738
3839 let popup = document . createElement ( "div" ) ;
@@ -45,7 +46,8 @@ export default async function ({ feature, console }) {
4546 let downloadButton = popup . querySelector ( ".downloadButton" ) ;
4647 let lastDownloadFunction = ( ) => { }
4748 let mimeType = popup . querySelector ( "select" ) ;
48-
49+ let microphoneCheckbox = popup . querySelector ( ".microphoneCheckbox" ) ;
50+ let desktopSoundCheckbox = popup . querySelector ( ".desktopSoundCheckbox" ) ;
4951
5052 closeButton . addEventListener ( 'click' , ( ) => {
5153 document . querySelector ( ".STE-ReactModalPortal" ) . remove ( )
@@ -58,20 +60,79 @@ export default async function ({ feature, console }) {
5860
5961 const canvas = feature . traps . vm . renderer . canvas ;
6062 const preview = popup . querySelector ( "video" )
61- const projectTitle = document . querySelector ( "input.inplace-input" ) || document . querySelector ( "input.project-title-input_title-field_en5Gd" )
62- // document.querySelector(".menu-bar_account-info-group_MeJZP").append(preview)
63+
64+ await new Promise ( async ( resolve , reject ) => {
65+ ( async ( ) => {
66+ const rem = await ScratchTools . waitForElement ( "input.inplace-input" )
67+ resolve ( rem ) ;
68+ } ) ( ) ;
69+ ( async ( ) => {
70+ const rem = await ScratchTools . waitForElement ( "input.project-title-input_title-field_en5Gd" )
71+ resolve ( rem ) ;
72+ } ) ( ) ;
73+ ( async ( ) => {
74+ const rem = await ScratchTools . waitForElement ( ".project-title" )
75+ resolve ( rem ) ;
76+ } ) ( ) ;
77+ } )
78+
79+ let projectTitle = document . querySelector ( "input.inplace-input" ) || document . querySelector ( "input.project-title-input_title-field_en5Gd" ) || document . querySelector ( ".project-title" ) ;
80+
81+ ScratchTools . waitForElements ( "input.inplace-input" , async function ( _projectTitle ) {
82+ projectTitle = _projectTitle
83+ } )
84+
85+ ScratchTools . waitForElements ( "input.project-title-input_title-field_en5Gd" , async function ( _projectTitle ) {
86+ projectTitle = _projectTitle
87+ } )
88+
89+ ScratchTools . waitForElements ( ".project-title" , async function ( _projectTitle ) {
90+ projectTitle = _projectTitle
91+ } )
92+
6393
6494 let mediaRecorder ;
6595 let recordedChunks = [ ] ;
66- // console.log(startButton)
67- // Start recording
68- startButton . addEventListener ( 'click' , ( ) => {
96+
97+ startButton . addEventListener ( 'click' , async ( ) => {
6998 startButton . classList . add ( "STE-hide-button" ) ;
7099 stopButton . classList . remove ( "STE-hide-button" ) ;
71100
72101 // Capture the canvas element as a stream
73- const stream = canvas . captureStream ( 30 ) ; // 30 FPS
74- mediaRecorder = new MediaRecorder ( stream ) ;
102+ const canvasStream = canvas . captureStream ( 30 ) ; // 30 FPS
103+
104+ // Get the audio context from the Scratch VM
105+ const audioContext = feature . traps . vm . runtime . audioEngine . audioContext ;
106+ const audioDestination = audioContext . createMediaStreamDestination ( ) ;
107+
108+ if ( microphoneCheckbox . checked ) {
109+ // Capture the microphone audio
110+ let micStream ;
111+ try {
112+ micStream = await navigator . mediaDevices . getUserMedia ( { audio : true } ) ;
113+ } catch ( err ) {
114+ console . error ( "Error capturing microphone audio:" , err ) ;
115+ }
116+
117+ if ( micStream ) {
118+ const micSource = audioContext . createMediaStreamSource ( micStream ) ;
119+ micSource . connect ( audioDestination ) ;
120+ }
121+ }
122+
123+ // Connect the audio engine's output
124+ if ( desktopSoundCheckbox . checked ) {
125+ feature . traps . vm . runtime . audioEngine . inputNode . connect ( audioDestination ) ;
126+ }
127+
128+ // Combine the canvas video track and audio tracks
129+ const combinedStream = new MediaStream ( ) ;
130+ canvasStream . getVideoTracks ( ) . forEach ( track => combinedStream . addTrack ( track ) ) ;
131+ if ( microphoneCheckbox . checked || desktopSoundCheckbox . checked ) {
132+ audioDestination . stream . getAudioTracks ( ) . forEach ( track => combinedStream . addTrack ( track ) ) ;
133+ }
134+
135+ mediaRecorder = new MediaRecorder ( combinedStream ) ;
75136
76137 mediaRecorder . ondataavailable = function ( event ) {
77138 if ( event . data . size > 0 ) {
@@ -85,19 +146,20 @@ export default async function ({ feature, console }) {
85146 } ) ;
86147 preview . src = URL . createObjectURL ( blob ) ;
87148 preview . controls = true ;
88- preview . download = `${ projectTitle . value } .${ mimeType . value } `
89- downloadButton . removeEventListener ( "click" , lastDownloadFunction )
149+ // console.log(projectTitle)
150+ preview . download = `${ projectTitle . value } .${ mimeType . value } ` ;
151+ downloadButton . removeEventListener ( "click" , lastDownloadFunction ) ;
90152 lastDownloadFunction = async ( ) => {
91- const url = URL . createObjectURL ( blob )
92- const a = document . createElement ( 'a' )
93- a . href = url
94- a . download = `${ projectTitle . value } .${ mimeType . value } `
95- document . body . appendChild ( a )
96- a . click ( )
97- document . body . removeChild ( a )
98- URL . revokeObjectURL ( url )
153+ const url = URL . createObjectURL ( blob ) ;
154+ const a = document . createElement ( 'a' ) ;
155+ a . href = url ;
156+ a . download = `${ projectTitle . value } .${ mimeType . value } ` ;
157+ document . body . appendChild ( a ) ;
158+ a . click ( ) ;
159+ document . body . removeChild ( a ) ;
160+ URL . revokeObjectURL ( url ) ;
99161 }
100- downloadButton . addEventListener ( "click" , lastDownloadFunction )
162+ downloadButton . addEventListener ( "click" , lastDownloadFunction ) ;
101163 recordedChunks = [ ] ;
102164 } ;
103165
@@ -106,7 +168,6 @@ export default async function ({ feature, console }) {
106168 stopButton . disabled = false ;
107169 } ) ;
108170
109- // Stop recording
110171 stopButton . addEventListener ( 'click' , ( ) => {
111172 mediaRecorder . stop ( ) ;
112173 startButton . disabled = false ;
0 commit comments