From 0fea009f70e7782453c2c43bf35e12e20d260d92 Mon Sep 17 00:00:00 2001 From: Ethan Shen <42264778+nczitzk@users.noreply.github.com> Date: Mon, 25 Mar 2024 02:12:49 +0800 Subject: [PATCH] feat(route): add 1x.com (#14936) * feat(route): add 1x.com * fix radar * fix example --- lib/routes-deprecated/1x/index.js | 69 -------------- lib/routes/1x/index.ts | 121 ++++++++++++++++++++++++ lib/routes/1x/namespace.ts | 8 ++ lib/routes/1x/templates/description.art | 17 ++++ 4 files changed, 146 insertions(+), 69 deletions(-) delete mode 100644 lib/routes-deprecated/1x/index.js create mode 100644 lib/routes/1x/index.ts create mode 100644 lib/routes/1x/namespace.ts create mode 100644 lib/routes/1x/templates/description.art diff --git a/lib/routes-deprecated/1x/index.js b/lib/routes-deprecated/1x/index.js deleted file mode 100644 index fee500801..000000000 --- a/lib/routes-deprecated/1x/index.js +++ /dev/null @@ -1,69 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); - -const categories = { - latest: 'latest', - popular: 'popular', - published: 'published', - abstract: 'latest:15:', - action: 'latest:1:', - animals: 'latest:21:', - architecture: 'latest:11:', - conceptual: 'latest:17:', - 'creative-edit': 'latest:10:', - documentary: 'latest:8:', - everyday: 'latest:14:', - 'fine-art-nude': 'latest:12:', - humour: 'latest:3:', - landscape: 'latest:6:', - macro: 'latest:2:', - mood: 'latest:4:', - night: 'latest:9:', - performance: 'latest:19:', - portrait: 'latest:13:', - 'still-life': 'latest:18:', - street: 'latest:7:', - underwater: 'latest:20:', - wildlife: 'latest:5:', -}; - -module.exports = async (ctx) => { - const category = ctx.params.category || 'latest'; - - const rootUrl = `https://1x.com`; - const currentUrl = `${rootUrl}/gallery/${category}`; - const apiUrl = `${rootUrl}/backend/lm.php?style=normal&mode=${categories[category]}&from=0&autoload=`; - const response = await got({ - method: 'get', - url: apiUrl, - }); - - const $ = cheerio.load(response.data); - - const items = $('root data') - .html() - .split('\n') - .slice(0, -1) - .map((item) => { - item = $(item); - - const id = item - .find('.photos-feed-image') - .attr('id') - .match(/img-(\d+)/)[1]; - - return { - guid: id, - link: `${rootUrl}/photo/${id}`, - author: item.find('.photos-feed-data-name').eq(0).text(), - title: item.find('.photos-feed-data-title').text() || 'Untitled', - description: ``, - }; - }); - - ctx.state.data = { - title: `${category} - 1X`, - link: currentUrl, - item: items, - }; -}; diff --git a/lib/routes/1x/index.ts b/lib/routes/1x/index.ts new file mode 100644 index 000000000..f978ff551 --- /dev/null +++ b/lib/routes/1x/index.ts @@ -0,0 +1,121 @@ +import { Route } from '@/types'; +import { getCurrentPath } from '@/utils/helpers'; +const __dirname = getCurrentPath(import.meta.url); + +import got from '@/utils/got'; +import { load } from 'cheerio'; +import { art } from '@/utils/render'; +import * as path from 'node:path'; + +export const handler = async (ctx) => { + const { category = 'latest/awarded' } = ctx.req.param(); + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; + + const rootUrl = 'https://1x.com'; + const currentUrl = new URL(`gallery/${category}`, rootUrl).href; + + const { data: currentResponse } = await got(currentUrl); + + const $ = load(currentResponse); + + const language = $('html').prop('lang'); + const apiUrl = new URL(`backend/lm2.php?style=normal&mode=${$('input#lm_mode').prop('value')}`, rootUrl).href; + + const { data: response } = await got(apiUrl); + + const $$ = load(response); + + const items = $$('div.photos-feed-item') + .slice(0, limit) + .toArray() + .map((item) => { + item = $(item); + + const title = item.find('span.photos-feed-data-title').first().text() || 'Untitled'; + const image = item.find('img').prop('src'); + const author = item.find('span.photos-feed-data-name').first().text(); + + const text = `${title} by ${author}`; + + const description = art(path.join(__dirname, 'templates/description.art'), { + images: image + ? [ + { + src: image, + alt: title, + }, + ] + : undefined, + description: text, + }); + + const id = item.find('img[id]').prop('id').split(/-/).pop(); + const guid = `1x-${id}`; + + return { + title, + description, + link: new URL(`photo/${id}`, rootUrl).href, + author, + guid, + id: guid, + content: { + html: description, + text, + }, + image, + banner: image, + language, + enclosure_url: image, + enclosure_type: image ? `image/${image.split(/\./).pop()}` : undefined, + enclosure_title: title, + }; + }); + + const image = new URL($('img.themedlogo').prop('src'), rootUrl).href; + + return { + title: $('title').text(), + description: $('meta[name="description"]').prop('content'), + link: currentUrl, + item: items, + allowEmpty: true, + image, + author: $('meta[property="og:site_name"]').prop('content'), + language, + }; +}; + +export const route: Route = { + path: '/:category{.+}?', + name: 'Gallery', + url: '1x.com', + maintainers: ['nczitzk'], + handler, + example: '/1x/latest/awarded', + parameters: { category: 'Category, Latest Awarded by default' }, + description: `::: tip +Fill in the field in the path with the part of the corresponding page URL after \`https://1x.com/gallery/\` or \`https://1x.com/photo/\`. Here are the examples: + +If you subscribe to [Abstract Awarded](https://1x.com/gallery/abstract/awarded), you should fill in the path with the part \`abstract/awarded\` from the page URL \`https://1x.com/gallery/abstract/awarded\`. In this case, the route will be [\`/1x/abstract/awarded\`](https://rsshub.app/1x/abstract/awarded). + +If you subscribe to [Wildlife Published](https://1x.com/gallery/wildlife/published), you should fill in the path with the part \`wildlife/published\` from the page URL \`https://1x.com/gallery/wildlife/published\`. In this case, the route will be [\`/1x/wildlife/published\`](https://rsshub.app/1x/wildlife/published). +:::`, + categories: ['design', 'picture'], + + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['/gallery/:category*', '/photos/:category*'], + target: '/1x/:category', + }, + ], +}; diff --git a/lib/routes/1x/namespace.ts b/lib/routes/1x/namespace.ts new file mode 100644 index 000000000..4e8cc283e --- /dev/null +++ b/lib/routes/1x/namespace.ts @@ -0,0 +1,8 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: '1x.com', + url: '1x.com', + categories: ['design', 'picture'], + description: '1x.com • In Pursuit of the Sublime. Browse 200,000 curated photos from photographers all over the world.', +}; diff --git a/lib/routes/1x/templates/description.art b/lib/routes/1x/templates/description.art new file mode 100644 index 000000000..dfab19230 --- /dev/null +++ b/lib/routes/1x/templates/description.art @@ -0,0 +1,17 @@ +{{ if images }} + {{ each images image }} + {{ if image?.src }} +
+ {{ image.alt }} +
+ {{ /if }} + {{ /each }} +{{ /if }} + +{{ if description }} + {{@ description }} +{{ /if }} \ No newline at end of file