Last active
February 19, 2022 04:27
-
-
Save asdfugil/50d3ef5592a17ce09495f1c8c12f6d5e to your computer and use it in GitHub Desktop.
chinaq.tv 中國人線上看 downloader 下載器
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
chinaq.tv downloader | |
=================== | |
chinaq.tv 中國人線上看 downloader 下載器 | |
Dependencies 依賴: yt-dlp, nodejs, node-fetch | |
Usage 用法: node download.mjs [-l, --list-sources] <url> <source> | |
其中url為網址 | |
source為例如WYun等來源 | |
例子 Examples: | |
``` | |
node download.mjs https://chinaq.tv/cn200717/39.html | |
node download.mjs -l https://chinaq.tv/cn200717/39.html | |
node download.mjs https://chinaq.tv/cn200717/39.html wyun | |
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
'use strict' | |
// chinaq.tv downloader | |
// MIT License | |
// Author: Nick Chan | |
// Year: 2022 | |
import fetch from 'node-fetch' | |
import { spawnSync, spawn } from 'child_process' | |
if (!process.argv[2]) { | |
console.error(`Usage: ${process.argv[1]} [-l, --list-sources] <url> [source name]`); | |
process.exit(1); | |
} | |
const url = ['-s', '--sync', '-l', '--list-sources'].some(x => x === process.argv[2]) ? process.argv[3] : process.argv[2] | |
const ua = "Mozilla/5.0 (Linux; Android 8.0.0; SM-G9350) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.60 Mobile Safari/537.36"; | |
const playlist_regex = /https?:\/\/.*\..*\/cn(\d+)\/?/ | |
if (/https?:\/\/.*\..*\/cn\d+\/+\d+\.html(?:\/?)+(?:#\d+)?/.test(url)) { | |
const html = await fetch(url,{ headers: { 'user-agent': ua }}).then(res => res.text()) | |
const data_regex = /<a href="#\d+" data-data="(.*?)"><strong>片源/gm | |
let match = null | |
const json_arr = [] | |
let title; | |
// site vaients | |
const regex_1 = /<title>(.*?) - 中國人線上看<\/title>/ | |
const regex_2_1 = /t = '(.*)';\n/ | |
const regex_2_2 = /<h1><span id="htitle">(.*)<\/span><font style="font-size:0.6em">/ | |
if (html.match(regex_1)) title = html.match(regex_1)[1] | |
else title = html.match(regex_2_1)[1] + ' 第' + html.match(regex_2_2)[1] + '集' | |
while ((match = data_regex.exec(html)) !== null) { | |
const data = JSON.parse(Buffer.from(match[1].split('').reverse().join(''),'base64').toString('utf8')) | |
json_arr.push(data) | |
} | |
if (process.argv[3] === url) { | |
console.log('Sources: ' + json_arr.map(x => x.source).join(', ')) | |
process.exit(0) | |
} | |
const selected_src = process.argv[3] ? json_arr.find(x => x.source.toLowerCase() === process.argv[3].toLowerCase()) : json_arr.find(x => x.source.toLowerCase() !== 'youtube') | |
if (!selected_src) { | |
console.error(`Source ${process.argv[3]} is not found for this video.`) | |
process.exit(3); | |
} | |
console.log('Selecting source: ' + selected_src.source); | |
if (selected_src.source.toLowerCase() === 'youtube') { | |
const ytdl_child = spawnSync('yt-dlp', [ '--output', `${title} ${selected_src.source}.%(ext)s`, `https://youtube.com/watch?v=${selected_src.ids[0]}`], { stdio: 'inherit' }) | |
process.exit(ytdl_child.status === null ? 255 : ytdl_child.status) | |
} | |
const player_html = await fetch('https://chinaq.tv/a/m3u8/?ref=' + selected_src.ids[0], { headers: {'user-agent':ua}}).then(res => res.text()) | |
const m3u8url = player_html.match(/var m3u8url = '(https?:\/\/.*\.m3u8)'/)[1] | |
let status; | |
let count = 0 | |
while (status !== 0) { | |
const exited = spawnSync('yt-dlp', ['--hls-prefer-native', '--no-check-certificates', '--user-agent', ua, '--output', `${title} ${selected_src.source}.%(ext)s`, '--force-generic-extractor', '--fragment-retries', '20', m3u8url ], { stdio: 'inherit' }) | |
status = exited.status | |
count += 1 | |
if (count > 100) { | |
console.log(`ERROR: too many retries (${count})`); | |
process.exit(4); | |
} | |
} | |
} else if (playlist_regex.test(url)) { | |
const html = await fetch(url,{ headers: { 'user-agent': ua }}).then(res => res.text()); | |
const playlist_id = url.match(playlist_regex)[1] | |
const matches = html.matchAll(new RegExp(`onclick="xxx\\('cn` + playlist_id + `','(\\d+)',1\\);return false;" href="javascript\\:void\\(0\\)"`,'g')) | |
let i = 0 | |
let episodes = [] | |
for (const match of matches) {episodes.push(match[1])} | |
if (episodes.length === 0) { | |
console.error('No episodes found.') | |
process.exit(5); | |
} | |
for (const episode of episodes.reverse()) { | |
i++; | |
let downloader = spawn | |
if (process.argv[2] === '-s' || process.argv[2] === '--sync') downloader = spawnSync | |
const selected_src = process.argv[2] === '-s' || process.argv[2] === '--sync' ? process.argv[4] : process.argv[3] | |
const video_url = url + '/' + episode + '.html' | |
const args = [ process.argv[1], video_url ] | |
if (selected_src) args.push(selected_src) | |
console.log(`Downloading video ${i} of ${episodes.length}: ` + video_url); | |
if (downloader = spawn) await new Promise((resolve) => setTimeout(() => resolve(),1000)) // avoid spamming the server | |
downloader(process.argv[0], args, { stdio: 'inherit' }) | |
} | |
} else { | |
console.error('Invalid URL.'); | |
process.exit(-1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment