diff --git a/inc/processJsonPost.js b/inc/processJsonPost.js index d57711d..f54efa1 100644 --- a/inc/processJsonPost.js +++ b/inc/processJsonPost.js @@ -1,5 +1,6 @@ const compilePostComments = require('./compilePostComments.js')(); const procPostMedia = require('./processPostMedia.js')(); +const config = require('../config'); async function processReplies(data, post_id, depth, user_preferences) { let return_replies = []; @@ -399,8 +400,98 @@ async function finalizeJsonPost( return { post_data: post_data, comments: comments_html }; } +async function getPostItem(post_json, req) { + let protocol = config.https_enabled || config.api_force_https ? 'https' : 'http'; + let thumbnail = ''; + let post_image = ''; + let is_self_link = false; + let valid_reddit_self_domains = ['reddit.com']; + + if (post_json.domain) { + let tld = post_json.domain.split('self.'); + if (tld.length > 1) { + if (!tld[1].includes('.')) { + is_self_link = true; + post_json.url = teddifyUrl(post_json.url); + } + } + if ( + config.valid_media_domains.includes(post_json.domain) || + valid_reddit_self_domains.includes(post_json.domain) + ) { + is_self_link = true; + post_json.url = teddifyUrl(post_json.url); + } + } + + if (post_json.preview && post_json.thumbnail !== 'self') { + if (!post_json.url.startsWith('/r/') && isGif(post_json.url)) { + let s = await downloadAndSave(post_json.thumbnail, 'thumb_'); + thumbnail = `${protocol}://${config.domain}${s}`; + } else { + if (post_json.preview.images[0].resolutions[0]) { + let s = await downloadAndSave( + post_json.preview.images[0].resolutions[0].url, + 'thumb_' + ); + thumbnail = `${protocol}://${config.domain}${s}`; + if (!isGif(post_json.url) && !post_json.post_hint.includes(':video')) { + s = await downloadAndSave(post_json.preview.images[0].source.url); + post_image = `${protocol}://${config.domain}${s}`; + } + } + } + } + + post_json.permalink = `${protocol}://${config.domain}${post_json.permalink}`; + + if (is_self_link) post_json.url = post_json.permalink; + + if (req.query.hasOwnProperty('full_thumbs')) { + if (!post_image) post_image = thumbnail; + + thumbnail = post_image; + } + + let enclosure = ''; + if (thumbnail != '') { + let mime = ''; + let ext = thumbnail.split('.').pop(); + if (ext === 'png') mime = 'image/png'; + else mime = 'image/jpeg'; + enclosure = ``; + } + + let append_desc_html = `
[link] [comments]`; + + return ` + + ${post_json.title} + ${post_json.author} + ${post_json.created} + ${new Date( + post_json.created_utc * 1000 + ).toGMTString()} + ${post_json.domain} + ${post_json.id} + ${thumbnail} + ${enclosure} + ${post_json.permalink} + ${post_json.url} + + ${post_json.num_comments} + ${post_json.ups} + ${post_json.stickied} + ${is_self_link} + + `; +} + module.exports = { processReplies, processJsonPost, finalizeJsonPost, + getPostItem }; diff --git a/inc/teddit_api/handlePost.js b/inc/teddit_api/handlePost.js new file mode 100644 index 0000000..ea74cd5 --- /dev/null +++ b/inc/teddit_api/handlePost.js @@ -0,0 +1,88 @@ +const { processJsonPost, getPostItem } = require('../processJsonPost'); + +module.exports = function () { + const config = require('../../config'); + this.handleTedditApiPost = async ( + json, + req, + res, + from, + api_type, + api_target + ) => { + if (!config.api_enabled) { + res.setHeader('Content-Type', 'application/json'); + let msg = { + info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.', + }; + return res.end(JSON.stringify(msg)); + } + + console.log('Teddit API request - post'); + let _json = json; // Keep the original json + if (from === 'redis') json = JSON.parse(json); + + if (api_type === 'rss') { + let protocol = config.https_enabled || config.api_force_https ? 'https' : 'http'; + let items = ''; + + let post = json[0].data.children[0].data; + let comments = json[1].data.children; + + items += await getPostItem(post, req); + + for (var i = 0; i < comments.length; i++) { + let comment = comments[i].data; + let kind = comments[i].kind; + + let title = `/u/${comment.author} on ${post.title}`; + + comment.permalink = `${protocol}://${config.domain}${comment.permalink}`; + + if (kind !== 'more') { + items += ` + + ${title} + ${comment.author} + ${comment.created} + ${new Date( + comment.created_utc * 1000 + ).toGMTString()} + ${comment.id} + ${comment.permalink} + + ${comment.ups} + + `; + } + } + + let xml_output = ` + + + + ${post.title} + ${post.url} + ${items} + + `; + res.setHeader('Content-Type', 'application/rss+xml'); + return res.end(xml_output); + } else { + res.setHeader('Content-Type', 'application/json'); + if (api_target === 'reddit') { + return res.end(JSON.stringify(json)); + } else { + let processed_json = await processJsonPost( + json, + true, + req.cookies + ); + + return res.end(JSON.stringify(processed_json)); + } + } + }; +}; diff --git a/routes/subreddit.js b/routes/subreddit.js index 5891d35..68143fe 100644 --- a/routes/subreddit.js +++ b/routes/subreddit.js @@ -10,6 +10,7 @@ const processSubredditAbout = require('../inc/processSubredditAbout.js'); const processSearchResults = require('../inc/processSearchResults.js'); const processJsonSubreddit = require('../inc/processJsonSubreddit.js'); const tedditApiSubreddit = require('../inc/teddit_api/handleSubreddit.js')(); +const tedditApiPost = require('../inc/teddit_api/handlePost.js')(); const processMoreComments = require('../inc/processMoreComments.js'); const processJsonSubredditsExplore = require('../inc/processSubredditsExplore.js'); @@ -605,6 +606,14 @@ subredditRoutes.get( let viewing_comment = false; let comment_ids = req.query.comment_ids; let context = parseInt(req.query.context); + let api_req = req.query.api; + let api_type = req.query.type; + let api_target = req.query.target; + + if (req.query.hasOwnProperty('api')) api_req = true; + else api_req = false; + + let raw_json = api_req && req.query.raw_json == '1' ? 1 : 0; if (req.params.comment_id) { comment_id = `${req.params.comment_id}/`; @@ -637,7 +646,7 @@ subredditRoutes.get( let comments_url = `/r/${subreddit}/comments/${id}/${snippet}/${comment_id}`; let post_url = `/r/${subreddit}/comments/${id}/${snippet}/`; - let comments_key = `${comments_url}:sort:${sortby}`; + let comments_key = `${comments_url}:sort:${sortby}:raw_json:${raw_json}`; redis.get(comments_key, (error, json) => { if (error) { @@ -654,57 +663,68 @@ subredditRoutes.get( if (json) { console.log(`Got ${comments_url} key from redis.`); (async () => { - let parsed = false; - let more_comments = null; - if (comment_ids) { - let key = `${post_url}:morechildren:comment_ids:${comment_ids}`; - more_comments = await processMoreComments( - fetch, - redis, - post_url, - comment_ids, - id + if (api_req) { + return handleTedditApiPost( + json, + req, + res, + 'redis', + api_type, + api_target ); + } else { + let parsed = false; + let more_comments = null; + if (comment_ids) { + let key = `${post_url}:morechildren:comment_ids:${comment_ids}`; + more_comments = await processMoreComments( + fetch, + redis, + post_url, + comment_ids, + id + ); - if (more_comments === false) { - return res.redirect(post_url); - } else { - json = JSON.parse(json); - json[1].data.children = more_comments; - parsed = true; + if (more_comments === false) { + return res.redirect(post_url); + } else { + json = JSON.parse(json); + json[1].data.children = more_comments; + parsed = true; + } } - } - let processed_json = await processJsonPost(json, parsed, req.cookies); - let finalized_json = await finalizeJsonPost( - processed_json, - id, - post_url, - more_comments, - viewing_comment, - req.cookies - ); - return res.render('post', { - post: finalized_json.post_data, - comments: finalized_json.comments, - viewing_comment: viewing_comment, - post_url: post_url, - subreddit: subreddit, - sortby: sortby, - user_preferences: req.cookies, - instance_nsfw_enabled: config.nsfw_enabled, - instance_videos_muted: config.videos_muted, - post_media_max_heights: config.post_media_max_heights, - redis_key: comments_key, - instance_config: config, - }); + let processed_json = await processJsonPost(json, parsed, req.cookies); + let finalized_json = await finalizeJsonPost( + processed_json, + id, + post_url, + more_comments, + viewing_comment, + req.cookies + ); + return res.render('post', { + post: finalized_json.post_data, + comments: finalized_json.comments, + viewing_comment: viewing_comment, + post_url: post_url, + subreddit: subreddit, + sortby: sortby, + user_preferences: req.cookies, + instance_nsfw_enabled: config.nsfw_enabled, + instance_videos_muted: config.videos_muted, + post_media_max_heights: config.post_media_max_heights, + redis_key: comments_key, + instance_config: config, + }); + } })(); } else { let url = ''; if (config.use_reddit_oauth) - url = `https://oauth.reddit.com${comments_url}?api_type=json&sort=${sortby}&context=${context}`; + url = `https://oauth.reddit.com${comments_url}?api_type=json&sort=${sortby}&context=${context}&raw_json=${raw_json}`; else - url = `https://reddit.com${comments_url}.json?api_type=json&sort=${sortby}&context=${context}`; + url = `https://reddit.com${comments_url}.json?api_type=json&sort=${sortby}&context=${context}&raw_json=${raw_json}`; fetch(encodeURI(url), redditApiGETHeaders()) .then((result) => { @@ -730,51 +750,62 @@ subredditRoutes.get( `Fetched the JSON from reddit.com${comments_url}.` ); (async () => { - let more_comments = null; - if (comment_ids) { - let key = `${post_url}:morechildren:comment_ids:${comment_ids}`; - more_comments = await processMoreComments( - fetch, - redis, - post_url, - comment_ids, - id + if (api_req) { + return handleTedditApiPost( + json, + req, + res, + 'from_online', + api_type, + api_target ); + } else { + let more_comments = null; + if (comment_ids) { + let key = `${post_url}:morechildren:comment_ids:${comment_ids}`; + more_comments = await processMoreComments( + fetch, + redis, + post_url, + comment_ids, + id + ); - if (more_comments === false) { - return res.redirect(post_url); - } else { - json[1].data.children = more_comments; + if (more_comments === false) { + return res.redirect(post_url); + } else { + json[1].data.children = more_comments; + } } - } - let processed_json = await processJsonPost( - json, - true, - req.cookies - ); - let finalized_json = await finalizeJsonPost( - processed_json, - id, - post_url, - more_comments, - viewing_comment, - req.cookies - ); - return res.render('post', { - post: finalized_json.post_data, - comments: finalized_json.comments, - viewing_comment: viewing_comment, - post_url: post_url, - subreddit: subreddit, - sortby: sortby, - user_preferences: req.cookies, - instance_nsfw_enabled: config.nsfw_enabled, - instance_videos_muted: config.videos_muted, - post_media_max_heights: config.post_media_max_heights, - redis_key: comments_key, - instance_config: config, - }); + let processed_json = await processJsonPost( + json, + true, + req.cookies + ); + let finalized_json = await finalizeJsonPost( + processed_json, + id, + post_url, + more_comments, + viewing_comment, + req.cookies + ); + return res.render('post', { + post: finalized_json.post_data, + comments: finalized_json.comments, + viewing_comment: viewing_comment, + post_url: post_url, + subreddit: subreddit, + sortby: sortby, + user_preferences: req.cookies, + instance_nsfw_enabled: config.nsfw_enabled, + instance_videos_muted: config.videos_muted, + post_media_max_heights: config.post_media_max_heights, + redis_key: comments_key, + instance_config: config, + }); + } })(); } }