package handler import ( "context" "fmt" "strconv" "time" "uptodownBot/internal/domain" ) func (h *Handler) handlePlaylist(ctx context.Context, chatID int64, userID int64, url string, info *domain.MediaInfo) { state := &domain.PlaylistState{ Entries: info.PlaylistEntries, Page: 0, PerPage: 10, Selected: make(map[string]bool), } ss := &stepState{ URL: url, MediaInfo: info, PlaylistState: state, } h.setUserStepState(chatID, ss) kb := playlistNavKeyboard(state, 0) h.sendWithKB(ctx, chatID, fmt.Sprintf("Playlist: %s (%d videos)\n\nSelect videos to download:", info.Title, info.PlaylistCount), kb) } func (h *Handler) getPlaylistState(chatID int64) *domain.PlaylistState { ss := h.getUserStepState(chatID) if ss == nil { return nil } return ss.PlaylistState } func (h *Handler) handlePlaylistPage(ctx context.Context, chatID int64, msgID int, userID int64, pageStr string) { state := h.getPlaylistState(chatID) if state == nil { return } page, err := strconv.Atoi(pageStr) if err != nil { return } totalPages := (len(state.Entries) + state.PerPage - 1) / state.PerPage if page < 0 || page >= totalPages { return } state.Page = page selectedCount := countSelected(state) kb := playlistNavKeyboard(state, selectedCount) ss := h.getUserStepState(chatID) title := "" if ss != nil { title = ss.MediaInfo.Title } h.editText(ctx, chatID, msgID, fmt.Sprintf("Playlist: %s (%d videos)", title, len(state.Entries)), &kb) } func (h *Handler) handlePlaylistEntry(ctx context.Context, chatID int64, msgID int, userID int64, entryID string) { state := h.getPlaylistState(chatID) if state == nil { return } state.Selected[entryID] = !state.Selected[entryID] selectedCount := countSelected(state) kb := playlistNavKeyboard(state, selectedCount) ss := h.getUserStepState(chatID) title := "" if ss != nil { title = ss.MediaInfo.Title } h.editText(ctx, chatID, msgID, fmt.Sprintf("Playlist: %s (%d videos)", title, len(state.Entries)), &kb) } func (h *Handler) handlePlaylistSelectAll(ctx context.Context, chatID int64, msgID int, userID int64) { state := h.getPlaylistState(chatID) if state == nil { return } allSelected := true for _, e := range state.Entries { if !state.Selected[e.ID] { allSelected = false break } } for _, e := range state.Entries { state.Selected[e.ID] = !allSelected } selectedCount := 0 if !allSelected { selectedCount = len(state.Entries) } kb := playlistNavKeyboard(state, selectedCount) ss := h.getUserStepState(chatID) title := "" if ss != nil { title = ss.MediaInfo.Title } h.editText(ctx, chatID, msgID, fmt.Sprintf("Playlist: %s (%d videos)", title, len(state.Entries)), &kb) } func (h *Handler) handlePlaylistConfirm(ctx context.Context, chatID int64, msgID int, userID int64) { state := h.getPlaylistState(chatID) if state == nil { return } var selected []domain.PlaylistEntry for _, e := range state.Entries { if state.Selected[e.ID] { selected = append(selected, e) } } if len(selected) == 0 { h.editText(ctx, chatID, msgID, "No videos selected.", nil) return } h.clearStepState(chatID) h.editText(ctx, chatID, msgID, fmt.Sprintf("Downloading %d videos sequentially...", len(selected)), nil) for i, entry := range selected { h.sendText(ctx, chatID, fmt.Sprintf("Downloading (%d/%d): %s", i+1, len(selected), entry.Title)) info, err := h.YtDlp.GetMediaInfo(entry.URL) if err != nil { h.sendText(ctx, chatID, fmt.Sprintf("Failed: %s", err.Error())) continue } prefs, prefErr := h.Repo.GetOrCreatePreferences(userID) if prefErr != nil { prefs = &domain.UserPreferences{ DefaultMediaType: domain.MediaTypeVideoAudio, DefaultQuality: "best", DefaultContainer: "mp4", } } format := &domain.Format{ID: prefs.DefaultQuality} for _, f := range info.Formats { if f.ID == prefs.DefaultQuality { format = &f break } } job := &domain.DownloadJob{ ID: h.generateJobID(), UserID: userID, ChatID: chatID, URL: entry.URL, MediaType: prefs.DefaultMediaType, SelectedFormat: format, Container: prefs.DefaultContainer, Language: prefs.DefaultLanguage, Status: domain.StatusDownloading, Title: entry.Title, FileSize: format.Filesize, } h.setActiveJob(userID, job) h.runDownload(ctx, job, 0) // Wait for active job to complete for { j := h.getActiveJob(userID) if j == nil || j.Status == domain.StatusCompleted || j.Status == domain.StatusFailed || j.Status == domain.StatusCancelled { break } time.Sleep(500 * time.Millisecond) } } h.sendText(ctx, chatID, "All playlist downloads completed.") } func countSelected(state *domain.PlaylistState) int { count := 0 for _, s := range state.Selected { if s { count++ } } return count }