UNPKG

@kelvdra/scraper

Version:

A simple scraper by kelvdra.

190 lines (176 loc) 5.17 kB
import axios from 'axios' import yts from 'yt-search' import crypto from 'crypto' class savetube { constructor() { this.ky = 'C5D58EF67A7584E4A29F6C35BBC4EB12'; this.fmt = ['144', '240', '360', '480', '720', '1080', 'mp3']; this.m = /^((?:https?:)?\/\/)?((?:www|m|music)\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?(?:embed\/)?(?:v\/)?(?:shorts\/)?([a-zA-Z0-9_-]{11})/; this.is = axios.create({ headers: { 'content-type': 'application/json', 'origin': 'https://yt.savetube.me', 'user-agent': 'Mozilla/5.0 (Android 15; Mobile; SM-F958; rv:130.0) Gecko/130.0 Firefox/130.0' } }) } async decrypt(enc) { try { const [sr, ky] = [Buffer.from(enc,'base64'), Buffer.from(this.ky,'hex')] const [iv, dt] = [sr.slice(0,16), sr.slice(16)] const dc = crypto.createDecipheriv('aes-128-cbc', ky, iv); return JSON.parse(Buffer.concat([dc.update(dt), dc.final()]).toString()); } catch (e) { throw new Error(`Error while decrypting data: ${e.message}`); } } async getCdn() { const response = await this.is.get("https://media.savetube.vip/api/random-cdn"); if (!response.status) return response; return { status: true, data: response.data.cdn }; } async download(url, format = 'mp3') { const id = url.match(this.m)?.[3]; if (!id) { return { status: false, msg: "ID cannot be extracted from url" }; } if (!format || !this.fmt.includes(format)) { return { status: false, msg: "Formats not found", list: this.fmt }; } try { const u = await this.getCdn(); if (!u.status) return u; const res = await this.is.post(`https://${u.data}/v2/info`, { url: `https://www.youtube.com/watch?v=${id}` }); const dec = await this.decrypt(res.data.data); const dl = await this.is.post(`https://${u.data}/download`, { id: id, downloadType: format === 'mp3' ? 'audio' : 'video', quality: format === 'mp3' ? '128' : format, key: dec.key }) return { status: true, title: dec.title, format: format, thumb: dec.thumbnail || `https://i.ytimg.com/vi/${id}/hqdefault.jpg`, duration: dec.duration, cached: dec.fromCache, url: dl.data.data.downloadUrl }; } catch (error) { return { status: false, error: error.message }; } } } export const ytmp3 = async (link, quality = "mp3") => { try { const info = await yts(link); const sv = new savetube() const result = await sv.download(link, quality) return { status: true, creator: '@kelvdra/scraper', metadata: info.all[0], download: result }; } catch (e) { return { status: false, creator: "@kelvdra/scraper", message: e.message }; } } export const ytmp4 = async (link, quality = "360") => { if (!link.includes('youtube.com') && !link.includes('youtu.be')) { return { status: false, message: 'URL YouTube tidak valid' }; } try { const info = await yts(link); const sv = new savetube() const result = await sv.download(link, quality) return { status: true, creator: '@kelvdra/scraper', metadata: info.all[0], download: result }; } catch (e) { return { status: false, creator: "@kelvdra/scraper", message: e.message }; } } export const playmp3 = async (query, quality = "mp3") => { try { const searchResult = await yts(query); if (!searchResult.status || !searchResult.results.length) return { status: false, message: 'Video tidak ditemukan' }; const sv = new savetube() const results = []; for (let video of searchResult.results.slice(0, 5)) { const downloadInfo = await sv.download(video.url, quality) results.push({ title: video.title, author: video.author.name, duration: video.timestamp, url: video.url, thumbnail: video.thumbnail, download: downloadInfo }); } return { status: true, creator: '@hydra/scraper', type: 'audio', results }; } catch (err) { return { status: false, message: err.message }; } } export const playmp4 = async (query, quality = 360) => { try { const searchResult = await yts(query); if (!searchResult.status || !searchResult.results.length) return { status: false, message: 'Video tidak ditemukan' }; const sv = new savetube() const results = []; for (let video of searchResult.results.slice(0, 5)) { const downloadInfo = await sv.download(video.url, quality) results.push({ title: video.title, author: video.author.name, duration: video.timestamp, url: video.url, thumbnail: video.thumbnail, download: downloadInfo }); } return { status: true, creator: '@hydra/scraper', type: 'video', results }; } catch (err) { return { status: false, message: err.message }; } }