From 1fd7bbcd038cddf40540bae7083b9adf4c728912 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sat, 10 Oct 2026 15:11:52 +0200 Subject: feat(android): back the videos of the chosen albums up beside the photos A 'Videos too' option, off by default, sends them into the same YYYY/YYYY-MM folders. Files are read from the phone a ranged chunk at a time, so a large video is never whole in the page. Co-Authored-By: Claude Opus 5.5 --- .../src/meshbay_hub/static/locales/de.js | 5 ++ .../src/meshbay_hub/static/locales/en.js | 5 ++ .../src/meshbay_hub/static/locales/es.js | 5 ++ .../src/meshbay_hub/static/locales/fr.js | 5 ++ .../src/meshbay_hub/static/locales/it.js | 5 ++ .../src/meshbay_hub/static/locales/ja.js | 5 ++ .../src/meshbay_hub/static/locales/nl.js | 5 ++ .../src/meshbay_hub/static/locales/pl.js | 5 ++ .../src/meshbay_hub/static/locales/pt-BR.js | 5 ++ .../src/meshbay_hub/static/locales/zh-CN.js | 5 ++ .../src/meshbay_hub/static/photo-sync-settings.js | 57 +++++++++++++++++----- .../src/meshbay_hub/static/photo-sync.js | 2 +- .../meshbay-hub/src/meshbay_hub/static/platform.js | 28 ++++++++--- 13 files changed, 118 insertions(+), 19 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js index 5f508da..ca15345 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -1313,6 +1313,11 @@ export default { 'photosync.albums': "Alben auf diesem Telefon", 'photosync.album_camera': "Kamera", 'photosync.album_count': "{n} Fotos · {size}", + 'photosync.include_videos': "Auch Videos", + 'photosync.include_videos_hint': "Videos sind groß: Sie werden wie Fotos über WLAN gesendet, und die erste Sicherung kann dauern.", + 'photosync.album_count_videos': "{n} Fotos, {v} Videos · {size}", + 'photosync.confirm_videos': "{count} Fotos und Videos ({size}), davon {videos} Videos ({videoSize}), werden nach {dir} gesendet, danach einmal täglich die neuen.", + 'photosync.video_permission_needed': "MeshBay braucht auch Zugriff auf die Videos dieses Telefons.", 'photosync.scope_existing': "Die Fotos, die schon auf diesem Telefon sind, dann neue", 'photosync.scope_new': "Nur Fotos, die ab jetzt aufgenommen werden", 'photosync.start': "Sicherung starten", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js index 6b7d73f..8619bdb 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -1294,6 +1294,11 @@ export default { 'photosync.albums': "Albums on this phone", 'photosync.album_camera': "Camera", 'photosync.album_count': "{n} photos · {size}", + 'photosync.include_videos': "Videos too", + 'photosync.include_videos_hint': "Videos are large: they go on Wi-Fi like photos, and the first backup can take a while.", + 'photosync.album_count_videos': "{n} photos, {v} videos · {size}", + 'photosync.confirm_videos': "{count} photos and videos ({size}), including {videos} videos ({videoSize}), will be sent to {dir}, then new ones once a day.", + 'photosync.video_permission_needed': "MeshBay needs access to this phone's videos too.", 'photosync.scope_existing': "The photos already on this phone, then new ones", 'photosync.scope_new': "Only photos taken from now on", 'photosync.start': "Start backup", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js index f2c1cfc..19db8f7 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -1307,6 +1307,11 @@ export default { 'photosync.albums': "Álbumes de este teléfono", 'photosync.album_camera': "Cámara", 'photosync.album_count': "{n} fotos · {size}", + 'photosync.include_videos': "También los vídeos", + 'photosync.include_videos_hint': "Los vídeos ocupan mucho: se envían por Wi-Fi como las fotos, y la primera copia puede tardar.", + 'photosync.album_count_videos': "{n} fotos, {v} vídeos · {size}", + 'photosync.confirm_videos': "Se enviarán {count} fotos y vídeos ({size}), de ellos {videos} vídeos ({videoSize}), a {dir} y luego los nuevos una vez al día.", + 'photosync.video_permission_needed': "MeshBay también necesita acceso a los vídeos de este teléfono.", 'photosync.scope_existing': "Las fotos que ya están en este teléfono y luego las nuevas", 'photosync.scope_new': "Solo las fotos tomadas a partir de ahora", 'photosync.start': "Iniciar la copia", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index 3b5b881..3f3cd42 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -1322,6 +1322,11 @@ export default { 'photosync.albums': "Albums de ce téléphone", 'photosync.album_camera': "Appareil photo", 'photosync.album_count': "{n} photos · {size}", + 'photosync.include_videos': "Les vidéos aussi", + 'photosync.include_videos_hint': "Les vidéos sont lourdes : elles partent en Wi-Fi comme les photos, et la première sauvegarde peut être longue.", + 'photosync.album_count_videos': "{n} photos, {v} vidéos · {size}", + 'photosync.confirm_videos': "{count} photos et vidéos ({size}), dont {videos} vidéos ({videoSize}), seront envoyées dans {dir}, puis les nouvelles une fois par jour.", + 'photosync.video_permission_needed': "MeshBay a aussi besoin d'accéder aux vidéos de ce téléphone.", 'photosync.scope_existing': "Les photos déjà sur ce téléphone, puis les nouvelles", 'photosync.scope_new': "Seulement les photos prises à partir de maintenant", 'photosync.start': "Lancer la sauvegarde", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js index 9935edb..f238495 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -1321,6 +1321,11 @@ export default { 'photosync.albums': "Album su questo telefono", 'photosync.album_camera': "Fotocamera", 'photosync.album_count': "{n} foto · {size}", + 'photosync.include_videos': "Anche i video", + 'photosync.include_videos_hint': "I video sono pesanti: partono in Wi-Fi come le foto, e il primo backup può richiedere tempo.", + 'photosync.album_count_videos': "{n} foto, {v} video · {size}", + 'photosync.confirm_videos': "{count} foto e video ({size}), di cui {videos} video ({videoSize}), verranno inviati in {dir}, poi quelli nuovi una volta al giorno.", + 'photosync.video_permission_needed': "MeshBay ha bisogno di accedere anche ai video di questo telefono.", 'photosync.scope_existing': "Le foto già su questo telefono, poi quelle nuove", 'photosync.scope_new': "Solo le foto scattate da ora in poi", 'photosync.start': "Avvia il backup", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js index 2af2f49..6a59d1d 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -1305,6 +1305,11 @@ export default { 'photosync.albums': "この電話のアルバム", 'photosync.album_camera': "カメラ", 'photosync.album_count': "{n} 枚 · {size}", + 'photosync.include_videos': "動画も含める", + 'photosync.include_videos_hint': "動画はサイズが大きいため、写真と同じく Wi-Fi で送信され、最初のバックアップには時間がかかることがあります。", + 'photosync.album_count_videos': "写真 {n} 枚、動画 {v} 本 · {size}", + 'photosync.confirm_videos': "写真と動画 {count} 件 ({size}、うち動画 {videos} 本 {videoSize}) を {dir} に送り、その後は新しいものを1日1回送ります。", + 'photosync.video_permission_needed': "MeshBay にこのスマートフォンの動画へのアクセスも許可してください。", 'photosync.scope_existing': "この電話にすでにある写真と、これからの写真", 'photosync.scope_new': "これから撮る写真のみ", 'photosync.start': "バックアップを開始", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js index 9e83c86..4b68975 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -1323,6 +1323,11 @@ export default { 'photosync.albums': "Albums op deze telefoon", 'photosync.album_camera': "Camera", 'photosync.album_count': "{n} foto's · {size}", + 'photosync.include_videos': "Ook video's", + 'photosync.include_videos_hint': "Video's zijn groot: ze gaan net als foto's via wifi, en de eerste back-up kan even duren.", + 'photosync.album_count_videos': "{n} foto's, {v} video's · {size}", + 'photosync.confirm_videos': "{count} foto's en video's ({size}), waarvan {videos} video's ({videoSize}), worden naar {dir} gestuurd, daarna eenmaal per dag de nieuwe.", + 'photosync.video_permission_needed': "MeshBay heeft ook toegang nodig tot de video's op deze telefoon.", 'photosync.scope_existing': "De foto's die al op deze telefoon staan, daarna nieuwe", 'photosync.scope_new': "Alleen foto's die vanaf nu worden gemaakt", 'photosync.start': "Back-up starten", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js index cb0d0b6..8b1b5db 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -1349,6 +1349,11 @@ export default { 'photosync.albums': "Albumy na tym telefonie", 'photosync.album_camera': "Aparat", 'photosync.album_count': "Zdjęcia: {n} · {size}", + 'photosync.include_videos': "Również filmy", + 'photosync.include_videos_hint': "Filmy są duże: wysyłane są przez Wi-Fi jak zdjęcia, a pierwsza kopia może potrwać.", + 'photosync.album_count_videos': "Zdjęcia: {n}, filmy: {v} · {size}", + 'photosync.confirm_videos': "Zdjęcia i filmy ({count}, {size}), w tym filmy: {videos} ({videoSize}), zostaną wysłane do {dir}, a potem nowe raz dziennie.", + 'photosync.video_permission_needed': "MeshBay potrzebuje też dostępu do filmów w tym telefonie.", 'photosync.scope_existing': "Zdjęcia, które już są na tym telefonie, a potem nowe", 'photosync.scope_new': "Tylko zdjęcia zrobione od teraz", 'photosync.start': "Rozpocznij kopię", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js index cb3aa70..9fc634b 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js @@ -1308,6 +1308,11 @@ export default { 'photosync.albums': "Álbuns deste celular", 'photosync.album_camera': "Câmera", 'photosync.album_count': "{n} fotos · {size}", + 'photosync.include_videos': "Vídeos também", + 'photosync.include_videos_hint': "Vídeos são pesados: vão pelo Wi-Fi como as fotos, e o primeiro backup pode demorar.", + 'photosync.album_count_videos': "{n} fotos, {v} vídeos · {size}", + 'photosync.confirm_videos': "{count} fotos e vídeos ({size}), sendo {videos} vídeos ({videoSize}), serão enviados para {dir} e depois os novos uma vez por dia.", + 'photosync.video_permission_needed': "O MeshBay também precisa de acesso aos vídeos deste celular.", 'photosync.scope_existing': "As fotos que já estão neste celular, depois as novas", 'photosync.scope_new': "Somente as fotos tiradas a partir de agora", 'photosync.start': "Iniciar backup", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js index bb47b00..5859934 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js @@ -1294,6 +1294,11 @@ export default { 'photosync.albums': "这部手机上的相册", 'photosync.album_camera': "相机", 'photosync.album_count': "{n} 张照片 · {size}", + 'photosync.include_videos': "包括视频", + 'photosync.include_videos_hint': "视频体积较大:它们和照片一样通过 Wi-Fi 发送,首次备份可能需要一些时间。", + 'photosync.album_count_videos': "{n} 张照片,{v} 个视频 · {size}", + 'photosync.confirm_videos': "将把 {count} 个照片和视频({size},其中 {videos} 个视频,{videoSize})发送到 {dir},之后每天发送一次新的。", + 'photosync.video_permission_needed': "MeshBay 还需要访问本机的视频。", 'photosync.scope_existing': "手机上已有的照片,然后是新照片", 'photosync.scope_new': "仅从现在起拍摄的照片", 'photosync.start': "开始备份", diff --git a/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js index 7809efa..088de76 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js @@ -105,6 +105,11 @@ export function PhotoSyncSection({ destination }) {

${status.permission === 'partial' && html`

${t('photosync.permission_partial')}

`} + ${cfg.includeVideos && status.videoPermission === 'denied' && html` +

${t('photosync.video_permission_needed')}

+ `} ${status.failure ? html`

${t(FAILURES[status.failure] || 'photosync.fail_other')}

` : stateLine(run, status) && html`

${stateLine(run, status)}

`} @@ -129,19 +134,32 @@ function Setup({ status, onPermission, onDone }) { const [albums, setAlbums] = useState(null); const [chosen, setChosen] = useState(() => new Set(cfg ? cfg.albums : [])); const [existing, setExisting] = useState(cfg ? cfg.includeExisting : true); + const [videos, setVideos] = useState(cfg ? !!cfg.includeVideos : false); const [busy, setBusy] = useState(false); const [error, setError] = useState(''); const permitted = status.permission !== 'denied'; + const videosAllowed = status.videoPermission !== 'denied'; useEffect(() => { if (!permitted) return; - platform.photoSync.albums().then((list) => { + platform.photoSync.albums(videos && videosAllowed).then((list) => { setAlbums(list); // The camera, and only the camera, unless the person chose before: // screenshots and saved images are where photos nobody meant to share live. - if (!cfg) setChosen(new Set(list.filter((a) => a.camera).map((a) => a.id))); + if (!cfg) { + setChosen((prev) => (prev.size ? prev : new Set(list.filter((a) => a.camera).map((a) => a.id)))); + } }).catch((e) => setError(e.message)); - }, [permitted]); + }, [permitted, videos, videosAllowed]); + + // Ticking videos asks for them then, not before: Android 13+ asks apart. + const toggleVideos = async () => { + const next = !videos; + setVideos(next); + if (next && !videosAllowed) { + try { onPermission(await platform.photoSync.permit(true)); } catch (e) { setError(e.message); } + } + }; const allow = async () => { setError(''); @@ -159,14 +177,19 @@ function Setup({ status, onPermission, onDone }) { setBusy(true); setError(''); try { - const settings = { albums: [...chosen], includeExisting: existing }; - // Asked when the photos would come from somewhere new in time; never - // for a change of albums alone, which the person just made. - if (!cfg || cfg.includeExisting !== existing) { + const settings = { albums: [...chosen], includeExisting: existing, includeVideos: videos }; + // Asked when the photos would come from somewhere new in time, or when + // videos start going; never for a change of albums alone, which the + // person just made. + if (!cfg || cfg.includeExisting !== existing || (videos && !cfg.includeVideos)) { const est = await platform.photoSync.estimate(settings); - const params = { count: est.count, size: formatSize(est.bytes), dir: status.dir }; - const yes = await ask(t(existing && est.count > 0 ? 'photosync.confirm' - : 'photosync.confirm_new', params)); + const params = { + count: est.count, size: formatSize(est.bytes), dir: status.dir, + videos: est.videos || 0, videoSize: formatSize(est.videoBytes || 0), + }; + const key = !(est.count > 0) ? 'photosync.confirm_new' + : est.videos > 0 ? 'photosync.confirm_videos' : 'photosync.confirm'; + const yes = await ask(t(key, params)); if (!yes) { setBusy(false); return; } } onDone(await platform.photoSync.configure(settings)); @@ -194,10 +217,22 @@ function Setup({ status, onPermission, onDone }) { ${a.camera ? t('photosync.album_camera') : a.name} - ${t('photosync.album_count', { n: a.count, size: formatSize(a.bytes) })} + ${videos && a.videos + ? t('photosync.album_count_videos', + { n: a.count, v: a.videos, size: formatSize(a.bytes + a.videoBytes) }) + : t('photosync.album_count', { n: a.count, size: formatSize(a.bytes) })} `)} + + ${videos && !videosAllowed && html` +

${t('photosync.video_permission_needed')}

`}