Spaces:
Sleeping
Sleeping
| package main | |
| import ( | |
| "context" | |
| "encoding/json" | |
| "fmt" | |
| "io" | |
| "log" | |
| "net/http" | |
| "net/url" | |
| "os" | |
| "time" | |
| ) | |
| const PORT = ":7860" | |
| // Response structure untuk error | |
| type ErrorResponse struct { | |
| Error string `json:"error"` | |
| Message string `json:"message"` | |
| URL string `json:"url,omitempty"` | |
| Example string `json:"example,omitempty"` | |
| } | |
| // Response structure untuk test | |
| type TestResponse struct { | |
| Message string `json:"message"` | |
| Original string `json:"original"` | |
| Encoded string `json:"encoded"` | |
| TestURL string `json:"test_url"` | |
| TestWithReferer string `json:"test_with_referer"` | |
| } | |
| // Health response | |
| type HealthResponse struct { | |
| Status string `json:"status"` | |
| Message string `json:"message"` | |
| Timestamp string `json:"timestamp"` | |
| } | |
| func main() { | |
| // Setup router | |
| http.HandleFunc("/", homeHandler) | |
| // 🔥 GABUNGKAN HANDLER UNTUK /proxy-video (GET + OPTIONS) | |
| http.HandleFunc("/proxy-video", func(w http.ResponseWriter, r *http.Request) { | |
| if r.Method == http.MethodOptions { | |
| setCORSHeaders(w) | |
| w.WriteHeader(http.StatusOK) | |
| return | |
| } | |
| proxyVideoHandler(w, r) | |
| }) | |
| http.HandleFunc("/test", testHandler) | |
| http.HandleFunc("/health", healthHandler) | |
| // Start server | |
| log.Printf("[PROXY] Server running at http://localhost%s", PORT) | |
| log.Printf("[VIDEO] Usage: http://localhost%s/proxy-video?u=ENCODED_URL", PORT) | |
| log.Printf("[TEST] Test: http://localhost%s/test", PORT) | |
| log.Printf("[INFO] Example with referer: /proxy-video?u=ENCODED_URL&referer=https://vid30s.com/") | |
| if err := http.ListenAndServe(PORT, nil); err != nil { | |
| log.Fatal("[ERROR] Server error:", err) | |
| } | |
| } | |
| // HOME HALAMAN UTAMA (HTML FORM) - Baca dari file | |
| func homeHandler(w http.ResponseWriter, r *http.Request) { | |
| if r.URL.Path != "/" { | |
| http.NotFound(w, r) | |
| return | |
| } | |
| w.Header().Set("Content-Type", "text/html; charset=utf-8") | |
| // Baca file template.html | |
| htmlContent, err := os.ReadFile("template.html") | |
| if err != nil { | |
| log.Printf("[ERROR] Failed to read template: %v", err) | |
| http.Error(w, "Internal Server Error", http.StatusInternalServerError) | |
| return | |
| } | |
| fmt.Fprint(w, string(htmlContent)) | |
| } | |
| // MAIN PROXY HANDLER | |
| func proxyVideoHandler(w http.ResponseWriter, r *http.Request) { | |
| // Hanya GET yang diterima (OPTIONS sudah di-handle di atas) | |
| if r.Method != http.MethodGet { | |
| http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
| return | |
| } | |
| videoURL := r.URL.Query().Get("u") | |
| if videoURL == "" { | |
| w.Header().Set("Content-Type", "application/json") | |
| w.WriteHeader(http.StatusBadRequest) | |
| json.NewEncoder(w).Encode(ErrorResponse{ | |
| Error: "Missing parameter", | |
| Message: "Please provide video URL with ?u= parameter", | |
| Example: "/proxy-video?u=https://example.com/video.mp4", | |
| }) | |
| return | |
| } | |
| decodedURL, err := url.QueryUnescape(videoURL) | |
| if err != nil { | |
| decodedURL = videoURL | |
| } | |
| parsedURL, err := url.Parse(decodedURL) | |
| if err != nil || parsedURL.Scheme == "" || parsedURL.Host == "" { | |
| w.Header().Set("Content-Type", "application/json") | |
| w.WriteHeader(http.StatusBadRequest) | |
| json.NewEncoder(w).Encode(ErrorResponse{ | |
| Error: "Invalid URL", | |
| Message: "Please provide a valid URL", | |
| }) | |
| return | |
| } | |
| log.Printf("[LINK] New request for: %s", decodedURL) | |
| referer := r.URL.Query().Get("referer") | |
| if referer == "" { | |
| referer = fmt.Sprintf("%s://%s/", parsedURL.Scheme, parsedURL.Host) | |
| } | |
| origin := r.URL.Query().Get("origin") | |
| if origin == "" { | |
| origin = fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host) | |
| } | |
| userAgent := r.URL.Query().Get("ua") | |
| if userAgent == "" { | |
| userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" | |
| } | |
| accept := r.URL.Query().Get("accept") | |
| if accept == "" { | |
| accept = "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5" | |
| } | |
| rangeHeader := r.Header.Get("Range") | |
| if rangeHeader == "" { | |
| rangeHeader = "bytes=0-" | |
| } | |
| ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) | |
| defer cancel() | |
| req, err := http.NewRequestWithContext(ctx, "GET", decodedURL, nil) | |
| if err != nil { | |
| log.Printf("[ERROR] Error creating request: %v", err) | |
| w.Header().Set("Content-Type", "application/json") | |
| w.WriteHeader(http.StatusInternalServerError) | |
| json.NewEncoder(w).Encode(ErrorResponse{ | |
| Error: "Request creation failed", | |
| Message: err.Error(), | |
| URL: decodedURL, | |
| }) | |
| return | |
| } | |
| req.Header.Set("Referer", referer) | |
| req.Header.Set("Origin", origin) | |
| req.Header.Set("User-Agent", userAgent) | |
| req.Header.Set("Accept", accept) | |
| req.Header.Set("Accept-Language", "en-US,en;q=0.9") | |
| req.Header.Set("Accept-Encoding", "gzip, deflate, br") | |
| req.Header.Set("Connection", "keep-alive") | |
| req.Header.Set("Range", rangeHeader) | |
| log.Printf("[SEND] Forwarding to: %s", parsedURL.Host) | |
| client := &http.Client{ | |
| Timeout: 60 * time.Second, | |
| CheckRedirect: func(req *http.Request, via []*http.Request) error { | |
| return nil | |
| }, | |
| } | |
| resp, err := client.Do(req) | |
| if err != nil { | |
| log.Printf("[ERROR] Proxy error: %v", err) | |
| w.Header().Set("Content-Type", "application/json") | |
| w.WriteHeader(http.StatusInternalServerError) | |
| json.NewEncoder(w).Encode(ErrorResponse{ | |
| Error: "Proxy error", | |
| Message: err.Error(), | |
| URL: decodedURL, | |
| }) | |
| return | |
| } | |
| defer resp.Body.Close() | |
| log.Printf("[RECV] Response status: %d", resp.StatusCode) | |
| forwardHeaders := []string{ | |
| "Content-Type", | |
| "Content-Length", | |
| "Accept-Ranges", | |
| "Content-Range", | |
| "ETag", | |
| "Last-Modified", | |
| } | |
| for _, header := range forwardHeaders { | |
| if value := resp.Header.Get(header); value != "" { | |
| w.Header().Set(header, value) | |
| } | |
| } | |
| setCORSHeaders(w) | |
| w.WriteHeader(resp.StatusCode) | |
| _, err = io.Copy(w, resp.Body) | |
| if err != nil { | |
| log.Printf("[ERROR] Streaming error: %v", err) | |
| } | |
| } | |
| // TEST ENDPOINT | |
| func testHandler(w http.ResponseWriter, r *http.Request) { | |
| testURL := "https://vidoycdn.b-cdn.net/dqYoJcz0BB-r0gaEycmpl?token=SsO2H5sCmonrqwz5NigK3w&expires=1782138064" | |
| encodedURL := url.QueryEscape(testURL) | |
| w.Header().Set("Content-Type", "application/json") | |
| json.NewEncoder(w).Encode(TestResponse{ | |
| Message: "Test your proxy with these URLs", | |
| Original: testURL, | |
| Encoded: encodedURL, | |
| TestURL: fmt.Sprintf("/proxy-video?u=%s", encodedURL), | |
| TestWithReferer: fmt.Sprintf("/proxy-video?u=%s&referer=https://vid30s.com/", encodedURL), | |
| }) | |
| } | |
| // HEALTH CHECK | |
| func healthHandler(w http.ResponseWriter, r *http.Request) { | |
| w.Header().Set("Content-Type", "application/json") | |
| json.NewEncoder(w).Encode(HealthResponse{ | |
| Status: "OK", | |
| Message: "Proxy server is running", | |
| Timestamp: time.Now().Format(time.RFC3339), | |
| }) | |
| } | |
| // SET CORS HEADERS | |
| func setCORSHeaders(w http.ResponseWriter) { | |
| w.Header().Set("Access-Control-Allow-Origin", "*") | |
| w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") | |
| w.Header().Set("Access-Control-Allow-Headers", "Range, Content-Type") | |
| } |