Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/lib/download-format.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, expect, it } from 'vitest';
import { formatBytes, formatEta, formatSpeed } from './download-format';

describe('download-format utilities', () => {
describe('formatBytes', () => {
it('formats bytes under 1KB correctly', () => {
expect(formatBytes(0)).toBe('0 B');
expect(formatBytes(512)).toBe('512 B');
});

it('formats kilobytes correctly', () => {
expect(formatBytes(1024)).toBe('1.0 KB');
expect(formatBytes(1536)).toBe('1.5 KB');
});

it('formats megabytes correctly', () => {
expect(formatBytes(1024 * 1024)).toBe('1.0 MB');
expect(formatBytes(234 * 1024 * 1024)).toBe('234.0 MB');
expect(formatBytes(512 * 1024 * 1024)).toBe('512.0 MB');
});

it('formats gigabytes correctly', () => {
expect(formatBytes(1024 * 1024 * 1024)).toBe('1.00 GB');
expect(formatBytes(2.5 * 1024 * 1024 * 1024)).toBe('2.50 GB');
});
});

describe('formatSpeed', () => {
it('formats 0 or negative speed as 0 KB/s', () => {
expect(formatSpeed(0)).toBe('0 KB/s');
expect(formatSpeed(-100)).toBe('0 KB/s');
});

it('formats KB/s speed', () => {
expect(formatSpeed(512 * 1024)).toBe('512 KB/s');
});

it('formats MB/s speed', () => {
expect(formatSpeed(2.5 * 1024 * 1024)).toBe('2.5 MB/s');
});
});

describe('formatEta', () => {
it('returns empty string for non-positive or non-finite values', () => {
expect(formatEta(null)).toBe('');
expect(formatEta(undefined)).toBe('');
expect(formatEta(0)).toBe('');
expect(formatEta(-10)).toBe('');
expect(formatEta(NaN)).toBe('');
expect(formatEta(Infinity)).toBe('');
});

it('formats seconds (< 60s)', () => {
expect(formatEta(45)).toBe('45s');
});

it('formats minutes and seconds (< 1h)', () => {
expect(formatEta(200)).toBe('3m 20s');
expect(formatEta(180)).toBe('3m');
});

it('formats hours and minutes (>= 1h)', () => {
expect(formatEta(3600)).toBe('1h');
expect(formatEta(3660)).toBe('1h 1m');
});
});
});
25 changes: 25 additions & 0 deletions src/lib/download-format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export function formatBytes(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}

export function formatSpeed(bytesPerSec: number): string {
if (bytesPerSec <= 0) return "0 KB/s";
if (bytesPerSec < 1024 * 1024) return `${(bytesPerSec / 1024).toFixed(0)} KB/s`;
return `${(bytesPerSec / (1024 * 1024)).toFixed(1)} MB/s`;
}

export function formatEta(seconds: number | null | undefined): string {
if (seconds == null || seconds <= 0 || !Number.isFinite(seconds)) return "";
if (seconds < 60) return `${Math.round(seconds)}s`;
if (seconds < 3600) {
const m = Math.floor(seconds / 60);
const s = Math.round(seconds % 60);
return s > 0 ? `${m}m ${s}s` : `${m}m`;
}
const h = Math.floor(seconds / 3600);
const m = Math.round((seconds % 3600) / 60);
return m > 0 ? `${h}h ${m}m` : `${h}h`;
}
65 changes: 65 additions & 0 deletions src/lib/i18n/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,71 @@
"toast_replaced": "Αντικαταστάθηκε το αρχικό. Εξοικονομήθηκαν {{saved}} ({{pct}}%) με {{encoder}}"
},
"telegram": {
"title": "Πλατφόρμα Telegram",
"checking_session": "Έλεγχος συνεδρίας...",
"login": "Είσοδος",
"logout": "Έξοδος",
"logged_as": "Έγινε είσοδος ως {{phone}}",
"phone_label": "Αριθμός τηλεφώνου",
"phone_placeholder": "+30 690 123 4567",
"phone_hint": "Συμπεριλάβετε τον κωδικό χώρας",
"send_code": "Αποστολή κωδικού",
"sending_code": "Αποστολή...",
"code_label": "Κωδικός επαλήθευσης",
"code_placeholder": "00000",
"code_hint": "Στείλαμε κωδικό στην εφαρμογή Telegram",
"verify": "Επαλήθευση",
"verifying": "Γίνεται η επαλήθευση...",
"invalid_code": "Μη έγκυρος κωδικός. Δοκιμάστε ξανά.",
"password_label": "Κωδικός πρόσβασης δύο παραγόντων",
"password_placeholder": "Ο κωδικός σας δύο παραγόντων",
"password_hint": "Υπόδειξη: {{hint}}",
"password_submit": "Επιβεβαίωση",
"password_verifying": "Επαλήθευση...",
"invalid_password": "Μη έγκυρος κωδικός. Δοκιμάστε ξανά.",
"chats_title": "Οι συνομιλίες σας",
"loading_chats": "Φόρτωση συνομιλίων...",
"chats_error": "Αποτυχία φόρτωσης συνομιλίων",
"no_chats": "Δεν βρέθηκαν συνομιλίες.",
"chat_count": "{{count}} συνομιλίες",
"chat_count_one": "{{count}} συνομιλία",
"chat_type_private": "Ιδιωτική",
"chat_type_group": "Ομάδα",
"chat_type_channel": "Κανάλι",
"media_title": "Μέσα",
"loading_media": "Φόρτωση μέσων...",
"no_media": "Δεν βρέθηκαν μέσα σε αυτήν την συνομιλία.",
"media_error": "Αποτυχία φόρτωσης μέσων",
"filter_all": "Όλα",
"filter_photo": "Φωτογραφίες",
"filter_video": "Βίντεο",
"filter_document": "Αρχεία",
"filter_audio": "Ηχητικά",
"search_files": "Αναζήτηση αρχείων...",
"searching": "Αναζήτηση...",
"back_to_chats": "Πίσω στις συνομιλίες",
"download_btn": "Λήψη",
"downloading": "Μεταφόρτωση...",
"downloaded": "Ολοκληρωμένα",
"choose_folder": "Επιλογή φακέλου μεταφορτώσεων",
"unknown_error": "Άγνωστο σφάλμα",
"download_all": "Λήψη όλων",
"batch_progress": "{{done}} από {{total}} αρχεία",
"batch_cancelled": "Διαδοχική μεταφόρτωση ακυρώθηκε",
"batch_complete": "Όλα τα αρχεία έχουν μεταφορτωθεί",
"load_more": "Φόρτωση επιπλέον",
"status_waiting": "Αναμονή",
"status_skipped": "Παραλήφθηκε",
"status_error": "Σφάλμα",
"file_count": "{{count}} αντικείμενα",
"cancel_batch": "Ακύρωση",
"qr_title": "Σαρώστε για είσοδο",
"qr_instruction": "Ανοίξτε το Telegram στο τηλέφωνο σας, και πηγαίντε Ρυθμίσεις > Συσκευές > Σύνδεση συσκευής desktop",
"qr_loading": "Δημιουργία κωδικού QR...",
"qr_error": "Αποτυχία δημιουργίας κωδικού QR",
"or_separator": "ή",
"use_phone": "Κάνε χρήση αριθμού τηλεφώνου",
"back_to_qr": "Πίσω στον κωδικό QR",
"upload_title": "Upload to Telegram / Leech Bot",
"auth_user_session": "User Session",
"auth_bot_token": "Bot Token",
Expand Down
78 changes: 38 additions & 40 deletions src/lib/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,44 @@
"qr_error": "Failed to generate QR code",
"or_separator": "or",
"use_phone": "Use phone number instead",
"back_to_qr": "Back to QR code"
"back_to_qr": "Back to QR code",
"upload_title": "Upload to Telegram / Leech Bot",
"auth_user_session": "User Session",
"auth_bot_token": "Bot Token",
"destination_chat": "Destination Chat / Channel",
"dest_placeholder": "@channel_name, chat_id or -100123456789",
"user_session_hint": "Select from your chat list or type a Chat ID / @channel_username",
"bot_token_label": "Bot Token",
"bot_chat_id_label": "Target Chat ID / Username",
"custom_filename_label": "Custom Filename & Extension",
"custom_filename_hint": "Rename file or change extension before uploading to Telegram",
"custom_thumbnail_label": "Custom Thumbnail",
"pick_thumbnail": "Pick image from disk…",
"caption_label": "Upload Caption (Optional)",
"caption_placeholder": "Add a caption or notes for Telegram media post...",
"tier_limit_label": "Telegram File Limit Mode",
"tier_free": "Free Tier",
"tier_premium": "Premium Tier",
"split_warning": "File size ({{size}}) exceeds {{limit}} Telegram limit. Will auto-split into {{parts}} parts.",
"start_upload": "Start Telegram Upload",
"uploading": "Uploading to Telegram...",
"upload_success": "Uploaded to Telegram successfully!",
"upload_failed": "Telegram upload failed.",
"error_no_chat": "Please select or enter a destination chat ID or @username.",
"error_no_bot_token": "Please enter a valid Bot Token.",
"error_invalid_bot_token": "Invalid Bot Token format. Expected format: 123456789:ABCdef...",
"error_no_file": "No source file provided for upload.",
"send_as_label": "Send Media As",
"send_as_video": "Video (Streamable)",
"send_as_document": "Document / File",
"send_as_audio": "Audio Track",
"send_to_telegram": "Upload to Telegram",
"err_bot_not_started": "Bot not started by user. Open Telegram, search your bot, and press /start first.",
"err_bot_not_member": "Bot is not in the destination chat. Add your bot as Administrator with 'Post Messages' permission.",
"err_chat_not_found": "Destination chat not found. Verify the @username or numeric Chat ID (-100...).",
"err_unauthorized": "Invalid Bot Token. Check token from @BotFather.",
"err_bot_file_too_large": "File exceeds Telegram Bot API limit (50MB). Switch to 'User Session' mode for files up to 2GB/4GB.",
"err_rate_limit": "Telegram rate limit reached. Please wait a moment before retrying."
},
"settings": {
"title": "Settings",
Expand Down Expand Up @@ -4093,44 +4130,5 @@
"test_title": "Tracking notifications are working",
"test_body": "You will see notifications like this when your packages move."
}
},
"telegram": {
"upload_title": "Upload to Telegram / Leech Bot",
"auth_user_session": "User Session",
"auth_bot_token": "Bot Token",
"destination_chat": "Destination Chat / Channel",
"dest_placeholder": "@channel_name, chat_id or -100123456789",
"user_session_hint": "Select from your chat list or type a Chat ID / @channel_username",
"bot_token_label": "Bot Token",
"bot_chat_id_label": "Target Chat ID / Username",
"custom_filename_label": "Custom Filename & Extension",
"custom_filename_hint": "Rename file or change extension before uploading to Telegram",
"custom_thumbnail_label": "Custom Thumbnail",
"pick_thumbnail": "Pick image from disk…",
"caption_label": "Upload Caption (Optional)",
"caption_placeholder": "Add a caption or notes for Telegram media post...",
"tier_limit_label": "Telegram File Limit Mode",
"tier_free": "Free Tier",
"tier_premium": "Premium Tier",
"split_warning": "File size ({{size}}) exceeds {{limit}} Telegram limit. Will auto-split into {{parts}} parts.",
"start_upload": "Start Telegram Upload",
"uploading": "Uploading to Telegram...",
"upload_success": "Uploaded to Telegram successfully!",
"upload_failed": "Telegram upload failed.",
"error_no_chat": "Please select or enter a destination chat ID or @username.",
"error_no_bot_token": "Please enter a valid Bot Token.",
"error_invalid_bot_token": "Invalid Bot Token format. Expected format: 123456789:ABCdef...",
"error_no_file": "No source file provided for upload.",
"send_as_label": "Send Media As",
"send_as_video": "Video (Streamable)",
"send_as_document": "Document / File",
"send_as_audio": "Audio Track",
"send_to_telegram": "Upload to Telegram",
"err_bot_not_started": "Bot not started by user. Open Telegram, search your bot, and press /start first.",
"err_bot_not_member": "Bot is not in the destination chat. Add your bot as Administrator with 'Post Messages' permission.",
"err_chat_not_found": "Destination chat not found. Verify the @username or numeric Chat ID (-100...).",
"err_unauthorized": "Invalid Bot Token. Check token from @BotFather.",
"err_bot_file_too_large": "File exceeds Telegram Bot API limit (50MB). Switch to 'User Session' mode for files up to 2GB/4GB.",
"err_rate_limit": "Telegram rate limit reached. Please wait a moment before retrying."
}
}
65 changes: 65 additions & 0 deletions src/lib/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,71 @@
"toast_replaced": "Original sustituido. Ahorrado {{saved}} ({{pct}}%) con {{encoder}}"
},
"telegram": {
"title": "Telegram",
"checking_session": "Comprobando sesión...",
"login": "Iniciar sesión",
"logout": "Cerrar sesión",
"logged_as": "Sesión iniciada como {{phone}}",
"phone_label": "Número de teléfono",
"phone_placeholder": "+34 600 000 000",
"phone_hint": "Incluye el código de país",
"send_code": "Enviar código",
"sending_code": "Enviando...",
"code_label": "Código de verificación",
"code_placeholder": "00000",
"code_hint": "Enviamos un código a tu app de Telegram",
"verify": "Verificar",
"verifying": "Verificando...",
"invalid_code": "Código no válido. Inténtalo de nuevo.",
"password_label": "Contraseña de dos factores",
"password_placeholder": "Tu contraseña 2FA",
"password_hint": "Pista: {{hint}}",
"password_submit": "Confirmar",
"password_verifying": "Verificando...",
"invalid_password": "Contraseña no válida. Inténtalo de nuevo.",
"chats_title": "Tus chats",
"loading_chats": "Cargando chats...",
"chats_error": "Error al cargar chats",
"no_chats": "No hay chats.",
"chat_count": "{{count}} chats",
"chat_count_one": "{{count}} chat",
"chat_type_private": "Privado",
"chat_type_group": "Grupo",
"chat_type_channel": "Canal",
"media_title": "Multimedia",
"loading_media": "Cargando multimedia...",
"no_media": "No se encontró multimedia en este chat.",
"media_error": "Error al cargar multimedia",
"filter_all": "Todo",
"filter_photo": "Fotos",
"filter_video": "Vídeos",
"filter_document": "Archivos",
"filter_audio": "Audio",
"search_files": "Buscar archivos...",
"searching": "Buscando...",
"back_to_chats": "Volver a chats",
"download_btn": "Descargar",
"downloading": "Descargando...",
"downloaded": "Descargado",
"choose_folder": "Elegir carpeta de descarga",
"unknown_error": "Error desconocido",
"download_all": "Descargar todo",
"batch_progress": "{{done}} de {{total}} archivos",
"batch_cancelled": "Descarga en lote cancelada",
"batch_complete": "Todos los archivos descargados",
"load_more": "Cargar más",
"status_waiting": "Esperando",
"status_skipped": "Omitido",
"status_error": "Error",
"file_count": "{{count}} elementos",
"cancel_batch": "Cancelar",
"qr_title": "Escanea para iniciar sesión",
"qr_instruction": "Abre Telegram en tu teléfono y ve a Ajustes > Dispositivos > Vincular dispositivo de escritorio",
"qr_loading": "Generando código QR...",
"qr_error": "Error al generar código QR",
"or_separator": "o",
"use_phone": "Usar número de teléfono",
"back_to_qr": "Volver al código QR",
"upload_title": "Upload to Telegram / Leech Bot",
"auth_user_session": "User Session",
"auth_bot_token": "Bot Token",
Expand Down
Loading
Loading