diff --git a/src/core/scraper/vidlox.js b/src/core/scraper/vidlox.js new file mode 100644 index 00000000..c9ab9726 --- /dev/null +++ b/src/core/scraper/vidlox.js @@ -0,0 +1,37 @@ +/** + * @module + */ +/* eslint-disable require-await */ + +import { matchPattern } from "../../tools/matchpattern.js"; + +/** + * L'expression rationnelle pour extraire l'URL de la vidéo. + * + * @constant {RegExp} + */ +const URL_REGEXP = /sources: \["([^"]+)",/u; + +/** + * Extrait les informations nécessaire pour lire une vidéo sur Kodi. + * + * @param {URL} _url L'URL d'une vidéo de Bigo Live. + * @param {object} content Le contenu de l'URL. + * @param {Function} content.html La fonction retournant la promesse contenant + * le document HTML. + * @returns {Promise.} Une promesse contenant le lien du + * fichier ou null. + */ +const action = async function (_url, content) { + const doc = await content.html(); + for (const script of doc.querySelectorAll("script:not([src])")) { + const result = URL_REGEXP.exec(script.text); + if (null === result) { + continue; + } + + return result[1]; + } + return null; +}; +export const extract = matchPattern(action, "*://vidlox.me/*"); diff --git a/src/core/scrapers.js b/src/core/scrapers.js index f2a2a53b..2f6b6396 100644 --- a/src/core/scrapers.js +++ b/src/core/scrapers.js @@ -46,6 +46,7 @@ import * as ultimedia from "./scraper/ultimedia.js"; import * as veoh from "./scraper/veoh.js"; import * as video from "./scraper/video.js"; import * as videopress from "./scraper/videopress.js"; +import * as vidlox from "./scraper/vidlox.js"; import * as vimeo from "./scraper/vimeo.js"; import * as vrtnu from "./scraper/vrtnu.js"; import * as youtube from "./scraper/youtube.js"; @@ -92,6 +93,7 @@ const SCRAPERS = [ ultimedia, veoh, videopress, + vidlox, vimeo, vrtnu, youtube, diff --git a/test/integration/scraper/vidlox.js b/test/integration/scraper/vidlox.js new file mode 100644 index 00000000..20a3e92c --- /dev/null +++ b/test/integration/scraper/vidlox.js @@ -0,0 +1,20 @@ +import assert from "assert"; +import { extract } from "../../../src/core/scrapers.js"; + +describe("Scraper: Vidlox", function () { + it("should return URL when it's not a video", async function () { + const url = "https://vidlox.me/faq"; + const options = { depth: 0, incognito: false }; + + const file = await extract(new URL(url), options); + assert.strictEqual(file, url); + }); + + it("should return video URL", async function () { + const url = "https://vidlox.me/30fxi9o50b3v"; + const options = { depth: 0, incognito: false }; + + const file = await extract(new URL(url), options); + assert.ok(file.endsWith("/master.m3u8"), `"${file}".endsWith(...)`); + }); +}); diff --git a/test/unit/core/scraper/vidlox.js b/test/unit/core/scraper/vidlox.js new file mode 100644 index 00000000..6441ce04 --- /dev/null +++ b/test/unit/core/scraper/vidlox.js @@ -0,0 +1,45 @@ +import assert from "assert"; +import { extract } from "../../../../src/core/scraper/vidlox.js"; + +describe("core/scraper/vidlox.js", function () { + describe("extract()", function () { + it("should return null when it's a unsupported URL", async function () { + const url = "https://twitter.com/vidloxtv"; + + const file = await extract(new URL(url)); + assert.strictEqual(file, null); + }); + + it("should return null when it's not a video", async function () { + const url = "https://vidlox.me/foo"; + const content = { + html: () => Promise.resolve(new DOMParser().parseFromString(` + + + `, "text/html")), + }; + + const file = await extract(new URL(url), content); + assert.strictEqual(file, null); + }); + + it("should return video URL", async function () { + const url = "https://vidlox.me/foo"; + const content = { + html: () => Promise.resolve(new DOMParser().parseFromString(` + + + + + `, "text/html")), + }; + + const file = await extract(new URL(url), content); + assert.strictEqual(file, "https://bar.baz/qux.m3u8"); + }); + }); +});