@yeci226/nhentai-ts
Version:
A scraper for NHentai with types
137 lines (132 loc) • 4.96 kB
text/typescript
import PDFDocument from 'pdfkit'
import { tmpdir } from 'os'
import { writeFile, unlink, readFile, mkdir, stat } from 'fs/promises'
import { createWriteStream, existsSync } from 'fs'
import axios from 'axios'
import JSZip from 'jszip'
import { join } from 'path'
export class Pages {
#title: string
/**
*
* @param pages An array of URLS of the doujin pages
*/
constructor(public pages: string[], __title: string) {
this.#title = __title
}
/**
* Builds a PDF from the doujin pages
* @returns Buffer of the PDF
*/
public async PDF(): Promise<Buffer>
/**
* Builds a PDF from the doujin pages
* @param filename Filename of the PDF
* @returns The filename where the PDF is saved
*/
public async PDF(filename: string): Promise<string>
public async PDF(filename?: string): Promise<Buffer | string> {
const pdf = new PDFDocument({ autoFirstPage: false })
const file = filename
? `${filename}${filename.endsWith('.pdf') ? '' : '.pdf'}`
: `${tmpdir()}/${Math.random().toString(36)}.pdf`
const stream = createWriteStream(file)
pdf.pipe(stream)
for (const url of this.pages) {
const { data } = await axios.get<Buffer>(url, {
headers: url.includes('cdn.dogehls.xyz')
? { Referer: 'https://nhentai.to' }
: {},
responseType: 'arraybuffer'
})
pdf.addPage()
pdf.image(new Uint8Array(data), 0, 0, {
fit: [pdf.page.width, pdf.page.height]
})
const index = this.pages.indexOf(url)
if (index === this.pages.length - 1) pdf.end()
}
await new Promise((resolve, reject) => {
stream.on('finish', () => resolve(file))
stream.on('error', reject)
})
if (filename) return file
const buffer = await readFile(file)
await unlink(file)
return buffer
}
/**
* Builds a zip of doujin pages
* @returns Buffer of the result zip
*/
public async zip(): Promise<Buffer>
/**
* Builds a zip of doujin pages and saves it locally
* @param filename Filename of the zip where it should be saved
* @returns The filename of the saved zip
*/
public async zip(filename: string): Promise<string>
public async zip(filename?: string): Promise<Buffer | string> {
const zip = new JSZip()
const folder = zip.folder(this.#title)
for (const url of this.pages)
folder.file(
`${this.pages.indexOf(url) + 1}.${
url.split('.')[url.split('.').length - 1]
}`,
new Uint8Array(
(
await axios.get<Buffer>(url, {
headers: url.includes('cdn.dogehls.xyz')
? { Referer: 'https://nhentai.to' }
: {},
responseType: 'arraybuffer'
})
).data
),
{ binary: true }
)
const buffer = await zip.generateAsync({ type: 'nodebuffer' })
if (filename) {
await writeFile(
`${filename}${filename.endsWith('.zip') ? '' : '.zip'}`,
new Uint8Array(buffer)
)
return `${filename}${filename.endsWith('.zip') ? '' : '.zip'}`
}
return buffer
}
/**
* Downloads the pages of a doujin and saves all of it in a folder
* @param folderName The name of the folder in which all of the pages should be saved
*/
public async download(folderName: string): Promise<void> {
if (!folderName)
throw new Error(
'No folder name provided to save the downloaded doujin pages'
)
if (!existsSync(folderName))
await mkdir(folderName, { recursive: true })
const isDirectory = (await stat(folderName)).isDirectory()
if (!isDirectory)
throw new Error(
'Expected a directory for saving the downloads, but recieved a file.'
)
for (const url of this.pages)
await writeFile(
join(
folderName,
`${this.pages.indexOf(url) + 1}.${
url.split('.')[url.split('.').length - 1]
}`
),
new Uint8Array(
(
await axios.get<Buffer>(url, {
responseType: 'arraybuffer'
})
).data
)
)
}
}