diff --git a/config.js.template b/config.js.template
index b9cd00e..dcab1cf 100644
--- a/config.js.template
+++ b/config.js.template
@@ -2,6 +2,7 @@ const config = {
domain: process.env.DOMAIN || '127.0.0.1', // Or for example 'teddit.net'
reddit_app_id: process.env.REDDIT_APP_ID || 'H6-HjZ5pUPjaFQ', // You should obtain your own Reddit app ID. For testing purposes it's okay to use this project's default app ID. Create your Reddit app here: https://old.reddit.com/prefs/apps/. Make sure to create an "installed app" type of app.
cert_dir: process.env.CERT_DIR || '', // For example '/home/teddit/letsencrypt/live/teddit.net', if you are using https. No trailing slash.
+ flairs_enabled: process.env.FLAIRS_ENABLED !== "true" || true, // Enables the rendering of user and link flairs on teddit
api_enabled: process.env.API_ENABLED !== "true" || true, // Teddit API feature. Might increase loads significantly on your instance.
video_enabled: process.env.VIDEO_ENABLED !== "true" || true,
redis_enabled: process.env.REDIS_ENABLED !== "true" || true, // If disabled, does not cache Reddit API calls
@@ -34,7 +35,7 @@ const config = {
shorts: 60 * 60 * 24 * 31
},
post_comments_sort: 'confidence', // "confidence" is the default sorting in Reddit. Must be one of: confidence, top, new, controversial, old, random, qa, live.
- valid_media_domains: ['preview.redd.it', 'external-preview.redd.it', 'i.redd.it', 'v.redd.it', 'a.thumbs.redditmedia.com', 'b.thumbs.redditmedia.com', 'thumbs.gfycat.com', 'i.ytimg.com'],
+ valid_media_domains: ['preview.redd.it', 'external-preview.redd.it', 'i.redd.it', 'v.redd.it', 'a.thumbs.redditmedia.com', 'b.thumbs.redditmedia.com', 'emoji.redditmedia.com', 'thumbs.gfycat.com', 'i.ytimg.com'],
reddit_api_error_text: `Seems like your instance is either blocked (e.g. due to API rate limiting), reddit is currently down, or your API key is expired and not renewd properly. This can also happen for other reasons.`
};
diff --git a/dist/css/styles.css b/dist/css/styles.css
index 043de90..7bccea4 100644
--- a/dist/css/styles.css
+++ b/dist/css/styles.css
@@ -172,11 +172,6 @@ body.dark #search form input[type="text"] {
background: #0f0f0f;
color: white;
}
-body.dark #links .link .entry .title span.postflair,
-body.dark #post .info .title span.postflair {
- color: #eaeaea;
- background-color: #404040;
-}
a {
color: var(--linkcolor);
text-decoration: none;
@@ -402,16 +397,6 @@ input[type="submit"]:hover,
cursor: pointer;
text-decoration: none;
}
-#links .link .entry .title span.postflair,
-#post .info .title span.postflair {
- display: inline-block;
- border-radius: 4px;
- color: #404040;
- background-color: #e8e8e8;
- font-size: x-small;
- margin-left: 10px;
- padding: 0 2px;
-}
/* SUBREDDIT LINKS */
#links {
float: left;
@@ -994,6 +979,40 @@ input[type="submit"]:hover,
#user .entries .entry a.context {
margin-right: 10px;
}
+/* FLAIR */
+.flair,
+#links .link .entry .title span.flair,
+#post .info .title span.flair {
+ display: inline-block;
+ border-radius: 4px;
+ color: #404040;
+ background-color: #e8e8e8;
+ font-size: x-small;
+ margin-left: 10px;
+ padding: 0 2px;
+}
+body.dark .flair {
+ color: #eaeaea !important;
+ background-color: #404040 !important;
+}
+#post .comments .flair,
+#user .comment .meta .flair {
+ margin-left: 0 !important;
+}
+#links .link .entry .meta p.submitted .flair,
+#user .comment .meta .flair,
+#user .entries p.submitted .flair {
+ margin-right: 4px;
+}
+.flair .emoji {
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: contain;
+ display: inline-block;
+ height: 16px;
+ width: 16px;
+ vertical-align: middle;
+}
/* SIDEBAR */
#sidebar {
float: left;
diff --git a/inc/commons.js b/inc/commons.js
index c04c178..2cea66e 100644
--- a/inc/commons.js
+++ b/inc/commons.js
@@ -1,4 +1,4 @@
-module.exports = function(request, fs) {
+module.exports = function(request, fs) {
const config = require('../config')
this.downloadFile = (url) => {
return new Promise(resolve => {
@@ -51,7 +51,7 @@ module.exports = function(request, fs) {
if(video_exts.includes(file_ext) || !image_exts.includes(file_ext))
url = url.replace(u.host, `${config.domain}/vids`) + '.mp4'
}
-
+
} catch(e) { }
return url
}
@@ -59,7 +59,7 @@ module.exports = function(request, fs) {
this.kFormatter = (num) => {
return Math.abs(num) > 999 ? Math.sign(num)*((Math.abs(num)/1000).toFixed(1)) + 'k' : Math.sign(num)*Math.abs(num)
}
-
+
this.timeDifference = (time) => {
time = parseInt(time) * 1000
let ms_per_minute = 60 * 1000
@@ -121,7 +121,7 @@ module.exports = function(request, fs) {
return r
}
}
-
+
this.toUTCString = (time) => {
let d = new Date();
d.setTime(time*1000);
@@ -165,7 +165,7 @@ module.exports = function(request, fs) {
})
})
}
-
+
this.isGif = (url) => {
try {
url = new URL(url)
@@ -197,4 +197,61 @@ module.exports = function(request, fs) {
return ''
}
}
+
+ this.formatLinkFlair = async (post) => {
+ if (!config.flairs_enabled) {
+ return ''
+ }
+
+ const wrap = (inner) => `${inner}`
+
+ if (post.link_flair_text === null)
+ return ''
+
+ if (post.link_flair_type === 'text')
+ return wrap(post.link_flair_text)
+
+ if (post.link_flair_type === 'richtext') {
+ let flair = ''
+ for (let fragment of post.link_flair_richtext) {
+ if (fragment.e === 'text')
+ flair += fragment.t
+ else if (fragment.e === 'emoji')
+ flair += ``
+ }
+ return wrap(flair)
+ }
+
+ return ''
+ }
+
+ this.formatUserFlair = async (post) => {
+ if (!config.flairs_enabled) {
+ return ''
+ }
+
+ // Generate the entire HTML here for consistency in both pug and HTML
+ const wrap = (inner) => `${inner}`
+
+ if (post.author_flair_text === null)
+ return ''
+
+ if (post.author_flair_type === 'text')
+ return wrap(post.author_flair_text)
+
+ if (post.author_flair_type === 'richtext') {
+ let flair = ''
+ for (let fragment of post.author_flair_richtext) {
+ // `e` seems to mean `type`
+ if (fragment.e === 'text')
+ flair += fragment.t // `t` is the text
+ else if (fragment.e === 'emoji')
+ flair += `` // `u` is the emoji URL
+ }
+ return wrap(flair)
+ }
+
+ return ''
+ }
+
}
diff --git a/inc/compilePostComments.js b/inc/compilePostComments.js
index 3abf2cd..dcab27d 100644
--- a/inc/compilePostComments.js
+++ b/inc/compilePostComments.js
@@ -18,7 +18,7 @@ module.exports = function() {
let moderator = false
let submitter = false
let edited_span = ''
-
+
if(post_author === comments.author) {
classlist.push('submitter')
submitter_link = `[S]`
@@ -48,6 +48,7 @@ module.exports = function() {
${commentAuthor(comments, classlist, submitter && submitter_link, moderator && moderator_badge)}
+
${comments.user_flair}
${ups}
${timeDifference(comments.created)}${edited_span}
@@ -104,7 +105,7 @@ module.exports = function() {
let submitter = false
let ups = ''
let edited_span = ''
-
+
if(post_author === comment.author) {
classlist.push('submitter')
submitter_link = `[S]`
@@ -134,6 +135,7 @@ module.exports = function() {
`
}
next_comment_parent_id = null
-
+
resolve(comments_html)
})()
})
diff --git a/inc/downloadAndSave.js b/inc/downloadAndSave.js
index fdd83e3..d0224db 100644
--- a/inc/downloadAndSave.js
+++ b/inc/downloadAndSave.js
@@ -1,12 +1,12 @@
-module.exports = function(tools) {
+module.exports = function(tools) {
const config = require('../config')
const {spawn} = require('child_process')
const fs = require('fs')
this.downloadAndSave = (url, file_prefix = '', gifmp4, isYouTubeThumbnail) => {
- /**
+ /**
* This function downloads media (video or image) to disk.
* Returns a localized URL
- *
+ *
* For example for images:
* https://external-preview.redd.it/DiaeK_j5fqpBqbatvo7GZzbHNJY2oxEym93B_3.jpg
* =>
@@ -32,21 +32,23 @@ module.exports = function(tools) {
if(gifmp4) {
file_ext = 'mp4'
} else {
- if(!pathname.includes('.')) {
- /**
+ if (file_prefix === 'flair_') {
+ // Flair emojis end in the name without a file extension
+ file_ext = 'png'
+ } else if(!pathname.includes('.')) { /**
* Sometimes reddit API returns video without extension, like
* "DASH_480" and not "DASH_480.mp4".
*/
file_ext = 'mp4'
has_extension = false
- } else {
+ } else {
file_ext = pathname.substring(pathname.lastIndexOf('.') + 1)
}
}
-
+
if(file_prefix === 'thumb_')
dir = 'thumbs/'
- if(file_prefix === 'flair')
+ if(file_prefix === 'flair_')
dir = 'flairs/'
if(valid_video_extensions.includes(file_ext) || gifmp4) {
@@ -130,7 +132,14 @@ module.exports = function(tools) {
if(temp_url.searchParams.get('width')) {
width = temp_url.searchParams.get('width')
}
- filename = `${file_prefix}w:${temp_url.searchParams.get('width')}_${temp_url.pathname.split('/').slice(-1)}`
+ if(file_prefix === 'flair_') {
+ // Flair emojis have a full path of `UUID/name`,
+ // so we need to incorporate the UUID to avoid duplicates
+ // since names alone are not unique across all of reddit
+ filename = `${pathname.slice(1).replace('/', '_')}.png` // Only first replacement is fine
+ } else {
+ filename = `${file_prefix}w:${temp_url.searchParams.get('width')}_${temp_url.pathname.split('/').slice(-1)}`
+ }
}
path = `./dist/pics/${dir}${filename}`
if(!fs.existsSync(path)) {
diff --git a/inc/processJsonPost.js b/inc/processJsonPost.js
index a40485d..467009f 100644
--- a/inc/processJsonPost.js
+++ b/inc/processJsonPost.js
@@ -7,7 +7,7 @@ module.exports = function(fetch) {
if(!parsed) {
json = JSON.parse(json)
}
-
+
let post = json[0].data.children[0].data
let post_id = post.name
let comments = json[1].data.children
@@ -32,14 +32,16 @@ module.exports = function(fetch) {
media: null,
images: null,
crosspost: false,
- selftext: unescape(post.selftext_html)
+ selftext: unescape(post.selftext_html),
+ link_flair: await formatLinkFlair(post),
+ user_flair: await formatUserFlair(post)
}
let validEmbedDomains = ['gfycat.com', 'youtube.com']
let has_gif = false
let gif_to_mp4 = null
let reddit_video = null
-
+
if(post.preview) {
if(post.preview.reddit_video_preview) {
if(post.preview.reddit_video_preview.is_gif) {
@@ -66,7 +68,7 @@ module.exports = function(fetch) {
}
obj = await processPostMedia(obj, post, post.media, has_gif, reddit_video, gif_to_mp4)
-
+
if(post.crosspost_parent_list) {
post.crosspost = post.crosspost_parent_list[0]
}
@@ -84,7 +86,8 @@ module.exports = function(fetch) {
ups: post.crosspost.ups,
selftext: unescape(post.selftext_html),
selftext_crosspost: unescape(post.crosspost.selftext_html),
- is_crosspost: true
+ is_crosspost: true,
+ user_flair: await formatUserFlair(post)
}
}
@@ -93,7 +96,7 @@ module.exports = function(fetch) {
source: await downloadAndSave(post.preview.images[0].source.url)
}
}
-
+
if(obj.media) {
if(obj.media.source === 'external') {
if(post.preview) {
@@ -103,7 +106,7 @@ module.exports = function(fetch) {
}
}
}
-
+
if(post.gallery_data) {
obj.gallery = true
obj.gallery_items = []
@@ -120,13 +123,13 @@ module.exports = function(fetch) {
}
}
}
-
+
let comms = []
for(var i = 0; i < comments.length; i++) {
let comment = comments[i].data
let kind = comments[i].kind
let obj = {}
-
+
if(kind !== 'more') {
obj = {
author: comment.author,
@@ -143,7 +146,8 @@ module.exports = function(fetch) {
score_hidden: comment.score_hidden,
edited: comment.edited,
replies: [],
- depth: 0
+ depth: 0,
+ user_flair: await formatUserFlair(comment)
}
} else {
obj = {
@@ -155,26 +159,26 @@ module.exports = function(fetch) {
children: []
}
}
-
+
if(comment.replies && kind !== 'more') {
if(comment.replies.data) {
if(comment.replies.data.children.length > 0) {
- obj.replies = processReplies(comment.replies.data.children, post_id, 1)
+ obj.replies = await processReplies(comment.replies.data.children, post_id, 1)
}
}
}
-
+
if(comment.children) {
for(var j = 0; j < comment.children.length; j++) {
obj.children.push(comment.children[j])
}
}
-
+
comms.push(obj)
}
-
+
obj.comments = comms
-
+
resolve(obj)
})()
})
@@ -198,7 +202,7 @@ module.exports = function(fetch) {
return { post_data: post_data, comments: comments_html }
}
- this.processReplies = (data, post_id, depth) => {
+ this.processReplies = async (data, post_id, depth) => {
let return_replies = []
for(var i = 0; i < data.length; i++) {
let kind = data[i].kind
@@ -220,7 +224,8 @@ module.exports = function(fetch) {
score_hidden: reply.score_hidden,
edited: reply.edited,
replies: [],
- depth: depth
+ depth: depth,
+ user_flair: await formatUserFlair(reply)
}
} else {
obj = {
@@ -233,13 +238,13 @@ module.exports = function(fetch) {
depth: depth
}
}
-
+
if(reply.replies && kind !== 'more') {
if(reply.replies.data.children.length) {
for(var j = 0; j < reply.replies.data.children.length; j++) {
let comment = reply.replies.data.children[j].data
let objct = {}
-
+
if(comment.author && comment.body_html) {
objct = {
author: comment.author,
@@ -255,7 +260,8 @@ module.exports = function(fetch) {
distinguished: comment.distinguished,
distinguished: comment.edited,
replies: [],
- depth: depth + 1
+ depth: depth + 1,
+ user_flair: await formatUserFlair(comment)
}
} else {
objct = {
@@ -273,11 +279,11 @@ module.exports = function(fetch) {
}
}
}
-
+
if(comment.replies) {
if(comment.replies.data) {
if(comment.replies.data.children.length > 0) {
- objct.replies = processReplies(comment.replies.data.children, post_id, depth )
+ objct.replies = await processReplies(comment.replies.data.children, post_id, depth )
}
}
}
@@ -286,13 +292,13 @@ module.exports = function(fetch) {
}
}
}
-
+
if(reply.children) {
for(var j = 0; j < reply.children.length; j++) {
obj.children.push(reply.children[j])
}
}
-
+
return_replies.push(obj)
}
return return_replies
diff --git a/inc/processJsonSubreddit.js b/inc/processJsonSubreddit.js
index 27535b2..1b4c04c 100644
--- a/inc/processJsonSubreddit.js
+++ b/inc/processJsonSubreddit.js
@@ -11,7 +11,7 @@ module.exports = function() {
} else {
let before = json.data.before
let after = json.data.after
-
+
let ret = {
info: {
before: before,
@@ -19,9 +19,9 @@ module.exports = function() {
},
links: []
}
-
+
let children_len = json.data.children.length
-
+
for(var i = 0; i < children_len; i++) {
let data = json.data.children[i].data
let images = null
@@ -39,7 +39,7 @@ module.exports = function() {
is_self_link = true
}
}
-
+
if(data.preview && data.thumbnail !== 'self') {
if(!data.url.startsWith('/r/') && isGif(data.url)) {
images = {
@@ -73,7 +73,9 @@ module.exports = function() {
url: data.url,
stickied: data.stickied,
is_self_link: is_self_link,
- subreddit_front: subreddit_front
+ subreddit_front: subreddit_front,
+ link_flair: await formatLinkFlair(data),
+ user_flair: await formatUserFlair(data)
}
ret.links.push(obj)
}
diff --git a/inc/processJsonUser.js b/inc/processJsonUser.js
index f081138..fe86322 100644
--- a/inc/processJsonUser.js
+++ b/inc/processJsonUser.js
@@ -21,7 +21,7 @@ module.exports = function() {
if(!after && !before) {
user_front = true
}
-
+
if(json.overview.data.children) {
if(json.overview.data.children[posts_limit - 1]) {
after = json.overview.data.children[posts_limit - 1].data.name
@@ -30,16 +30,16 @@ module.exports = function() {
before = json.overview.data.children[0].data.name
}
}
-
+
for(var i = 0; i < posts_limit; i++) {
let post = json.overview.data.children[i].data
let thumbnail = 'self'
let type = json.overview.data.children[i].kind
let obj
-
+
let post_id = post.permalink.split('/').slice(-2)[0] + '/'
let url = post.permalink.replace(post_id, '')
-
+
if(type === 't3') {
let duration = null
if(post.media) {
@@ -62,7 +62,8 @@ module.exports = function() {
edited: post.edited,
selftext_html: unescape(post.selftext_html),
num_comments: post.num_comments,
- permalink: post.permalink
+ permalink: post.permalink,
+ user_flair: await formatUserFlair(post)
}
}
if(type === 't1') {
@@ -79,7 +80,8 @@ module.exports = function() {
num_comments: post.num_comments,
permalink: post.permalink,
link_author: post.link_author,
- link_title: post.link_title
+ link_title: post.link_title,
+ user_flair: await formatUserFlair(post)
}
}
posts.push(obj)
@@ -98,7 +100,7 @@ module.exports = function() {
after: after,
posts: posts
}
-
+
resolve(obj)
})()
})
diff --git a/views/post.pug b/views/post.pug
index 3eea48b..96c0114 100644
--- a/views/post.pug
+++ b/views/post.pug
@@ -12,7 +12,7 @@ html
#post
header
div
- p subreddit:
+ p subreddit:
a(href="/r/" + subreddit + "")
p /r/#{subreddit}
.info
@@ -23,8 +23,7 @@ html
.title
a(href="" + post.url + "")
h2 #{cleanTitle(post.title)}
- if post.link_flair_text
- span(class="postflair") #{post.link_flair_text}
+ != post.link_flair
span(class="domain") (#{post.domain})
p.submitted
span(title="" + toUTCString(post.created) + "") submitted #{timeDifference(post.created)} by
@@ -33,6 +32,7 @@ html
else
a(href="/u/" + post.author + "")
| #{post.author}
+ != post.user_flair
if post.crosspost.is_crosspost === true
.crosspost
.title
@@ -52,6 +52,7 @@ html
else
a(href="/u/" + post.crosspost.author + "")
| #{post.crosspost.author}
+ != post.user_flair
p.to to
a(href="/r/" + post.crosspost.subreddit + "")
| #{post.crosspost.subreddit}
@@ -128,8 +129,8 @@ html
source(src="" + post.media.source + "", type="video/mp4")
| Your browser does not support the video element.
a(href="" + post.media.source + "") [media]
- if post.selftext
- div.usertext-body !{post.selftext}
+ if post.selftext
+ div.usertext-body !{post.selftext}
if viewing_comment
.infobar
p you are viewing a single comment's thread.
diff --git a/views/subreddit.pug b/views/subreddit.pug
index 31fa078..bbf90a0 100644
--- a/views/subreddit.pug
+++ b/views/subreddit.pug
@@ -84,14 +84,12 @@ html
if link.is_self_link
a(href="" + link.permalink + "")
h2(class="" + (link.stickied ? 'green' : '') + "") #{cleanTitle(link.title)}
- if link.link_flair_text
- span(class="postflair") #{link.link_flair_text}
+ != link.link_flair
span (#{link.domain})
else
a(href="" + link.url + "")
h2(class="" + (link.stickied ? 'green' : '') + "") #{cleanTitle(link.title)}
- if link.link_flair_text
- span(class="postflair") #{link.link_flair_text}
+ != link.link_flair
span (#{link.domain})
.meta
p.submitted submitted
@@ -101,6 +99,7 @@ html
else
a(href="/u/" + link.author + "")
| #{link.author}
+ != link.user_flair
p.to to
a(href="/r/" + link.subreddit + "")
| #{link.subreddit}
diff --git a/views/user.pug b/views/user.pug
index 8344c37..d1766b3 100644
--- a/views/user.pug
+++ b/views/user.pug
@@ -80,9 +80,11 @@ html
.title
a(href="" + post.permalink + "") #{cleanTitle(post.title)}
.meta
- p.submitted(title="" + toUTCString(post.created) + "") submitted #{timeDifference(post.created)} by
+ p.submitted(title="" + toUTCString(post.created) + "") submitted #{timeDifference(post.created)}
+ | by
a(href="/u/" + data.username + "") #{data.username}
| to
+ != post.user_flair
a(href="/r/" + post.subreddit + "", class="subreddit") #{post.subreddit}
a.comments(href="" + post.permalink + "") #{post.num_comments} comments
if post.type === 't1'
@@ -98,7 +100,7 @@ html
a(href="/u/" + post.link_author + "") #{post.link_author}
.subreddit
p in
- a(href="/r/" + post.subreddit + "") #{post.subreddit}
+ a(href="/r/" + post.subreddit + "") #{post.subreddit}
.comment
details(open="")
summary
@@ -108,10 +110,12 @@ html
.meta
p.author
a(href="/u/" + data.username + "") #{data.username}
+ p
+ != post.user_flair
p.ups #{post.ups} points
p.created(title="" + toUTCString(post.created) + "") #{timeDifference(post.created)}
.body
- div !{post.body_html}
+ div !{post.body_html}
a.context(href="" + post.permalink + "?context=10") context
a.comments.t1(href="" + post.url + "") full comments (#{post.num_comments})
if data.before || data.after
@@ -127,28 +131,3 @@ html
br
p(title="" + toUTCString(data.created) + "") account created: #{toDateString(data.created)}
p verified: #{(data.verified) ? "yes" : "no" }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-