diff --git a/client/src/console/locales/ar.json b/client/src/console/locales/ar.json index bdfeb135..fcdb488e 100644 --- a/client/src/console/locales/ar.json +++ b/client/src/console/locales/ar.json @@ -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": "ترتيب حسب التاريخ", diff --git a/client/src/console/locales/bn.json b/client/src/console/locales/bn.json index b75d46c4..63a15854 100644 --- a/client/src/console/locales/bn.json +++ b/client/src/console/locales/bn.json @@ -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": "তারিখ অনুযায়ী সাজান", diff --git a/client/src/console/locales/de.json b/client/src/console/locales/de.json index ede1e477..f356c402 100644 --- a/client/src/console/locales/de.json +++ b/client/src/console/locales/de.json @@ -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", diff --git a/client/src/console/locales/en.json b/client/src/console/locales/en.json index aa71dbec..ef6fe872 100644 --- a/client/src/console/locales/en.json +++ b/client/src/console/locales/en.json @@ -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", diff --git a/client/src/console/locales/es.json b/client/src/console/locales/es.json index 996edf16..c37f0f75 100644 --- a/client/src/console/locales/es.json +++ b/client/src/console/locales/es.json @@ -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", diff --git a/client/src/console/locales/fa.json b/client/src/console/locales/fa.json index 7c50101b..e54eb2ea 100644 --- a/client/src/console/locales/fa.json +++ b/client/src/console/locales/fa.json @@ -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": "مرتب‌سازی بر اساس تاریخ", diff --git a/client/src/console/locales/fr.json b/client/src/console/locales/fr.json index 09a032ca..4ce9b01a 100644 --- a/client/src/console/locales/fr.json +++ b/client/src/console/locales/fr.json @@ -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", diff --git a/client/src/console/locales/hi.json b/client/src/console/locales/hi.json index d1914cae..c171a8b8 100644 --- a/client/src/console/locales/hi.json +++ b/client/src/console/locales/hi.json @@ -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": "तारीख़ के अनुसार क्रम", diff --git a/client/src/console/locales/hu.json b/client/src/console/locales/hu.json index 2052d6bb..00fab6c4 100644 --- a/client/src/console/locales/hu.json +++ b/client/src/console/locales/hu.json @@ -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", diff --git a/client/src/console/locales/id.json b/client/src/console/locales/id.json index 4be87d28..7baa70ba 100644 --- a/client/src/console/locales/id.json +++ b/client/src/console/locales/id.json @@ -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", diff --git a/client/src/console/locales/it.json b/client/src/console/locales/it.json index 8709cf3b..d4578c90 100644 --- a/client/src/console/locales/it.json +++ b/client/src/console/locales/it.json @@ -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", diff --git a/client/src/console/locales/ja.json b/client/src/console/locales/ja.json index b01007aa..137c398e 100644 --- a/client/src/console/locales/ja.json +++ b/client/src/console/locales/ja.json @@ -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": "日付順", diff --git a/client/src/console/locales/ko.json b/client/src/console/locales/ko.json index 8d03056b..a761f70c 100644 --- a/client/src/console/locales/ko.json +++ b/client/src/console/locales/ko.json @@ -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": "날짜순", diff --git a/client/src/console/locales/pl.json b/client/src/console/locales/pl.json index f0dc5e81..b2bf6038 100644 --- a/client/src/console/locales/pl.json +++ b/client/src/console/locales/pl.json @@ -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", diff --git a/client/src/console/locales/pt-BR.json b/client/src/console/locales/pt-BR.json index 0b01336f..2b5ebc4c 100644 --- a/client/src/console/locales/pt-BR.json +++ b/client/src/console/locales/pt-BR.json @@ -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", diff --git a/client/src/console/locales/ru.json b/client/src/console/locales/ru.json index 896399d8..afce43df 100644 --- a/client/src/console/locales/ru.json +++ b/client/src/console/locales/ru.json @@ -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": "По дате", diff --git a/client/src/console/locales/th.json b/client/src/console/locales/th.json index 34e315f4..97649d05 100644 --- a/client/src/console/locales/th.json +++ b/client/src/console/locales/th.json @@ -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": "เรียงตามวันที่", diff --git a/client/src/console/locales/tr.json b/client/src/console/locales/tr.json index 0fd66d05..58ec2af1 100644 --- a/client/src/console/locales/tr.json +++ b/client/src/console/locales/tr.json @@ -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", diff --git a/client/src/console/locales/vi.json b/client/src/console/locales/vi.json index 29a92cb9..afd6b131 100644 --- a/client/src/console/locales/vi.json +++ b/client/src/console/locales/vi.json @@ -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", diff --git a/client/src/console/locales/zh-CN.json b/client/src/console/locales/zh-CN.json index 8dadbe5b..5ddb8f69 100644 --- a/client/src/console/locales/zh-CN.json +++ b/client/src/console/locales/zh-CN.json @@ -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": "按日期排序", diff --git a/client/src/console/locales/zh-TW.json b/client/src/console/locales/zh-TW.json index 3b0850a2..489e73b9 100644 --- a/client/src/console/locales/zh-TW.json +++ b/client/src/console/locales/zh-TW.json @@ -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": "依日期排序", diff --git a/client/src/i18n/locales/ar.ts b/client/src/i18n/locales/ar.ts index 850fee76..88db504c 100644 --- a/client/src/i18n/locales/ar.ts +++ b/client/src/i18n/locales/ar.ts @@ -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}، لكنه لم يعد على الجهاز.", diff --git a/client/src/i18n/locales/bn.ts b/client/src/i18n/locales/bn.ts index b1bf195f..68559798 100644 --- a/client/src/i18n/locales/bn.ts +++ b/client/src/i18n/locales/bn.ts @@ -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} পাঠিয়েছে, কিন্তু এটি আর কনসোলে নেই।", diff --git a/client/src/i18n/locales/de.ts b/client/src/i18n/locales/de.ts index fb87c4d1..b69a7876 100644 --- a/client/src/i18n/locales/de.ts +++ b/client/src/i18n/locales/de.ts @@ -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.", diff --git a/client/src/i18n/locales/en.ts b/client/src/i18n/locales/en.ts index 0ae62a2d..c259fd22 100644 --- a/client/src/i18n/locales/en.ts +++ b/client/src/i18n/locales/en.ts @@ -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.", diff --git a/client/src/i18n/locales/es.ts b/client/src/i18n/locales/es.ts index 96094c9c..0b53c9d6 100644 --- a/client/src/i18n/locales/es.ts +++ b/client/src/i18n/locales/es.ts @@ -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.", diff --git a/client/src/i18n/locales/fa.ts b/client/src/i18n/locales/fa.ts index 136d064b..d4ed5df5 100644 --- a/client/src/i18n/locales/fa.ts +++ b/client/src/i18n/locales/fa.ts @@ -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} را فرستاد، اما دیگر روی کنسول نیست.", diff --git a/client/src/i18n/locales/fr.ts b/client/src/i18n/locales/fr.ts index 8370f2dd..30e6732a 100644 --- a/client/src/i18n/locales/fr.ts +++ b/client/src/i18n/locales/fr.ts @@ -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.", diff --git a/client/src/i18n/locales/hi.ts b/client/src/i18n/locales/hi.ts index 51cb2082..6795959c 100644 --- a/client/src/i18n/locales/hi.ts +++ b/client/src/i18n/locales/hi.ts @@ -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} भेजा, पर यह अब कंसोल पर नहीं है।", diff --git a/client/src/i18n/locales/hu.ts b/client/src/i18n/locales/hu.ts index f7288b41..d59e92a2 100644 --- a/client/src/i18n/locales/hu.ts +++ b/client/src/i18n/locales/hu.ts @@ -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.", diff --git a/client/src/i18n/locales/id.ts b/client/src/i18n/locales/id.ts index 47f9f807..50da6442 100644 --- a/client/src/i18n/locales/id.ts +++ b/client/src/i18n/locales/id.ts @@ -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.", diff --git a/client/src/i18n/locales/it.ts b/client/src/i18n/locales/it.ts index 9c0f546b..6d942a9d 100644 --- a/client/src/i18n/locales/it.ts +++ b/client/src/i18n/locales/it.ts @@ -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.", diff --git a/client/src/i18n/locales/ja.ts b/client/src/i18n/locales/ja.ts index cf134b50..b5494b3c 100644 --- a/client/src/i18n/locales/ja.ts +++ b/client/src/i18n/locales/ja.ts @@ -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} が送られましたが、もうコンソールにありません。", diff --git a/client/src/i18n/locales/ko.ts b/client/src/i18n/locales/ko.ts index c666060f..5f461661 100644 --- a/client/src/i18n/locales/ko.ts +++ b/client/src/i18n/locales/ko.ts @@ -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}을(를) 보냈지만 더 이상 콘솔에 없습니다.", diff --git a/client/src/i18n/locales/pl.ts b/client/src/i18n/locales/pl.ts index 3a4f09b8..ec1e7c10 100644 --- a/client/src/i18n/locales/pl.ts +++ b/client/src/i18n/locales/pl.ts @@ -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.", diff --git a/client/src/i18n/locales/pt-BR.ts b/client/src/i18n/locales/pt-BR.ts index 2c43dbf1..f3b322a2 100644 --- a/client/src/i18n/locales/pt-BR.ts +++ b/client/src/i18n/locales/pt-BR.ts @@ -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.", diff --git a/client/src/i18n/locales/ru.ts b/client/src/i18n/locales/ru.ts index 76a095d5..9338fc6c 100644 --- a/client/src/i18n/locales/ru.ts +++ b/client/src/i18n/locales/ru.ts @@ -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}, но этого больше нет на консоли.", diff --git a/client/src/i18n/locales/th.ts b/client/src/i18n/locales/th.ts index 3c3fc597..766d2e7c 100644 --- a/client/src/i18n/locales/th.ts +++ b/client/src/i18n/locales/th.ts @@ -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} มา แต่ไม่มีอยู่บนเครื่องแล้ว", diff --git a/client/src/i18n/locales/tr.ts b/client/src/i18n/locales/tr.ts index c62fa2ec..95f8660c 100644 --- a/client/src/i18n/locales/tr.ts +++ b/client/src/i18n/locales/tr.ts @@ -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.", diff --git a/client/src/i18n/locales/vi.ts b/client/src/i18n/locales/vi.ts index e8b1bc19..57fe954e 100644 --- a/client/src/i18n/locales/vi.ts +++ b/client/src/i18n/locales/vi.ts @@ -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.", diff --git a/client/src/i18n/locales/zh-CN.ts b/client/src/i18n/locales/zh-CN.ts index f111b000..56d73a94 100644 --- a/client/src/i18n/locales/zh-CN.ts +++ b/client/src/i18n/locales/zh-CN.ts @@ -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},但它已不在主机上。", diff --git a/client/src/i18n/locales/zh-TW.ts b/client/src/i18n/locales/zh-TW.ts index c3fc5f43..3bb35ae8 100644 --- a/client/src/i18n/locales/zh-TW.ts +++ b/client/src/i18n/locales/zh-TW.ts @@ -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},但它已不在主機上。", diff --git a/client/src/layout/useConsoleHandoff.test.ts b/client/src/layout/useConsoleHandoff.test.ts index d83b657b..df5a4a15 100644 --- a/client/src/layout/useConsoleHandoff.test.ts +++ b/client/src/layout/useConsoleHandoff.test.ts @@ -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"); diff --git a/client/src/layout/useConsoleHandoff.ts b/client/src/layout/useConsoleHandoff.ts index c58dc299..cdee4672 100644 --- a/client/src/layout/useConsoleHandoff.ts +++ b/client/src/layout/useConsoleHandoff.ts @@ -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 | 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; + toast: ReturnType["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]); diff --git a/payload/ava1/ava1_data.c b/payload/ava1/ava1_data.c index c8efbff0..9bc96261 100644 --- a/payload/ava1/ava1_data.c +++ b/payload/ava1/ava1_data.c @@ -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. */ diff --git a/payload/ava1/ava1_data.h b/payload/ava1/ava1_data.h index 21b69fe1..30ce9429 100644 --- a/payload/ava1/ava1_data.h +++ b/payload/ava1/ava1_data.h @@ -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; diff --git a/payload/ava1/ava1_op.c b/payload/ava1/ava1_op.c index d915852d..edbd78f8 100644 --- a/payload/ava1/ava1_op.c +++ b/payload/ava1/ava1_op.c @@ -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 { diff --git a/payload/ava1/ava1_op.h b/payload/ava1/ava1_op.h index aca929ea..7294fa24 100644 --- a/payload/ava1/ava1_op.h +++ b/payload/ava1/ava1_op.h @@ -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]); diff --git a/payload/src/files_handoff.c b/payload/src/files_handoff.c index e7e87fd0..9a50c6da 100644 --- a/payload/src/files_handoff.c +++ b/payload/src/files_handoff.c @@ -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); diff --git a/payload/tests/files_handoff_selftest.c b/payload/tests/files_handoff_selftest.c index a2378c65..a3a30d7a 100644 --- a/payload/tests/files_handoff_selftest.c +++ b/payload/tests/files_handoff_selftest.c @@ -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);