Skip to content

Instantly share code, notes, and snippets.

@asdfugil
Last active February 19, 2022 04:27
Show Gist options
  • Save asdfugil/50d3ef5592a17ce09495f1c8c12f6d5e to your computer and use it in GitHub Desktop.
Save asdfugil/50d3ef5592a17ce09495f1c8c12f6d5e to your computer and use it in GitHub Desktop.
chinaq.tv 中國人線上看 downloader 下載器
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
```
#!/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