full
This commit is contained in:
93
utils/instagramMediaImport.js
Normal file
93
utils/instagramMediaImport.js
Normal file
@@ -0,0 +1,93 @@
|
||||
const axios = require('axios')
|
||||
const PostModel = require('../models/PostModel')
|
||||
const InstagramImportedPostModel = require('../models/InstagramImportedPostModel')
|
||||
const { saveBase64PostFiles } = require('./postBase64Files')
|
||||
const { resolvePostModerationStatus } = require('./contentModeration')
|
||||
const { savePostWithModerationStatus } = require('./postStatusSave')
|
||||
const path = require('path')
|
||||
|
||||
const imageDir = path.join(__dirname, '../storage/posts/images')
|
||||
const videoDir = path.join(__dirname, '../storage/posts/videos')
|
||||
|
||||
/**
|
||||
* CAROUSEL_ALBUM items need a second Graph API call (children edge) to resolve
|
||||
* each slide's own media_url — intentionally out of scope for v1, single
|
||||
* image/video posts only.
|
||||
*/
|
||||
const SUPPORTED_MEDIA_TYPES = ['IMAGE', 'VIDEO']
|
||||
|
||||
async function downloadMediaAsFile (mediaUrl, mediaType) {
|
||||
const response = await axios.get(mediaUrl, {
|
||||
responseType: 'arraybuffer',
|
||||
timeout: 20000,
|
||||
})
|
||||
const contentType =
|
||||
response.headers['content-type'] ||
|
||||
(mediaType === 'VIDEO' ? 'video/mp4' : 'image/jpeg')
|
||||
const base64Data = `data:${contentType};base64,${Buffer.from(response.data).toString('base64')}`
|
||||
const ext = mediaType === 'VIDEO' ? 'mp4' : 'jpg'
|
||||
|
||||
return {
|
||||
data: base64Data,
|
||||
type: contentType,
|
||||
name: `instagram-import.${ext}`,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports one Instagram media item as a Modstagram post, if not already imported
|
||||
* and if its type is supported. Shared by the manual/automatic import endpoints
|
||||
* and the recurring sync cron — same dedup + storage + moderation path either way.
|
||||
* Returns the created post, or null if the item was skipped (already imported /
|
||||
* unsupported type).
|
||||
*/
|
||||
async function importInstagramMedia (user, mediaItem) {
|
||||
if (!SUPPORTED_MEDIA_TYPES.includes(mediaItem.media_type)) {
|
||||
return null
|
||||
}
|
||||
if (!mediaItem.media_url) {
|
||||
return null
|
||||
}
|
||||
|
||||
const alreadyImported = await InstagramImportedPostModel.findOne({
|
||||
user: user._id,
|
||||
instagram_media_id: String(mediaItem.id),
|
||||
})
|
||||
if (alreadyImported) {
|
||||
return null
|
||||
}
|
||||
|
||||
const file = await downloadMediaAsFile(mediaItem.media_url, mediaItem.media_type)
|
||||
const [savedFile] = await saveBase64PostFiles([file], imageDir, videoDir)
|
||||
|
||||
const filePaths = [
|
||||
{
|
||||
path: savedFile.path,
|
||||
type: savedFile.mimetype.startsWith('video/') ? 'video' : 'image',
|
||||
},
|
||||
]
|
||||
|
||||
const captionText = mediaItem.caption ? String(mediaItem.caption).trim().slice(0, 2000) : null
|
||||
const moderation = resolvePostModerationStatus({ caption: captionText, filePaths })
|
||||
if (moderation.status === 'reject') {
|
||||
return null
|
||||
}
|
||||
|
||||
const post = await savePostWithModerationStatus(PostModel, {
|
||||
user_id: user._id,
|
||||
caption: captionText,
|
||||
files: filePaths,
|
||||
type: filePaths[0].type,
|
||||
status: moderation.status,
|
||||
})
|
||||
|
||||
await InstagramImportedPostModel.create({
|
||||
user: user._id,
|
||||
instagram_media_id: String(mediaItem.id),
|
||||
post: post._id,
|
||||
})
|
||||
|
||||
return post
|
||||
}
|
||||
|
||||
module.exports = { importInstagramMedia, SUPPORTED_MEDIA_TYPES }
|
||||
Reference in New Issue
Block a user