94 lines
2.9 KiB
JavaScript
94 lines
2.9 KiB
JavaScript
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 }
|