import {IPage, ISearchItem, ISearchParams, ISearchProvider, Order, Sort, StationId} from "../type";
|
import {HTMLElement, parse} from "node-html-parser";
|
import {beginToPage, wrappedFetch} from "../lib";
|
|
export class Provider8Mandarake implements ISearchProvider {
|
readonly stationId = StationId.Mandarake;
|
|
async search(params: ISearchParams): Promise<IPage> {
|
return exeSearch(params);
|
}
|
}
|
|
async function exeSearch(params: ISearchParams) {
|
const {keyword, begin = 1, maxNum = 48, category, minPrice, maxPrice, shipping, sort, order} = params;
|
const pageNum = beginToPage(begin, maxNum);
|
const warn: string[] = [];
|
const url = new URL("https://order.mandarake.co.jp/order/listPage/list");
|
url.searchParams.set("keyword", keyword);
|
url.searchParams.set("page", String(pageNum));
|
url.searchParams.set("dispCount", String(maxNum));
|
if (category) {
|
url.searchParams.set("categoryCode", category);
|
}
|
if (minPrice) {
|
url.searchParams.set("minPrice", String(minPrice));
|
}
|
if (maxPrice) {
|
url.searchParams.set("maxPrice", String(maxPrice));
|
}
|
if (shipping === 'free') {
|
warn.push('unsupported shipping');
|
}
|
if (sort) {
|
if (sort === Sort.Latest) {
|
url.searchParams.set("sort", 'arrival');
|
if (order === Order.Asc) {
|
url.searchParams.set("sortOrder", '0');
|
} else {
|
url.searchParams.set("sortOrder", '1');
|
}
|
} else if (sort ===Sort.Price) {
|
url.searchParams.set("sort", 'price');
|
if (order === Order.Desc) {
|
url.searchParams.set("sortOrder", '1');
|
} else {
|
url.searchParams.set("sortOrder", '0');
|
}
|
} else if(sort!==Sort.Default) {
|
warn.push(`unsupported sort: ${sort}`);
|
}
|
}
|
|
|
const resp = await wrappedFetch(url, {
|
headers: {
|
'Cookie': 'tr_mndrk_user=778e501f.64c8374096346; adult_confirm=1; initialized_cart=1;',
|
}
|
});
|
|
if (!resp.ok) {
|
if (resp.status === 404) {
|
return {...params, dataList: [], error: '404 Not Found'};
|
}
|
return {...params, dataList: [], error: `HTTP error! status: ${resp.status}`};
|
}
|
|
// noinspection TypeScriptUnresolvedReference
|
const html = await resp.text();
|
const result = parseProducts(parse(html));
|
|
return {
|
srcUrl: url.toString(),
|
warning: warn.join(', '),
|
begin,
|
maxNum,
|
pageNum,
|
...params,
|
...result,
|
};
|
}
|
|
function parseProducts(doc: HTMLElement): Omit<IPage, keyof ISearchParams> {
|
const map = new Map<string, ISearchItem>();
|
doc.querySelectorAll('.thumlarge .block[data-itemidx]')
|
.map(parseItem)
|
.forEach(item => item.itemId && map.set(item.itemId, item));
|
|
const totalText = doc.querySelector('.item_list_head .count')?.textContent;
|
const total = totalText ? parseInt(totalText.replace(/\D+/g, '')) : 0;
|
|
return {
|
total: Math.max(total || 0, map.size),
|
dataList: Array.from(map.values())
|
};
|
}
|
|
function parseItem(el: HTMLElement): ISearchItem {
|
const priceArr = el.querySelector('.price')?.textContent?.split(/\(/);
|
const basePrice = priceArr[0]?.replace(/\D+/g, '');
|
const priceText = (priceArr[1] || priceArr[0]).replace(/\D+/g, '');
|
const title = el.querySelector('.title a');
|
const img = el.querySelector('.pic img');
|
|
return {
|
itemId: el.getAttribute('data-itemidx'),
|
url: title?.getAttribute('href'),
|
image: {
|
imageUrl: img?.getAttribute('src'),
|
},
|
name: title?.textContent?.trim(),
|
price: priceText ? parseInt(priceText) : undefined,
|
basePrice: basePrice ? parseInt(basePrice) : undefined,
|
};
|
}
|