rsshub
Version:
Make RSS Great Again!
133 lines (131 loc) • 4.64 kB
JavaScript
import { n as init_esm_shims, t as __dirname } from "./esm-shims-CzJ_djXG.mjs";
import { t as ofetch_default } from "./ofetch-BIyrKU3Y.mjs";
import { t as art } from "./render-BQo6B4tL.mjs";
import path from "node:path";
import { load } from "cheerio";
//#region lib/routes/app-sales/util.ts
init_esm_shims();
const baseUrl = "https://www.app-sales.net";
/**
* Formats price change information into a standardized tag
* @param priceOld - Old price
* @param priceNew - New price
* @param priceDisco - Discount
* @returns Formatted price change tag string. Returns empty string when no new price exists.
*/
const formatPriceChangeTag = (priceOld, priceNew, priceDisco) => priceNew?.trim() ? `[${[
priceOld && `${priceOld}→`,
priceNew,
priceDisco && ` ${priceDisco}`
].filter(Boolean).join("")}]` : "";
/**
* Processes DOM elements into structured data items
* @param $ - CheerioAPI instance
* @param selector - CSS selector for target elements
* @returns Parsed data items array.
*/
const processItems = ($, selector) => $(selector).toArray().map((el) => {
const $el = $(el);
const appName = $el.find("p.app-name").text()?.trim();
const appDev = $el.find("p.app-dev").text()?.trim();
const appNote = $el.find("p.app-dev").next("p")?.text()?.trim() ?? "";
const rating = $el.find("p.rating").contents().last().text()?.trim();
const downloads = $el.find("p.downloads").contents().last().text()?.trim();
const bookmarks = $el.find("p.bookmarks").contents().last().text()?.trim();
const priceNew = $el.find("div.price-new").text()?.trim();
const priceOld = $el.find("div.price-old").text()?.trim();
const priceDisco = $el.find("div.price-disco").text()?.trim();
const isHot = $el.hasClass("sale-hot");
const isFree = priceNew?.toLocaleUpperCase() === "FREE";
const title = `${appName} ${formatPriceChangeTag(priceOld, priceNew, priceDisco)}`;
const image = $el.find("div.app-icon img").attr("src");
const linkUrl = $el.find("div.sale-list-action a").attr("href");
const description = art(path.join(__dirname, "templates/description-4540098f.art"), {
images: image ? [{
alt: `${appName}-${appDev}`,
src: image
}] : void 0,
appName,
appDev,
appNote,
rating,
downloads,
bookmarks,
priceNew,
priceOld,
priceDisco,
linkUrl
});
const categories = [isHot ? "Hot" : void 0, isFree ? "Free" : void 0].filter(Boolean);
const authors = appDev;
const guid = [
appName,
appDev,
rating,
downloads,
bookmarks,
priceNew
].filter(Boolean).join("-");
return {
title: title.trim(),
description,
link: linkUrl,
category: categories,
author: authors,
guid,
id: guid,
content: {
html: description,
text: description
},
image,
banner: image
};
});
/**
* Retrieves pagination URLs from page navigation
* @param $ - CheerioAPI instance
* @param targetUrl - Base URL for relative path resolution
* @returns Array of absolute pagination URLs.
*/
const getAvailablePageUrls = ($, targetUrl) => $("ul.pagination li.waves-effect a").slice(0, -1).toArray().filter((el) => {
return $(el).attr("href");
}).map((el) => {
const $el = $(el);
return new URL($el.attr("href"), targetUrl).href;
});
/**
* Aggregates items across paginated pages
* @param $ - Initial page CheerioAPI instance
* @param selector - Target element CSS selector
* @param targetUrl - Base URL for pagination requests
* @param country - Country ID for request headers
* @param limit - Maximum number of items to return
* @returns Aggregated items array within specified limit.
*/
const fetchItems = async ($, selector, targetUrl, country, limit) => {
const initialItems = processItems($, selector);
if (initialItems.length >= limit) return initialItems.slice(0, limit);
/**
* Recursive helper function to process paginated URLs
*
* @param remainingUrls - Array of URLs yet to be processed
* @param aggregated - Accumulator for collected items
*
* @returns Promise resolving to aggregated items
*/
const processPage = async (remainingUrls, aggregated) => {
if (aggregated.length >= limit || remainingUrls.length === 0) return aggregated.slice(0, limit);
const [currentUrl, ...restUrls] = remainingUrls;
try {
const pageItems = processItems(load(await ofetch_default(currentUrl, { headers: { Cookie: `countryId=${country};` } })), selector);
const newItems = [...aggregated, ...pageItems];
return newItems.length >= limit ? newItems.slice(0, limit) : processPage(restUrls, newItems);
} catch {
return processPage(restUrls, aggregated);
}
};
return await processPage(getAvailablePageUrls($, targetUrl), initialItems);
};
//#endregion
export { fetchItems as n, baseUrl as t };