skribd / src /service /EverandDownloader.js
rkwyu
Support everand podcast
58293b7
import cliProgress from "cli-progress"
import { puppeteerSg } from "../utils/request/PuppeteerSg.js";
import { pdfGenerator } from "../utils/io/PdfGenerator.js";
import { configLoader } from "../utils/io/ConfigLoader.js";
import { directoryIo } from "../utils/io/DirectoryIo.js"
import * as everandRegex from "../const/EverandRegex.js"
import { Image } from "../object/Image.js"
import sharp from "sharp";
import axios from "axios";
import fs from "fs"
const output = configLoader.load("DIRECTORY", "output")
class EverandDownloader {
constructor() {
if (!EverandDownloader.instance) {
EverandDownloader.instance = this
}
return EverandDownloader.instance
}
async execute(url) {
if (url.match(everandRegex.PODCAST_SERIES)) {
await this.series(url, )
} else if (url.match(everandRegex.PODCAST_EPISODE)) {
await this.listen(`https://www.everand.com/listen/podcast/${everandRegex.PODCAST_EPISODE.exec(url)[1]}`)
} else if (url.match(everandRegex.PODCAST_LISTEN)) {
await this.listen(url)
} else {
throw new Error(`Unsupported URL: ${url}`)
}
}
async listen(url, isEpisode) {
if (typeof isEpisode === "undefined") {
isEpisode = true
}
const episodeId = everandRegex.PODCAST_LISTEN.exec(url)[1]
// navigate to everand
let page = await puppeteerSg.getPage(url)
// wait rendering
await new Promise(resolve => setTimeout(resolve, 1000))
// get title, audio-url, series-url
const title = await page.evaluate(() => eval('Scribd.current_doc.short_title'))
const audioUrl = await page.evaluate(() => document.querySelector('audio#audioplayer').src)
const seriesUrl = await page.evaluate(() => document.querySelector('a[href^="https://www.everand.com/podcast-show/"]').href)
// prepare output dir
let seriesId = everandRegex.PODCAST_SERIES.exec(seriesUrl)[1]
let dir = `${output}/${seriesId}`
await directoryIo.create(dir)
// download audio
const bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
if (isEpisode) {
bar.start(1, 0)
}
let path = `${dir}/${episodeId}_${title}.mp3`
const resp = await axios.get(audioUrl, { responseType: 'stream' })
resp.data.pipe(fs.createWriteStream(path))
if (isEpisode) {
bar.update(1)
bar.stop()
}
await page.close()
if (isEpisode) {
await puppeteerSg.close()
}
}
async series(url) {
const seriesId = everandRegex.PODCAST_SERIES.exec(url)[1]
// navigate to everand
let page = await puppeteerSg.getPage(url)
// wait rendering
await new Promise(resolve => setTimeout(resolve, 1000))
// get number-of-episodes
const totalEpisode = await page.evaluate(() => parseInt(document.querySelector('span[data-e2e="podcast-series-header-total-episodes"]').textContent.replace("episodes", "").trim()))
// get pages
const totalPage = await page.evaluate(() => [...document.querySelectorAll('div[data-e2e="pagination"] a[aria-label^="Page"]')].at(-1).textContent)
const bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic)
bar.start(totalEpisode, 0)
xx:
for (let i = 1; i <= totalPage; i++) {
await page.goto(`${url}?page=${i}&sort=desc`, { waitUntil: "load" })
await new Promise(resolve => setTimeout(resolve, 1000))
let episodes = await page.evaluate(() => [...document.querySelectorAll('div.breakpoint_hide.below a[data-e2e="podcast-episode-player-button"]')].map(x => x.href))
for (let j = 0; j < episodes.length; j++ ) {
await this.listen(episodes[j], false)
bar.update(((i - 1) * 10) + (j + 1))
break xx
}
}
bar.stop()
await page.close()
await puppeteerSg.close()
}
}
export const everandDownloader = new EverandDownloader()