mirror of
https://git.sr.ht/~cadence/bibliogram
synced 2024-11-22 16:17:29 +00:00
Add preferRSS setting
This commit is contained in:
parent
c5d8b5788b
commit
6e136dc77a
@ -85,6 +85,10 @@ class TtlCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends TtlCache<T>
|
||||||
|
* @template T
|
||||||
|
*/
|
||||||
class RequestCache extends TtlCache {
|
class RequestCache extends TtlCache {
|
||||||
/**
|
/**
|
||||||
* @param {number} ttl time to keep each resource in milliseconds
|
* @param {number} ttl time to keep each resource in milliseconds
|
||||||
@ -97,7 +101,6 @@ class RequestCache extends TtlCache {
|
|||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {() => Promise<T>} callback
|
* @param {() => Promise<T>} callback
|
||||||
* @returns {Promise<T>}
|
* @returns {Promise<T>}
|
||||||
* @template T
|
|
||||||
*/
|
*/
|
||||||
getOrFetch(key, callback) {
|
getOrFetch(key, callback) {
|
||||||
this.cleanKey(key)
|
this.cleanKey(key)
|
||||||
@ -116,7 +119,6 @@ class RequestCache extends TtlCache {
|
|||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {() => Promise<T>} callback
|
* @param {() => Promise<T>} callback
|
||||||
* @returns {Promise<T>}
|
* @returns {Promise<T>}
|
||||||
* @template T
|
|
||||||
*/
|
*/
|
||||||
getOrFetchPromise(key, callback) {
|
getOrFetchPromise(key, callback) {
|
||||||
return this.getOrFetch(key, callback).then(result => {
|
return this.getOrFetch(key, callback).then(result => {
|
||||||
@ -126,5 +128,56 @@ class RequestCache extends TtlCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
*/
|
||||||
|
class UserRequestCache extends TtlCache {
|
||||||
|
constructor(ttl) {
|
||||||
|
super(ttl)
|
||||||
|
/** @type {Map<string, {data: T, isReel: boolean, isFailedPromise: boolean, htmlFailed: boolean, time: number}>} */
|
||||||
|
this.cache
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {boolean} isReel
|
||||||
|
* @param {any} [data]
|
||||||
|
*/
|
||||||
|
set(key, isReel, data) {
|
||||||
|
const existing = this.cache.get(key)
|
||||||
|
// Preserve html failure status if now requesting as reel
|
||||||
|
const htmlFailed = isReel && existing && existing.htmlFailed
|
||||||
|
this.cache.set(key, {data, isReel, isFailedPromise: false, htmlFailed, time: Date.now()})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {boolean} isHtmlPreferred
|
||||||
|
* @param {boolean} willFetchReel
|
||||||
|
* @param {() => Promise<T>} callback
|
||||||
|
* @returns {Promise<T>}
|
||||||
|
*/
|
||||||
|
getOrFetch(key, willFetchReel, isHtmlPreferred, callback) {
|
||||||
|
this.cleanKey(key)
|
||||||
|
if (this.cache.has(key)) {
|
||||||
|
const existing = this.cache.get(key)
|
||||||
|
if ((!existing.isReel || !isHtmlPreferred || existing.htmlFailed) && !existing.isFailedPromise) return Promise.resolve(existing.data)
|
||||||
|
}
|
||||||
|
const pending = callback().then(result => {
|
||||||
|
if (this.getWithoutClean(key) === pending) { // if nothing has replaced the current cache in the meantime
|
||||||
|
this.set(key, willFetchReel, result)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}).catch(error => {
|
||||||
|
this.cache.get(key).htmlFailed = true
|
||||||
|
this.cache.get(key).isFailedPromise = true
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
this.set(key, willFetchReel, pending)
|
||||||
|
return pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.TtlCache = TtlCache
|
module.exports.TtlCache = TtlCache
|
||||||
module.exports.RequestCache = RequestCache
|
module.exports.RequestCache = RequestCache
|
||||||
|
module.exports.UserRequestCache = UserRequestCache
|
||||||
|
@ -2,20 +2,26 @@ const constants = require("./constants")
|
|||||||
const {request} = require("./utils/request")
|
const {request} = require("./utils/request")
|
||||||
const switcher = require("./utils/torswitcher")
|
const switcher = require("./utils/torswitcher")
|
||||||
const {extractSharedData} = require("./utils/body")
|
const {extractSharedData} = require("./utils/body")
|
||||||
const {TtlCache, RequestCache} = require("./cache")
|
const {TtlCache, RequestCache, UserRequestCache} = require("./cache")
|
||||||
const RequestHistory = require("./structures/RequestHistory")
|
const RequestHistory = require("./structures/RequestHistory")
|
||||||
const db = require("./db")
|
const db = require("./db")
|
||||||
require("./testimports")(constants, request, extractSharedData, RequestCache, RequestHistory)
|
require("./testimports")(constants, request, extractSharedData, UserRequestCache, RequestHistory)
|
||||||
|
|
||||||
const requestCache = new RequestCache(constants.caching.resource_cache_time)
|
const requestCache = new RequestCache(constants.caching.resource_cache_time)
|
||||||
|
const userRequestCache = new UserRequestCache(constants.caching.resource_cache_time)
|
||||||
/** @type {import("./cache").TtlCache<import("./structures/TimelineEntry")>} */
|
/** @type {import("./cache").TtlCache<import("./structures/TimelineEntry")>} */
|
||||||
const timelineEntryCache = new TtlCache(constants.caching.resource_cache_time)
|
const timelineEntryCache = new TtlCache(constants.caching.resource_cache_time)
|
||||||
const history = new RequestHistory(["user", "timeline", "post", "reel"])
|
const history = new RequestHistory(["user", "timeline", "post", "reel"])
|
||||||
|
|
||||||
async function fetchUser(username) {
|
async function fetchUser(username, isRSS) {
|
||||||
if (constants.allow_user_from_reel === "never") {
|
let mode = constants.allow_user_from_reel
|
||||||
|
if (mode === "preferForRSS") {
|
||||||
|
if (isRSS) mode = "prefer"
|
||||||
|
else mode = "fallback"
|
||||||
|
}
|
||||||
|
if (mode === "never") {
|
||||||
return fetchUserFromHTML(username)
|
return fetchUserFromHTML(username)
|
||||||
} else if (constants.allow_user_from_reel === "prefer") {
|
} else if (mode === "prefer") {
|
||||||
const userID = db.prepare("SELECT user_id FROM Users WHERE username = ?").pluck().get(username)
|
const userID = db.prepare("SELECT user_id FROM Users WHERE username = ?").pluck().get(username)
|
||||||
if (userID) return fetchUserFromCombined(userID, username)
|
if (userID) return fetchUserFromCombined(userID, username)
|
||||||
else return fetchUserFromHTML(username)
|
else return fetchUserFromHTML(username)
|
||||||
@ -24,7 +30,6 @@ async function fetchUser(username) {
|
|||||||
if (error === constants.symbols.INSTAGRAM_DEMANDS_LOGIN || error === constants.symbols.RATE_LIMITED) {
|
if (error === constants.symbols.INSTAGRAM_DEMANDS_LOGIN || error === constants.symbols.RATE_LIMITED) {
|
||||||
const userID = db.prepare("SELECT user_id FROM Users WHERE username = ?").pluck().get(username)
|
const userID = db.prepare("SELECT user_id FROM Users WHERE username = ?").pluck().get(username)
|
||||||
if (userID) {
|
if (userID) {
|
||||||
requestCache.cache.delete("user/"+username)
|
|
||||||
return fetchUserFromCombined(userID, username)
|
return fetchUserFromCombined(userID, username)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,7 +39,7 @@ async function fetchUser(username) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fetchUserFromHTML(username) {
|
function fetchUserFromHTML(username) {
|
||||||
return requestCache.getOrFetch("user/"+username, () => {
|
return userRequestCache.getOrFetch("user/"+username, false, true, () => {
|
||||||
return switcher.request("user_html", `https://www.instagram.com/${username}/`, async res => {
|
return switcher.request("user_html", `https://www.instagram.com/${username}/`, async res => {
|
||||||
if (res.status === 302) throw constants.symbols.INSTAGRAM_DEMANDS_LOGIN
|
if (res.status === 302) throw constants.symbols.INSTAGRAM_DEMANDS_LOGIN
|
||||||
if (res.status === 429) throw constants.symbols.RATE_LIMITED
|
if (res.status === 429) throw constants.symbols.RATE_LIMITED
|
||||||
@ -74,7 +79,7 @@ function fetchUserFromCombined(userID, username) {
|
|||||||
user_id: userID,
|
user_id: userID,
|
||||||
include_reel: true
|
include_reel: true
|
||||||
}))
|
}))
|
||||||
return requestCache.getOrFetch("user/"+username, () => {
|
return userRequestCache.getOrFetch("user/"+username, true, false, () => {
|
||||||
return switcher.request("reel_graphql", `https://www.instagram.com/graphql/query/?${p.toString()}`, async res => {
|
return switcher.request("reel_graphql", `https://www.instagram.com/graphql/query/?${p.toString()}`, async res => {
|
||||||
if (res.status === 429) throw constants.symbols.RATE_LIMITED
|
if (res.status === 429) throw constants.symbols.RATE_LIMITED
|
||||||
return res
|
return res
|
||||||
@ -192,8 +197,8 @@ function fetchShortcodeData(shortcode) {
|
|||||||
.run({shortcode: data.shortcode, id: data.id, id_as_numeric: data.id, username: data.owner.username, json: JSON.stringify(data)})
|
.run({shortcode: data.shortcode, id: data.id, id_as_numeric: data.id, username: data.owner.username, json: JSON.stringify(data)})
|
||||||
}
|
}
|
||||||
// if we have the owner but only a reelUser, update it. this code is gross.
|
// if we have the owner but only a reelUser, update it. this code is gross.
|
||||||
if (requestCache.hasNotPromise("user/"+data.owner.username)) {
|
if (userRequestCache.hasNotPromise("user/"+data.owner.username)) {
|
||||||
const user = requestCache.getWithoutClean("user/"+data.owner.username)
|
const user = userRequestCache.getWithoutClean("user/"+data.owner.username)
|
||||||
if (user.fromReel) {
|
if (user.fromReel) {
|
||||||
user.data.full_name = data.owner.full_name
|
user.data.full_name = data.owner.full_name
|
||||||
user.data.is_verified = data.owner.is_verified
|
user.data.is_verified = data.owner.is_verified
|
||||||
@ -214,7 +219,7 @@ module.exports.fetchUser = fetchUser
|
|||||||
module.exports.fetchTimelinePage = fetchTimelinePage
|
module.exports.fetchTimelinePage = fetchTimelinePage
|
||||||
module.exports.getOrCreateShortcode = getOrCreateShortcode
|
module.exports.getOrCreateShortcode = getOrCreateShortcode
|
||||||
module.exports.fetchShortcodeData = fetchShortcodeData
|
module.exports.fetchShortcodeData = fetchShortcodeData
|
||||||
module.exports.requestCache = requestCache
|
module.exports.userRequestCache = userRequestCache
|
||||||
module.exports.timelineEntryCache = timelineEntryCache
|
module.exports.timelineEntryCache = timelineEntryCache
|
||||||
module.exports.getOrFetchShortcode = getOrFetchShortcode
|
module.exports.getOrFetchShortcode = getOrFetchShortcode
|
||||||
module.exports.history = history
|
module.exports.history = history
|
||||||
|
@ -20,7 +20,7 @@ let constants = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
allow_user_from_reel: "fallback", // one of: "never", "fallback", "prefer".
|
allow_user_from_reel: "preferForRSS", // one of: "never", "fallback", "prefer", "preferForRSS"
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
rss_enabled: true
|
rss_enabled: true
|
||||||
|
@ -180,9 +180,9 @@ class TimelineEntry extends TimelineBaseMethods {
|
|||||||
}
|
}
|
||||||
// The owner may be in the user cache, so copy from that.
|
// The owner may be in the user cache, so copy from that.
|
||||||
// This could be implemented better.
|
// This could be implemented better.
|
||||||
else if (collectors.requestCache.hasNotPromise("user/"+this.data.owner.username)) {
|
else if (collectors.userRequestCache.hasNotPromise("user/"+this.data.owner.username)) {
|
||||||
/** @type {import("./User")} */
|
/** @type {import("./User")} */
|
||||||
const user = collectors.requestCache.getWithoutClean("user/"+this.data.owner.username)
|
const user = collectors.userRequestCache.getWithoutClean("user/"+this.data.owner.username)
|
||||||
if (user.data.full_name) {
|
if (user.data.full_name) {
|
||||||
this.data.owner = {
|
this.data.owner = {
|
||||||
id: user.data.id,
|
id: user.data.id,
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
const constants = require("../../lib/constants")
|
const constants = require("../../lib/constants")
|
||||||
const {fetchUser, requestCache} = require("../../lib/collectors")
|
const {fetchUser, userRequestCache} = require("../../lib/collectors")
|
||||||
const {render} = require("pinski/plugins")
|
const {render} = require("pinski/plugins")
|
||||||
const {pugCache} = require("../passthrough")
|
const {pugCache} = require("../passthrough")
|
||||||
|
|
||||||
module.exports = [
|
module.exports = [
|
||||||
{route: `/u/(${constants.external.username_regex})/rss.xml`, methods: ["GET"], code: ({fill}) => {
|
{route: `/u/(${constants.external.username_regex})/rss.xml`, methods: ["GET"], code: ({fill}) => {
|
||||||
if (constants.settings.rss_enabled) {
|
if (constants.settings.rss_enabled) {
|
||||||
return fetchUser(fill[0]).then(async user => {
|
return fetchUser(fill[0], true).then(async user => {
|
||||||
const content = await user.timeline.fetchFeed()
|
const content = await user.timeline.fetchFeed()
|
||||||
const xml = content.xml()
|
const xml = content.xml()
|
||||||
return {
|
return {
|
||||||
@ -27,10 +27,10 @@ module.exports = [
|
|||||||
statusCode: 503,
|
statusCode: 503,
|
||||||
contentType: "text/html",
|
contentType: "text/html",
|
||||||
headers: {
|
headers: {
|
||||||
"Retry-After": requestCache.getTtl("user/"+fill[0], 1000)
|
"Retry-After": userRequestCache.getTtl("user/"+fill[0], 1000)
|
||||||
},
|
},
|
||||||
content: pugCache.get("pug/blocked.pug").web({
|
content: pugCache.get("pug/blocked.pug").web({
|
||||||
expiresMinutes: requestCache.getTtl("user/"+fill[0], 1000*60)
|
expiresMinutes: userRequestCache.getTtl("user/"+fill[0], 1000*60)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const constants = require("../../lib/constants")
|
const constants = require("../../lib/constants")
|
||||||
const {fetchUser, getOrFetchShortcode, requestCache, history} = require("../../lib/collectors")
|
const {fetchUser, getOrFetchShortcode, userRequestCache, history} = require("../../lib/collectors")
|
||||||
const {render, redirect} = require("pinski/plugins")
|
const {render, redirect} = require("pinski/plugins")
|
||||||
const {pugCache} = require("../passthrough")
|
const {pugCache} = require("../passthrough")
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
route: `/u/(${constants.external.username_regex})`, methods: ["GET"], code: ({url, fill}) => {
|
route: `/u/(${constants.external.username_regex})`, methods: ["GET"], code: ({url, fill}) => {
|
||||||
const params = url.searchParams
|
const params = url.searchParams
|
||||||
return fetchUser(fill[0]).then(async user => {
|
return fetchUser(fill[0], false).then(async user => {
|
||||||
const page = +params.get("page")
|
const page = +params.get("page")
|
||||||
if (typeof page === "number" && !isNaN(page) && page >= 1) {
|
if (typeof page === "number" && !isNaN(page) && page >= 1) {
|
||||||
await user.timeline.fetchUpToPage(page - 1)
|
await user.timeline.fetchUpToPage(page - 1)
|
||||||
@ -53,10 +53,10 @@ module.exports = [
|
|||||||
statusCode: 503,
|
statusCode: 503,
|
||||||
contentType: "text/html",
|
contentType: "text/html",
|
||||||
headers: {
|
headers: {
|
||||||
"Retry-After": requestCache.getTtl("user/"+fill[0], 1000)
|
"Retry-After": userRequestCache.getTtl("user/"+fill[0], 1000)
|
||||||
},
|
},
|
||||||
content: pugCache.get("pug/blocked.pug").web({
|
content: pugCache.get("pug/blocked.pug").web({
|
||||||
expiresMinutes: requestCache.getTtl("user/"+fill[0], 1000*60)
|
expiresMinutes: userRequestCache.getTtl("user/"+fill[0], 1000*60)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -67,7 +67,7 @@ module.exports = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
route: `/fragment/user/(${constants.external.username_regex})/(\\d+)`, methods: ["GET"], code: async ({url, fill}) => {
|
route: `/fragment/user/(${constants.external.username_regex})/(\\d+)`, methods: ["GET"], code: async ({url, fill}) => {
|
||||||
return fetchUser(fill[0]).then(async user => {
|
return fetchUser(fill[0], false).then(async user => {
|
||||||
const pageNumber = +fill[1]
|
const pageNumber = +fill[1]
|
||||||
const pageIndex = pageNumber - 1
|
const pageIndex = pageNumber - 1
|
||||||
await user.timeline.fetchUpToPage(pageIndex)
|
await user.timeline.fetchUpToPage(pageIndex)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const {instance, pugCache, wss} = require("./passthrough")
|
const {instance, pugCache, wss} = require("./passthrough")
|
||||||
const {requestCache, timelineEntryCache, history} = require("../lib/collectors")
|
const {userRequestCache, timelineEntryCache, history} = require("../lib/collectors")
|
||||||
const constants = require("../lib/constants")
|
const constants = require("../lib/constants")
|
||||||
const util = require("util")
|
const util = require("util")
|
||||||
const repl = require("repl")
|
const repl = require("repl")
|
||||||
|
Loading…
Reference in New Issue
Block a user