diff --git a/docs/social-media.md b/docs/social-media.md index 635b1f7b24..ebe1f0deae 100644 --- a/docs/social-media.md +++ b/docs/social-media.md @@ -312,6 +312,16 @@ Tiny Tiny RSS 会给所有 iframe 元素添加 `sandbox="allow-scripts"` 属性 +## piapro + +### 用户最新作品 + + + +### 全站最新作品 + + + ## pixiv ### 用户收藏 diff --git a/lib/router.js b/lib/router.js index e17e13c083..f12c45657f 100644 --- a/lib/router.js +++ b/lib/router.js @@ -2187,4 +2187,8 @@ router.get('/netease/ds/:id', require('./routes/netease/ds')); // javlibrary router.get('/javlibrary/userposts/:uid', require('./routes/javlibrary/userposts')); +// piapro +router.get('/piapro/user/:pid', require('./routes/piapro/user')); +router.get('/piapro/public/:type/:tag?/:category?', require('./routes/piapro/public')); + module.exports = router; diff --git a/lib/routes/piapro/public.js b/lib/routes/piapro/public.js new file mode 100644 index 0000000000..2c25f0920a --- /dev/null +++ b/lib/routes/piapro/public.js @@ -0,0 +1,62 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const utils = require('./utils'); + +const base = 'https://piapro.jp'; + +const selectors = { + music: '.snd_list li .title', + illust: '#itemcont .i_main', + text: '.txt_list li .title', +}; + +module.exports = async (ctx) => { + const type = selectors[ctx.params.type] ? ctx.params.type : 'music'; + const tag = ctx.params.tag ? ctx.params.tag : ''; + const category = ctx.params.category ? ctx.params.category : ''; + + const url = `${base}/${type}/?tag=${encodeURIComponent(tag)}&categoryId=${category}`; + + const list_response = await got.get(url); + const $ = cheerio.load(list_response.data); + + const type_name = $('#top_titlebox img') + .first() + .attr('alt'); + const category_name = $('#_filterbox_category .name').text(); + const tag_name = $('#_filterbox_character .name').text(); + + const list = $(selectors[type]) + .map(function() { + return ( + base + + $(this) + .find('a') + .first() + .attr('href') + ); + }) + .toArray(); + + const out = await Promise.all( + list.slice(0, 10).map(async (url) => { + const cache = await ctx.cache.get(url); + if (cache) { + return Promise.resolve(JSON.parse(cache)); + } + + const response = await got.get(url); + const item = utils.parseContent(response.data); + item.link = url; + + ctx.cache.set(url, JSON.stringify(item)); + return Promise.resolve(item); + }) + ); + + ctx.state.data = { + title: `piapro - ${type_name}${tag ? ' - ' + tag_name : ''}${category ? ' - ' + category_name : ''}`, + link: url, + item: out.filter((item) => item !== ''), + }; +}; diff --git a/lib/routes/piapro/user.js b/lib/routes/piapro/user.js new file mode 100644 index 0000000000..221ba0e36e --- /dev/null +++ b/lib/routes/piapro/user.js @@ -0,0 +1,53 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const utils = require('./utils'); + +const base = 'https://piapro.jp'; + +module.exports = async (ctx) => { + const pid = ctx.params.pid; + + const url = `${base}/my_page/?view=content&pid=${pid}`; + + const list_response = await got.get(url); + + const $ = cheerio.load(list_response.data); + + const user_name = $('#main_name h2').text(); + const user_description = $('#main_name p').text(); + + const list = $('#itemcont .i_main') + .map(function() { + return ( + base + + $(this) + .find('a') + .last() + .attr('href') + ); + }) + .toArray(); + + const out = await Promise.all( + list.slice(0, 10).map(async (url) => { + const cache = await ctx.cache.get(url); + if (cache) { + return Promise.resolve(JSON.parse(cache)); + } + + const response = await got.get(url); + const item = utils.parseContent(response.data); + item.link = url; + + ctx.cache.set(url, JSON.stringify(item)); + return Promise.resolve(item); + }) + ); + + ctx.state.data = { + title: `piapro - ${user_name}の投稿作品`, + description: user_description, + link: url, + item: out.filter((item) => item !== ''), + }; +}; diff --git a/lib/routes/piapro/utils.js b/lib/routes/piapro/utils.js new file mode 100644 index 0000000000..993b419848 --- /dev/null +++ b/lib/routes/piapro/utils.js @@ -0,0 +1,45 @@ +const cheerio = require('cheerio'); + +const parseContent = (htmlString) => { + const $ = cheerio.load(htmlString); + + const title = $('h1.cd_works-title'); + const author = $('.cd_user-box-main a.cd_user-name'); + const time = $('.cd_dtl_data') + .text() + .match(/\d+\/\d+\/\d+ \d+:\d+:\d+/)[0]; + + const blank = cheerio.load('
'); + const content = blank('div'); + + // description + $('p.cd_dtl_cap').appendTo(content); + + // image + $('.illust-whole').appendTo(content); + + // audio + const testAudio = htmlString.match(/initNotLoginAudioFunc\(([\s\S]*?)\);/m); + if (testAudio) { + const audioInfo = testAudio[1]; + const contentId = audioInfo.match(/contentId:\s*'(.*?)',/)[1]; + const createDate = audioInfo.match(/createDate:\s*'(.*?)',/)[1]; + const audio_url = `https://cdn.piapro.jp/mp3_a/${contentId.substring(0, 2)}/${contentId}_${createDate}_audition.mp3`; + $(``).appendTo(content); + $('img#bigJacket_1').appendTo(content); + } + + // text + $('.main_txt').appendTo(content); + + return { + title: title.text().trim(), + author: author.text().trim(), + description: content.html(), + pubDate: new Date(time), + }; +}; + +module.exports = { + parseContent, +};