Created
December 18, 2022 13:07
-
-
Save gkio/675b704a83e9bd1ec168a383051d3f3a to your computer and use it in GitHub Desktop.
This file contains hidden or 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
use std::thread; | |
use reqwest::Client; | |
use reqwest::Proxy; | |
use std::fs; | |
use std::io::{self, BufRead}; | |
use std::sync::{Mutex, MutexGuard}; | |
use std::thread::sleep; | |
use std::time::Duration; | |
use rand::Rng; | |
const CLIENT_ID: &str = "XXXXXXXXXX"; | |
struct TokenAndSig { | |
token: String, | |
sig: String, | |
} | |
async fn get_info(client: &reqwest::Client, channel: &str) -> TokenAndSig { | |
let url = format!("https://api.twitch.tv/api/channels/{}/access_token?need_https=true&oauth_token=&platform=web&player_backend=mediaplayer&player_type=site&client_id={}", channel, &CLIENT_ID); | |
// headers | |
let mut headers = reqwest::header::HeaderMap::new(); | |
headers.insert("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36".parse().unwrap()); | |
headers.insert("Accept", "application/vnd.twitchtv.v5+json; charset=UTF-8".parse().unwrap()); | |
headers.insert("Content-Type", "application/json; charset=UTF-8".parse().unwrap()); | |
headers.insert("Accept-Language", "en-us".parse().unwrap()); | |
headers.insert("Referer", format!("https://www.twitch.tv/{}", channel).as_str().parse().unwrap()); | |
let response_blob = client.get(&url).headers(headers).send().await.unwrap(); | |
let response_text = response_blob.text().await.unwrap(); | |
// text to json | |
let json: serde_json::Value = serde_json::from_str(&response_text).unwrap(); | |
let token = json["token"].as_str().unwrap(); | |
let sig = json["sig"].as_str().unwrap(); | |
let token = token.replace("\\", ""); | |
TokenAndSig { token, sig: sig.to_string() } | |
} | |
async fn get_video_sequence(client: &reqwest::Client, channel: &str, token: &str, sig: &str) -> String { | |
let url = format!("https://usher.ttvnw.net/api/channel/hls/{}.m3u8?allow_source=true&baking_bread=true&baking_brownies=true&baking_brownies_timeout=1050&fast_bread=true&p=3168255&player_backend=mediaplayer&playlist_include_framerate=true&reassignments_supported=false&rtqos=business_logic_reverse&cdm=wv&sig={}&token={}", channel, &sig, &token); | |
let mut headers = reqwest::header::HeaderMap::new(); | |
headers.insert("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36".parse().unwrap()); | |
headers.insert("Accept", "application/vnd.twitchtv.v5+json; charset=UTF-8".parse().unwrap()); | |
let response_blob = client.get(&url).headers(headers).send().await.unwrap(); | |
let body = response_blob.text().await.unwrap(); | |
let start = body.find("https://").unwrap() + "https://".len(); | |
let end = body[start..].find(".m3u8").unwrap() + start; | |
return format!("https://{}.m3u8", &body[start..end]); | |
} | |
fn read_proxy(filename: &str) -> io::Result<Vec<Vec<String>>> { | |
let file = fs::File::open(filename)?; | |
let reader = io::BufReader::new(file); | |
let mut fields = Vec::new(); | |
for line in reader.lines() { | |
let line = line?; | |
let f: Vec<String> = line.split(':').map(|s| s.to_owned()).collect(); | |
fields.push(f); | |
} | |
Ok(fields) | |
} | |
fn create_proxy_client(proxy: &Vec<String>) -> reqwest::Client { | |
let username = "XXX"; | |
let password = "XXX"; | |
// "http://user:pass@proxy_ip:proxy_port" | |
let proxy_url = format!("http://{}:{}@{}:{}", username, password, proxy[0], proxy[1]); | |
let proxy = Proxy::all(&*proxy_url).unwrap(); | |
let client = Client::builder() | |
.proxy(proxy).build().unwrap(); | |
client | |
} | |
async fn send_view(client: &reqwest::Client, url: &str) { | |
let mut headers = reqwest::header::HeaderMap::new(); | |
headers.insert("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36".parse().unwrap()); | |
headers.insert("Accept", "application/vnd.twitchtv.v5+json; charset=UTF-8".parse().unwrap()); | |
let res = client.get(&*url).headers(headers).send().await.unwrap(); | |
println!("Status: {}", res.status()); | |
} | |
async fn execute(i: usize,proxies: &Vec<Vec<String>>, channel: &str) { | |
let proxy = proxies.get(i).clone().unwrap(); | |
let http_client = create_proxy_client(&proxy); | |
let info = get_info(&http_client, &channel).await; | |
let video_sequence_url = get_video_sequence(&http_client, &channel, &info.token, &info.sig).await; | |
loop { | |
send_view(&http_client, &video_sequence_url).await; | |
sleep(Duration::from_secs(5)); | |
} | |
} | |
async fn execute_in_thread(i: usize, proxies: &Vec<Vec<String>>, username: &str) { | |
execute(i,&proxies, username).await; | |
} | |
static PROXIES: Mutex<Vec<Vec<String>>> = Mutex::new(Vec::new()); | |
#[tokio::main] | |
async fn main() { | |
let proxies = read_proxy("proxy.txt").unwrap(); | |
let n_threads = proxies.len() as usize; | |
let mut threads = Vec::new(); | |
for i in 0..n_threads { | |
let proxies = proxies.clone(); | |
threads.push(thread::spawn(move || { | |
let mut rt = tokio::runtime::Runtime::new().unwrap(); | |
rt.block_on(execute_in_thread(i, &proxies, "twitch_live_username")); | |
})); | |
} | |
for thread in threads { | |
thread.join().unwrap(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment