package main import ( "fmt" "io" "log" "net/http" "strings" "time" ) func main() { // Handler untuk proxy video http.HandleFunc("/proxy", proxyHandler) // Serve HTML generator http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "index.html") }) log.Println("🚀 Server running on :7860") log.Println("📡 Proxy endpoint: http://localhost:7860/proxy") log.Println("🌐 CORS enabled for all origins") log.Fatal(http.ListenAndServe(":7860", nil)) } func proxyHandler(w http.ResponseWriter, r *http.Request) { // ============ CORS HEADERS ============ // Allow all origins untuk remote upload w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, Range") w.Header().Set("Access-Control-Expose-Headers", "Content-Length, Content-Range, Content-Disposition, Accept-Ranges") w.Header().Set("Access-Control-Allow-Credentials", "true") w.Header().Set("Access-Control-Max-Age", "86400") // 24 jam cache preflight // Handle preflight OPTIONS request if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return } // ============ PARSE PARAMETERS ============ query := r.URL.Query() videoParam := query.Get("video") exp := query.Get("exp") sig := query.Get("sig") videoType := query.Get("type") // "mp4" atau "null" domain := query.Get("d") // domain CDN, misal: cdn.xvdhsy.pro if videoParam == "" || exp == "" || sig == "" || domain == "" { http.Error(w, "Missing parameters: video, exp, sig, d required", http.StatusBadRequest) return } // ============ REKONSTRUKSI URL ============ // Hapus .mp4 dari video parameter videoId := strings.TrimSuffix(videoParam, ".mp4") // Pastikan domain tidak memiliki protocol domain = strings.TrimPrefix(domain, "http://") domain = strings.TrimPrefix(domain, "https://") domain = strings.TrimSuffix(domain, "/") // Rekonstruksi URL asli // Format: https://{domain}/{videoId}?exp={exp}&sig={sig} originalURL := fmt.Sprintf("https://%s/%s?exp=%s&sig=%s", domain, videoId, exp, sig) log.Printf("📥 Proxy request: %s -> %s", r.URL.String(), originalURL) // ============ BUAT REQUEST ============ client := &http.Client{ Timeout: 120 * time.Second, // Timeout lebih lama untuk video besar } req, err := http.NewRequest("GET", originalURL, nil) if err != nil { http.Error(w, "Failed to create request", http.StatusInternalServerError) return } // Set headers untuk request ke CDN req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") req.Header.Set("Accept", "*/*") req.Header.Set("Accept-Encoding", "gzip, deflate, br") req.Header.Set("Accept-Language", "en-US,en;q=0.9") req.Header.Set("Connection", "keep-alive") req.Header.Set("Sec-Fetch-Dest", "video") req.Header.Set("Sec-Fetch-Mode", "cors") req.Header.Set("Sec-Fetch-Site", "cross-site") // Copy Range header untuk support streaming if rangeHeader := r.Header.Get("Range"); rangeHeader != "" { req.Header.Set("Range", rangeHeader) log.Printf("📊 Range request: %s", rangeHeader) } // ============ EKSEKUSI REQUEST ============ resp, err := client.Do(req) if err != nil { log.Printf("❌ Error fetching video: %v", err) http.Error(w, "Failed to fetch video", http.StatusInternalServerError) return } defer resp.Body.Close() log.Printf("✅ Response status: %s, Content-Length: %s", resp.Status, resp.Header.Get("Content-Length")) // ============ SET RESPONSE HEADERS ============ // CORS headers (already set above) // Content Type untuk video contentType := resp.Header.Get("Content-Type") if contentType == "" || !strings.Contains(contentType, "video") { contentType = "video/mp4" } w.Header().Set("Content-Type", contentType) // Support range requests (streaming & seeking) w.Header().Set("Accept-Ranges", "bytes") // Copy Content-Length jika ada if resp.Header.Get("Content-Length") != "" { w.Header().Set("Content-Length", resp.Header.Get("Content-Length")) } // Set Content-Disposition berdasarkan parameter type filename := videoId if videoType == "mp4" { filename = videoId + ".mp4" } // Gunakan attachment agar bisa di-download oleh remote upload w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) // Cache control untuk performance w.Header().Set("Cache-Control", "public, max-age=86400") // Support untuk Doodstream & platform sejenis w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Frame-Options", "SAMEORIGIN") // ============ KIRIM RESPONSE ============ // Copy status code w.WriteHeader(resp.StatusCode) // Copy body dengan buffering untuk performance buffer := make([]byte, 32*1024) // 32KB buffer io.CopyBuffer(w, resp.Body, buffer) log.Printf("✅ Stream completed for: %s", videoId) }