From ff73a343beec35689a17a212bec53053706623a4 Mon Sep 17 00:00:00 2001 From: KitsuneSemCalda Date: Sun, 25 Aug 2024 20:15:45 -0300 Subject: [PATCH] impl: adding a function to download the anime --- internal/Network/Downloader.go | 186 +++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 internal/Network/Downloader.go diff --git a/internal/Network/Downloader.go b/internal/Network/Downloader.go new file mode 100644 index 0000000..bbf9799 --- /dev/null +++ b/internal/Network/Downloader.go @@ -0,0 +1,186 @@ +package network + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + "time" + + "github.com/PuerkitoBio/goquery" + "github.com/cavaliergopher/grab/v3" + "gopkg.in/cheggaaa/pb.v1" + + "animatic-v2/internal/Message" + "animatic-v2/internal/Structures" +) + +func downloadFolderFormatter(str string) string { + regex := regexp.MustCompile(`https://animefire\.plus/video/([^/?]+)`) + match := regex.FindStringSubmatch(str) + if len(match) > 1 { + finalStep := match[1] + return finalStep + } + return "" +} + +func dirExists(path string) bool { + if _, err := os.Stat(path); os.IsNotExist(err) { + os.MkdirAll(path, os.ModePerm) + return false + } + + return true +} + +func isBloggerURL(videoURL string) bool { + return strings.Contains(videoURL, "blogger.com") +} + +func isYTDLPAvailable() bool { + cmd := exec.Command("which", "yt-dlp") // Use "where" em vez de "which" no Windows + if err := cmd.Run(); err != nil { + return false + } + return true +} + +func downloadBloggerVideo(videoURL, episodePath string) { + if isYTDLPAvailable() { + fmt.Println("Using yt-dlp to download Blogger video...") + cmd := exec.Command("yt-dlp", "-o", episodePath, videoURL) + if err := cmd.Run(); err != nil { + message.ErrorMessage(fmt.Sprintf("Failed to download video using yt-dlp: %s\n", err.Error())) + } + } +} + +func ExtractActualVideoLabel(videoSrc string) string { + var videoResponse Structures.VideoResponse + response, err := http.Get(videoSrc) + if err != nil { + message.ErrorMessage(err.Error()) + } + + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + message.ErrorMessage("Status Code is different from StatusOK") + } + + body, err := io.ReadAll(response.Body) + if err != nil { + message.ErrorMessage(err.Error()) + return "" + } + + err = json.Unmarshal(body, &videoResponse) + if err != nil { + message.ErrorMessage(err.Error()) + return "" + } + + if len(videoResponse.Data) == 0 { + message.ErrorMessage("No Video Found") + return "" + } + + return videoResponse.Data[len(videoResponse.Data)-1].Src +} + +func ExtractVideoURL(url string) string { + value, err := checkUrlStatus(url) + + if !value || err != nil { + message.ErrorMessage("Can't access video website") + } + + response, err := http.Get(url) + if err != nil { + message.ErrorMessage(err.Error()) + return "" + } + + doc, _ := goquery.NewDocumentFromReader(response.Body) + + videoElements := doc.Find("video") + + if videoElements.Length() > 0 { + oldDataVideo, _ := videoElements.Attr("data-video-src") + return oldDataVideo + } else { + videoElements = doc.Find("div") + if videoElements.Length() > 0 { + oldDataVideo, _ := videoElements.Attr("data-video-src") + return oldDataVideo + } + } + + message.ErrorMessage("Can't find video element in html") + return "" +} + +func downloadStandardVideo(URL, episodePath string) { + fmt.Println("Using standard download method...") + + startUrl := ExtractVideoURL(URL) + videoUrl := ExtractActualVideoLabel(startUrl) + + client := grab.NewClient() + req, _ := grab.NewRequest(episodePath, videoUrl) + req.HTTPRequest.Header.Set("Connection", "keep-alive") + + resp := client.Do(req) + + maxSizeInMB := int(resp.Size() / (1024 * 1024)) + minSizeInMB := 10 + + if maxSizeInMB < minSizeInMB { + maxSizeInMB = minSizeInMB + } + + bar := pb.StartNew(maxSizeInMB) + + fmt.Printf("Episode URL: %s \n", videoUrl) + for !resp.IsComplete() { + completedInMB := int(resp.Progress() * float64(maxSizeInMB)) + bar.Set(completedInMB) + time.Sleep(time.Millisecond * 500) + } + + bar.Finish() +} + +func downloadEp(path string, ep Structures.Episode) { + episodePath := filepath.Join(path, ep.Number+".mp4") + + if _, err := os.Stat(episodePath); os.IsNotExist(err) { + fmt.Println("Downloading the video...") + if isBloggerURL(ep.URL) { + downloadBloggerVideo(ep.URL, episodePath) + } else { + downloadStandardVideo(ep.URL, episodePath) + } + fmt.Println("Video downloaded successfully!") + } else { + fmt.Println("Video already downloaded.") + } +} + +func Download(anime Structures.Anime, eplist []Structures.Episode, Debug bool, Gac bool) { + var path string + if Gac { + path = filepath.Join(os.Getenv("HOME"), ".local", "goanime", "downloads", "anime", downloadFolderFormatter(anime.Url)) + dirExists(path) + } + + for _, ep := range eplist { + downloadEp(path, ep) + } +}