mirror of
https://github.com/phantomptr/ps5upload.git
synced 2026-10-19 02:20:02 -05:00
console files: handoff and upload-badge hardening
- The Uploading badge reads upload roots through ava1_data_upload_roots, under the open lock (where roots are written) and the table lock, and leaves out parked, finished, stopping and recovery-placeholder jobs. - files_handoff_take_json checks its buffer before taking, so a too-small buffer can no longer empty the slot and lose the handoff. - PC app: the Show on PC poll skips its ticks while a transfer runs on that console (like the other background management pollers); away from File System a handoff becomes a toast with "Open" instead of navigating. - Console "Show on PC" now says "Sent to your PC." (no promise it opens), in all 21 languages; new PC strings for the Open offer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RxCGb8Asp1A1AQYiv1kbgS
This commit is contained in:
1 parent
4b51f802d3
commit
0ffeba67f8
50 files changed
+207
-79
No files matched your search
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "إلغاء التحديد",
|
||||
"cw_files_selected": "تم تحديد {n}",
|
||||
"cw_files_show_pc": "العرض على الكمبيوتر",
|
||||
"cw_files_show_pc_done": "أُرسل إلى الكمبيوتر. يُفتح في نظام ملفات PS5Upload.",
|
||||
"cw_files_show_pc_done": "أُرسل إلى الكمبيوتر.",
|
||||
"cw_files_show_pc_failed": "تعذّر الإرسال إلى الكمبيوتر.",
|
||||
"cw_files_some_failed": "فشل {n}: {reason}",
|
||||
"cw_files_sort_date": "ترتيب حسب التاريخ",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "বাছাই সরান",
|
||||
"cw_files_selected": "{n}টি নির্বাচিত",
|
||||
"cw_files_show_pc": "PC-তে দেখান",
|
||||
"cw_files_show_pc_done": "আপনার PC-তে পাঠানো হয়েছে। এটি PS5Upload-এর ফাইল সিস্টেমে খুলবে।",
|
||||
"cw_files_show_pc_done": "আপনার PC-তে পাঠানো হয়েছে।",
|
||||
"cw_files_show_pc_failed": "PC-তে পাঠানো যায়নি।",
|
||||
"cw_files_some_failed": "{n}টি ব্যর্থ: {reason}",
|
||||
"cw_files_sort_date": "তারিখ অনুযায়ী সাজান",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Auswahl aufheben",
|
||||
"cw_files_selected": "{n} ausgewählt",
|
||||
"cw_files_show_pc": "Auf dem PC zeigen",
|
||||
"cw_files_show_pc_done": "An deinen PC gesendet. Es öffnet sich im Dateisystem von PS5Upload.",
|
||||
"cw_files_show_pc_done": "An deinen PC gesendet.",
|
||||
"cw_files_show_pc_failed": "Konnte nicht an den PC gesendet werden.",
|
||||
"cw_files_some_failed": "{n} fehlgeschlagen: {reason}",
|
||||
"cw_files_sort_date": "Nach Datum sortieren",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Select none",
|
||||
"cw_files_selected": "{n} selected",
|
||||
"cw_files_show_pc": "Show on PC",
|
||||
"cw_files_show_pc_done": "Sent to your PC. It opens in PS5Upload's File System.",
|
||||
"cw_files_show_pc_done": "Sent to your PC.",
|
||||
"cw_files_show_pc_failed": "Couldn't send it to the PC.",
|
||||
"cw_files_some_failed": "{n} failed: {reason}",
|
||||
"cw_files_sort_date": "Sort by date",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Quitar selección",
|
||||
"cw_files_selected": "{n} seleccionados",
|
||||
"cw_files_show_pc": "Mostrar en el PC",
|
||||
"cw_files_show_pc_done": "Enviado a tu PC. Se abre en el sistema de archivos de PS5Upload.",
|
||||
"cw_files_show_pc_done": "Enviado a tu PC.",
|
||||
"cw_files_show_pc_failed": "No se pudo enviar al PC.",
|
||||
"cw_files_some_failed": "{n} fallaron: {reason}",
|
||||
"cw_files_sort_date": "Ordenar por fecha",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "لغو انتخاب",
|
||||
"cw_files_selected": "{n} انتخاب شده",
|
||||
"cw_files_show_pc": "نمایش روی رایانه",
|
||||
"cw_files_show_pc_done": "به رایانهٔ شما فرستاده شد. در سیستم فایل PS5Upload باز میشود.",
|
||||
"cw_files_show_pc_done": "به رایانهٔ شما فرستاده شد.",
|
||||
"cw_files_show_pc_failed": "فرستادن به رایانه ممکن نشد.",
|
||||
"cw_files_some_failed": "{n} ناموفق: {reason}",
|
||||
"cw_files_sort_date": "مرتبسازی بر اساس تاریخ",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Tout désélectionner",
|
||||
"cw_files_selected": "{n} sélectionné(s)",
|
||||
"cw_files_show_pc": "Afficher sur le PC",
|
||||
"cw_files_show_pc_done": "Envoyé à votre PC. Il s'ouvre dans le système de fichiers de PS5Upload.",
|
||||
"cw_files_show_pc_done": "Envoyé à votre PC.",
|
||||
"cw_files_show_pc_failed": "Impossible de l'envoyer au PC.",
|
||||
"cw_files_some_failed": "{n} en échec : {reason}",
|
||||
"cw_files_sort_date": "Trier par date",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "चयन हटाएँ",
|
||||
"cw_files_selected": "{n} चुने गए",
|
||||
"cw_files_show_pc": "PC पर दिखाएँ",
|
||||
"cw_files_show_pc_done": "आपके PC पर भेजा गया। यह PS5Upload के फ़ाइल सिस्टम में खुलेगा।",
|
||||
"cw_files_show_pc_done": "आपके PC पर भेजा गया।",
|
||||
"cw_files_show_pc_failed": "PC पर नहीं भेज सके।",
|
||||
"cw_files_some_failed": "{n} विफल: {reason}",
|
||||
"cw_files_sort_date": "तारीख़ के अनुसार क्रम",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Kijelölés törlése",
|
||||
"cw_files_selected": "{n} kijelölve",
|
||||
"cw_files_show_pc": "Megjelenítés a PC-n",
|
||||
"cw_files_show_pc_done": "Elküldve a PC-dre. A PS5Upload fájlrendszerében nyílik meg.",
|
||||
"cw_files_show_pc_done": "Elküldve a PC-dre.",
|
||||
"cw_files_show_pc_failed": "Nem sikerült elküldeni a PC-re.",
|
||||
"cw_files_some_failed": "{n} sikertelen: {reason}",
|
||||
"cw_files_sort_date": "Rendezés dátum szerint",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Batalkan pilihan",
|
||||
"cw_files_selected": "{n} dipilih",
|
||||
"cw_files_show_pc": "Tampilkan di PC",
|
||||
"cw_files_show_pc_done": "Dikirim ke PC Anda. Terbuka di Sistem File PS5Upload.",
|
||||
"cw_files_show_pc_done": "Dikirim ke PC Anda.",
|
||||
"cw_files_show_pc_failed": "Tidak dapat mengirim ke PC.",
|
||||
"cw_files_some_failed": "{n} gagal: {reason}",
|
||||
"cw_files_sort_date": "Urutkan menurut tanggal",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Deseleziona tutto",
|
||||
"cw_files_selected": "{n} selezionati",
|
||||
"cw_files_show_pc": "Mostra sul PC",
|
||||
"cw_files_show_pc_done": "Inviato al tuo PC. Si apre nel file system di PS5Upload.",
|
||||
"cw_files_show_pc_done": "Inviato al tuo PC.",
|
||||
"cw_files_show_pc_failed": "Impossibile inviarlo al PC.",
|
||||
"cw_files_some_failed": "{n} non riusciti: {reason}",
|
||||
"cw_files_sort_date": "Ordina per data",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "選択を解除",
|
||||
"cw_files_selected": "{n} 件選択中",
|
||||
"cw_files_show_pc": "PC で表示",
|
||||
"cw_files_show_pc_done": "PC に送信しました。PS5Upload のファイルシステムで開きます。",
|
||||
"cw_files_show_pc_done": "PC に送信しました。",
|
||||
"cw_files_show_pc_failed": "PC に送信できませんでした。",
|
||||
"cw_files_some_failed": "{n} 件失敗: {reason}",
|
||||
"cw_files_sort_date": "日付順",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "선택 해제",
|
||||
"cw_files_selected": "{n}개 선택됨",
|
||||
"cw_files_show_pc": "PC에서 보기",
|
||||
"cw_files_show_pc_done": "PC로 보냈습니다. PS5Upload의 파일 시스템에서 열립니다.",
|
||||
"cw_files_show_pc_done": "PC로 보냈습니다.",
|
||||
"cw_files_show_pc_failed": "PC로 보낼 수 없습니다.",
|
||||
"cw_files_some_failed": "{n}개 실패: {reason}",
|
||||
"cw_files_sort_date": "날짜순",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Odznacz wszystko",
|
||||
"cw_files_selected": "Zaznaczono: {n}",
|
||||
"cw_files_show_pc": "Pokaż na PC",
|
||||
"cw_files_show_pc_done": "Wysłano do twojego PC. Otworzy się w systemie plików PS5Upload.",
|
||||
"cw_files_show_pc_done": "Wysłano do twojego PC.",
|
||||
"cw_files_show_pc_failed": "Nie udało się wysłać do PC.",
|
||||
"cw_files_some_failed": "Nieudane ({n}): {reason}",
|
||||
"cw_files_sort_date": "Sortuj według daty",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Limpar seleção",
|
||||
"cw_files_selected": "{n} selecionados",
|
||||
"cw_files_show_pc": "Mostrar no PC",
|
||||
"cw_files_show_pc_done": "Enviado ao seu PC. Abre no sistema de arquivos do PS5Upload.",
|
||||
"cw_files_show_pc_done": "Enviado ao seu PC.",
|
||||
"cw_files_show_pc_failed": "Não foi possível enviar ao PC.",
|
||||
"cw_files_some_failed": "{n} falharam: {reason}",
|
||||
"cw_files_sort_date": "Ordenar por data",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Снять выбор",
|
||||
"cw_files_selected": "Выбрано: {n}",
|
||||
"cw_files_show_pc": "Показать на ПК",
|
||||
"cw_files_show_pc_done": "Отправлено на ваш ПК. Откроется в файловой системе PS5Upload.",
|
||||
"cw_files_show_pc_done": "Отправлено на ваш ПК.",
|
||||
"cw_files_show_pc_failed": "Не удалось отправить на ПК.",
|
||||
"cw_files_some_failed": "Не удалось ({n}): {reason}",
|
||||
"cw_files_sort_date": "По дате",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "ไม่เลือกเลย",
|
||||
"cw_files_selected": "เลือกแล้ว {n}",
|
||||
"cw_files_show_pc": "แสดงบน PC",
|
||||
"cw_files_show_pc_done": "ส่งไปยัง PC ของคุณแล้ว จะเปิดในระบบไฟล์ของ PS5Upload",
|
||||
"cw_files_show_pc_done": "ส่งไปยัง PC ของคุณแล้ว",
|
||||
"cw_files_show_pc_failed": "ไม่สามารถส่งไปยัง PC ได้",
|
||||
"cw_files_some_failed": "ล้มเหลว {n}: {reason}",
|
||||
"cw_files_sort_date": "เรียงตามวันที่",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Seçimi kaldır",
|
||||
"cw_files_selected": "{n} seçili",
|
||||
"cw_files_show_pc": "PC'de göster",
|
||||
"cw_files_show_pc_done": "PC'ne gönderildi. PS5Upload'ın Dosya Sistemi'nde açılır.",
|
||||
"cw_files_show_pc_done": "PC'ne gönderildi.",
|
||||
"cw_files_show_pc_failed": "PC'ye gönderilemedi.",
|
||||
"cw_files_some_failed": "{n} başarısız: {reason}",
|
||||
"cw_files_sort_date": "Tarihe göre sırala",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "Bỏ chọn",
|
||||
"cw_files_selected": "Đã chọn {n}",
|
||||
"cw_files_show_pc": "Hiển thị trên PC",
|
||||
"cw_files_show_pc_done": "Đã gửi đến PC của bạn. Sẽ mở trong Hệ thống tệp của PS5Upload.",
|
||||
"cw_files_show_pc_done": "Đã gửi đến PC của bạn.",
|
||||
"cw_files_show_pc_failed": "Không thể gửi đến PC.",
|
||||
"cw_files_some_failed": "{n} thất bại: {reason}",
|
||||
"cw_files_sort_date": "Sắp xếp theo ngày",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "取消全选",
|
||||
"cw_files_selected": "已选 {n} 项",
|
||||
"cw_files_show_pc": "在电脑上显示",
|
||||
"cw_files_show_pc_done": "已发送到你的电脑,将在 PS5Upload 的文件系统中打开。",
|
||||
"cw_files_show_pc_done": "已发送到你的电脑。",
|
||||
"cw_files_show_pc_failed": "无法发送到电脑。",
|
||||
"cw_files_some_failed": "{n} 项失败:{reason}",
|
||||
"cw_files_sort_date": "按日期排序",
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"cw_files_select_none": "取消全選",
|
||||
"cw_files_selected": "已選取 {n} 項",
|
||||
"cw_files_show_pc": "在電腦上顯示",
|
||||
"cw_files_show_pc_done": "已傳送到你的電腦,將在 PS5Upload 的檔案系統中開啟。",
|
||||
"cw_files_show_pc_done": "已傳送到你的電腦。",
|
||||
"cw_files_show_pc_failed": "無法傳送到電腦。",
|
||||
"cw_files_some_failed": "{n} 項失敗:{reason}",
|
||||
"cw_files_sort_date": "依日期排序",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "الوجهات الأخيرة",
|
||||
"fs_show_on_console_sent": "أُرسل إلى PS5 — يُفتح في PS5Upload Files",
|
||||
"fs_show_on_console_failed": "تعذّر الإرسال إلى PS5: {error}",
|
||||
"fs_opened_from_ps5": "فُتح من جهاز PS5 الخاص بك: {path}",
|
||||
"fs_handoff_from_ps5": "أرسل جهاز PS5 الخاص بك {path}",
|
||||
"fs_handoff_open": "فتح",
|
||||
"cw_files_show_pc": "العرض على الكمبيوتر",
|
||||
"cw_files_show_pc_done": "أُرسل إلى الكمبيوتر. يُفتح في نظام ملفات PS5Upload.",
|
||||
"cw_files_show_pc_done": "أُرسل إلى الكمبيوتر.",
|
||||
"cw_files_show_pc_failed": "تعذّر الإرسال إلى الكمبيوتر.",
|
||||
"cw_files_from_pc": "فُتح من الكمبيوتر.",
|
||||
"cw_files_from_pc_gone": "أرسل الكمبيوتر {path}، لكنه لم يعد على الجهاز.",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "সাম্প্রতিক গন্তব্য",
|
||||
"fs_show_on_console_sent": "PS5-এ পাঠানো হয়েছে — এটি PS5Upload Files-এ খুলবে",
|
||||
"fs_show_on_console_failed": "PS5-এ পাঠানো যায়নি: {error}",
|
||||
"fs_opened_from_ps5": "আপনার PS5 থেকে খোলা হয়েছে: {path}",
|
||||
"fs_handoff_from_ps5": "আপনার PS5 {path} পাঠিয়েছে",
|
||||
"fs_handoff_open": "খুলুন",
|
||||
"cw_files_show_pc": "PC-তে দেখান",
|
||||
"cw_files_show_pc_done": "আপনার PC-তে পাঠানো হয়েছে। এটি PS5Upload-এর ফাইল সিস্টেমে খুলবে।",
|
||||
"cw_files_show_pc_done": "আপনার PC-তে পাঠানো হয়েছে।",
|
||||
"cw_files_show_pc_failed": "PC-তে পাঠানো যায়নি।",
|
||||
"cw_files_from_pc": "আপনার PC থেকে খোলা হয়েছে।",
|
||||
"cw_files_from_pc_gone": "আপনার PC {path} পাঠিয়েছে, কিন্তু এটি আর কনসোলে নেই।",
|
||||
|
||||
@@ -636,8 +636,10 @@ fs_recent_tooltip: "Letzte Ziele",
|
||||
"fs_show_on_console_sent": "An die PS5 gesendet – es öffnet sich in PS5Upload Files",
|
||||
"fs_show_on_console_failed": "Konnte nicht an die PS5 gesendet werden: {error}",
|
||||
"fs_opened_from_ps5": "Von deiner PS5 geöffnet: {path}",
|
||||
"fs_handoff_from_ps5": "Deine PS5 hat {path} gesendet",
|
||||
"fs_handoff_open": "Öffnen",
|
||||
"cw_files_show_pc": "Auf dem PC zeigen",
|
||||
"cw_files_show_pc_done": "An deinen PC gesendet. Es öffnet sich im Dateisystem von PS5Upload.",
|
||||
"cw_files_show_pc_done": "An deinen PC gesendet.",
|
||||
"cw_files_show_pc_failed": "Konnte nicht an den PC gesendet werden.",
|
||||
"cw_files_from_pc": "Von deinem PC geöffnet.",
|
||||
"cw_files_from_pc_gone": "Dein PC hat {path} gesendet, aber es ist nicht mehr auf der Konsole.",
|
||||
|
||||
@@ -1088,8 +1088,10 @@ fs_recent_tooltip: "Recent destinations",
|
||||
"fs_show_on_console_sent": "Sent to the PS5 — it opens in PS5Upload Files",
|
||||
"fs_show_on_console_failed": "Couldn't send it to the PS5: {error}",
|
||||
"fs_opened_from_ps5": "Opened from your PS5: {path}",
|
||||
"fs_handoff_from_ps5": "Your PS5 sent {path}",
|
||||
"fs_handoff_open": "Open",
|
||||
"cw_files_show_pc": "Show on PC",
|
||||
"cw_files_show_pc_done": "Sent to your PC. It opens in PS5Upload's File System.",
|
||||
"cw_files_show_pc_done": "Sent to your PC.",
|
||||
"cw_files_show_pc_failed": "Couldn't send it to the PC.",
|
||||
"cw_files_from_pc": "Opened from your PC.",
|
||||
"cw_files_from_pc_gone": "Your PC sent {path}, but it isn't on the console any more.",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "Destinos recientes",
|
||||
"fs_show_on_console_sent": "Enviado a la PS5: se abre en PS5Upload Files",
|
||||
"fs_show_on_console_failed": "No se pudo enviar a la PS5: {error}",
|
||||
"fs_opened_from_ps5": "Abierto desde tu PS5: {path}",
|
||||
"fs_handoff_from_ps5": "Tu PS5 envió {path}",
|
||||
"fs_handoff_open": "Abrir",
|
||||
"cw_files_show_pc": "Mostrar en el PC",
|
||||
"cw_files_show_pc_done": "Enviado a tu PC. Se abre en el sistema de archivos de PS5Upload.",
|
||||
"cw_files_show_pc_done": "Enviado a tu PC.",
|
||||
"cw_files_show_pc_failed": "No se pudo enviar al PC.",
|
||||
"cw_files_from_pc": "Abierto desde tu PC.",
|
||||
"cw_files_from_pc_gone": "Tu PC envió {path}, pero ya no está en la consola.",
|
||||
|
||||
@@ -1011,8 +1011,10 @@ fs_recent_tooltip: "مقصدهای اخیر",
|
||||
"fs_show_on_console_sent": "به PS5 فرستاده شد — در PS5Upload Files باز میشود",
|
||||
"fs_show_on_console_failed": "فرستادن به PS5 ممکن نشد: {error}",
|
||||
"fs_opened_from_ps5": "از PS5 شما باز شد: {path}",
|
||||
"fs_handoff_from_ps5": "PS5 شما {path} را فرستاد",
|
||||
"fs_handoff_open": "باز کردن",
|
||||
"cw_files_show_pc": "نمایش روی رایانه",
|
||||
"cw_files_show_pc_done": "به رایانهٔ شما فرستاده شد. در سیستم فایل PS5Upload باز میشود.",
|
||||
"cw_files_show_pc_done": "به رایانهٔ شما فرستاده شد.",
|
||||
"cw_files_show_pc_failed": "فرستادن به رایانه ممکن نشد.",
|
||||
"cw_files_from_pc": "از رایانهٔ شما باز شد.",
|
||||
"cw_files_from_pc_gone": "رایانهٔ شما {path} را فرستاد، اما دیگر روی کنسول نیست.",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "Destinations récentes",
|
||||
"fs_show_on_console_sent": "Envoyé à la PS5 — il s'ouvre dans PS5Upload Files",
|
||||
"fs_show_on_console_failed": "Impossible de l'envoyer à la PS5 : {error}",
|
||||
"fs_opened_from_ps5": "Ouvert depuis votre PS5 : {path}",
|
||||
"fs_handoff_from_ps5": "Votre PS5 a envoyé {path}",
|
||||
"fs_handoff_open": "Ouvrir",
|
||||
"cw_files_show_pc": "Afficher sur le PC",
|
||||
"cw_files_show_pc_done": "Envoyé à votre PC. Il s'ouvre dans le système de fichiers de PS5Upload.",
|
||||
"cw_files_show_pc_done": "Envoyé à votre PC.",
|
||||
"cw_files_show_pc_failed": "Impossible de l'envoyer au PC.",
|
||||
"cw_files_from_pc": "Ouvert depuis votre PC.",
|
||||
"cw_files_from_pc_gone": "Votre PC a envoyé {path}, mais il n'est plus sur la console.",
|
||||
|
||||
@@ -636,8 +636,10 @@ fs_recent_tooltip: "हाल के गंतव्य",
|
||||
"fs_show_on_console_sent": "PS5 पर भेजा गया — यह PS5Upload Files में खुलेगा",
|
||||
"fs_show_on_console_failed": "PS5 पर नहीं भेज सके: {error}",
|
||||
"fs_opened_from_ps5": "आपके PS5 से खोला गया: {path}",
|
||||
"fs_handoff_from_ps5": "आपके PS5 ने {path} भेजा",
|
||||
"fs_handoff_open": "खोलें",
|
||||
"cw_files_show_pc": "PC पर दिखाएँ",
|
||||
"cw_files_show_pc_done": "आपके PC पर भेजा गया। यह PS5Upload के फ़ाइल सिस्टम में खुलेगा।",
|
||||
"cw_files_show_pc_done": "आपके PC पर भेजा गया।",
|
||||
"cw_files_show_pc_failed": "PC पर नहीं भेज सके।",
|
||||
"cw_files_from_pc": "आपके PC से खोला गया।",
|
||||
"cw_files_from_pc_gone": "आपके PC ने {path} भेजा, पर यह अब कंसोल पर नहीं है।",
|
||||
|
||||
@@ -947,8 +947,10 @@ fs_recent_tooltip: "Legutóbbi célok",
|
||||
"fs_show_on_console_sent": "Elküldve a PS5-re – a PS5Upload Files-ban nyílik meg",
|
||||
"fs_show_on_console_failed": "Nem sikerült elküldeni a PS5-re: {error}",
|
||||
"fs_opened_from_ps5": "Megnyitva a PS5-ödről: {path}",
|
||||
"fs_handoff_from_ps5": "A PS5-öd elküldte ezt: {path}",
|
||||
"fs_handoff_open": "Megnyitás",
|
||||
"cw_files_show_pc": "Megjelenítés a PC-n",
|
||||
"cw_files_show_pc_done": "Elküldve a PC-dre. A PS5Upload fájlrendszerében nyílik meg.",
|
||||
"cw_files_show_pc_done": "Elküldve a PC-dre.",
|
||||
"cw_files_show_pc_failed": "Nem sikerült elküldeni a PC-re.",
|
||||
"cw_files_from_pc": "Megnyitva a PC-dről.",
|
||||
"cw_files_from_pc_gone": "A PC-d elküldte ezt: {path}, de már nincs a konzolon.",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "Tujuan terbaru",
|
||||
"fs_show_on_console_sent": "Dikirim ke PS5 — terbuka di PS5Upload Files",
|
||||
"fs_show_on_console_failed": "Tidak dapat mengirim ke PS5: {error}",
|
||||
"fs_opened_from_ps5": "Dibuka dari PS5 Anda: {path}",
|
||||
"fs_handoff_from_ps5": "PS5 Anda mengirim {path}",
|
||||
"fs_handoff_open": "Buka",
|
||||
"cw_files_show_pc": "Tampilkan di PC",
|
||||
"cw_files_show_pc_done": "Dikirim ke PC Anda. Terbuka di Sistem File PS5Upload.",
|
||||
"cw_files_show_pc_done": "Dikirim ke PC Anda.",
|
||||
"cw_files_show_pc_failed": "Tidak dapat mengirim ke PC.",
|
||||
"cw_files_from_pc": "Dibuka dari PC Anda.",
|
||||
"cw_files_from_pc_gone": "PC Anda mengirim {path}, tetapi sudah tidak ada di konsol.",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "Destinazioni recenti",
|
||||
"fs_show_on_console_sent": "Inviato alla PS5: si apre in PS5Upload Files",
|
||||
"fs_show_on_console_failed": "Impossibile inviarlo alla PS5: {error}",
|
||||
"fs_opened_from_ps5": "Aperto dalla tua PS5: {path}",
|
||||
"fs_handoff_from_ps5": "La tua PS5 ha inviato {path}",
|
||||
"fs_handoff_open": "Apri",
|
||||
"cw_files_show_pc": "Mostra sul PC",
|
||||
"cw_files_show_pc_done": "Inviato al tuo PC. Si apre nel file system di PS5Upload.",
|
||||
"cw_files_show_pc_done": "Inviato al tuo PC.",
|
||||
"cw_files_show_pc_failed": "Impossibile inviarlo al PC.",
|
||||
"cw_files_from_pc": "Aperto dal tuo PC.",
|
||||
"cw_files_from_pc_gone": "Il tuo PC ha inviato {path}, ma non è più sulla console.",
|
||||
|
||||
@@ -636,8 +636,10 @@ fs_recent_tooltip: "最近の保存先",
|
||||
"fs_show_on_console_sent": "PS5 に送信しました — PS5Upload Files で開きます",
|
||||
"fs_show_on_console_failed": "PS5 に送信できませんでした: {error}",
|
||||
"fs_opened_from_ps5": "PS5 から開きました: {path}",
|
||||
"fs_handoff_from_ps5": "PS5 から {path} が送られました",
|
||||
"fs_handoff_open": "開く",
|
||||
"cw_files_show_pc": "PC で表示",
|
||||
"cw_files_show_pc_done": "PC に送信しました。PS5Upload のファイルシステムで開きます。",
|
||||
"cw_files_show_pc_done": "PC に送信しました。",
|
||||
"cw_files_show_pc_failed": "PC に送信できませんでした。",
|
||||
"cw_files_from_pc": "PC から開きました。",
|
||||
"cw_files_from_pc_gone": "PC から {path} が送られましたが、もうコンソールにありません。",
|
||||
|
||||
@@ -636,8 +636,10 @@ fs_recent_tooltip: "최근 대상 위치",
|
||||
"fs_show_on_console_sent": "PS5로 보냈습니다 — PS5Upload Files에서 열립니다",
|
||||
"fs_show_on_console_failed": "PS5로 보낼 수 없습니다: {error}",
|
||||
"fs_opened_from_ps5": "PS5에서 열림: {path}",
|
||||
"fs_handoff_from_ps5": "PS5가 {path}을(를) 보냈습니다",
|
||||
"fs_handoff_open": "열기",
|
||||
"cw_files_show_pc": "PC에서 보기",
|
||||
"cw_files_show_pc_done": "PC로 보냈습니다. PS5Upload의 파일 시스템에서 열립니다.",
|
||||
"cw_files_show_pc_done": "PC로 보냈습니다.",
|
||||
"cw_files_show_pc_failed": "PC로 보낼 수 없습니다.",
|
||||
"cw_files_from_pc": "PC에서 열었습니다.",
|
||||
"cw_files_from_pc_gone": "PC가 {path}을(를) 보냈지만 더 이상 콘솔에 없습니다.",
|
||||
|
||||
@@ -889,8 +889,10 @@ fs_recent_tooltip: "Ostatnie miejsca docelowe",
|
||||
"fs_show_on_console_sent": "Wysłano do PS5 — otworzy się w PS5Upload Files",
|
||||
"fs_show_on_console_failed": "Nie udało się wysłać do PS5: {error}",
|
||||
"fs_opened_from_ps5": "Otwarto z twojej PS5: {path}",
|
||||
"fs_handoff_from_ps5": "Twoja PS5 wysłała {path}",
|
||||
"fs_handoff_open": "Otwórz",
|
||||
"cw_files_show_pc": "Pokaż na PC",
|
||||
"cw_files_show_pc_done": "Wysłano do twojego PC. Otworzy się w systemie plików PS5Upload.",
|
||||
"cw_files_show_pc_done": "Wysłano do twojego PC.",
|
||||
"cw_files_show_pc_failed": "Nie udało się wysłać do PC.",
|
||||
"cw_files_from_pc": "Otwarto z twojego PC.",
|
||||
"cw_files_from_pc_gone": "Twój PC wysłał {path}, ale tego nie ma już na konsoli.",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "Destinos recentes",
|
||||
"fs_show_on_console_sent": "Enviado ao PS5 — abre no PS5Upload Files",
|
||||
"fs_show_on_console_failed": "Não foi possível enviar ao PS5: {error}",
|
||||
"fs_opened_from_ps5": "Aberto pelo seu PS5: {path}",
|
||||
"fs_handoff_from_ps5": "Seu PS5 enviou {path}",
|
||||
"fs_handoff_open": "Abrir",
|
||||
"cw_files_show_pc": "Mostrar no PC",
|
||||
"cw_files_show_pc_done": "Enviado ao seu PC. Abre no sistema de arquivos do PS5Upload.",
|
||||
"cw_files_show_pc_done": "Enviado ao seu PC.",
|
||||
"cw_files_show_pc_failed": "Não foi possível enviar ao PC.",
|
||||
"cw_files_from_pc": "Aberto pelo seu PC.",
|
||||
"cw_files_from_pc_gone": "Seu PC enviou {path}, mas não está mais no console.",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "Недавние места назначения",
|
||||
"fs_show_on_console_sent": "Отправлено на PS5 — откроется в PS5Upload Files",
|
||||
"fs_show_on_console_failed": "Не удалось отправить на PS5: {error}",
|
||||
"fs_opened_from_ps5": "Открыто с вашей PS5: {path}",
|
||||
"fs_handoff_from_ps5": "Ваша PS5 отправила {path}",
|
||||
"fs_handoff_open": "Открыть",
|
||||
"cw_files_show_pc": "Показать на ПК",
|
||||
"cw_files_show_pc_done": "Отправлено на ваш ПК. Откроется в файловой системе PS5Upload.",
|
||||
"cw_files_show_pc_done": "Отправлено на ваш ПК.",
|
||||
"cw_files_show_pc_failed": "Не удалось отправить на ПК.",
|
||||
"cw_files_from_pc": "Открыто с вашего ПК.",
|
||||
"cw_files_from_pc_gone": "Ваш ПК отправил {path}, но этого больше нет на консоли.",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "ปลายทางล่าสุด",
|
||||
"fs_show_on_console_sent": "ส่งไปยัง PS5 แล้ว — จะเปิดใน PS5Upload Files",
|
||||
"fs_show_on_console_failed": "ไม่สามารถส่งไปยัง PS5: {error}",
|
||||
"fs_opened_from_ps5": "เปิดจาก PS5 ของคุณ: {path}",
|
||||
"fs_handoff_from_ps5": "PS5 ของคุณส่ง {path} มา",
|
||||
"fs_handoff_open": "เปิด",
|
||||
"cw_files_show_pc": "แสดงบน PC",
|
||||
"cw_files_show_pc_done": "ส่งไปยัง PC ของคุณแล้ว จะเปิดในระบบไฟล์ของ PS5Upload",
|
||||
"cw_files_show_pc_done": "ส่งไปยัง PC ของคุณแล้ว",
|
||||
"cw_files_show_pc_failed": "ไม่สามารถส่งไปยัง PC ได้",
|
||||
"cw_files_from_pc": "เปิดจาก PC ของคุณ",
|
||||
"cw_files_from_pc_gone": "PC ของคุณส่ง {path} มา แต่ไม่มีอยู่บนเครื่องแล้ว",
|
||||
|
||||
@@ -635,8 +635,10 @@ fs_recent_tooltip: "Son hedefler",
|
||||
"fs_show_on_console_sent": "PS5'e gönderildi — PS5Upload Files'ta açılır",
|
||||
"fs_show_on_console_failed": "PS5'e gönderilemedi: {error}",
|
||||
"fs_opened_from_ps5": "PS5'inden açıldı: {path}",
|
||||
"fs_handoff_from_ps5": "PS5'in {path} gönderdi",
|
||||
"fs_handoff_open": "Aç",
|
||||
"cw_files_show_pc": "PC'de göster",
|
||||
"cw_files_show_pc_done": "PC'ne gönderildi. PS5Upload'ın Dosya Sistemi'nde açılır.",
|
||||
"cw_files_show_pc_done": "PC'ne gönderildi.",
|
||||
"cw_files_show_pc_failed": "PC'ye gönderilemedi.",
|
||||
"cw_files_from_pc": "PC'nden açıldı.",
|
||||
"cw_files_from_pc_gone": "PC'n {path} gönderdi, ancak artık konsolda değil.",
|
||||
|
||||
@@ -637,8 +637,10 @@ fs_recent_tooltip: "Đích đến gần đây",
|
||||
"fs_show_on_console_sent": "Đã gửi đến PS5 — sẽ mở trong PS5Upload Files",
|
||||
"fs_show_on_console_failed": "Không thể gửi đến PS5: {error}",
|
||||
"fs_opened_from_ps5": "Đã mở từ PS5 của bạn: {path}",
|
||||
"fs_handoff_from_ps5": "PS5 của bạn đã gửi {path}",
|
||||
"fs_handoff_open": "Mở",
|
||||
"cw_files_show_pc": "Hiển thị trên PC",
|
||||
"cw_files_show_pc_done": "Đã gửi đến PC của bạn. Sẽ mở trong Hệ thống tệp của PS5Upload.",
|
||||
"cw_files_show_pc_done": "Đã gửi đến PC của bạn.",
|
||||
"cw_files_show_pc_failed": "Không thể gửi đến PC.",
|
||||
"cw_files_from_pc": "Đã mở từ PC của bạn.",
|
||||
"cw_files_from_pc_gone": "PC của bạn đã gửi {path}, nhưng nó không còn trên máy nữa.",
|
||||
|
||||
@@ -642,8 +642,10 @@ fs_recent_tooltip: "最近的目标位置",
|
||||
"fs_show_on_console_sent": "已发送到 PS5 — 将在 PS5Upload Files 中打开",
|
||||
"fs_show_on_console_failed": "无法发送到 PS5:{error}",
|
||||
"fs_opened_from_ps5": "已从你的 PS5 打开:{path}",
|
||||
"fs_handoff_from_ps5": "你的 PS5 发送了 {path}",
|
||||
"fs_handoff_open": "打开",
|
||||
"cw_files_show_pc": "在电脑上显示",
|
||||
"cw_files_show_pc_done": "已发送到你的电脑,将在 PS5Upload 的文件系统中打开。",
|
||||
"cw_files_show_pc_done": "已发送到你的电脑。",
|
||||
"cw_files_show_pc_failed": "无法发送到电脑。",
|
||||
"cw_files_from_pc": "已从你的电脑打开。",
|
||||
"cw_files_from_pc_gone": "你的电脑发送了 {path},但它已不在主机上。",
|
||||
|
||||
@@ -636,8 +636,10 @@ fs_recent_tooltip: "最近的目的地",
|
||||
"fs_show_on_console_sent": "已傳送到 PS5 — 將在 PS5Upload Files 中開啟",
|
||||
"fs_show_on_console_failed": "無法傳送到 PS5:{error}",
|
||||
"fs_opened_from_ps5": "已從你的 PS5 開啟:{path}",
|
||||
"fs_handoff_from_ps5": "你的 PS5 傳送了 {path}",
|
||||
"fs_handoff_open": "開啟",
|
||||
"cw_files_show_pc": "在電腦上顯示",
|
||||
"cw_files_show_pc_done": "已傳送到你的電腦,將在 PS5Upload 的檔案系統中開啟。",
|
||||
"cw_files_show_pc_done": "已傳送到你的電腦。",
|
||||
"cw_files_show_pc_failed": "無法傳送到電腦。",
|
||||
"cw_files_from_pc": "已從你的電腦開啟。",
|
||||
"cw_files_from_pc_gone": "你的電腦傳送了 {path},但它已不在主機上。",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { useFsNavStore, openInFileSystem } from "../state/fsNavigation";
|
||||
import { createHandoffPoller, HANDOFF_POLL_FILES_MS } from "./useConsoleHandoff";
|
||||
import { createHandoffPoller, deliverHandoff, HANDOFF_POLL_FILES_MS } from "./useConsoleHandoff";
|
||||
|
||||
beforeEach(() => vi.useFakeTimers());
|
||||
afterEach(() => vi.useRealTimers());
|
||||
@@ -50,6 +50,40 @@ describe("Show on PC: the handoff poll", () => {
|
||||
p.stop();
|
||||
});
|
||||
|
||||
it("skips its ticks while a transfer runs, then asks again", async () => {
|
||||
let busy = true;
|
||||
const take = vi.fn().mockResolvedValue({ supported: true, path: null, ageMs: 0 });
|
||||
const p = createHandoffPoller({ take, delay: () => 1000, onPath: vi.fn(), skip: () => busy });
|
||||
await vi.advanceTimersByTimeAsync(3000);
|
||||
expect(take).not.toHaveBeenCalled();
|
||||
busy = false;
|
||||
await vi.advanceTimersByTimeAsync(1000);
|
||||
expect(take).toHaveBeenCalledTimes(1);
|
||||
p.stop();
|
||||
});
|
||||
|
||||
it("on File System it opens at once; elsewhere a toast offers Open and nothing navigates", () => {
|
||||
const tr = ((_k: string, _v?: unknown, fallback?: string) => fallback ?? _k) as never;
|
||||
const navigate = vi.fn();
|
||||
const toast = vi.fn();
|
||||
deliverHandoff("/data/a", { onFiles: true, navigate, tr, toast });
|
||||
expect(navigate).toHaveBeenCalledWith("/files");
|
||||
expect(useFsNavStore.getState().consume()).toBe("/data/a");
|
||||
expect(toast.mock.calls[0][0].action).toBeUndefined();
|
||||
|
||||
navigate.mockClear();
|
||||
toast.mockClear();
|
||||
deliverHandoff("/data/b", { onFiles: false, navigate, tr, toast });
|
||||
expect(navigate).not.toHaveBeenCalled();
|
||||
expect(useFsNavStore.getState().requestedPath).toBeNull();
|
||||
const offer = toast.mock.calls[0][0];
|
||||
expect(offer.message).toBe("Your PS5 sent /data/b");
|
||||
expect(offer.action.label).toBe("Open");
|
||||
offer.action.onClick();
|
||||
expect(navigate).toHaveBeenCalledWith("/files");
|
||||
expect(useFsNavStore.getState().consume()).toBe("/data/b");
|
||||
});
|
||||
|
||||
it("the folder reaches File System as a one-shot request", () => {
|
||||
const navigate = vi.fn();
|
||||
openInFileSystem(navigate, "/mnt/usb0/games");
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useRef } from "react";
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
|
||||
import { takeHandoffForPc, type HandoffTake } from "../api/consoleFiles";
|
||||
import { transferScreenBusy } from "../lib/ps5Transfers";
|
||||
import { useDocumentVisible } from "../lib/visibility";
|
||||
import { useConnectionStore } from "../state/connection";
|
||||
import { openInFileSystem } from "../state/fsNavigation";
|
||||
@@ -11,6 +12,8 @@ import { useToast } from "../state/toasts";
|
||||
/** How often the console is asked for a "Show on PC" handoff: often on File System, less elsewhere. */
|
||||
export const HANDOFF_POLL_FILES_MS = 3_000;
|
||||
export const HANDOFF_POLL_OTHER_MS = 10_000;
|
||||
/** How long the "Open" offer stays up when a handoff arrives away from File System. */
|
||||
export const HANDOFF_OFFER_MS = 15_000;
|
||||
|
||||
export interface HandoffPoller {
|
||||
stop(): void;
|
||||
@@ -26,12 +29,18 @@ export function createHandoffPoller(opts: {
|
||||
delay: () => number;
|
||||
onPath: (path: string) => void;
|
||||
onUnsupported?: () => void;
|
||||
/** True to skip this tick (a transfer runs: background management calls wait). */
|
||||
skip?: () => boolean;
|
||||
}): HandoffPoller {
|
||||
let stopped = false;
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
const tick = async () => {
|
||||
timer = undefined;
|
||||
if (stopped) return;
|
||||
if (opts.skip?.()) {
|
||||
timer = setTimeout(() => void tick(), opts.delay());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const r = await opts.take();
|
||||
if (stopped) return;
|
||||
@@ -55,10 +64,38 @@ export function createHandoffPoller(opts: {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A handoff arrived: on File System it opens at once with a toast; elsewhere the user is never
|
||||
* pulled off the screen they are on, a toast offers "Open" instead.
|
||||
*/
|
||||
export function deliverHandoff(
|
||||
path: string,
|
||||
ctx: {
|
||||
onFiles: boolean;
|
||||
navigate: (to: string) => void;
|
||||
tr: ReturnType<typeof useTr>;
|
||||
toast: ReturnType<typeof useToast>["toast"];
|
||||
},
|
||||
): void {
|
||||
const { navigate: go, tr: t, toast: show } = ctx;
|
||||
if (ctx.onFiles) {
|
||||
openInFileSystem(go, path);
|
||||
show({ tone: "info", message: t("fs_opened_from_ps5", { path }, `Opened from your PS5: ${path}`) });
|
||||
return;
|
||||
}
|
||||
show({
|
||||
tone: "info",
|
||||
duration: HANDOFF_OFFER_MS,
|
||||
message: t("fs_handoff_from_ps5", { path }, `Your PS5 sent ${path}`),
|
||||
action: { label: t("fs_handoff_open", undefined, "Open"), onClick: () => openInFileSystem(go, path) },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* "Show on PC": the console's Files page left a folder for this app. While the helper is up and
|
||||
* the window is visible, the handoff is taken (every 3 s on File System, every 10 s elsewhere)
|
||||
* and File System opens it with a toast. An older helper is asked once per connection.
|
||||
* the window is visible and no transfer runs on it, the handoff is taken (every 3 s on File System,
|
||||
* every 10 s elsewhere). On File System it opens at once with a toast; elsewhere a toast offers
|
||||
* "Open". An older helper is asked once per connection.
|
||||
*/
|
||||
export function useConsoleHandoff(): void {
|
||||
const host = useConnectionStore((s) => s.host);
|
||||
@@ -79,11 +116,8 @@ export function useConsoleHandoff(): void {
|
||||
const p = createHandoffPoller({
|
||||
take: () => takeHandoffForPc(host),
|
||||
delay: () => (live.current.onFiles ? HANDOFF_POLL_FILES_MS : HANDOFF_POLL_OTHER_MS),
|
||||
onPath: (path) => {
|
||||
const { navigate: go, tr: t, toast: show } = live.current;
|
||||
openInFileSystem(go, path);
|
||||
show({ tone: "info", message: t("fs_opened_from_ps5", { path }, `Opened from your PS5: ${path}`) });
|
||||
},
|
||||
skip: () => transferScreenBusy(host),
|
||||
onPath: (path) => deliverHandoff(path, live.current),
|
||||
});
|
||||
return () => p.stop();
|
||||
}, [host, up, visible]);
|
||||
|
||||
@@ -933,6 +933,38 @@ static int root_in_use(const uint8_t id[16], const char *root) {
|
||||
return busy;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char (*roots)[AVA1_MAX_PATH + 1];
|
||||
unsigned max, n;
|
||||
} upload_roots_t;
|
||||
|
||||
/* Under the table lock and g_open_mu (ava1_data_upload_roots): kind and root are written by
|
||||
* ava1_recv_open under g_open_mu, `attached` under the table lock, so both reads are stable. */
|
||||
static void upload_root_each(ava1_job_t *j, void *ctx) {
|
||||
static const uint8_t zero_owner[32];
|
||||
upload_roots_t *r = ctx;
|
||||
size_t len;
|
||||
if (r->n >= r->max || j->kind != AVA1_JOB_UPLOAD || !j->attached) return; /* parked: nobody is sending */
|
||||
if (memcmp(j->owner, zero_owner, 32) == 0) return; /* a start-time recovery placeholder */
|
||||
if (__atomic_load_n(&j->finished, __ATOMIC_ACQUIRE) || __atomic_load_n(&j->stopping, __ATOMIC_ACQUIRE)) return;
|
||||
len = strnlen(j->root, AVA1_MAX_PATH);
|
||||
if (!len) return;
|
||||
memcpy(r->roots[r->n], j->root, len);
|
||||
r->roots[r->n][len] = '\0';
|
||||
r->n++;
|
||||
}
|
||||
|
||||
int ava1_data_upload_roots(char (*roots)[AVA1_MAX_PATH + 1], unsigned max) {
|
||||
upload_roots_t r;
|
||||
r.roots = roots;
|
||||
r.max = max;
|
||||
r.n = 0;
|
||||
pthread_mutex_lock(&g_open_mu);
|
||||
ava1_job_foreach(upload_root_each, &r);
|
||||
pthread_mutex_unlock(&g_open_mu);
|
||||
return (int)r.n;
|
||||
}
|
||||
|
||||
/* Consumes `o`'s refusal flag under O.mu. The slot cannot be reused while its open runs
|
||||
* (the drain that frees it follows open_now), so the flag always belongs to this open.
|
||||
* 1 = a pipelined frame was not queued; `status`/`msg` carry why. */
|
||||
|
||||
@@ -73,6 +73,9 @@ void ava1_unswept_add(int64_t delta);
|
||||
uint64_t ava1_unswept_total(void);
|
||||
/* 0 once ava1_data_stop has begun: long background work (recovery) checks it and returns. */
|
||||
int ava1_data_running(void);
|
||||
/* The roots of up to `max` uploads a session is sending now (attached, not finished or stopping,
|
||||
* not a recovery placeholder), read under the open lock: the console page's "Uploading" badge. */
|
||||
int ava1_data_upload_roots(char (*roots)[AVA1_MAX_PATH + 1], unsigned max);
|
||||
/* Housekeeping loop iterations since start (tests: a slow recovery must not stall the reaper). */
|
||||
extern unsigned ava1_house_ticks;
|
||||
|
||||
|
||||
+1
-25
@@ -460,32 +460,8 @@ int ava1_jobs_list_all(const uint8_t me[32], uint8_t *out, size_t cap, size_t *o
|
||||
return list_jobs(NULL, out, cap, out_len);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char (*roots)[AVA1_MAX_PATH + 1];
|
||||
unsigned max, n;
|
||||
} roots_t;
|
||||
|
||||
static void root_one(ava1_job_t *j, void *ctx) {
|
||||
roots_t *r = ctx;
|
||||
size_t len;
|
||||
if (r->n >= r->max || j->kind != AVA1_JOB_UPLOAD || __atomic_load_n(&j->finished, __ATOMIC_ACQUIRE)) return;
|
||||
len = strnlen(j->root, AVA1_MAX_PATH);
|
||||
if (!len) return;
|
||||
memcpy(r->roots[r->n], j->root, len);
|
||||
r->roots[r->n][len] = '\0';
|
||||
r->n++;
|
||||
}
|
||||
|
||||
int ava1_jobs_upload_roots(char (*roots)[AVA1_MAX_PATH + 1], unsigned max) {
|
||||
roots_t r;
|
||||
r.roots = roots;
|
||||
r.max = max;
|
||||
r.n = 0;
|
||||
/* Under the table lock, without the job's: a root is written once, as the job opens, so a listing
|
||||
* at that moment may read it half-written (bounded: the buffer is fixed and NUL-capped here). The
|
||||
* cost is one wrong badge for one poll; nothing acts on these paths. */
|
||||
ava1_job_foreach(root_one, &r);
|
||||
return (int)r.n;
|
||||
return ava1_data_upload_roots(roots, max);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -55,9 +55,10 @@ int ava1_op_list_rpc(const uint8_t owner[32], uint8_t *out, size_t cap, size_t *
|
||||
* downloads, copies, operations) as a JobListResult. `me` is the page's identity; the entries
|
||||
* carry no owner, so the page marks its own with ava1_jobs_owner_is. */
|
||||
int ava1_jobs_list_all(const uint8_t me[32], uint8_t *out, size_t cap, size_t *out_len);
|
||||
/* The roots (destination paths, as requested) of up to `max` running uploads, any owner's, into
|
||||
* roots[i] (each AVA1_MAX_PATH + 1 bytes, NUL-terminated). Returns how many. The console page shows
|
||||
* an "Uploading" badge on those folders. */
|
||||
/* The roots (destination paths, as requested) of up to `max` uploads a session is sending now
|
||||
* (ava1_data_upload_roots: attached, not finished, parked or a recovery placeholder), into roots[i]
|
||||
* (each AVA1_MAX_PATH + 1 bytes, NUL-terminated). Returns how many. The console page shows an
|
||||
* "Uploading" badge on those folders. */
|
||||
int ava1_jobs_upload_roots(char (*roots)[AVA1_MAX_PATH + 1], unsigned max);
|
||||
/* 1 when job `id` is listed and `me` owns it, else 0 (an absent job included). */
|
||||
int ava1_jobs_owner_is(const uint8_t id[16], const uint8_t me[32]);
|
||||
|
||||
@@ -84,8 +84,9 @@ int files_handoff_take_json(int side, long long now_ms, char *out, size_t cap) {
|
||||
char path[FILES_PREFS_PATH_MAX];
|
||||
long long age = 0;
|
||||
size_t at;
|
||||
if (!files_handoff_take(side, now_ms, path, &age)) return answer(out, cap, "{}");
|
||||
/* before the take: a buffer too small must not empty the slot and lose the handoff */
|
||||
if (cap < FILES_HANDOFF_OUT_MAX) return -1;
|
||||
if (!files_handoff_take(side, now_ms, path, &age)) return answer(out, cap, "{}");
|
||||
at = (size_t)snprintf(out, cap, "{\"path\":\"");
|
||||
at += webui_json_escape(out + at, cap - at, path, strlen(path)); /* fits: 6x the path + 64 */
|
||||
snprintf(out + at, cap - at, "\",\"age_ms\":%lld}", age);
|
||||
|
||||
@@ -82,6 +82,11 @@ static void test_json(void) {
|
||||
CHECK(files_handoff_put(FILES_HANDOFF_TO_CONSOLE, "/data/My \"Game\" \\ 游戏", 100) == 0);
|
||||
n = files_handoff_take_json(FILES_HANDOFF_TO_CONSOLE, 350, out, sizeof out);
|
||||
CHECK(n > 0 && strcmp(out, "{\"path\":\"/data/My \\\"Game\\\" \\\\ 游戏\",\"age_ms\":250}") == 0);
|
||||
/* a buffer too small refuses before taking: the handoff is still there */
|
||||
CHECK(files_handoff_put(FILES_HANDOFF_TO_CONSOLE, "/data/keep", 0) == 0);
|
||||
CHECK(files_handoff_take_json(FILES_HANDOFF_TO_CONSOLE, 1, out, 16) == -1);
|
||||
n = files_handoff_take_json(FILES_HANDOFF_TO_CONSOLE, 1, out, sizeof out);
|
||||
CHECK(n > 0 && strstr(out, "/data/keep") != NULL);
|
||||
/* through the method bodies */
|
||||
n = files_mgmt_handoff_put(B("{\"to\":\"console\",\"path\":\"/mnt/usb0/x\"}"), 0, out, sizeof out);
|
||||
CHECK(n > 0 && strcmp(out, "{\"ok\":true}") == 0);
|
||||
|
||||
Reference in new issue
Block a user