Spaces:
Paused
Paused
| using namespace std; | |
| const int NUM_THREADS = 12; | |
| mutex log_mutex; | |
| // Koleksi User-Agent Modern (Desktop & Mobile) | |
| const vector<string> USER_AGENTS = { | |
| "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", | |
| "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", | |
| "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0", | |
| "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", | |
| "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Mobile/15E148 Safari/604.1", | |
| "Mozilla/5.0 (Linux; Android 14; SM-S928B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36", | |
| "Mozilla/5.0 (iPad; CPU OS 17_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/124.0.6367.111 Mobile/15E148 Safari/604.1", | |
| "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0" | |
| }; | |
| size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) { | |
| return fwrite(ptr, size, nmemb, stream); | |
| } | |
| bool download_chunk(const string& url, const string& out_filename, long start_byte, long end_byte, int thread_id) { | |
| CURL *curl; | |
| FILE *fp; | |
| CURLcode res; | |
| curl = curl_easy_init(); | |
| if (curl) { | |
| fp = fopen(out_filename.c_str(), "wb"); | |
| string range = to_string(start_byte) + "-" + to_string(end_byte); | |
| // 1. Pilih User-Agent acak/berbeda untuk thread ini | |
| string selected_ua = USER_AGENTS[thread_id % USER_AGENTS.size()]; | |
| // 2. Rakit Header HTTP Siluman (Mirip Request Video Asli) | |
| struct curl_slist *headers = NULL; | |
| headers = curl_slist_append(headers, ("User-Agent: " + selected_ua).c_str()); | |
| headers = curl_slist_append(headers, "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"); | |
| headers = curl_slist_append(headers, "Accept-Language: en-US,en;q=0.9,id;q=0.8"); | |
| headers = curl_slist_append(headers, "Connection: keep-alive"); | |
| headers = curl_slist_append(headers, "Sec-Fetch-Dest: video"); // Manipulasi bahwa ini adalah request elemen <video> | |
| headers = curl_slist_append(headers, "Sec-Fetch-Mode: no-cors"); | |
| headers = curl_slist_append(headers, "Sec-Fetch-Site: cross-site"); | |
| headers = curl_slist_append(headers, "Cache-Control: no-cache"); | |
| headers = curl_slist_append(headers, "Pragma: no-cache"); | |
| curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); | |
| curl_easy_setopt(curl, CURLOPT_RANGE, range.c_str()); | |
| curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); | |
| curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); | |
| curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Pasang header siluman | |
| curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); | |
| curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); | |
| curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Ikuti jika server melempar redirect (CDN behavior) | |
| curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L); | |
| int retries = 3; | |
| while (retries > 0) { | |
| res = curl_easy_perform(curl); | |
| if (res == CURLE_OK) break; | |
| retries--; | |
| this_thread::sleep_for(chrono::milliseconds(500)); // Delay super singkat jika gagal, lalu hantam lagi | |
| } | |
| fclose(fp); | |
| curl_slist_free_all(headers); // Bersihkan memory header | |
| curl_easy_cleanup(curl); | |
| lock_guard<mutex> guard(log_mutex); | |
| if (res == CURLE_OK) { | |
| cout << "Thread " << thread_id << " (UA: " << selected_ua.substr(13, 15) << "...) Selesai!\n"; | |
| return true; | |
| } else { | |
| cout << "Thread " << thread_id << " Gagal: " << curl_easy_strerror(res) << "\n"; | |
| return false; | |
| } | |
| } | |
| return false; | |
| } | |
| int main(int argc, char* argv[]) { | |
| if (argc < 4) { | |
| cout << "Usage: ./speedster <URL> <OUTPUT_FILE> <TOTAL_SIZE>\n"; | |
| return 1; | |
| } | |
| string url = argv[1]; | |
| string output_file = argv[2]; | |
| long total_size = stol(argv[3]); | |
| long chunk_size = total_size / NUM_THREADS; | |
| vector<thread> threads; | |
| vector<string> temp_files; | |
| cout << "[Speedster Engine] Memulai 12 Threads Siluman Paralel...\n"; | |
| for (int i = 0; i < NUM_THREADS; ++i) { | |
| long start_byte = i * chunk_size; | |
| long end_byte = (i == NUM_THREADS - 1) ? total_size - 1 : (start_byte + chunk_size - 1); | |
| string temp_file = output_file + ".part" + to_string(i); | |
| temp_files.push_back(temp_file); | |
| threads.emplace_back(download_chunk, url, temp_file, start_byte, end_byte, i); | |
| } | |
| for (auto& t : threads) { | |
| t.join(); | |
| } | |
| cout << "[Speedster Engine] Merging chunks...\n"; | |
| ofstream final_file(output_file, ios_base::binary); | |
| for (const string& temp_file : temp_files) { | |
| ifstream file_chunk(temp_file, ios_base::binary); | |
| final_file << file_chunk.rdbuf(); | |
| file_chunk.close(); | |
| remove(temp_file.c_str()); | |
| } | |
| final_file.close(); | |
| cout << "[Speedster Engine] Selesai: " << output_file << "\n"; | |
| return 0; | |
| } | |