@@ -123,15 +123,16 @@ export class RedditProvider extends SocialAbstract implements SocialProvider {
123123 } ;
124124 }
125125
126- private async uploadFileToReddit ( accessToken : string , path : string ) {
126+ private async uploadFileToReddit (
127+ accessToken : string ,
128+ path : string
129+ ) : Promise < { url : string ; assetId ?: string } > {
127130 const mimeType = lookup ( path ) ;
128131 const formData = new FormData ( ) ;
129132 formData . append ( 'filepath' , path . split ( '/' ) . pop ( ) ) ;
130133 formData . append ( 'mimetype' , mimeType || 'application/octet-stream' ) ;
131134
132- const {
133- args : { action, fields } ,
134- } = await (
135+ const responseData = await (
135136 await this . fetch (
136137 'https://oauth.reddit.com/api/media/asset' ,
137138 {
@@ -147,6 +148,13 @@ export class RedditProvider extends SocialAbstract implements SocialProvider {
147148 )
148149 ) . json ( ) ;
149150
151+ const {
152+ args : { action, fields } ,
153+ asset,
154+ } = responseData ;
155+
156+ const assetId = asset ?. asset_id || asset ?. id ;
157+
150158 const { data } = await axios . get ( path , {
151159 responseType : 'arraybuffer' ,
152160 } ) ;
@@ -169,7 +177,11 @@ export class RedditProvider extends SocialAbstract implements SocialProvider {
169177 body : upload ,
170178 } ) ;
171179
172- return [ ...( await d . text ( ) ) . matchAll ( / < L o c a t i o n > ( .* ?) < \/ L o c a t i o n > / g) ] [ 0 ] [ 1 ] ;
180+ const url = [
181+ ...( await d . text ( ) ) . matchAll ( / < L o c a t i o n > ( .* ?) < \/ L o c a t i o n > / g) ,
182+ ] [ 0 ] [ 1 ] ;
183+
184+ return { url, assetId } ;
173185 }
174186
175187 async post (
@@ -181,53 +193,159 @@ export class RedditProvider extends SocialAbstract implements SocialProvider {
181193
182194 const valueArray : PostResponse [ ] = [ ] ;
183195 for ( const firstPostSettings of post . settings . subreddit ) {
184- const postData = {
196+ const hasVideo =
197+ post . media ?. some ( ( m ) => m . path . indexOf ( 'mp4' ) > - 1 ) === true ;
198+ const images = ( post . media || [ ] ) . filter (
199+ ( m ) => m . path . indexOf ( 'mp4' ) === - 1
200+ ) ;
201+
202+ // Determine post kind based on actual content
203+ let useGalleryEndpoint = false ;
204+ let kind : string ;
205+
206+ if ( hasVideo ) {
207+ kind = 'video' ;
208+ } else if ( images . length > 1 ) {
209+ useGalleryEndpoint = true ;
210+ kind = 'gallery' ;
211+ } else if ( images . length === 1 ) {
212+ kind = 'image' ;
213+ } else {
214+ kind = firstPostSettings . value . type ;
215+ }
216+
217+ let postData : any = {
185218 api_type : 'json' ,
186219 title : firstPostSettings . value . title || '' ,
187- kind :
188- firstPostSettings . value . type === 'media'
189- ? post . media [ 0 ] . path . indexOf ( 'mp4' ) > - 1
190- ? 'video'
191- : 'image'
192- : firstPostSettings . value . type ,
220+ kind,
221+ text : post . message ,
222+ sr : firstPostSettings . value . subreddit ,
193223 ...( firstPostSettings . value . flair
194224 ? { flair_id : firstPostSettings . value . flair . id }
195225 : { } ) ,
196- ...( firstPostSettings . value . type === 'link'
197- ? {
198- url : firstPostSettings . value . url ,
199- }
200- : { } ) ,
201- ...( firstPostSettings . value . type === 'media'
202- ? {
203- url : await this . uploadFileToReddit (
204- accessToken ,
205- post . media [ 0 ] . path
206- ) ,
207- ...( post . media [ 0 ] . path . indexOf ( 'mp4' ) > - 1
208- ? {
209- video_poster_url : await this . uploadFileToReddit (
210- accessToken ,
211- post . media [ 0 ] . thumbnail
212- ) ,
213- }
214- : { } ) ,
215- }
216- : { } ) ,
217- text : post . message ,
218- sr : firstPostSettings . value . subreddit ,
219226 } ;
220227
221- const all = await (
222- await this . fetch ( 'https://oauth.reddit.com/api/submit' , {
223- method : 'POST' ,
224- headers : {
225- Authorization : `Bearer ${ accessToken } ` ,
226- 'Content-Type' : 'application/x-www-form-urlencoded' ,
227- } ,
228- body : new URLSearchParams ( postData ) ,
229- } )
230- ) . json ( ) ;
228+ // Handle different post types
229+ if ( firstPostSettings . value . type === 'link' ) {
230+ postData . url = firstPostSettings . value . url ;
231+ } else if ( firstPostSettings . value . type === 'media' ) {
232+ if ( kind === 'video' ) {
233+ const videoUpload = await this . uploadFileToReddit (
234+ accessToken ,
235+ post . media [ 0 ] . path
236+ ) ;
237+ postData . url = videoUpload . url ;
238+
239+ if ( post . media [ 0 ] . thumbnail ) {
240+ const thumbnailUpload = await this . uploadFileToReddit (
241+ accessToken ,
242+ post . media [ 0 ] . thumbnail
243+ ) ;
244+ postData . video_poster_url = thumbnailUpload . url ;
245+ }
246+ } else if ( ! useGalleryEndpoint ) {
247+ const imageUpload = await this . uploadFileToReddit (
248+ accessToken ,
249+ post . media [ 0 ] . path
250+ ) ;
251+ postData . url = imageUpload . url ;
252+ }
253+ }
254+
255+ let all : any ;
256+
257+ if ( useGalleryEndpoint && images . length > 1 ) {
258+ // Upload all images for gallery
259+ const uploads : { url : string ; assetId ?: string } [ ] = [ ] ;
260+ for ( let i = 0 ; i < images . length ; i ++ ) {
261+ const uploaded = await this . uploadFileToReddit (
262+ accessToken ,
263+ images [ i ] . path
264+ ) ;
265+ uploads . push ( uploaded ) ;
266+
267+ if ( i < images . length - 1 ) {
268+ await timer ( 1000 ) ;
269+ }
270+ }
271+
272+ const items = uploads
273+ . filter ( ( u ) => u . assetId )
274+ . map ( ( u ) => ( {
275+ media_id : u . assetId ,
276+ caption : '' ,
277+ outbound_url : '' ,
278+ } ) ) ;
279+
280+ if ( items . length < 2 ) {
281+ // Fallback to single image if insufficient asset IDs
282+ const firstImageUpload = await this . uploadFileToReddit (
283+ accessToken ,
284+ images [ 0 ] . path
285+ ) ;
286+ postData = {
287+ api_type : 'json' ,
288+ kind : 'image' ,
289+ sr : firstPostSettings . value . subreddit ,
290+ title : firstPostSettings . value . title || '' ,
291+ url : firstImageUpload . url ,
292+ ...( firstPostSettings . value . flair
293+ ? { flair_id : firstPostSettings . value . flair . id }
294+ : { } ) ,
295+ } ;
296+
297+ all = await (
298+ await this . fetch ( 'https://oauth.reddit.com/api/submit' , {
299+ method : 'POST' ,
300+ headers : {
301+ Authorization : `Bearer ${ accessToken } ` ,
302+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
303+ } ,
304+ body : new URLSearchParams ( postData ) ,
305+ } )
306+ ) . json ( ) ;
307+ } else {
308+ // Submit gallery post
309+ const galleryBody = {
310+ sr : firstPostSettings . value . subreddit ,
311+ title : firstPostSettings . value . title || '' ,
312+ items,
313+ api_type : 'json' ,
314+ resubmit : true ,
315+ sendreplies : true ,
316+ nsfw : false ,
317+ spoiler : false ,
318+ ...( firstPostSettings . value . flair
319+ ? { flair_id : firstPostSettings . value . flair . id }
320+ : { } ) ,
321+ } ;
322+
323+ all = await (
324+ await this . fetch (
325+ 'https://oauth.reddit.com/api/submit_gallery_post.json?raw_json=1' ,
326+ {
327+ method : 'POST' ,
328+ headers : {
329+ Authorization : `Bearer ${ accessToken } ` ,
330+ 'Content-Type' : 'application/json' ,
331+ } ,
332+ body : JSON . stringify ( galleryBody ) ,
333+ }
334+ )
335+ ) . json ( ) ;
336+ }
337+ } else {
338+ all = await (
339+ await this . fetch ( 'https://oauth.reddit.com/api/submit' , {
340+ method : 'POST' ,
341+ headers : {
342+ Authorization : `Bearer ${ accessToken } ` ,
343+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
344+ } ,
345+ body : new URLSearchParams ( postData ) ,
346+ } )
347+ ) . json ( ) ;
348+ }
231349
232350 const { id, name, url } = await new Promise < {
233351 id : string ;
@@ -238,6 +356,11 @@ export class RedditProvider extends SocialAbstract implements SocialProvider {
238356 res ( all . json . data ) ;
239357 }
240358
359+ if ( ! all ?. json ?. data ?. websocket_url ) {
360+ res ( { id : '' , name : '' , url : '' } ) ;
361+ return ;
362+ }
363+
241364 const ws = new WebSocket ( all . json . data . websocket_url ) ;
242365 ws . on ( 'message' , ( data : any ) => {
243366 setTimeout ( ( ) => {
0 commit comments