dongsii's picture
Update main.go
05a470b verified
Raw
History Blame Contribute Delete
5.55 kB
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)
}