feat(scraper): add each scraper and its parser flow

このコミットが含まれているのは:
sinkaroid 2023-04-18 11:29:19 +07:00
コミット 759c79c266
この署名に対応する既知のキーがデータベースに存在しません
GPGキーID: ABD69470B2390135
14個のファイルの変更971行の追加0行の削除

78
src/scraper/pornhub/pornhubGetController.ts ノーマルファイル
ファイルの表示

@ -0,0 +1,78 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import { IVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const resolve = await lust.fetchBody(url);
const $ = load(resolve);
class PornHub {
link: string;
id: string;
title: string;
image: string;
duration: string;
views: string;
rating: string;
videoInfo: string;
upVote: string;
downVote: string;
video: string;
tags: string[];
models: string[];
constructor() {
this.link = $("link[rel='canonical']").attr("href") || "None";
this.id = this.link.split("=")[1] || "None";
this.title = $("meta[property='og:title']").attr("content") || "None";
this.image = $("meta[property='og:image']").attr("content") || "None";
//get <meta property="video:duration" content="
this.duration = $("meta[property='video:duration']").attr("content") || "0";
this.views = $("div.views > span.count").text() || "None";
this.rating = $("div.ratingPercent > span.percent").text() || "None";
this.videoInfo = $("div.videoInfo").text() || "None";
this.upVote = $("span.votesUp").attr("data-rating") || "None";
this.downVote = $("span.votesDown").attr("data-rating") || "None";
this.video = $("meta[property='og:video:url']").attr("content") || "None";
this.tags = $("div.video-info-row")
.find("a")
.map((i, el) => {
return $(el).text();
}).get();
this.tags.shift();
this.tags = this.tags.map((el) => lust.removeHtmlTagWithoutSpace(el));
this.models = $("div.pornstarsWrapper.js-pornstarsWrapper")
.find("a")
.map((i, el) => {
return $(el).attr("data-mxptext");
}).get();
}
}
const ph = new PornHub();
const data: IVideoData = {
success: true,
data: {
title: lust.removeHtmlTagWithoutSpace(ph.title),
id: ph.id,
image: ph.image,
duration: lust.secondToMinute(Number(ph.duration)),
views: ph.views,
rating: ph.rating,
uploaded: ph.videoInfo,
upvoted: ph.upVote,
downvoted: ph.downVote,
models: ph.models,
tags: ph.tags.filter((el) => el !== "Suggest" && el !== " Suggest")
},
source: ph.link,
assets: [ph.video, ph.image]
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -0,0 +1,59 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import c from "../../utils/options";
import { ISearchVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const res = await lust.fetchBody(url);
const $ = load(res);
class PornhubSearch {
search: object[];
data: object;
constructor() {
this.search = $("div.wrap")
.map((i, el) => {
const link = $(el).find("a").attr("href");
const id = link?.split("=")[1];
const title = $(el).find("a").attr("title");
const image = $(el).find("img").attr("src");
const duration = $(el).find("var.duration").text();
const views = $(el).find("div.videoDetailsBlock").find("span.views").text();
return {
link: `${c.PORNHUB}${link}`,
id: id,
title: title,
image: image,
duration: duration,
views: views,
video: `${c.PORNHUB}/embed/${id}`,
};
}).get();
this.data = this.search.filter((el: any) => {
return el.link.includes("javascript:void(0)") === false && el.image?.startsWith("data:image") === false;
});
}
}
const ph = new PornhubSearch();
if (ph.search.length === 0) throw Error("No result found");
const data = ph.data as string[];
const result: ISearchVideoData = {
success: true,
data: data,
source: url,
};
return result;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

79
src/scraper/redtube/redtubeGetController.ts ノーマルファイル
ファイルの表示

@ -0,0 +1,79 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import { IVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const resolve = await lust.fetchBody(url);
const $ = load(resolve);
class RedTube {
link: string;
id: string;
title: string;
image: string;
duration: string;
views: string;
rating: string;
publish: string;
upVote: string;
downVote: null;
video: string;
tags: string[];
models: string[];
constructor() {
this.link = $("link[rel='canonical']").attr("href") || "None";
this.id = this.link.split("/")[3] || "None";
this.title = $("meta[property='og:title']").attr("content") || "None";
this.image = $("meta[property='og:image']").attr("content") || "None";
this.duration = $("meta[property='og:video:duration']").attr("content") || "0";
this.views = $("span.video_view_count").text() || "None";
this.rating = $("div.rating_percent.js_rating_percent").attr("data-percent") + "%" || "None";
this.publish = $("span.video-infobox-date-added").text().replace("Published on ", "") || "None";
this.upVote = this.rating;
this.downVote = null;
this.video = $("meta[name='twitter:player']").attr("content") || "None";
this.tags = $("a.item.video_carousel_item.video_carousel_category, a.item.video_carousel_item.video_carousel_tag")
.map((i, el) => {
return $(el).text();
}).get();
this.tags = this.tags.map((el) => lust.removeHtmlTagWithoutSpace(el));
this.models = $("div.pornstar-name.pornstarPopupWrapper")
.find("a")
.map((i, el) => {
return $(el).text();
}
).get();
this.models = this.models.map((el) => lust.removeHtmlTagWithoutSpace(el));
this.models = this.models.filter((el) => !el.includes("Subscribe") && !el.includes("Rank"))
.filter((el, i, arr) => arr.indexOf(el) === i);
}
}
const red = new RedTube();
const data: IVideoData = {
success: true,
data: {
title: lust.removeHtmlTagWithoutSpace(red.title),
id: red.id,
image: red.image,
duration: lust.secondToMinute(Number(red.duration)),
views: red.views,
rating: red.rating,
uploaded: red.publish,
upvoted: red.upVote,
downvoted: red.downVote,
models: red.models,
tags: red.tags
},
source: red.link,
assets: [red.video, red.image]
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -0,0 +1,72 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import c from "../../utils/options";
import { ISearchVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const res = await lust.fetchBody(url);
const $ = load(res);
class RedTubeSearch {
views: string[];
search: object[];
data: object;
constructor() {
this.views = $("span.video_count")
.map((i, el) => {
const views = $(el).text();
return views;
}).get();
this.search = $("a.video_link")
.map((i, el) => {
const link = $(el).attr("href");
const id = link?.split("/")[1];
const title = $(el).find("img").attr("alt");
const image = $(el).find("img").attr("data-src");
const duration = $(el).find("span.duration").text().split(" ").map((el: string) => {
return el.replace(/[^0-9:]/g, "");
}).filter((el: string) => {
return el.includes(":");
}).join(" ");
return {
link: `${c.REDTUBE}${link}`,
id: id,
title: title,
image: image,
duration: duration,
views: this.views[i],
video: `https://embed.redtube.com/?id=${id}`,
};
}).get();
this.data = this.search.filter((el: any) => {
return el.link.includes("javascript:void(0)") === false && el.image?.startsWith("data:image") === false;
});
}
}
const red = new RedTubeSearch();
if (red.search.length === 0) throw Error("No result found");
const data = red.data as string[];
const result: ISearchVideoData = {
success: true,
data: data,
source: url,
};
return result;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

83
src/scraper/xhamster/xhamsterGetController.ts ノーマルファイル
ファイルの表示

@ -0,0 +1,83 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import { IVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const resolve = await lust.fetchBody(url);
const $ = load(resolve);
class Xhamster {
link: string;
id: string;
title: string;
image: string;
duration: any;
views: string;
rating: string;
publish: string;
upVote: string;
downVote: string;
video: string;
tags: string[];
models: string[];
constructor() {
this.link = $("link[rel='canonical']").attr("href") || "None";
this.id = this.link.split("/")[3] + "/" + this.link.split("/")[4] || "None";
this.title = $("meta[property='og:title']").attr("content") || "None";
this.image = $("meta[property='og:image']").attr("content") || "None";
this.duration = $("script#initials-script").html() || "None";
//remove window.initials={ and };
this.duration = this.duration.replace("window.initials=", "");
this.duration = this.duration.replace(/;/g, "");
this.duration = JSON.parse(this.duration);
this.duration = this.duration.videoModel.duration || "None";
this.views = $("div.header-icons").find("span").first().text() || "None";
this.rating = $("div.header-icons").find("span").eq(1).text() || "None";
this.publish = $("div.entity-info-container__date").attr("data-tooltip") || "None";
this.upVote = $("div.rb-new__info").text().split("/")[0].trim() || "None";
this.downVote = $("div.rb-new__info").text().split("/")[1].trim() || "None";
this.video = "https://xheve2.com/embed/" + this.link.split("-").pop() || "None";
this.tags = $("a.video-tag")
.map((i, el) => {
return $(el).text();
}).get();
this.tags = this.tags.map((el) => lust.removeHtmlTagWithoutSpace(el));
this.models = $("a.video-tag")
.map((i, el) => {
return $(el).attr("href");
}
).get();
this.models = this.models.filter((el) => el.startsWith("https://xheve2.com/pornstars/"));
this.models = this.models.map((el) => el.replace("https://xheve2.com/pornstars/", ""));
}
}
const xh = new Xhamster();
const data: IVideoData = {
success: true,
data: {
title: lust.removeHtmlTagWithoutSpace(xh.title),
id: xh.id,
image: xh.image,
duration: lust.secondToMinute(Number(xh.duration)),
views: xh.views,
rating: xh.rating,
uploaded: xh.publish,
upvoted: xh.upVote,
downvoted: xh.downVote,
models: xh.models,
tags: xh.tags
},
source: xh.link,
assets: [xh.video, xh.image]
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -0,0 +1,56 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import { ISearchVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const res = await lust.fetchBody(url);
const $ = load(res);
class XhamsterSearch {
search: any;
constructor() {
const views = $("div.video-thumb-views")
.map((i, el) => {
const views = $(el).text();
return views;
}).get();
const duration = $("span[data-role='video-duration']")
.map((i, el) => {
const duration = $(el).text();
return duration;
}).get();
this.search = $("a.video-thumb__image-container")
.map((i, el) => {
const link = $(el).attr("href");
return {
link: `${link}`,
id: link?.split("/")[3] + "/" + link?.split("/")[4],
title: $(el).find("img").attr("alt"),
image: $(el).find("img").attr("src"),
duration: duration[i],
views: views[i],
video: $(el).attr("data-previewvideo"),
};
}).get();
}
}
const xh = new XhamsterSearch();
if (xh.search.length === 0) throw Error("No result found");
const data = xh.search as unknown as string[];
const result: ISearchVideoData = {
success: true,
data: data,
source: url,
};
return result;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

102
src/scraper/xnxx/xnxxGetController.ts ノーマルファイル
ファイルの表示

@ -0,0 +1,102 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import { IVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
console.log(url);
const resolve = await lust.fetchBody(url);
const $ = load(resolve);
class Xnxx {
link: string;
id: string;
title: string;
image: string;
duration: string;
views: string;
uploaded: string;
action: string[];
upVote: string;
downVote: string;
favVote: string;
tags: string[];
models: string[];
thumbnail: string;
bigimg: string;
video: string;
constructor() {
const thumb = $("script")
.map((i, el) => {
return $(el).text();
}).get()
.filter((el) => el.includes("html5player.setThumbSlideBig"))[0] || "None";
this.thumbnail = thumb.match(/html5player.setThumbSlideBig\((.*?)\)/)?.[1] || "None";
this.bigimg = thumb.match(/html5player.setThumbUrl169\((.*?)\)/)?.[1] || "None";
this.video = thumb.match(/html5player.setVideoUrlHigh\((.*?)\)/)?.[1] || "None";
this.link = $("meta[property='og:url']").attr("content") || "None";
this.id = this.link.split(".com/")[1] || "None";
this.title = $("meta[property='og:title']").attr("content") || "None";
this.image = $("meta[property='og:image']").attr("content") || "None";
this.duration = $("meta[property='og:duration']").attr("content") || "None";
this.views = $("span.metadata").text() || "None";
this.views = this.views.split("-")[2] || "None";
this.uploaded = $("script[type='application/ld+json']").text() || "None";
this.uploaded = this.uploaded
.split("uploadDate")[1]
.split("}")[0]
.split(":")[1]
.replace(/"/g, "")
.replace(/,/g, "") || "None";
this.action = $("span.vote-actions")
.find("span.value")
.map((i, el) => {
return $(el).text();
}).get();
this.upVote = this.action[0] || "None";
this.downVote = this.action[1] || "None";
this.favVote = $("span.rating-box.value").text() || "None";
this.models = $("a.is-pornstar")
.map((i, el) => {
return $(el).text();
}).get();
this.tags = $("div.metadata-row.video-tags")
.find("a")
.map((i, el) => {
return $(el).text();
}).get();
}
}
const x = new Xnxx();
const data: IVideoData = {
success: true,
data: {
title: lust.removeHtmlTagWithoutSpace(x.title),
id: x.id,
image: x.image,
duration: lust.secondToMinute(Number(x.duration)),
views: lust.removeHtmlTag(x.views),
rating: x.favVote,
uploaded: x.uploaded.trim(),
upvoted: x.upVote,
downvoted: x.downVote,
models: x.models,
tags: x.tags.filter((el) => el !== "Edit tags and models")
},
source: x.link,
assets: lust.removeAllSingleQuoteOnArray([x.thumbnail, x.bigimg, x.video])
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

55
src/scraper/xnxx/xnxxGetRelatedController.ts ノーマルファイル
ファイルの表示

@ -0,0 +1,55 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import c from "../../utils/options";
import { ISearchVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const res = await lust.fetchBody(url);
const $ = load(res);
class PornhubSearch {
search: object[];
data: object;
constructor() {
// in <div id="video-player-bg"> get <script>var video_related=
this.search = $("div#video-player-bg")
.map((i, el) => {
const script = $(el).find("script").html();
const video_related = script?.split("var video_related=")[1];
//stop and replace everything after the last ];
const badJson = video_related?.split("];")[0] + "]";
const actualResult = JSON.parse(String(badJson));
const result = actualResult.map((el: any) => {
return {
link: `${c.XNXX}${el.u}`,
id: el.u,
title: el.t,
image: el.i,
duration: el.d,
views: `${el.n}, ${el.r}`,
video: null
};
});
return result;
}).get();
}
}
const x = new PornhubSearch();
if (x.search.length === 0) throw Error("No result found");
const data = x.search as unknown as string[];
const result: ISearchVideoData = {
success: true,
data: data,
source: url,
};
return result;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

49
src/scraper/xnxx/xnxxSearchController.ts ノーマルファイル
ファイルの表示

@ -0,0 +1,49 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import c from "../../utils/options";
import { ISearchVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const res = await lust.fetchBody(url);
const $ = load(res);
class PornhubSearch {
search: object[];
constructor() {
this.search = $("div.mozaique > div")
.map((i, el) => {
return {
link: `${c.XNXX}${$(el).find("a").attr("href")}`,
id: $(el).find("a").attr("href"),
title: $(el).find("div.thumb-under").text().split("\n")
.map((el) => el.trim()).filter((el) => el !== "")[0],
image: $(el).find("img").attr("data-src"),
duration: $(el).find("div.thumb-under").text().split("\n")
.map((el) => el.trim()).filter((el) => el !== "")[2],
rating: $(el).find("div.thumb-under").text().split("\n")
.map((el) => el.trim()).filter((el) => el !== "")[1],
video: null
};
}).get();
}
}
const x = new PornhubSearch();
if (x.search.length === 0) throw Error("No result found");
const data = x.search as unknown as string[];
const result: ISearchVideoData = {
success: true,
data: data,
source: url,
};
return result;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

92
src/scraper/xvideos/xvideosGetController.ts ノーマルファイル
ファイルの表示

@ -0,0 +1,92 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import { IVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const resolve = await lust.fetchBody(url);
const $ = load(resolve);
class Xvideos {
link: string;
id: string;
title: string;
image: string;
duration: string;
views: string;
rating: string;
publish: string;
upVote: string;
downVote: string;
video: string;
tags: string[];
models: string[];
thumbnail: string;
bigimg: string;
constructor() {
this.link = $("meta[property='og:url']").attr("content") || "None";
this.id = this.link.split("/")[3] + "/" + this.link.split("/")[4] || "None";
this.title = $("meta[property='og:title']").attr("content") || "None";
this.image = $("meta[property='og:image']").attr("content") || "None";
this.duration = $("meta[property='og:duration']").attr("content") || "0";
this.views = $("div#v-views").find("strong.mobile-hide").text() || "None";
this.rating = $("span.rating-total-txt").text() || "None";
this.publish = $("script[type='application/ld+json']").text() || "None";
this.publish = this.publish
.split("uploadDate")[1]
.split("}")[0]
.split(":")[1]
.replace(/"/g, "")
.replace(/,/g, "") || "None";
this.upVote = $("span.rating-good-nbr").text() || "None";
this.downVote = $("span.rating-bad-nbr").text() || "None";
const thumb = $("script")
.map((i, el) => {
return $(el).text();
}).get()
.filter((el) => el.includes("html5player.setThumbSlideBig"))[0] || "None";
this.thumbnail = thumb.match(/html5player.setThumbSlideBig\((.*?)\)/)?.[1] || "None";
this.bigimg = thumb.match(/html5player.setThumbUrl169\((.*?)\)/)?.[1] || "None";
this.video = thumb.match(/html5player.setVideoUrlHigh\((.*?)\)/)?.[1] || "None";
this.tags = $("a.is-keyword.btn.btn-default")
.map((i, el) => {
return $(el).text();
}).get();
this.models = $("li.model")
.map((i, el) => {
return $(el).find("a").attr("href") || "None";
}
).get();
this.models = this.models.map((el) => el.split("/")[2]);
}
}
const xv = new Xvideos();
const data: IVideoData = {
success: true,
data: {
title: lust.removeHtmlTagWithoutSpace(xv.title),
id: xv.id,
image: xv.image,
duration: lust.secondToMinute(Number(xv.duration)),
views: lust.removeHtmlTag(xv.views),
rating: xv.rating,
uploaded: xv.publish,
upvoted: xv.upVote,
downvoted: xv.downVote,
models: xv.models,
tags: xv.tags,
},
source: xv.link,
assets: lust.removeAllSingleQuoteOnArray([xv.thumbnail, xv.bigimg, xv.video])
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -0,0 +1,54 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import c from "../../utils/options";
import { ISearchVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const res = await lust.fetchBody(url);
const $ = load(res);
console.log(url);
class XvideosSearch {
search: object[];
data: object;
constructor() {
this.search = $("div#video-player-bg")
.map((i, el) => {
const script = $(el).find("script").html();
const video_related = script?.split("var video_related=")[1];
const badJson = video_related?.split("];")[0] + "]";
const actualResult = JSON.parse(String(badJson));
const result = actualResult.map((el: any) => {
return {
link: `${c.XVIDEOS}${el.u}`,
id: el.u,
title: el.t,
image: el.i,
duration: el.d,
views: `${el.n}, ${el.r}`,
video: null
};
});
return result;
}).get();
}
}
const x = new XvideosSearch();
if (x.search.length === 0) throw Error("No result found");
const data = x.search as unknown as string[];
const result: ISearchVideoData = {
success: true,
data: data,
source: url,
};
return result;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -0,0 +1,66 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import c from "../../utils/options";
import { ISearchVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
console.log(url);
const res = await lust.fetchBody(url);
const $ = load(res);
class XvideosSearch {
search: object[];
constructor() {
const data = $("div.thumb-under")
.map((i, el) => {
return {
title: $(el).find("a").attr("title"),
duration: $(el).find("span.duration")
.map((i, el) => {
return $(el).text();
}).get()[0],
};
}).get();
this.search = $("div.mozaique.cust-nb-cols")
.find("div.thumb")
.map((i, el) => {
return {
link: `${c.XVIDEOS}${$(el).find("a").attr("href")}` || "None",
id: $(el).find("a").attr("href") || "None",
image: $(el).find("img").attr("data-src") || "None",
title: data[i].title || "None",
duration: data[i].duration === data[i + 1]?.duration
? ""
: data[i].duration || "None",
rating: null,
video: null
};
}).get();
this.search = this.search.filter((el: any) => {
return !el.id.includes("THUMBNUM");
});
this.search = this.search.filter((el: any) => {
return el.id.includes("/video");
});
}
}
const xv = new XvideosSearch();
if (xv.search.length === 0) throw Error("No result found");
const data = xv.search as unknown as string[];
const result: ISearchVideoData = {
success: true,
data: data,
source: url,
};
return result;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

73
src/scraper/youporn/youpornGetController.ts ノーマルファイル
ファイルの表示

@ -0,0 +1,73 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import { IVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const resolve = await lust.fetchBody(url);
const $ = load(resolve);
class YouPorn {
link: string;
id: string;
title: string;
image: string;
duration: string;
views: string;
rating: string;
publish: string;
upVote: string;
downVote: null;
video: string;
tags: string[];
models: string[];
constructor() {
this.link = $("link[rel='canonical']").attr("href") || "None";
this.id = this.link.replace("https://www.youporn.com/watch/", "") || "None";
this.title = $("meta[property='og:title']").attr("content") || "None";
this.image = $("meta[property='og:image']").attr("content") || "None";
this.duration = $("meta[property='video:duration']").attr("content") || "0";
this.views = $("div.feature.infoValueBlock").find("div[data-value]").attr("data-value") || "0";
this.rating = $("div.feature").find("span").text().replace(/[^0-9.,%]/g, "") || "0";
this.publish = $("div.video-uploaded").find("span").text() || "None";
this.upVote = this.views;
this.downVote = null;
this.video = `https://www.youporn.com/embed/${this.id}`;
this.tags = $("a[data-espnode='category_tag'], a[data-espnode='porntag_tag']")
.map((i, el) => {
return $(el).text();
}).get();
this.models = $("a[data-espnode='pornstar_tag']")
.map((i, el) => {
return $(el).text();
}).get();
}
}
const yp = new YouPorn();
const data: IVideoData = {
success: true,
data: {
title: lust.removeHtmlTagWithoutSpace(yp.title),
id: yp.id,
image: yp.image,
duration: lust.secondToMinute(Number(yp.duration)),
views: yp.views,
rating: yp.rating,
uploaded: yp.publish,
upvoted: yp.upVote,
downvoted: yp.downVote,
models: yp.models,
tags: yp.tags
},
source: yp.link,
assets: [yp.video, yp.image]
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -0,0 +1,53 @@
import { load } from "cheerio";
import LustPress from "../../LustPress";
import c from "../../utils/options";
import { ISearchVideoData } from "../../interfaces";
const lust = new LustPress();
export async function scrapeContent(url: string) {
try {
const res = await lust.fetchBody(url);
const $ = load(res);
class YouPornSearch {
dur: string[];
search: object[];
constructor() {
this.dur = $("div.video-duration").map((i, el) => {
return $(el).text();
}).get();
this.search = $("a[href^='/watch/']")
.map((i, el) => {
const link = $(el).attr("href");
const id = `${link}`.split("/")[2] + "/" + `${link}`.split("/")[3];
const title = $(el).find("div.video-box-title").text();
const image = $(el).find("img").attr("data-thumbnail");
return {
link: `${c.YOUPORN}${link}`,
id: id,
title: lust.removeHtmlTagWithoutSpace(title),
image: image,
duration: this.dur[i],
views: null,
video: `https://www.youporn.com/embed/${id}`,
};
}).get();
}
}
const yp = new YouPornSearch();
if (yp.search.length === 0) throw Error("No result found");
const data = yp.search as unknown as string[];
const result: ISearchVideoData = {
success: true,
data: data,
source: url,
};
return result;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}