GitBucket
4.20.0
Toggle navigation
Sign in
Files
Branches
1
Tags
Issues
Pull requests
Labels
Priorities
Milestones
Wiki
Forks
frozendragon
/
anime-fetcher
Browse code
Fixed the queuing
master
1 parent
73eb6e2
commit
1f3d93f4767406558acd1eca1cbb84fb2ed3e6c7
frozendragon
authored
21 hours ago
Patch
Showing
1 changed file
main.go
Ignore Space
Show notes
View
main.go
package main import ( "encoding/json" "errors" "fmt" "github.com/PuerkitoBio/goquery" chi "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "html" "io" "log" "net/http" "net/url" "os" "os/exec" "strings" "time" ) func main() { requestChan := make(chan Request, 1000) r := chi.NewRouter() r.Use(middleware.Logger) r.Get("/", func(w http.ResponseWriter, r *http.Request) { file, err := os.ReadFile("frontend/index.html") if err != nil { panic(err) } w.Write(file) }) r.Post("/success", func(w http.ResponseWriter, r *http.Request) { file, err := os.ReadFile("frontend/success.html") if err != nil { panic(err) } w.Write(file) }) r.Post("/failed", func(w http.ResponseWriter, r *http.Request) { file, err := os.ReadFile("frontend/failed.html") if err != nil { panic(err) } w.Write(file) }) //r.Get("/session", func(w http.ResponseWriter, r *http.Request) { // m1 := make(map[string]Request) // // m.Range(func(key, value any) bool { // // Type assertion is required since Range returns 'any' types // k := key.(string) // v := value.(Request) // // m1[k] = v // // return true // Return true to continue iterating // }) // // data, err := json.Marshal(m1) // if err != nil { // http.Error(w, "Failed to submit request", http.StatusInternalServerError) // return // } // // _, err = w.Write(data) // if err != nil { // http.Error(w, "Failed to submit request", http.StatusInternalServerError) // return // } // w.WriteHeader(http.StatusOK) //}) r.Post("/session", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, "Failed to parse form", http.StatusBadRequest) return } // Retrieve input values by their HTML "name" attribute u := r.FormValue("url") name := r.FormValue("name") season := r.FormValue("season") //UUID, err := uuid.NewUUID() //if err != nil { // http.Error(w, "Failed to submit request", http.StatusInternalServerError) // return //} requestChan <- Request{ URL: u, Name: name, Season: season, } http.Redirect(w, r, "/success", http.StatusTemporaryRedirect) }) go processRequest(requestChan) http.ListenAndServe(":3000", r) close(requestChan) } type Request struct { URL string `json:"url"` Name string `json:"name"` Season string `json:"season"` } func processRequest(requestChan chan Request) { for { v := <-requestChan Download(v) time.Sleep(time.Second * 30) } } func Download(request Request) { seasonID, err := extractSeasonID(request.URL) if err != nil { log.Printf("%+v\n", err) return } episodeCount, err := getEpisodeList(seasonID) if err != nil { log.Printf("%+v\n", err) return } for i := 1; i < episodeCount+1; i++ { sourceID, err := getAniwaveSourceID(seasonID, i) if err != nil { log.Printf("%+v\n", err) return } sourceUrl, err := getAniwavesSource(request.URL, sourceID) if err != nil { log.Printf("%+v\n", err) return } playEchoVideoSourceeID, err := extractSourceID(sourceUrl) if err != nil { log.Printf("%+v\n", err) return } playEchoVideoSourceUrl, err := getPlayEchoVideoSource(playEchoVideoSourceeID) if err != nil { log.Printf("%+v\n", err) return } playEchoVideoSourceUrl = strings.Replace(playEchoVideoSourceUrl, "hlsx3cdn.burntburst45.store", "hlsx4cdn.burntburst45.store", 1) name := "storage/Download/" + fmt.Sprintf("%s/%s/%s - %02d", request.Name, request.Season, request.Name, i) + ".%(ext)s" cmd := exec.Command("./yt-dlp_linux", "-o", name, "--referer", "https://play.echovideo.ru/", "--add-header", "Origin: https://play.echovideo.ru", playEchoVideoSourceUrl) // Redirect binary I/O directly to the terminal cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin // Run blocks until the command completes if err := cmd.Run(); err != nil { log.Printf("%+v\n", err) return } } } func extractSeasonID(u string) (string, error) { parsed, err := url.Parse(u) if err != nil { return "", nil } path := parsed.Path resource := strings.Split(path, "/") seasonID := resource[2][strings.LastIndex(resource[2], "-")+1:] return seasonID, nil } type GetEpisodeListResponse struct { Status int `json:"status"` Result string `json:"result"` } func getEpisodeList(seasonID string) (int, error) { client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", fmt.Sprintf("https://aniwaves.ru/ajax/episode/list/%s?vrf=", seasonID), nil) if err != nil { return 0, err } req.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0") response, err := client.Do(req) if err != nil { return 0, err } defer response.Body.Close() // Essential: Close the body body, err := io.ReadAll(response.Body) if err != nil { return 0, err } lsr := GetEpisodeListResponse{} err = json.Unmarshal(body, &lsr) if err != nil { return 0, err } clean := html.UnescapeString(lsr.Result) wrapped := "<html><body>" + clean + "</body></html>" doc, err := goquery.NewDocumentFromReader(strings.NewReader(wrapped)) if err != nil { return 0, err } count := doc.Find("ul[class='ep-range'] li").Length() return count, nil } type ListServersResponse struct { Status int `json:"status"` Result string `json:"result"` } func getAniwaveSourceID(seasonID string, episode int) (string, error) { client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", fmt.Sprintf("https://aniwaves.ru/ajax/server/list?servers=%s&eps=%d", seasonID, episode), nil) if err != nil { return "", err } req.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0") response, err := client.Do(req) if err != nil { return "", err } defer response.Body.Close() // Essential: Close the body body, err := io.ReadAll(response.Body) if err != nil { return "", err } lsr := ListServersResponse{} err = json.Unmarshal(body, &lsr) if err != nil { return "", err } clean := html.UnescapeString(lsr.Result) wrapped := "<html><body>" + clean + "</body></html>" doc, err := goquery.NewDocumentFromReader(strings.NewReader(wrapped)) if err != nil { return "", err } id, exist := doc.Find("div.type[data-type='dub'] ul li[data-sv-id='4']").Attr("data-link-id") if !exist { return "", errors.New("failed to find") } return id, nil } type SourceResponse struct { Status int `json:"status"` Result struct { Url string `json:"url"` Server int `json:"server"` SkipData struct { Intro []int `json:"intro"` Outro []int `json:"outro"` } `json:"skip_data"` Sources []interface{} `json:"sources"` Tracks []interface{} `json:"tracks"` HtmlGuide string `json:"htmlGuide"` } `json:"result"` } func getAniwavesSource(originalUrl, sourceID string) (string, error) { u := fmt.Sprintf("https://aniwaves.ru/ajax/sources?id=%s&asi=0&autoPlay=0", sourceID) client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", u, nil) if err != nil { return "", err } //GET /ajax/sources?id=UWxwb05ERkJXU1pUV1ZYT1k1b0FBQXQvLzl4TndCYk85aUtBc2tJeUpJd3d5Yzd3QzBCQzBnaWdVRWdIM2tzb0FnYUFvQUIwT0FBQUFHZ0FBQUFCazB4QWVtVFU5STlKNm1neWVvMEdtUU5BMDBCb0JvTkdnd2pSb1pQS0F5QTBCaHdCQU1NeUFja2pOTkxNcHRicTdmMkp4NFl4UDNFQXRKeUcxc3E0clpNTUl2bmhBdlphVzNJRDA5QTIvT2pqMTdBSmUySzkxVTBrUkVXYURKSmJjb1lXUWtYWUEzQXA0V1VRWFNHSWhoSno4ZnMzb3BTc1o3dnhFMjM2eit1ZnhkeVJUaFFrRlhPWTVvQT0=&asi=0&autoPlay=0 HTTP/2 //Host: aniwaves.ru //User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0 //Accept: application/json, text/javascript, */*; q=0.01 //Accept-Language: en-US,en;q=0.9 //Accept-Encoding: gzip, deflate, br, zstd //Referer: https://aniwaves.ru/watch/re-zero-kara-hajimeru-isekai-seikatsu-4th-season-82570/ep-1 //X-Requested-With: XMLHttpRequest //Sec-Fetch-Dest: empty //Sec-Fetch-Mode: cors //Sec-Fetch-Site: same-origin //Connection: keep-alive //Cookie: cf_clearance=E7EaYC.6t5eW.AiGWFA4dGhrVUHTO7X8IiaRkfJTAco-1780105219-1.2.1.1-MJ0WuQfXydhynpkJCtY1Q719ENTQz4VLOfYm3pIdw7pfD41mgVQaml14Ca3gIw7pb.YmpANb8l5LPr9Boav4XmsH0pr0Ip58lVK5hkowCFzvMSFqJDdu_a43B9Km.FA5Ql3uPbXZeNU3rSlJuJ1wBDvKhhVFo4yIaxaWoIcfsqwNdmwuz81b_yWoIge0K0ciDAVKcwV.8uv9XfHP1T4Rrs_OKdKFRZ6cp0TBYjiLOe4NwFbWBviDIxsR8tDEhxKTof0UEVq9R0mBosRPelxr3YxIxH5QyFrCKdpghW.kDOeLXGfP13cYVkwZFwT0jXVhkLZvCkHEgpEZ9stzMERj0Y2P2xUx9WMKJRQaRyPMJWKUCn9FtZSvHrvuW23TaWOx9ll00D_ZPzmTHdtESQrc8MTgZ5wBDvPaXbyH1ksv2RA; prefered_server_id=4; prefered_server_type=dub; _ga_2BSQBMWMM9=GS2.1.s1780942892$o1$g1$t1780942910$j42$l0$h0; _ga=GA1.1.764185913.1780942893 //Priority: u=0 //Pragma: no-cache //Cache-Control: no-cache //TE: trailers req.Header.Add("Referer", originalUrl) req.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0") resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() // Essential: Close the body body, err := io.ReadAll(resp.Body) if err != nil { return "", err } sr := SourceResponse{} err = json.Unmarshal(body, &sr) if err != nil { return "", err } return sr.Result.Url, nil } func extractSourceID(u string) (string, error) { parsedURL, err := url.Parse(u) if err != nil { return "", err } path := parsedURL.Path id := path[strings.LastIndex(path, "/")+1:] return id, nil } type PlayEchoVideoSourceResponse struct { Sources string `json:"sources"` Intro struct { Start int `json:"start"` End int `json:"end"` } `json:"intro"` Outro struct { Start int `json:"start"` End int `json:"end"` } `json:"outro"` } func getPlayEchoVideoSource(id string) (string, error) { url := fmt.Sprintf("https://play.echovideo.ru/embed-1/getSources?id=%s", id) //GET /embed-1/getSources?id=KPpOQdEl-krSDv4eXEEESOGQxIyn39hxhDVL-a2XPou3CoyWNh4VQmK3H0rMoVhfkd0GovoHTaEZ6Sm5lyMSpt5yt-Dn2lZCmvH1yzTQSxaKaYl25DzAviI4tquisv4m4sdprfru0X7FRb516Rwgo1lBQipOxMdBWJAVmU3pVFbH-FCBQorpYyUMwrsEZeectZ9x8EhnJ_wYs7Sd2a1lnficzNBqKBC3zWOvZLXKnwY HTTP/2 //Host: play.echovideo.ru //User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0 //Accept: */* //Accept-Language: en-US,en;q=0.9 //Accept-Encoding: gzip, deflate, br, zstd //Referer: https://play.echovideo.ru/embed-1/KPpOQdEl-krSDv4eXEEESOGQxIyn39hxhDVL-a2XPou3CoyWNh4VQmK3H0rMoVhfkd0GovoHTaEZ6Sm5lyMSpt5yt-Dn2lZCmvH1yzTQSxaKaYl25DzAviI4tquisv4m4sdprfru0X7FRb516Rwgo1lBQipOxMdBWJAVmU3pVFbH-FCBQorpYyUMwrsEZeectZ9x8EhnJ_wYs7Sd2a1lnficzNBqKBC3zWOvZLXKnwY?v=1&asi=0&autoPlay=0&ao=0&autostart=true //Connection: keep-alive //Sec-Fetch-Dest: empty //Sec-Fetch-Mode: cors //Sec-Fetch-Site: same-origin //Priority: u=4 //Pragma: no-cache //Cache-Control: no-cache client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", url, nil) if err != nil { return "", err } req.Header.Add("Referer", fmt.Sprintf("https://play.echovideo.ru/embed-1/%s?v=1&asi=0&autoPlay=0&ao=0&autostart=true", id)) resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() // Essential: Close the body body, err := io.ReadAll(resp.Body) if err != nil { return "", err } sr := PlayEchoVideoSourceResponse{} err = json.Unmarshal(body, &sr) if err != nil { return "", err } return sr.Sources, nil }
package main import ( "encoding/json" "errors" "fmt" "github.com/PuerkitoBio/goquery" chi "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/google/uuid" "html" "io" "log" "net/http" "net/url" "os" "os/exec" "strings" "sync" "time" ) func main() { m := &sync.Map{} r := chi.NewRouter() r.Use(middleware.Logger) r.Get("/", func(w http.ResponseWriter, r *http.Request) { file, err := os.ReadFile("frontend/index.html") if err != nil { panic(err) } w.Write(file) }) r.Post("/success", func(w http.ResponseWriter, r *http.Request) { file, err := os.ReadFile("frontend/success.html") if err != nil { panic(err) } w.Write(file) }) r.Post("/failed", func(w http.ResponseWriter, r *http.Request) { file, err := os.ReadFile("frontend/failed.html") if err != nil { panic(err) } w.Write(file) }) r.Get("/session", func(w http.ResponseWriter, r *http.Request) { m1 := make(map[string]Request) m.Range(func(key, value any) bool { // Type assertion is required since Range returns 'any' types k := key.(string) v := value.(Request) m1[k] = v return true // Return true to continue iterating }) data, err := json.Marshal(m1) if err != nil { http.Error(w, "Failed to submit request", http.StatusInternalServerError) return } _, err = w.Write(data) if err != nil { http.Error(w, "Failed to submit request", http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) }) r.Post("/session", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, "Failed to parse form", http.StatusBadRequest) return } // Retrieve input values by their HTML "name" attribute u := r.FormValue("url") name := r.FormValue("name") season := r.FormValue("season") UUID, err := uuid.NewUUID() if err != nil { http.Error(w, "Failed to submit request", http.StatusInternalServerError) return } m.Store(UUID.String(), Request{ URL: u, Name: name, Season: season, }) http.Redirect(w, r, "/success", http.StatusTemporaryRedirect) }) go processRequest(m) http.ListenAndServe(":3000", r) } type Request struct { URL string `json:"url"` Name string `json:"name"` Season string `json:"season"` } func processRequest(m *sync.Map) { for { m.Range(func(key, value any) bool { // Type assertion is required since Range returns 'any' types //k := key.(int) v := value.(Request) Download(v) return true // Return true to continue iterating }) m.Clear() time.Sleep(time.Second * 30) } } func Download(request Request) { seasonID, err := extractSeasonID(request.URL) if err != nil { log.Printf("%+v\n", err) return } episodeCount, err := getEpisodeList(seasonID) if err != nil { log.Printf("%+v\n", err) return } for i := 1; i < episodeCount+1; i++ { sourceID, err := getAniwaveSourceID(seasonID, i) if err != nil { log.Printf("%+v\n", err) return } sourceUrl, err := getAniwavesSource(request.URL, sourceID) if err != nil { log.Printf("%+v\n", err) return } playEchoVideoSourceeID, err := extractSourceID(sourceUrl) if err != nil { log.Printf("%+v\n", err) return } playEchoVideoSourceUrl, err := getPlayEchoVideoSource(playEchoVideoSourceeID) if err != nil { log.Printf("%+v\n", err) return } playEchoVideoSourceUrl = strings.Replace(playEchoVideoSourceUrl, "hlsx3cdn.burntburst45.store", "hlsx4cdn.burntburst45.store", 1) name := "storage/Download/" + fmt.Sprintf("%s/%s/%s - %d", request.Name, request.Season, request.Name, i) + ".%(ext)s" cmd := exec.Command("./yt-dlp_linux", "-o", name, "--referer", "https://play.echovideo.ru/", "--add-header", "Origin: https://play.echovideo.ru", playEchoVideoSourceUrl) // Redirect binary I/O directly to the terminal cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin // Run blocks until the command completes if err := cmd.Run(); err != nil { log.Printf("%+v\n", err) return } } } func extractSeasonID(u string) (string, error) { parsed, err := url.Parse(u) if err != nil { return "", nil } path := parsed.Path resource := strings.Split(path, "/") seasonID := resource[2][strings.LastIndex(resource[2], "-")+1:] return seasonID, nil } type GetEpisodeListResponse struct { Status int `json:"status"` Result string `json:"result"` } func getEpisodeList(seasonID string) (int, error) { client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", fmt.Sprintf("https://aniwaves.ru/ajax/episode/list/%s?vrf=", seasonID), nil) if err != nil { return 0, err } req.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0") response, err := client.Do(req) if err != nil { return 0, err } defer response.Body.Close() // Essential: Close the body body, err := io.ReadAll(response.Body) if err != nil { return 0, err } lsr := GetEpisodeListResponse{} err = json.Unmarshal(body, &lsr) if err != nil { return 0, err } clean := html.UnescapeString(lsr.Result) wrapped := "<html><body>" + clean + "</body></html>" doc, err := goquery.NewDocumentFromReader(strings.NewReader(wrapped)) if err != nil { return 0, err } count := doc.Find("ul[class='ep-range'] li").Length() return count, nil } type ListServersResponse struct { Status int `json:"status"` Result string `json:"result"` } func getAniwaveSourceID(seasonID string, episode int) (string, error) { client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", fmt.Sprintf("https://aniwaves.ru/ajax/server/list?servers=%s&eps=%d", seasonID, episode), nil) if err != nil { return "", err } req.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0") response, err := client.Do(req) if err != nil { return "", err } defer response.Body.Close() // Essential: Close the body body, err := io.ReadAll(response.Body) if err != nil { return "", err } lsr := ListServersResponse{} err = json.Unmarshal(body, &lsr) if err != nil { return "", err } clean := html.UnescapeString(lsr.Result) wrapped := "<html><body>" + clean + "</body></html>" doc, err := goquery.NewDocumentFromReader(strings.NewReader(wrapped)) if err != nil { return "", err } id, exist := doc.Find("div.type[data-type='dub'] ul li[data-sv-id='4']").Attr("data-link-id") if !exist { return "", errors.New("failed to find") } return id, nil } type SourceResponse struct { Status int `json:"status"` Result struct { Url string `json:"url"` Server int `json:"server"` SkipData struct { Intro []int `json:"intro"` Outro []int `json:"outro"` } `json:"skip_data"` Sources []interface{} `json:"sources"` Tracks []interface{} `json:"tracks"` HtmlGuide string `json:"htmlGuide"` } `json:"result"` } func getAniwavesSource(originalUrl, sourceID string) (string, error) { u := fmt.Sprintf("https://aniwaves.ru/ajax/sources?id=%s&asi=0&autoPlay=0", sourceID) client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", u, nil) if err != nil { return "", err } //GET /ajax/sources?id=UWxwb05ERkJXU1pUV1ZYT1k1b0FBQXQvLzl4TndCYk85aUtBc2tJeUpJd3d5Yzd3QzBCQzBnaWdVRWdIM2tzb0FnYUFvQUIwT0FBQUFHZ0FBQUFCazB4QWVtVFU5STlKNm1neWVvMEdtUU5BMDBCb0JvTkdnd2pSb1pQS0F5QTBCaHdCQU1NeUFja2pOTkxNcHRicTdmMkp4NFl4UDNFQXRKeUcxc3E0clpNTUl2bmhBdlphVzNJRDA5QTIvT2pqMTdBSmUySzkxVTBrUkVXYURKSmJjb1lXUWtYWUEzQXA0V1VRWFNHSWhoSno4ZnMzb3BTc1o3dnhFMjM2eit1ZnhkeVJUaFFrRlhPWTVvQT0=&asi=0&autoPlay=0 HTTP/2 //Host: aniwaves.ru //User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0 //Accept: application/json, text/javascript, */*; q=0.01 //Accept-Language: en-US,en;q=0.9 //Accept-Encoding: gzip, deflate, br, zstd //Referer: https://aniwaves.ru/watch/re-zero-kara-hajimeru-isekai-seikatsu-4th-season-82570/ep-1 //X-Requested-With: XMLHttpRequest //Sec-Fetch-Dest: empty //Sec-Fetch-Mode: cors //Sec-Fetch-Site: same-origin //Connection: keep-alive //Cookie: cf_clearance=E7EaYC.6t5eW.AiGWFA4dGhrVUHTO7X8IiaRkfJTAco-1780105219-1.2.1.1-MJ0WuQfXydhynpkJCtY1Q719ENTQz4VLOfYm3pIdw7pfD41mgVQaml14Ca3gIw7pb.YmpANb8l5LPr9Boav4XmsH0pr0Ip58lVK5hkowCFzvMSFqJDdu_a43B9Km.FA5Ql3uPbXZeNU3rSlJuJ1wBDvKhhVFo4yIaxaWoIcfsqwNdmwuz81b_yWoIge0K0ciDAVKcwV.8uv9XfHP1T4Rrs_OKdKFRZ6cp0TBYjiLOe4NwFbWBviDIxsR8tDEhxKTof0UEVq9R0mBosRPelxr3YxIxH5QyFrCKdpghW.kDOeLXGfP13cYVkwZFwT0jXVhkLZvCkHEgpEZ9stzMERj0Y2P2xUx9WMKJRQaRyPMJWKUCn9FtZSvHrvuW23TaWOx9ll00D_ZPzmTHdtESQrc8MTgZ5wBDvPaXbyH1ksv2RA; prefered_server_id=4; prefered_server_type=dub; _ga_2BSQBMWMM9=GS2.1.s1780942892$o1$g1$t1780942910$j42$l0$h0; _ga=GA1.1.764185913.1780942893 //Priority: u=0 //Pragma: no-cache //Cache-Control: no-cache //TE: trailers req.Header.Add("Referer", originalUrl) req.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0") resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() // Essential: Close the body body, err := io.ReadAll(resp.Body) if err != nil { return "", err } sr := SourceResponse{} err = json.Unmarshal(body, &sr) if err != nil { return "", err } return sr.Result.Url, nil } func extractSourceID(u string) (string, error) { parsedURL, err := url.Parse(u) if err != nil { return "", err } path := parsedURL.Path id := path[strings.LastIndex(path, "/")+1:] return id, nil } type PlayEchoVideoSourceResponse struct { Sources string `json:"sources"` Intro struct { Start int `json:"start"` End int `json:"end"` } `json:"intro"` Outro struct { Start int `json:"start"` End int `json:"end"` } `json:"outro"` } func getPlayEchoVideoSource(id string) (string, error) { url := fmt.Sprintf("https://play.echovideo.ru/embed-1/getSources?id=%s", id) //GET /embed-1/getSources?id=KPpOQdEl-krSDv4eXEEESOGQxIyn39hxhDVL-a2XPou3CoyWNh4VQmK3H0rMoVhfkd0GovoHTaEZ6Sm5lyMSpt5yt-Dn2lZCmvH1yzTQSxaKaYl25DzAviI4tquisv4m4sdprfru0X7FRb516Rwgo1lBQipOxMdBWJAVmU3pVFbH-FCBQorpYyUMwrsEZeectZ9x8EhnJ_wYs7Sd2a1lnficzNBqKBC3zWOvZLXKnwY HTTP/2 //Host: play.echovideo.ru //User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0 //Accept: */* //Accept-Language: en-US,en;q=0.9 //Accept-Encoding: gzip, deflate, br, zstd //Referer: https://play.echovideo.ru/embed-1/KPpOQdEl-krSDv4eXEEESOGQxIyn39hxhDVL-a2XPou3CoyWNh4VQmK3H0rMoVhfkd0GovoHTaEZ6Sm5lyMSpt5yt-Dn2lZCmvH1yzTQSxaKaYl25DzAviI4tquisv4m4sdprfru0X7FRb516Rwgo1lBQipOxMdBWJAVmU3pVFbH-FCBQorpYyUMwrsEZeectZ9x8EhnJ_wYs7Sd2a1lnficzNBqKBC3zWOvZLXKnwY?v=1&asi=0&autoPlay=0&ao=0&autostart=true //Connection: keep-alive //Sec-Fetch-Dest: empty //Sec-Fetch-Mode: cors //Sec-Fetch-Site: same-origin //Priority: u=4 //Pragma: no-cache //Cache-Control: no-cache client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", url, nil) if err != nil { return "", err } req.Header.Add("Referer", fmt.Sprintf("https://play.echovideo.ru/embed-1/%s?v=1&asi=0&autoPlay=0&ao=0&autostart=true", id)) resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() // Essential: Close the body body, err := io.ReadAll(resp.Body) if err != nil { return "", err } sr := PlayEchoVideoSourceResponse{} err = json.Unmarshal(body, &sr) if err != nil { return "", err } return sr.Sources, nil }
Show line notes below