mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-09-24 23:12:34 +08:00
feat(route/instagram): authless tags (#13944)
* feat(route/instagram): authless tags * fix: wwwClaimV2 condition
This commit is contained in:
@@ -11,7 +11,10 @@ const renderItems = (items) =>
|
||||
let description = '';
|
||||
switch (product_type) {
|
||||
case 'carousel_container': {
|
||||
const images = item.carousel_media.map((i) => i.image_versions2.candidates[0]);
|
||||
const images = item.carousel_media.map((i) => ({
|
||||
...i.image_versions2.candidates.sort((a, b) => b.width - a.width)[0],
|
||||
alt: item.accessibility_caption,
|
||||
}));
|
||||
description = art(path.join(__dirname, 'templates/images.art'), {
|
||||
summary,
|
||||
images,
|
||||
@@ -22,12 +25,12 @@ const renderItems = (items) =>
|
||||
case 'igtv':
|
||||
description = art(path.join(__dirname, 'templates/video.art'), {
|
||||
summary,
|
||||
image: item.image_versions2.candidates[0].url,
|
||||
image: item.image_versions2.candidates.sort((a, b) => b.width - a.width)[0],
|
||||
video: item.video_versions[0],
|
||||
});
|
||||
break;
|
||||
case 'feed': {
|
||||
const images = [item.image_versions2.candidates[0]];
|
||||
const images = [{ ...item.image_versions2.candidates.sort((a, b) => b.width - a.width)[0], alt: item.accessibility_caption }];
|
||||
description = art(path.join(__dirname, 'templates/images.art'), {
|
||||
summary,
|
||||
images,
|
||||
@@ -40,7 +43,7 @@ const renderItems = (items) =>
|
||||
|
||||
// Metadata
|
||||
const url = `https://www.instagram.com/p/${item.code}/`;
|
||||
const pubDate = parseDate(item.taken_at, 'X');
|
||||
const pubDate = parseDate(item.caption.created_at_utc || item.taken_at, 'X');
|
||||
const title = summary.split('\n')[0];
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
<img src="{{ i.url }}"
|
||||
{{ if i.height }} height="{{ i.height }}" {{ /if }}
|
||||
{{ if i.width }} width="{{ i.width }}" {{ /if }}
|
||||
{{ if i.alt }} alt="{{ i.alt }}" {{ /if }}
|
||||
>
|
||||
{{ /each }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { CookieJar } = require('tough-cookie');
|
||||
const config = require('@/config').value;
|
||||
const { renderItems } = require('../common-utils');
|
||||
const { baseUrl, COOKIE_URL, getUserInfo, getUserFeedItems, getTagsFeedItems, renderGuestItems } = require('./utils');
|
||||
const { baseUrl, COOKIE_URL, checkLogin, getUserInfo, getUserFeedItems, getTagsFeedItems, getLoggedOutTagsFeedItems, renderGuestItems } = require('./utils');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
// if (!config.instagram || !config.instagram.cookie) {
|
||||
@@ -15,19 +15,24 @@ module.exports = async (ctx) => {
|
||||
}
|
||||
|
||||
let cookieJar = await ctx.cache.get('instagram:cookieJar');
|
||||
const wwwClaimV2 = await ctx.cache.get('instagram:wwwClaimV2');
|
||||
const cacheMiss = !cookieJar;
|
||||
|
||||
if (cookie) {
|
||||
if (cacheMiss) {
|
||||
cookieJar = new CookieJar();
|
||||
if (cacheMiss) {
|
||||
cookieJar = new CookieJar();
|
||||
if (cookie) {
|
||||
for await (const c of cookie.split('; ')) {
|
||||
await cookieJar.setCookie(c, COOKIE_URL);
|
||||
}
|
||||
} else {
|
||||
cookieJar = CookieJar.fromJSON(cookieJar);
|
||||
}
|
||||
} else {
|
||||
cookieJar = new CookieJar();
|
||||
cookieJar = CookieJar.fromJSON(cookieJar);
|
||||
}
|
||||
|
||||
if (!wwwClaimV2 && cookie) {
|
||||
if (!(await checkLogin(cookieJar, ctx.cache))) {
|
||||
throw Error('Invalid cookie');
|
||||
}
|
||||
}
|
||||
|
||||
let feedTitle, feedLink, feedDescription, feedLogo;
|
||||
@@ -45,7 +50,7 @@ module.exports = async (ctx) => {
|
||||
feedLink = `${baseUrl}/${username}`;
|
||||
|
||||
if (cookie) {
|
||||
items = await getUserFeedItems(id, username, cookieJar, ctx.cache.tryGet);
|
||||
items = await getUserFeedItems(id, username, cookieJar, ctx.cache);
|
||||
} else {
|
||||
items = [...userInfo.edge_felix_video_timeline.edges, ...userInfo.edge_owner_to_timeline_media.edges];
|
||||
}
|
||||
@@ -58,7 +63,13 @@ module.exports = async (ctx) => {
|
||||
feedTitle = `#${tag} - Instagram`;
|
||||
feedLink = `${baseUrl}/explore/tags/${tag}`;
|
||||
|
||||
items = await getTagsFeedItems(tag, 'recent', cookieJar, ctx.cache.tryGet);
|
||||
if (cookie) {
|
||||
items = await getTagsFeedItems(tag, 'recent', cookieJar, ctx.cache.tryGet);
|
||||
} else {
|
||||
const hashTag = await getLoggedOutTagsFeedItems(tag, cookieJar);
|
||||
items = [...hashTag.edge_hashtag_to_media.edges, ...hashTag.edge_hashtag_to_top_posts.edges];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -6,7 +6,6 @@ const path = require('path');
|
||||
|
||||
const baseUrl = 'https://www.instagram.com';
|
||||
const COOKIE_URL = 'https://instagram.com';
|
||||
let igWwwClaim;
|
||||
|
||||
const getCSRFTokenFromJar = async (cookieJar) => {
|
||||
const cookieString = await cookieJar.getCookieString(COOKIE_URL);
|
||||
@@ -17,9 +16,28 @@ const getHeaders = async (cookieJar) => ({
|
||||
'X-ASBD-ID': 198387,
|
||||
'X-CSRFToken': await getCSRFTokenFromJar(cookieJar),
|
||||
'X-IG-App-ID': 936619743392459,
|
||||
'X-IG-WWW-Claim': igWwwClaim,
|
||||
'X-IG-WWW-Claim': undefined,
|
||||
});
|
||||
|
||||
const checkLogin = async (cookieJar, cache) => {
|
||||
const response = await got.post(`${baseUrl}/api/v1/web/fxcal/ig_sso_users/`, {
|
||||
// cookieJar,
|
||||
headers: {
|
||||
cookie: await cookieJar.getCookieString(COOKIE_URL),
|
||||
...(await getHeaders(cookieJar)),
|
||||
'X-IG-WWW-Claim': '0',
|
||||
},
|
||||
});
|
||||
|
||||
const wwwClaimV2 = response.headers['x-ig-set-www-claim'];
|
||||
|
||||
if (wwwClaimV2) {
|
||||
cache.set('instagram:wwwClaimV2', wwwClaimV2);
|
||||
}
|
||||
|
||||
return Boolean(response.data.status === 'ok');
|
||||
};
|
||||
|
||||
const getUserInfo = async (username, cookieJar, cache) => {
|
||||
let webProfileInfo;
|
||||
let id = await cache.get(`instagram:getIdByUsername:${username}`);
|
||||
@@ -29,7 +47,10 @@ const getUserInfo = async (username, cookieJar, cache) => {
|
||||
try {
|
||||
const response = await got(`${baseUrl}/api/v1/users/web_profile_info/`, {
|
||||
cookieJar,
|
||||
headers: await getHeaders(cookieJar),
|
||||
headers: {
|
||||
...(await getHeaders(cookieJar)),
|
||||
'X-IG-WWW-Claim': await cache.get('instagram:wwwClaimV2'),
|
||||
},
|
||||
searchParams: {
|
||||
username,
|
||||
},
|
||||
@@ -37,7 +58,6 @@ const getUserInfo = async (username, cookieJar, cache) => {
|
||||
if (response.url.includes('/accounts/login/')) {
|
||||
throw Error('Invalid cookie');
|
||||
}
|
||||
igWwwClaim = response.headers['x-ig-set-www-claim'] || igWwwClaim;
|
||||
|
||||
webProfileInfo = response.data.data.user;
|
||||
id = webProfileInfo.id;
|
||||
@@ -57,19 +77,26 @@ const getUserInfo = async (username, cookieJar, cache) => {
|
||||
return userInfoCache || webProfileInfo;
|
||||
};
|
||||
|
||||
const getUserFeedItems = (id, username, cookieJar, tryGet) =>
|
||||
tryGet(
|
||||
const getUserFeedItems = (id, username, cookieJar, cache) =>
|
||||
cache.tryGet(
|
||||
`instagram:feed:${id}`,
|
||||
async () => {
|
||||
const response = await got(`${baseUrl}/api/v1/feed/user/${username}/username/`, {
|
||||
cookieJar,
|
||||
headers: await getHeaders(cookieJar),
|
||||
// cookieJar,
|
||||
headers: {
|
||||
cookie: await cookieJar.getCookieString(COOKIE_URL),
|
||||
...(await getHeaders(cookieJar)),
|
||||
// 401 Unauthorized if cookie does not match with IP
|
||||
'X-IG-WWW-Claim': await cache.get('instagram:wwwClaimV2'),
|
||||
},
|
||||
searchParams: {
|
||||
count: 30,
|
||||
},
|
||||
});
|
||||
// 401 Unauthorized if cookie does not match with IP
|
||||
igWwwClaim = response.headers['x-ig-set-www-claim'] || igWwwClaim;
|
||||
if (response.url.includes('/accounts/login/')) {
|
||||
throw Error(`Invalid cookie.
|
||||
Please also check if your account is being blocked by Instagram.`);
|
||||
}
|
||||
|
||||
return response.data.items;
|
||||
},
|
||||
@@ -91,8 +118,6 @@ const getTagsFeedItems = (tag, tab, cookieJar, tryGet) =>
|
||||
tag_name: tag,
|
||||
},
|
||||
});
|
||||
// Looks like cookie IP check is not applied to tags
|
||||
igWwwClaim = response.headers['x-ig-set-www-claim'] || igWwwClaim;
|
||||
|
||||
return response.data.data[tab].sections.flatMap((section) => section.layout_content.medias.map((media) => media.media));
|
||||
},
|
||||
@@ -100,6 +125,29 @@ const getTagsFeedItems = (tag, tab, cookieJar, tryGet) =>
|
||||
false
|
||||
);
|
||||
|
||||
const getLoggedOutTagsFeedItems = async (tag, cookieJar) => {
|
||||
const response = await got(`${baseUrl}/api/v1/tags/logged_out_web_info/`, {
|
||||
// cookieJar, cookieJar is behaving weirdly here, so we use cookie header instead
|
||||
headers: {
|
||||
cookie: await cookieJar.getCookieString(COOKIE_URL),
|
||||
...(await getHeaders(cookieJar)),
|
||||
'X-IG-WWW-Claim': '0',
|
||||
},
|
||||
searchParams: {
|
||||
tag_name: tag,
|
||||
},
|
||||
});
|
||||
|
||||
const cookies = response.headers['set-cookie'];
|
||||
if (cookies) {
|
||||
for await (const cookie of cookies) {
|
||||
await cookieJar.setCookie(cookie, COOKIE_URL);
|
||||
}
|
||||
}
|
||||
|
||||
return response.data.data.hashtag;
|
||||
};
|
||||
|
||||
const renderGuestItems = (items) => {
|
||||
const renderVideo = (node, summary) =>
|
||||
art(path.join(__dirname, '../templates/video.art'), {
|
||||
@@ -125,19 +173,21 @@ const renderGuestItems = (items) => {
|
||||
switch (type) {
|
||||
// carousel, can include GraphVideo and GraphImage
|
||||
case 'GraphSidecar':
|
||||
description = node.edge_sidecar_to_children.edges
|
||||
.map(({ node }, i) => {
|
||||
const _type = node.__typename;
|
||||
switch (_type) {
|
||||
case 'GraphVideo':
|
||||
return renderVideo(node, i === 0 ? summary : '');
|
||||
case 'GraphImage':
|
||||
return renderImages(node, i === 0 ? summary : '');
|
||||
default:
|
||||
throw Error(`Instagram: Unhandled carousel type: ${_type}`);
|
||||
}
|
||||
})
|
||||
.join('');
|
||||
description = node.edge_sidecar_to_children
|
||||
? node.edge_sidecar_to_children.edges
|
||||
.map(({ node }, i) => {
|
||||
const _type = node.__typename;
|
||||
switch (_type) {
|
||||
case 'GraphVideo':
|
||||
return renderVideo(node, i === 0 ? summary : '');
|
||||
case 'GraphImage':
|
||||
return renderImages(node, i === 0 ? summary : '');
|
||||
default:
|
||||
throw Error(`Instagram: Unhandled carousel type: ${_type}`);
|
||||
}
|
||||
})
|
||||
.join('')
|
||||
: renderImages(node, summary);
|
||||
break;
|
||||
case 'GraphVideo':
|
||||
description = renderVideo(node, summary);
|
||||
@@ -151,6 +201,7 @@ const renderGuestItems = (items) => {
|
||||
|
||||
return {
|
||||
title: summary.split('\n')[0],
|
||||
id: node.id,
|
||||
pubDate: parseDate(node.taken_at_timestamp, 'X'),
|
||||
author: node.owner.username,
|
||||
link: `${baseUrl}/p/${node.shortcode}/`,
|
||||
@@ -163,8 +214,10 @@ const renderGuestItems = (items) => {
|
||||
module.exports = {
|
||||
baseUrl,
|
||||
COOKIE_URL,
|
||||
checkLogin,
|
||||
getUserInfo,
|
||||
getUserFeedItems,
|
||||
getTagsFeedItems,
|
||||
getLoggedOutTagsFeedItems,
|
||||
renderGuestItems,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user