fix: show bitrate for audio-only formats, filter subtitle languages, improve feedback
All checks were successful
CI / build (push) Successful in 50s

- Prioritize IsAudioOnly check in formatLabelForDisplay so bitrate
  is shown instead of yt-dlp's generic 'audio only' resolution
- Only collect languages from formats with audio/video to prevent
  subtitle-only tracks from triggering the language selection prompt
- Show 'Fetching metadata...' message before GetMediaInfo and edit
  with results, improving UX for URL input
- Add test coverage for audio-only format labels
This commit is contained in:
2026-06-26 12:12:33 +03:30
parent 464f20a46c
commit f5204b9554
3 changed files with 63 additions and 28 deletions

View File

@@ -72,6 +72,16 @@ func (h *Handler) handleURLInput(ctx context.Context, chatID int64, text string,
return
}
// Show a temporary status message that we will edit with results later.
fetchingMsg, _ := h.Bot.SendMessage(ctx, &tgbot.SendMessageParams{
ChatID: chatID,
Text: "Fetching metadata...",
})
fetchingMsgID := 0
if fetchingMsg != nil {
fetchingMsgID = fetchingMsg.ID
}
info, err := h.YtDlp.GetMediaInfo(url)
if err != nil {
errMsg := err.Error()
@@ -86,57 +96,68 @@ func (h *Handler) handleURLInput(ctx context.Context, chatID int64, text string,
{{Text: "Cancel", CallbackData: "pending_cancel"}},
},
}
h.Bot.SendMessage(ctx, &tgbot.SendMessageParams{
ChatID: chatID, Text: "This content is DRM protected and no YouTube alternative was found automatically. Try searching by name:", ReplyMarkup: kb,
})
h.editText(ctx, chatID, fetchingMsgID, "This content is DRM protected and no YouTube alternative was found automatically. Try searching by name:", &kb)
return
}
h.sendText(ctx, chatID, fmt.Sprintf("This content is DRM protected. Found on YouTube: %s", ytTitle))
h.editText(ctx, chatID, fetchingMsgID, fmt.Sprintf("This content is DRM protected. Found on YouTube: %s", ytTitle), nil)
info, err = h.YtDlp.GetMediaInfo(youtubeURL)
if err != nil {
h.sendText(ctx, chatID, fmt.Sprintf("YouTube fallback failed: %s", err.Error()))
h.editText(ctx, chatID, fetchingMsgID, fmt.Sprintf("YouTube fallback failed: %s", err.Error()), nil)
return
}
url = youtubeURL
} else {
switch {
case strings.Contains(errMsg, "Unsupported URL") || strings.Contains(errMsg, "is not a valid URL"):
h.sendText(ctx, chatID, "This URL is not supported.")
h.editText(ctx, chatID, fetchingMsgID, "This URL is not supported.", nil)
case strings.Contains(errMsg, "HTTP Error 403") || strings.Contains(errMsg, "cookies"):
h.sendText(ctx, chatID, "This content requires authentication (cookies).")
h.editText(ctx, chatID, fetchingMsgID, "This content requires authentication (cookies).", nil)
case strings.Contains(errMsg, "live"):
h.sendText(ctx, chatID, "Cannot download live streams.")
h.editText(ctx, chatID, fetchingMsgID, "Cannot download live streams.", nil)
default:
h.sendText(ctx, chatID, fmt.Sprintf("Error: %s", errMsg))
h.editText(ctx, chatID, fetchingMsgID, fmt.Sprintf("Error: %s", errMsg), nil)
}
return
}
}
if info.IsPlaylist {
h.editText(ctx, chatID, fetchingMsgID, fmt.Sprintf("Playlist detected: %s (%d videos)", info.Title, info.PlaylistCount), nil)
h.handlePlaylist(ctx, chatID, userID, url, info)
return
}
if len(info.Formats) == 0 {
h.sendText(ctx, chatID, "No formats available for this URL.")
h.editText(ctx, chatID, fetchingMsgID, "No formats available for this URL.", nil)
return
}
// Reject if user has already exhausted quota (limit of 0 = unlimited).
q, err := h.Repo.GetOrCreateQuotas(userID)
if err == nil {
q, qErr := h.Repo.GetOrCreateQuotas(userID)
if qErr == nil {
q = h.resetQuotasIfNeeded(q)
if (q.DailyLimitMB > 0 && q.DailyUsedMB >= q.DailyLimitMB) ||
(q.WeeklyLimitMB > 0 && q.WeeklyUsedMB >= q.WeeklyLimitMB) ||
(q.MonthlyLimitMB > 0 && q.MonthlyUsedMB >= q.MonthlyLimitMB) {
h.sendText(ctx, chatID, fmt.Sprintf(
h.editText(ctx, chatID, fetchingMsgID, fmt.Sprintf(
"Quota exceeded. Daily: %d/%d MB, Weekly: %d/%d MB, Monthly: %d/%d MB.",
q.DailyUsedMB, q.DailyLimitMB, q.WeeklyUsedMB, q.WeeklyLimitMB, q.MonthlyUsedMB, q.MonthlyLimitMB))
q.DailyUsedMB, q.DailyLimitMB, q.WeeklyUsedMB, q.WeeklyLimitMB, q.MonthlyUsedMB, q.MonthlyLimitMB), nil)
return
}
}
// Edit the fetching message to show the media title, then send a separate photo preview.
previewText := fmt.Sprintf("Title: %s", info.Title)
if info.Uploader != "" {
previewText += fmt.Sprintf("\nChannel: %s", info.Uploader)
}
if info.Duration > 0 {
previewText += fmt.Sprintf("\nDuration: %s", formatDuration(info.Duration))
}
h.editText(ctx, chatID, fetchingMsgID, previewText, nil)
h.sendMediaPreview(ctx, chatID, info)
ss := &stepState{
URL: url,
MediaInfo: info,
@@ -144,8 +165,6 @@ func (h *Handler) handleURLInput(ctx context.Context, chatID int64, text string,
CreatedAt: time.Now(),
}
h.sendMediaPreview(ctx, chatID, info)
ss.FormatIDs, _ = buildFormatList(info.Formats)
h.setUserStepState(chatID, ss)
labels := make([]string, len(ss.FormatIDs))
@@ -675,6 +694,19 @@ func findFormatByID(formats []domain.Format, id string) *domain.Format {
}
func formatLabelForDisplay(f domain.Format) string {
if f.IsAudioOnly {
label := f.Bitrate
if label == "" {
label = f.ID
if f.Extension != "" {
label += " (" + f.Extension + ")"
}
}
if f.Filesize > 0 {
label += fmt.Sprintf(" (%s)", formatBytes(f.Filesize))
}
return label
}
if f.Resolution != "" {
label := f.Resolution
if f.Filesize > 0 {
@@ -689,17 +721,6 @@ func formatLabelForDisplay(f domain.Format) string {
}
return label
}
// Audio-only with no bitrate: show format ID + extension
if f.IsAudioOnly {
label := f.ID
if f.Extension != "" {
label += " (" + f.Extension + ")"
}
if f.Filesize > 0 {
label += fmt.Sprintf(" (%s)", formatBytes(f.Filesize))
}
return label
}
return f.ID
}