package download import ( "errors" log2 "log" "math/rand" "net/url" "os" "os/exec" "path/filepath" "regexp" "strings" "time" "watn3y.de/bloaterbot/botIO" "watn3y.de/bloaterbot/commonlogic" "watn3y.de/bloaterbot/config" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" "github.com/rs/zerolog/log" ) // TODO logging // TODO error handling // TODO rewrite func Download(update tgbotapi.Update, bot *tgbotapi.BotAPI) { log.Debug().Int64("chat", update.Message.Chat.ID).Int64("user", update.Message.From.ID).Msg("starting download") commandArgs := strings.Fields(update.Message.CommandArguments()) msg := tgbotapi.MessageConfig{ BaseChat: tgbotapi.BaseChat{ChatID: update.Message.Chat.ID, ReplyToMessageID: update.Message.MessageID}, Text: "Downloading your YouTube Video...", } workingMessage := botIO.SendMessage(msg, bot) if len(commandArgs) >= 1 && matchURL(commandArgs[0]) != "" { } else { log.Error().Int64("chat", update.Message.Chat.ID).Int64("user", update.Message.From.ID).Str("args", update.Message.CommandArguments()).Msg("Failed to download YouTube Video. Empty args") message := tgbotapi.EditMessageTextConfig{ BaseEdit: tgbotapi.BaseEdit{ChatID: workingMessage.Chat.ID, MessageID: workingMessage.MessageID}, Text: "Please specify a valid YouTube URL.", } botIO.EditMessage(message, bot) return } downloadTarget := randomString(20) downloaded := runYTDL(commandArgs[0], downloadTarget) if !downloaded { message := tgbotapi.EditMessageTextConfig{ BaseEdit: tgbotapi.BaseEdit{ChatID: workingMessage.Chat.ID, MessageID: workingMessage.MessageID}, Text: "Something went wrong while downloading your media :(", } botIO.EditMessage(message, bot) return } files, err := os.ReadDir("data/videos/" + downloadTarget) if err != nil { log.Error().Err(err).Msg("failed to download. unable to read target directory") message := tgbotapi.EditMessageTextConfig{ BaseEdit: tgbotapi.BaseEdit{ChatID: workingMessage.Chat.ID, MessageID: workingMessage.MessageID}, Text: "Something went wrong while downloading your media :(", } botIO.EditMessage(message, bot) return } if len(files) > 1 { log.Error().Err(err).Msg("failed to download. too many files") message := tgbotapi.EditMessageTextConfig{ BaseEdit: tgbotapi.BaseEdit{ChatID: workingMessage.Chat.ID, MessageID: workingMessage.MessageID}, Text: "Something went wrong while downloading your media :(", } botIO.EditMessage(message, bot) return } message := tgbotapi.EditMessageTextConfig{ BaseEdit: tgbotapi.BaseEdit{ChatID: workingMessage.Chat.ID, MessageID: workingMessage.MessageID}, Text: "Downloaded! Uploading now", } botIO.EditMessage(message, bot) serveMedia(update, bot, downloadTarget, files[0].Name()) log.Debug().Str("URL", update.Message.CommandArguments()).Int64("user", update.Message.From.ID).Int64("chat", update.Message.Chat.ID).Msg("Served Video") time.Sleep(5 * time.Second) log.Info().Msg("downloaded file") bot.Send(tgbotapi.NewDeleteMessage(workingMessage.Chat.ID, workingMessage.MessageID)) } func randomString(n int) string { rand.Seed(time.Now().UnixNano()) var letters = []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") b := make([]rune, n) for i := range b { b[i] = letters[rand.Intn(len(letters))] } return string(b) } func runYTDL(URL string, targetDir string) (success bool) { cmd := exec.Command("yt-dlp", "-f", "bv*[ext=mp4]+ba[ext=m4a] / bv*+ba/b", "--no-playlist", "-o", "data/videos/"+targetDir+"/"+"%(title)s.%(ext)s", "--write-thumbnail", "--convert-thumbnails", "jpg", "-o", "thumbnail:data/videos/"+targetDir+"thumb"+"/"+"%(title)s.%(ext)s", URL) out, err := cmd.CombinedOutput() var ( ee *exec.ExitError pe *os.PathError ) if errors.As(err, &ee) { log2.Println("[download.ytdl] Failed to download URL "+URL, " NON ZERO EXIT CODE: ", ee.ExitCode()) success = false } else if errors.As(err, &pe) { log2.Println("[download.ytdl] Failed to download URL "+URL, " OS PATH ERROR: ", pe.Error()) success = false } else if err != nil { log2.Println("[download.ytdl] Failed to download URL "+URL, " GENERAL ERROR: ", err.Error()) success = false } else { log2.Println("[download.ytdl] Downloaded URL " + URL) success = true } if config.BotConfig.DebugMode { print(string(out)) } return success } func matchURL(URL string) (matchedURL string) { parsedURL, err := url.Parse(URL) if err != nil { log.Err(err).Msg("Failed to parse URL") return "" } var domainRegex = regexp.MustCompile(`(youtube\.com$)|(youtu\.be$)`) m := domainRegex.FindStringSubmatch(parsedURL.Hostname()) if len(m) >= 1 { return m[0] } else { return "" } } func serveMedia(update tgbotapi.Update, bot *tgbotapi.BotAPI, randomNoise string, file string) { fsPath := "data/videos/" + randomNoise + "/" + file fsThumbPath := "data/videos/" + randomNoise + "thumb" + "/" + strings.TrimSuffix(file, "mp4") + "jpg" fExt := filepath.Ext(fsPath) imageTypes := []string{".jpg", ".jpeg", ".png"} fsPathStat, _ := os.Stat(fsPath) if fsPathStat.Size() >= 52428800 { fExt = "thisfuckingshitistoobig" } if fExt == ".mp4" { vid := tgbotapi.VideoConfig{ BaseFile: tgbotapi.BaseFile{BaseChat: tgbotapi.BaseChat{ChatID: update.Message.Chat.ID, ReplyToMessageID: update.Message.MessageID}, File: tgbotapi.FilePath(fsPath)}, Thumb: tgbotapi.FilePath(fsThumbPath), Caption: config.BotConfig.Download.Prefix + "/" + randomNoise + "/" + url.PathEscape(file), SupportsStreaming: true, } if _, err := os.Stat(fsThumbPath); err != nil { vid.Thumb = nil } botIO.SendVideo(vid, bot) } else if commonlogic.ContainsString(imageTypes, fExt) { pic := tgbotapi.PhotoConfig{ BaseFile: tgbotapi.BaseFile{BaseChat: tgbotapi.BaseChat{ChatID: update.Message.Chat.ID, ReplyToMessageID: update.Message.MessageID}, File: tgbotapi.FilePath(fsPath)}, Caption: config.BotConfig.Download.Prefix + "/" + randomNoise + "/" + url.PathEscape(file), } botIO.SendPhoto(pic, bot) } else { message := tgbotapi.MessageConfig{ BaseChat: tgbotapi.BaseChat{ChatID: update.Message.Chat.ID, ReplyToMessageID: update.Message.MessageID}, DisableWebPagePreview: false, ParseMode: "html", Text: config.BotConfig.Download.Prefix + "/" + randomNoise + "/" + url.PathEscape(file), } botIO.SendMessage(message, bot) } }