This commit is contained in:
Cadence Fish
2020-01-15 03:38:33 +13:00
parent b5f163891c
commit 30b45c2573
17 changed files with 157 additions and 26 deletions

View File

@@ -22,6 +22,10 @@ class InstaCache {
return this.cache.get(key).data
}
getTtl(key, factor = 1) {
return Math.max((Math.floor(Date.now() - this.cache.get(key).time) / factor), 0)
}
/**
* @param {string} key
* @param {any} data

View File

@@ -5,7 +5,7 @@ const InstaCache = require("./cache")
const {User} = require("./structures")
require("./testimports")(constants, request, extractSharedData, InstaCache, User)
const cache = new InstaCache(600e3)
const cache = new InstaCache(constants.resource_cache_time)
function fetchUser(username) {
return cache.getOrFetch("user/"+username, () => {

View File

@@ -1,9 +1,11 @@
module.exports = {
image_cache_control: `public, max-age=${7*24*60*60}`,
resource_cache_time: 30*60*1000,
external: {
timeline_query_hash: "e769aa130647d2354c40ea6a439bfc08",
timeline_fetch_first: 12
timeline_fetch_first: 12,
username_regex: "[\\w.]+"
},
symbols: {

View File

@@ -1,8 +1,11 @@
const RSS = require("rss")
const constants = require("../constants")
const config = require("../../../config")
const TimelineImage = require("./TimelineImage")
const collectors = require("../collectors")
require("../testimports")(constants, TimelineImage)
/** @param {any[]} edges */
function transformEdges(edges) {
return edges.map(e => new TimelineImage(e.node))
}
@@ -13,6 +16,7 @@ class Timeline {
*/
constructor(user) {
this.user = user
/** @type {import("./TimelineImage")[][]} */
this.pages = []
this.addPage(this.user.data.edge_owner_to_timeline_media)
this.page_info = this.user.data.edge_owner_to_timeline_media.page_info
@@ -40,6 +44,23 @@ class Timeline {
this.pages.push(transformEdges(page.edges))
this.page_info = page.page_info
}
getFeed() {
const feed = new RSS({
title: `@${this.user.data.username}`,
feed_url: `${config.website_origin}/u/${this.user.data.username}/rss.xml`,
site_url: config.website_origin,
description: this.user.data.biography,
image_url: this.user.data.profile_pic_url,
pubDate: new Date(this.user.cachedAt),
ttl: this.user.getTtl(1000*60) // scale to minute
})
const page = this.pages[0] // only get posts from first page
for (const item of page) {
feed.item(item.getFeedData())
}
return feed
}
}
module.exports = Timeline

View File

@@ -1,9 +1,23 @@
const config = require("../../../config")
const {proxyImage} = require("../utils/proxyurl")
const {compile} = require("pug")
const rssDescriptionTemplate = compile(`
img(alt=alt src=src)
p(style='white-space: pre-line')= caption
`)
class GraphImage {
/**
* @param {import("../types").GraphImage} data
*/
constructor(data) {
this.data = data
this.data.edge_media_to_caption.edges.forEach(edge => edge.node.text = edge.node.text.replace(/\u2063/g, "")) // I don't know why U+2063 INVISIBLE SEPARATOR is in here, but it is, and it causes rendering issues with certain fonts.
}
getProxy(url) {
return proxyImage(url)
}
/**
@@ -32,10 +46,34 @@ class GraphImage {
else return null
}
getIntroduction() {
const caption = this.getCaption()
if (caption) return caption.split("\n")[0].split(". ")[0] // try to get first meaningful line or sentence
else return null
}
getAlt() {
// For some reason, pages 2+ don't contain a11y data. Instagram web client falls back to image caption.
return this.data.accessibility_caption || this.getCaption() || "No image description available."
}
getFeedData() {
return {
title: this.getIntroduction() || "No caption provided",
description: rssDescriptionTemplate({src: this.data.display_url, alt: this.getAlt(), caption: this.getCaption()}),
author: this.data.owner.username,
url: `${config.website_origin}/p/${this.data.shortcode}`,
guid: `${config.website_origin}/p/${this.data.shortcode}`,
date: new Date(this.data.taken_at_timestamp*1000)
/*
Readers should display the description as HTML rather than using the media enclosure.
enclosure: {
url: this.data.display_url,
type: "image/jpeg" //TODO: can instagram have PNGs? everything is JPEG according to https://medium.com/@autolike.it/how-to-avoid-low-res-thumbnails-on-instagram-android-problem-bc24f0ed1c7d
}
*/
}
}
}
module.exports = GraphImage

View File

@@ -1,5 +1,7 @@
const constants = require("../constants")
const {proxyImage} = require("../utils/proxyurl")
const Timeline = require("./Timeline")
require("../testimports")(Timeline)
require("../testimports")(constants, Timeline)
class User {
/**
@@ -11,6 +13,14 @@ class User {
this.followedBy = data.edge_followed_by.count
this.posts = data.edge_owner_to_timeline_media.count
this.timeline = new Timeline(this)
this.cachedAt = Date.now()
this.proxyProfilePicture = proxyImage(this.data.profile_pic_url)
}
getTtl(scale = 1) {
const expiresAt = this.cachedAt + constants.resource_cache_time
const ttl = expiresAt - Date.now()
return Math.ceil(Math.max(ttl, 0) / scale)
}
export() {

View File

@@ -0,0 +1,7 @@
function proxyImage(url) {
const params = new URLSearchParams()
params.set("url", url)
return "/imageproxy?"+params.toString()
}
module.exports.proxyImage = proxyImage