1+ export default async function ( { feature, console } ) {
2+ ScratchTools . waitForElements (
3+ ".preview .inner .flex-row.action-buttons" ,
4+ async function ( row ) {
5+ if ( feature . redux . getState ( ) ?. preview . projectInfo . author . username !== feature . redux . getState ( ) ?. session ?. session ?. user ?. username ) return ;
6+
7+ if ( row . querySelector ( ".ste-thumbnail" ) ) return ;
8+ let button = document . createElement ( "button" ) ;
9+ button . className = "button action-button ste-thumbnail" ;
10+ button . textContent = "Set Thumbnail" ;
11+ feature . self . hideOnDisable ( button ) ;
12+
13+ let input = document . createElement ( "input" ) ;
14+ input . className = "ste-thumbnail-input" ;
15+ input . style . display = "none" ;
16+ input . type = "file" ;
17+ input . accept = "image/*" ;
18+ input . addEventListener ( "input" , onThumbInput ) ;
19+ document . body . appendChild ( input ) ;
20+
21+ function onThumbInput ( ) {
22+ if ( input . files ?. [ 0 ] ) {
23+ setThumbnail ( input . files [ 0 ] ) ;
24+ }
25+ }
26+
27+ button . addEventListener ( "click" , async function ( ) {
28+ let upload = document . createElement ( "button" ) ;
29+ upload . textContent = "Upload Image or GIF" ;
30+ upload . style . marginRight = ".5rem" ;
31+ upload . addEventListener ( "click" , function ( ) {
32+ input . click ( ) ;
33+ } ) ;
34+
35+ async function getStage ( ) {
36+ return new Promise ( ( resolve ) => {
37+ feature . traps . vm . postIOData ( "video" , {
38+ forceTransparentPreview : true ,
39+ } ) ;
40+ feature . traps . vm . renderer . requestSnapshot ( ( dataURL ) => {
41+ feature . traps . vm . postIOData ( "video" , {
42+ forceTransparentPreview : false ,
43+ } ) ;
44+ resolve ( dataURL ) ;
45+ } ) ;
46+ } ) ;
47+ }
48+
49+ let useStage = document . createElement ( "button" ) ;
50+ useStage . textContent = "Use Stage" ;
51+ useStage . className = "ste-thumbnail-stage" ;
52+ useStage . addEventListener ( "click" , async function ( ) {
53+ function dataURLtoBlob ( dataurl ) {
54+ let arr = dataurl . split ( "," ) ;
55+ let mime = arr [ 0 ] . match ( / : ( .* ?) ; / ) [ 1 ] ;
56+ let bstr = atob ( arr [ 1 ] ) ;
57+ let n = bstr . length ;
58+ let u8arr = new Uint8Array ( n ) ;
59+ while ( n -- ) {
60+ u8arr [ n ] = bstr . charCodeAt ( n ) ;
61+ }
62+ return new Blob ( [ u8arr ] , { type : mime } ) ;
63+ }
64+
65+ let url = await getStage ( )
66+ console . log ( url )
67+ let blob = dataURLtoBlob ( url ) ;
68+
69+ let file = new File ( [ blob ] , "image.png" , { type : "image/png" } ) ;
70+
71+ let dataTransfer = new DataTransfer ( ) ;
72+ dataTransfer . items . add ( file ) ;
73+
74+ input . files = dataTransfer . files ;
75+
76+ onThumbInput ( ) ;
77+ } ) ;
78+
79+ if ( ! feature . traps . gui ( ) . vmStatus . started ) {
80+ useStage . setAttribute ( "disabled" , "" ) ;
81+ }
82+
83+ let modal = ScratchTools . modals . create ( {
84+ title : "Set Thumbnail" ,
85+ description :
86+ "You can set the thumbnail to an image you upload or you can set it to what is currently on the stage. The project needs to have been started already in order to upload from the stage." ,
87+ components : [
88+ {
89+ type : "html" ,
90+ content : upload ,
91+ } ,
92+ {
93+ type : "html" ,
94+ content : useStage ,
95+ } ,
96+ {
97+ type : "html" ,
98+ content : document . createElement ( "br" ) ,
99+ } ,
100+ ] ,
101+ } ) ;
102+
103+ useStage . addEventListener ( "click" , function ( ) {
104+ modal . close ( ) ;
105+ } ) ;
106+
107+ upload . addEventListener ( "click" , function ( ) {
108+ modal . close ( ) ;
109+ } ) ;
110+ } ) ;
111+ row . appendChild ( button ) ;
112+ }
113+ ) ;
114+
115+ async function setThumbnail ( file ) {
116+ let options = {
117+ body : file ,
118+ headers : {
119+ accept : "*/*" ,
120+ "content-type" : file . type ,
121+ "x-csrftoken" : feature . auth . csrf ( ) ,
122+ "x-requested-with" : "XMLHttpRequest" ,
123+ } ,
124+ referrer : window . location . href ,
125+ referrerPolicy : "strict-origin-when-cross-origin" ,
126+ method : "POST" ,
127+ mode : "cors" ,
128+ credentials : "include" ,
129+ } ;
130+
131+ let response = await fetch (
132+ `https://scratch.mit.edu/internalapi/project/thumbnail/${
133+ window . location . pathname . split ( "/" ) [ 2 ]
134+ } /set/`,
135+ options
136+ ) ;
137+
138+ if ( response . ok ) {
139+ ScratchTools . modals . create ( {
140+ title : "Successfully Set Thumbnail" ,
141+ description : "This project's thumbnail has been updated." ,
142+ components : [ ] ,
143+ } ) ;
144+ } else {
145+ ScratchTools . modals . create ( {
146+ title : "Failed to Set Thumbnail" ,
147+ description : "This project's thumbnail was not able to be updated." ,
148+ components : [ ] ,
149+ } ) ;
150+ }
151+ }
152+ }
153+
0 commit comments