@@ -11,7 +11,7 @@ export default async function ({ feature, console }) {
11
11
} )
12
12
13
13
let openPopup = document . createElement ( "button" ) ;
14
-
14
+
15
15
ScratchTools . waitForElements ( ".preview .inner .flex-row.action-buttons" , async function ( row ) {
16
16
if ( row . querySelector ( ".ste-video-recorder-open" ) ) return ;
17
17
openPopup = document . createElement ( "button" ) ;
@@ -20,8 +20,9 @@ export default async function ({ feature, console }) {
20
20
row . insertAdjacentElement ( "afterbegin" , openPopup ) ;
21
21
openPopup . addEventListener ( 'click' , ( ) => {
22
22
document . body . append ( popup )
23
- } )
23
+ } )
24
24
} )
25
+
25
26
ScratchTools . waitForElements ( ".menu-bar_account-info-group_MeJZP" , async function ( row ) {
26
27
if ( row . querySelector ( ".ste-video-recorder-open" ) ) return ;
27
28
openPopup = document . createElement ( "div" ) ;
@@ -32,7 +33,7 @@ export default async function ({ feature, console }) {
32
33
row . insertAdjacentElement ( "afterbegin" , openPopup ) ;
33
34
openPopup . addEventListener ( 'click' , ( ) => {
34
35
document . body . append ( popup )
35
- } )
36
+ } )
36
37
} )
37
38
38
39
let popup = document . createElement ( "div" ) ;
@@ -45,7 +46,8 @@ export default async function ({ feature, console }) {
45
46
let downloadButton = popup . querySelector ( ".downloadButton" ) ;
46
47
let lastDownloadFunction = ( ) => { }
47
48
let mimeType = popup . querySelector ( "select" ) ;
48
-
49
+ let microphoneCheckbox = popup . querySelector ( ".microphoneCheckbox" ) ;
50
+ let desktopSoundCheckbox = popup . querySelector ( ".desktopSoundCheckbox" ) ;
49
51
50
52
closeButton . addEventListener ( 'click' , ( ) => {
51
53
document . querySelector ( ".STE-ReactModalPortal" ) . remove ( )
@@ -58,20 +60,79 @@ export default async function ({ feature, console }) {
58
60
59
61
const canvas = feature . traps . vm . renderer . canvas ;
60
62
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
+
63
93
64
94
let mediaRecorder ;
65
95
let recordedChunks = [ ] ;
66
- // console.log(startButton)
67
- // Start recording
68
- startButton . addEventListener ( 'click' , ( ) => {
96
+
97
+ startButton . addEventListener ( 'click' , async ( ) => {
69
98
startButton . classList . add ( "STE-hide-button" ) ;
70
99
stopButton . classList . remove ( "STE-hide-button" ) ;
71
100
72
101
// 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 ) ;
75
136
76
137
mediaRecorder . ondataavailable = function ( event ) {
77
138
if ( event . data . size > 0 ) {
@@ -85,19 +146,20 @@ export default async function ({ feature, console }) {
85
146
} ) ;
86
147
preview . src = URL . createObjectURL ( blob ) ;
87
148
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 ) ;
90
152
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 ) ;
99
161
}
100
- downloadButton . addEventListener ( "click" , lastDownloadFunction )
162
+ downloadButton . addEventListener ( "click" , lastDownloadFunction ) ;
101
163
recordedChunks = [ ] ;
102
164
} ;
103
165
@@ -106,7 +168,6 @@ export default async function ({ feature, console }) {
106
168
stopButton . disabled = false ;
107
169
} ) ;
108
170
109
- // Stop recording
110
171
stopButton . addEventListener ( 'click' , ( ) => {
111
172
mediaRecorder . stop ( ) ;
112
173
startButton . disabled = false ;
0 commit comments