Skip to content

Instantly share code, notes, and snippets.

@gkio
Created December 18, 2022 13:07
Show Gist options
  • Save gkio/675b704a83e9bd1ec168a383051d3f3a to your computer and use it in GitHub Desktop.
Save gkio/675b704a83e9bd1ec168a383051d3f3a to your computer and use it in GitHub Desktop.
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