refactor: redesign format selection with unified list and independent audio/video picking
All checks were successful
CI / build (push) Successful in 52s

- Replace type/quality selection flow with unified format list (video+audio combined)
- Add independent audio/video format picking: video-only formats prompt for audio track,
  audio-only formats offer optional video addition (skip when content is audio-only)
- Fix formatKeyboard to use format IDs as callback data instead of display labels
- Remove dead mediaTypeKeyboard function
- Fix double-close panic in readProgress (caller is sole owner of updates channel)
- Add FormatString field to DownloadJob for composite format specs (e.g. '137+140')
- Change quota calculation from sum of all format sizes to max single format size
- Fix 'Delete all data' button to use danger style with proper text
- Clean up routePrefixedCallback: remove type_/quality_, add format_/secondary_ routes
- Fix back navigation to rebuild format IDs list
This commit is contained in:
2026-06-25 15:37:47 +03:30
parent 7de79b50e1
commit e65ade6c3c
7 changed files with 286 additions and 198 deletions

View File

@@ -231,8 +231,7 @@ func (h *Handler) HandleCallback(ctx context.Context, cb *models.CallbackQuery)
h.handleHelp(ctx, cb.Message.Message)
h.answerCb(ctx, cb.ID)
case data == "history":
h.handleHistory(ctx, cb.Message.Message, userID)
h.answerCb(ctx, cb.ID)
h.historyCallback(ctx, chatID, msgID, cb.ID, userID)
case data == "pending_cancel":
h.backToMenu(ctx, chatID, msgID, cb.ID, userID)
default:
@@ -244,10 +243,10 @@ func (h *Handler) routePrefixedCallback(ctx context.Context, chatID int64, msgID
defer h.answerCb(ctx, cbID)
switch {
case strings.HasPrefix(data, "type_"):
h.handleTypeSelection(ctx, chatID, msgID, userID, data[5:])
case strings.HasPrefix(data, "quality_"):
h.handleQualitySelection(ctx, chatID, msgID, userID, data[8:])
case strings.HasPrefix(data, "format_"):
h.handleFormatSelection(ctx, chatID, msgID, userID, data[7:])
case strings.HasPrefix(data, "secondary_"):
h.handleSecondarySelection(ctx, chatID, msgID, userID, data[10:])
case strings.HasPrefix(data, "lang_"):
h.handleLanguageSelection(ctx, chatID, msgID, userID, data[5:])
case strings.HasPrefix(data, "container_"):
@@ -262,14 +261,10 @@ func (h *Handler) routePrefixedCallback(ctx context.Context, chatID int64, msgID
h.handlePlaylistConfirm(ctx, chatID, msgID, userID)
case data == "back_settings":
h.backToSettings(ctx, chatID, msgID, userID)
case data == "delaccount_prompt":
h.deleteAccountPrompt(ctx, chatID, msgID, userID)
case strings.HasPrefix(data, "delaccount_"):
h.deleteAccountConfirm(ctx, chatID, msgID, userID)
case strings.HasPrefix(data, "settings_"):
h.handleSettingsSelection(ctx, chatID, msgID, userID, data[9:])
case strings.HasPrefix(data, "settings_set_"):
h.handleSettingsSet(ctx, chatID, msgID, userID, data[13:])
case strings.HasPrefix(data, "settings_"):
h.handleSettingsSelection(ctx, chatID, msgID, userID, data[9:])
}
}
@@ -367,8 +362,10 @@ func (h *Handler) answerCbWithText(ctx context.Context, callbackID, text string)
func (h *Handler) backToMenu(ctx context.Context, chatID int64, msgID int, cbID string, userID int64) {
defer h.answerCb(ctx, cbID)
h.clearActiveJob(userID)
h.editText(ctx, chatID, msgID, "Main Menu:", nil)
h.sendWithKB(ctx, chatID, "Main Menu:", mainKeyboard())
h.editText(ctx, chatID, msgID, "Main Menu:", &models.InlineKeyboardMarkup{
InlineKeyboard: mainKeyboard().InlineKeyboard,
})
h.clearStepState(chatID)
}
func (h *Handler) clearActiveJob(userID int64) {
@@ -451,7 +448,7 @@ func (h *Handler) handleSettings(ctx context.Context, msg *models.Message, userI
func (h *Handler) handleHistory(ctx context.Context, msg *models.Message, userID int64) {
records, err := h.Repo.GetHistory(userID, 10, 0)
if err != nil {
h.sendText(ctx, msg.Chat.ID, "Error loading history")
h.sendText(ctx, msg.Chat.ID, "Error loading history.")
return
}
if len(records) == 0 {
@@ -465,6 +462,37 @@ func (h *Handler) handleHistory(ctx context.Context, msg *models.Message, userID
h.sendText(ctx, msg.Chat.ID, text)
}
// historyCallback handles the history button from the main menu.
func (h *Handler) historyCallback(ctx context.Context, chatID int64, msgID int, cbID string, userID int64) {
defer h.answerCb(ctx, cbID)
records, err := h.Repo.GetHistory(userID, 10, 0)
if err != nil {
h.editText(ctx, chatID, msgID, "Error loading history.", &models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{{Text: "Back to Menu", CallbackData: "back_menu"}},
},
})
return
}
if len(records) == 0 {
h.editText(ctx, chatID, msgID, "No download history yet.", &models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{{Text: "Back to Menu", CallbackData: "back_menu"}},
},
})
return
}
text := "Recent downloads:\n"
for i, r := range records {
text += fmt.Sprintf("%d. %s (%s)\n", i+1, r.Title, formatBytes(r.FileSize))
}
h.editText(ctx, chatID, msgID, text, &models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{{Text: "Back to Menu", CallbackData: "back_menu"}},
},
})
}
// applyQuota adds file size to the user's quota counters.
func (h *Handler) applyQuota(userID int64, fileSize int64) {
q, err := h.Repo.GetOrCreateQuotas(userID)