- Add thumbnail preview with metadata (title, uploader, duration) before download - Auto-detect audio-only sites (Spotify, SoundCloud, etc.) and skip type selection - Add /download command for feature parity with UI button - Fix yt-dlp defer bug: StatusFailed was overwritten to StatusCompleted - Fix handleURLInput using editText with msgID=0 (now uses sendWithKB) - Fix filename detection: use job ID prefix for reliable file finding - Fix double answerCb between handleBackStep and handleDownloadPrompt - Fix runDownload unused msgID parameter - Fix context for download goroutine (use context.Background) - Remove unused startWebhook h parameter - Remove dead buildToggleKeyboard function - Add formatBytes, formatDuration, formatLabelForDisplay tests - Add IsAudioOnlyContent, MediaType.String tests - Add repo integration tests (users, preferences, quotas, history, delete) - Add cmd/bot tests (parseAllowedUsers, env helpers) - Add edge case tests for progress parser - Add zero/negative safety guard in formatDuration - Add .gitea, TODO.md, opencode.json to gitignore
176 lines
3.5 KiB
Go
176 lines
3.5 KiB
Go
package domain
|
|
|
|
import "os/exec"
|
|
|
|
// MediaType represents the type of media the user wants to download.
|
|
type MediaType int
|
|
|
|
const (
|
|
MediaTypeAudio MediaType = iota + 1
|
|
MediaTypeVideo
|
|
MediaTypeVideoAudio
|
|
)
|
|
|
|
func (m MediaType) String() string {
|
|
switch m {
|
|
case MediaTypeAudio:
|
|
return "audio"
|
|
case MediaTypeVideo:
|
|
return "video"
|
|
case MediaTypeVideoAudio:
|
|
return "video_audio"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// Format represents a downloadable format from yt-dlp.
|
|
type Format struct {
|
|
ID string
|
|
Extension string
|
|
Width int
|
|
Height int
|
|
Resolution string
|
|
Filesize int64
|
|
Codec string
|
|
Bitrate string
|
|
Language string
|
|
IsAudioOnly bool
|
|
IsVideoOnly bool
|
|
HasAudio bool
|
|
HasVideo bool
|
|
}
|
|
|
|
// MediaInfo holds the full information about a downloadable media item.
|
|
type MediaInfo struct {
|
|
URL string
|
|
Title string
|
|
Uploader string
|
|
Thumbnail string
|
|
Duration float64
|
|
Formats []Format
|
|
Languages []string
|
|
IsPlaylist bool
|
|
PlaylistCount int
|
|
PlaylistEntries []PlaylistEntry
|
|
EstimatedSize int64
|
|
}
|
|
|
|
// IsAudioOnlyContent returns true when no format carries a video stream.
|
|
func (m *MediaInfo) IsAudioOnlyContent() bool {
|
|
for _, f := range m.Formats {
|
|
if f.HasVideo {
|
|
return false
|
|
}
|
|
}
|
|
return len(m.Formats) > 0
|
|
}
|
|
|
|
// PlaylistEntry represents a single entry in a playlist.
|
|
type PlaylistEntry struct {
|
|
ID string
|
|
Title string
|
|
URL string
|
|
Duration float64
|
|
}
|
|
|
|
// DownloadStatus represents the current state of a download job.
|
|
type DownloadStatus int
|
|
|
|
const (
|
|
StatusPending DownloadStatus = iota
|
|
StatusFetching
|
|
StatusDownloading
|
|
StatusProcessing
|
|
StatusCompleted
|
|
StatusFailed
|
|
StatusCancelled
|
|
)
|
|
|
|
func (s DownloadStatus) String() string {
|
|
switch s {
|
|
case StatusPending:
|
|
return "Pending"
|
|
case StatusFetching:
|
|
return "Fetching info"
|
|
case StatusDownloading:
|
|
return "Downloading"
|
|
case StatusProcessing:
|
|
return "Processing"
|
|
case StatusCompleted:
|
|
return "Completed"
|
|
case StatusFailed:
|
|
return "Failed"
|
|
case StatusCancelled:
|
|
return "Cancelled"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
// DownloadJob represents an active download process for a user.
|
|
type DownloadJob struct {
|
|
ID string
|
|
UserID int64
|
|
ChatID int64
|
|
MessageID int
|
|
URL string
|
|
MediaType MediaType
|
|
SelectedFormat *Format
|
|
Container string
|
|
Language string
|
|
Status DownloadStatus
|
|
Progress float64
|
|
Speed string
|
|
ETA string
|
|
FileSize int64
|
|
FilePath string
|
|
Title string
|
|
QuotaApplied bool
|
|
CancelCh chan struct{}
|
|
Cmd *exec.Cmd
|
|
}
|
|
|
|
// UserPreferences holds per-user default preferences.
|
|
type UserPreferences struct {
|
|
DefaultMediaType MediaType
|
|
DefaultQuality string
|
|
DefaultContainer string
|
|
DefaultLanguage string
|
|
}
|
|
|
|
// UserQuotas holds the quota usage and limits for a user.
|
|
type UserQuotas struct {
|
|
DailyLimitMB int64
|
|
DailyUsedMB int64
|
|
DailyDate string
|
|
WeeklyLimitMB int64
|
|
WeeklyUsedMB int64
|
|
WeekStart string
|
|
MonthlyLimitMB int64
|
|
MonthlyUsedMB int64
|
|
MonthStart string
|
|
}
|
|
|
|
// DownloadRecord is a completed download entry stored in history.
|
|
type DownloadRecord struct {
|
|
ID int64
|
|
UserID int64
|
|
URL string
|
|
Title string
|
|
MediaType string
|
|
FormatID string
|
|
Container string
|
|
FileSize int64
|
|
Status string
|
|
CreatedAt string
|
|
}
|
|
|
|
// PlaylistState tracks the user's current playlist selection flow.
|
|
type PlaylistState struct {
|
|
Entries []PlaylistEntry
|
|
Page int
|
|
PerPage int
|
|
Selected map[string]bool
|
|
}
|