Problèmes (et solutions ?) pour la sauvegarde des photos


#1

Bonjour à tous,

Je me suis installé un Cozy en auto-hébergé depuis quelques jours et après quelques ajustement, ça se passe pas trop mal. :slight_smile:
Je commence à tester les applis mobile et desktop, et je bute sur un problème que, je pense, je n’arriverai pas à résoudre seul.

J’ai un soucis avec l’appli mobile “Cozy Drive” : la copie des photos ne se fait pas depuis mon smartphone vers mon instance Cozy.
En fait, il faudrait plutôt parler de plusieurs soucis…

Où sont les photos ?

Le premier problème est que l’appli mobile ne trouve aucune de mes photos, et du coup elle n’envoie rien (ce qui est cohérent…).
L’origine du problème est simple : l’appli mobile ne scanne pas le bon dossier. L’appli par défaut pour les photos déposent les jpeg dans un dossier nommé “DCIM/100ANDRO/”, mais l’appli Cozy recherche les photos dans le dossier “DCIM/Camera” (comme c’est indiqué dans un autre topic).

Lorsque je déplace des photos depuis le dossier “100ANDRO” vers “Camera”, l’appli Cozy les voit bien : je vois passer rapidement “n images restantes” au lancement de l’appli. On avance :slight_smile:
Savez-vous ce qu’il est possible de faire pour ce point ? L’appli photo ne propose pas de changer le dossier de destination, j’ai uniquement le choix entre stockage interne ou externe.

Couvrez cette extension, que je ne saurais voir.

Et du coup, on arrive sur le 2nd problème : les photos ne se retrouvent pas dans mon Cozy (je parle de celles que j’ai copiées manuellement dans le dossier “Camera”).
Là, c’est un peu plus tendu. Je vais essayer d’être clair dans mes explication, mais n’hésitez pas à me demander des précisions. :slight_smile:

Au niveau des access logs de mon Nginx, je vois passer un GET sur le chemin prévu de la photo qui renvoie un 404 (certainement pour vérifier que ce fichier n’existe pas déjà…).
Puis directement après un POST sur /settings/synchronized qui renvoie du 204. Et puis plus rien.

IP - - [27/Mar/2019:10:22:15 +0100] "GET /files/metadata?Path=%2FPhotos%2FSauvegard%C3%A9es%20depuis%20mon%20mobile%2FDSC_0001.JPG HTTP/1.1" 404 93 "-" "Mozilla/5.0 (Linux; Android 5.0; E2333 Build/26.1.B.3.109; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/73.0.3683.90 Mobile Safari/537.36 io.cozy.drive.mobile-1.15.4"
IP - - [27/Mar/2019:10:22:16 +0100] "POST /settings/synchronized HTTP/1.1" 204 0 "-" "Mozilla/5.0 (Linux; Android 5.0; E2333 Build/26.1.B.3.109; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/73.0.3683.90 Mobile Safari/537.36 io.cozy.drive.mobile-1.15.4"

Au niveau des logs Android, voici un extrait qui me semble intéressant :

D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 33 : ListLibraryItems plugin: calling uploadItem...
I/chromium(25282): [INFO:CONSOLE(33)] "ListLibraryItems plugin: calling uploadItem...", source: file:///android_asset/www/vendors.js (33)
W/System.err(25282): java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.startsWith(java.lang.String)' on a null object reference
W/System.err(25282): 	at io.cozy.plugins.listlibraryitems.ListLibraryItems$UploadFileTask.createThumbnail(ListLibraryItems.java:516)
W/System.err(25282): 	at io.cozy.plugins.listlibraryitems.ListLibraryItems$UploadFileTask.doInBackground(ListLibraryItems.java:327)
W/System.err(25282): 	at io.cozy.plugins.listlibraryitems.ListLibraryItems$UploadFileTask.doInBackground(ListLibraryItems.java:279)
W/System.err(25282): 	at android.os.AsyncTask$2.call(AsyncTask.java:288)
W/System.err(25282): 	at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err(25282): 	at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
W/System.err(25282): 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
W/System.err(25282): 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
W/System.err(25282): 	at java.lang.Thread.run(Thread.java:818)
D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 33 : startMediaBackup upload item error
I/chromium(25282): [INFO:CONSOLE(33)] "startMediaBackup upload item error", source: file:///android_asset/www/vendors.js (33)
D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 33 : {"code":-1,"source":"/storage/emulated/0/DCIM/Camera/DSC_0001.JPG","target":"https://laurent.deltalima.net/files/b23a7cbeed5f9203f6bcd247c60d9a20?Name=DSC_0001.JPG&Type=file&Tags=library&Executable=false","message":"java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.startsWith(java.lang.String)' on a null object reference"}
I/chromium(25282): [INFO:CONSOLE(33)] "{"code":-1,"source":"/storage/emulated/0/DCIM/Camera/DSC_0001.JPG","target":"https://laurent.deltalima.net/files/b23a7cbeed5f9203f6bcd247c60d9a20?Name=DSC_0001.JPG&Type=file&Tags=library&Executable=false","message":"java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.startsWith(java.lang.String)' on a null object reference"}", source: file:///android_asset/www/vendors.js (33)
D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 33 : {"id":79022,"fileName":"DSC_0001.JPG","width":3920,"height":2204,"libraryId":"-1739773001","creationDate":"2019-03-27T08:19:30.000Z","filePath":"/storage/emulated/0/DCIM/Camera/DSC_0001.JPG"}
I/chromium(25282): [INFO:CONSOLE(33)] "{"id":79022,"fileName":"DSC_0001.JPG","width":3920,"height":2204,"libraryId":"-1739773001","creationDate":"2019-03-27T08:19:30.000Z","filePath":"/storage/emulated/0/DCIM/Camera/DSC_0001.JPG"}", source: file:///android_asset/www/vendors.js (33)
D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 33 : Raven is recording exception
I/chromium(25282): [INFO:CONSOLE(33)] "Raven is recording exception", source: file:///android_asset/www/vendors.js (33)
D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 33 : Backup error: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.startsWith(java.lang.String)' on a null object reference
I/chromium(25282): [INFO:CONSOLE(33)] "Backup error: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.startsWith(java.lang.String)' on a null object reference", source: file:///android_asset/www/vendors.js (33)
D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 8 : %c action %cMEDIA_UPLOAD_END %c@ 08:52:35.932
I/chromium(25282): [INFO:CONSOLE(8)] "%c action %cMEDIA_UPLOAD_END %c@ 08:52:35.932", source: file:///android_asset/www/vendors.js (8)
D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 8 : console.groupEnd
I/chromium(25282): [INFO:CONSOLE(8)] "console.groupEnd", source: file:///android_asset/www/vendors.js (8)
D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 8 : %c action %cOPEN_FOLDER_SUCCESS %c@ 08:52:36.780
I/chromium(25282): [INFO:CONSOLE(8)] "%c action %cOPEN_FOLDER_SUCCESS %c@ 08:52:36.780", source: file:///android_asset/www/vendors.js (8)
D/SystemWebChromeClient(25282): file:///android_asset/www/vendors.js: Line 8 : console.groupEnd
I/chromium(25282): [INFO:CONSOLE(8)] "console.groupEnd", source: file:///android_asset/www/vendors.js (8)

L’erreur de base est un NPE sur la variable “contentType” passée à la méthode “createThumbnail(…)”. Le contentType vient du mime-type de la photo, qui est lui-même renseigné lors du scan des photos avec la fonction “getMimeType(…)”. Tout porte donc à croire que c’est la fonction “getMimeType(…)” qui renvoie null. Cette fonction utilise le SDK Android pour déterminer le mime-type. Après quelques recherches, je suis tombé sur une question stackoverflow indiquant qu’il faut ajouter un “toLowerCase()” sur l’extension.

Oh, comme par hasard, ma photo est nommée “DSC_0001.JPG”. Ni une, ni deux, je te renomme le fichier et relance l’appli Cozy … et pouf ma photo arrive bien dans mon Cozy :slight_smile:

Bref, ya peut-être quelque chose à faire dans la fonction “getMimeType(…)”.
Je serai bien tenté de vous faire un pull-request, mais j’aimerai tester le correctif avant de vous l’envoyer … sauf que je suis bien incapable de builder l’appli mobile (j’ai jeté l’éponge assez rapidement quand yarn a commencé à me parler mal).


#2

Salut @lme

Un grand, grand, grand merci pour ton retour !

Effectivement, on va chercher dans un dossier Camera (https://github.com/cozy/cordova-plugin-list-library-items/blob/master/src/android/io/cozy/plugins/listlibraryitems/ListLibraryItems.java#L221). Ça confirme la piste que j’avais en ajoutant d’autres dossiers ici (https://github.com/Crash--/cordova-plugin-list-library-items/commit/b8ca87ad49f2ebb45954ff69e24cce85373580f9#diff-948886ae2b0fcaf3ddc70c4c59104a97R209) mais j’ai pas eu le temps de débuger et de faire une nouvelle release. Le truc c’est que c’est pas très futur proof, mais c’est déjà mieux que rien.

Dans l’idéal il faudrait fournir la possibilité à l’utilisateur de sélectionner son dossier, mais ça pose pas mal de questions UI / Tech qui ne sont pas encore tranchées.

Merci pour le cheminement sur le mimetype, il me semble avoir entendu récemment une histoire d’extension en MAJ, ceci expliquerait cela !

Après tout ce travail, s’arrêter à des erreurs yarn… Frustrant ! Ça commence à déconner à partir de quelle commande : https://github.com/cozy/cozy-drive/blob/master/src/drive/targets/mobile/README.md ?


#3

Salut @_Crash,

Ça déconne à partir de yarn install.
La trace d’erreur complète est dispo ici : https://gist.github.com/lmeunier/df0084eb28642bdca54632e303522d3e


#4

https://nodejs.org/download/release/v10.15.0/

Tu sembles être en node 10.5.0 et nous y sommes pas encore passé. Si tu passes en node 8 (8.11.3) avec par exemple un outil comme nvm ça devrait aller bien mieux ;).


#5

Ça avance :slight_smile:

Le yarn install passe sans erreur (mais avec quelques warning) depuis que je suis passé à node 8.11.3.
Par contre, je bloque ensuite sur yarn genicon:drive:mobile.
La trace complète dispo ici : https://gist.github.com/lmeunier/7c85a6a5a5ab7ecd94e0ee95e55dc9c1

Ne connaissant pas yarn, je ne sais pas si ça vaut le coup d’essayer les commandes suivantes.


#6

Tu as bien imagemagick d’installé ?

Si oui, alors c’est peut-être parce qu’il faut créer le dossier :
src/drive/targets/mobile/www

Ensuite, il faudrait peut-être lancer un yarn prepare:drive:mobile pour que ça créé les dossiers ios / android, et refaire un generate icon.


#7

Oula, j’ai honte, j’étais sûr d’avoir ImageMagick d’installé … mais en fait non.
Après installation, ça passe tout seul.

J’ai un peu galéré ensuite avec l’installation d’Android SDK / Android Studio / la création d’une image / etc.
Mais c’est tout bon maintenant, j’ai réussi à démarrer un émulateur et l’appli Cozy Drive s’est installée toute seule. J’ai pu reproduire le problème avec des images ayant l’extension .JPG (en majuscule).

Petit question : si je modifie ListLibraryItems.java, je dois faire quoi ensuite pour retrouver ma modif dans l’émulateur ?


#8

De mon côté, (et je pense que je ne suis pas le seul, @rico @glelostec aussi), l’import des photos sur mon instance autohébergée ne fonctionne pas (et ce n’est pas lié à une extension en .JPG, tous mes fichiers sont en .jpg).
Cf : Sauvegarde des photos depuis Cozy Drive ne fonctionne plus


#9

Salut @cpique,

Je viens de parcourir rapidement le sujet qui tu as mis en lien. Ce qui semble être sûr c’est que nous n’avons pas le même problème :

  • de mon côté, c’était un soucis avec l’emplacement du stockage des photos et de l’extension en majuscule
  • de ton côté, il y a un timeout qui arrive au milieu, et il faudrait savoir d’où il vient

Si j’arrive à trouver un peu de temps, j’essaierai de lire plus en détails ton sujet et de te proposer des pistes pour récupérer des infos complémentaires.


#10

Salut !

Alors, tout dépend de comment tu modifies le fichier et où.

On va dire que tu le modifies dans Android Studio, alors dans ce cas tu as juste besoin de rebuilder et relancer l’application depuis cet IDE.

Sinon, tu devrais pouvoir t’en sortir avec nos commandes yarn :

yarn run:drive:android:emulator

#11

Salut,

Je suis plus vim qu’Android Studio :slight_smile:

Si j’ai bien compris le principe, dans le git cozy-drive, c’est le fichier src/drive/targets/mobile/config.xml qui fait référence au git “cordova-plugin-list-library-item” (dans lequel se trouve le fameux fichier Java à modifier). Du coup, j’ai modifié ce fichier en local pour faire référence à mon clone local du projet “cordova-plugin-list-library-items”.

diff --git a/src/drive/targets/mobile/config.xml b/src/drive/targets/mobile/config.xml
index 1920e707..5e1f1b86 100644
--- a/src/drive/targets/mobile/config.xml
+++ b/src/drive/targets/mobile/config.xml
@@ -104,7 +104,7 @@
     <plugin name="cordova-ios-plugin-no-export-compliance" spec="git+https://github.com/mikaoelitiana/cordova-ios-plugin-no-export-compliance.git#915556bf7713e9733005977af4fa4bfba40f203f" />
     <plugin name="io.cozy.jsbackgroundservice" spec="https://github.com/cozy/cordova-jsbackgroundservice.git#1.3.0" />
     <plugin name="com-darryncampbell-cordova-plugin-intent" spec="^1.1.1" />
-    <plugin name="io.cozy.plugins.listlibraryitems" spec="https://github.com/cozy/cordova-plugin-list-library-items.git#1.5.1">
+    <plugin name="io.cozy.plugins.listlibraryitems" spec="/home/laurent/Devel/_public/cordova-plugin-list-library-items">
         <variable name="PHOTO_LIBRARY_USAGE_DESCRIPTION" value=" " />
     </plugin>
     <plugin name="com.lampa.startapp" spec="^0.1.4" />

J’ai ensuite appliqué le petit fix dans le fichier ListLibraryItems.java et j’ai repris les commandes yarn à partir de yarn install jusqu’au lancement de l’émulateur dans lequel j’ai pu valider le correctif.

C’est la bonne méthode ?

Pour info, le pull request est dispo ici :


#12

En faisant pointer, dans le config.xml, ton plugin ça devrait effectivement fonctionner. Il faut quand même se méfier car Cordova en version 7, se marche un peu sur les pieds avec le config.xml et le package.json présent dans le même répertoire (et qu’il créé tout seul). Il me semble que ce dernier est prioritaire et qu’il faut donc le supprimer pour être sur d’avoir la bonne version installée (celle indiquée dans le config.xml).

Ensuite, tu n’as vraiment qu’à faire un yarn run:drive:android qui devrait :

  • regarder si des modifications ont été faites dans le config.xml / package.json
  • rebuilder le code natif si besoin
  • builder le js
  • lancer ton app

J’ai vu passer ta PR et je pense que le fix semble bon, mais pour être sur :

  • Le problème initial était que les images dont l’extension était en majuscule n’étaient pas synchronisées ?
  • Il n’y avait pas de renommage à la volée en minuscule etc, on est d’accord ? Donc pas d’impact sur les éléments déjà synchro, pas de doublonnage etc ?

Par curiosité, ton téléphone Android, c’est un Sony ?


#13

Merci pour l’info.
Je viens de vérifier dans le fichier package.json, je retrouve bien le lien vers mon dépôt local.

Oui, c’est bien ça.

Aucun renommage automatique. Je devais renommer manuellement les photos pour mettre l’extension en minuscule pour qu’elle soit synchronisée.

Avec le correctif, les photos dont l’extension est en majuscule sont maintenant automatiquement synchronisées (il n’y a pas de renommage du fichier, juste le MIME type qui est maintenant reconnu même quand l’extension est en majuscule).

Oui, c’est un Sony Xperia M4 Aqua Dual SIM (E2333) avec Android 5.0.


#14

Merci, pour toutes les info.

Ton fix sera dans la prochaine version qui ne devrait pas trop tarder.

Est-ce que tu comptes t’attaquer aussi aux différents noms de dossier ? :smiley:


#15

Oui, j’ai vu passer le merge de y-lohse.
C’est une bonne nouvelle :slight_smile:

Je vais voir ce que je peux faire. Je t’avoue que je découvre le développement Android, yarn, cordova et tout ce qui va avec … on verra bien si j’arrive à trouver une solution qui convienne.


#16

Hello,

(je vais peut-être enfoncer des portes ouvertes, mais ça me permet de poser un peu les choses à plat)

De ce que j’ai pu comprendre, il n’existe pas un unique endroit où les photos sont stockées, mais plusieurs dossiers dans lesquels les applications peuvent y placer des photos. Il n’y a rien de normalisé, c’est un peu au bon vouloir de chaque appli. Impossible de connaitre à l’avance tous ces dossiers, ça peut être “Camera”, “100ANDRO”, “Choucroute”, etc.

Mais … mais comment fait mon appli “Album” pour m’afficher toutes les photos, quelque soit leur emplacement ? (parce que, à force de faire des tests, je commence à en avoir un peu partout…)

Histoire d’essayer de comprendre, je me suis installé 2 applis pour lesquelles on a accès aux sources : LeafPic et Simple Gallery Pro.
Sur l’écran principale de ces applis, on peut voir la liste des “Dossiers” contenant des photos. Sur mon tél, j’ai 2x “Camera”, 2x “100ANDRO”, “Download” et 2x “Pictures”. Si j’ai des dossiers en double, ça doit venir du fait que j’ai un dossier sur le stockage interne, et un autre avec le même nom sur la carte SD. Sur vos téléphone, ça devrait remonter sensiblement la même chose (si vous pouviez me faire un rapide retour sur ce point, ce serait sympa).
Du coup, il serait donc possible de récupérer tous les dossiers contenant des photos et de les sauvegarder dans Cozy.

A priori, il existe une truande à base de MediaStore, query et group by pour récupérer ce qui nous intéresse.
Voir : https://stackoverflow.com/questions/11570521/android-how-to-query-a-list-of-bucket-name

Côté LeafPic, ça se passe ici : https://gitlab.com/HoraApps/LeafPic/blob/dev/app/src/main/java/org/horaapps/leafpic/data/provider/CPHelper.java#L56

Côté Simple Gallery Pro, ça se passe ici : https://github.com/SimpleMobileTools/Simple-Gallery/blob/master/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/MediaFetcher.kt#L38

Donc ya moyen de moyenner…

Ce que je vous propose :

  1. je modifie ListLibraryItems.java pour utiliser la truande group by
  2. on voit ce que ça donne sur un panel représentatif de téléphones (un peu plus que l’unique en ma possession)
  3. si ça fait le job, on peut même pousser le vice jusqu’à modifier la page “Paramètres” de l’appli mobile Cozy pour permettre à l’utilisateur de sélectionner les Buckets à sauvegarder (une liste de checkbox avec tous les buckets trouvés)

Vu ma méconnaissance du développement Android, vos remarques sont les bienvenues.


#17

Bonjour @lme
Je laisserai les développeurs de l’équipe te répondre sur le plan proposé.
Juste un grand merci pour ta contribution sur ces sujets !

Thomas (Product Owner dans l’équipe Drive & Photos)


#18

Salut @lme

En fait, cette astuce du Media Store, c’est un peu ce qu’on fait. Sauf qu’à partir des résultats du MediaStore, on vient filtrer les photos qui sont dans le dossier “Camera” (https://github.com/cozy/cordova-plugin-list-library-items/blob/master/src/android/io/cozy/plugins/listlibraryitems/ListLibraryItems.java#L221L226), et ce pour une raison (il me semble, ça date) qu’on ne voulait pas synchroniser les photos qui émanent de whatapps, messenger et que sais-je encore.

Au final, je pense qu’on n’a pas besoin de cette notion de group by etc puisque ce qu’on veut faire c’est juste tout synchroniser et tant pis si y a beaucoup trop d’images qui arrive ?

T’en penses quoi toi de synchroniser toutes les photos peu importe leur source ? Le fix ne devrait pas être très cher pour cette solution.

Personnellement, je suis à fond pour la 3 (et donc possiblement besoin du group by si je comprends bien), mais on n’a pas la bande passante en interne actuellement pour la gérer. Si tu veux t’y pencher, je répondrai à tes questions avec plaisir :slight_smile:


#19

Moi je suis totalement pour ! Sachant que je n’ai aucune appli “sociale” sur mon tél, il n’y a que les photos de l’appareil photo et les images enregistrées depuis les SMS/MMS qui sont présentes.
Mais comme tu l’as dis, ça risque de remonter trop de photos pour certaines personnes… (d’où le point 3)

Oui, le point 3 nécessite le group by (en tout cas on a la même compréhension … ce qui est un bon départ). Je vais voir ce que je peux faire. Dès que j’ai un premier truc testable, je vous fais une pull-request qui nous servira de base pour échanger.
Je pense que j’aurai quelques questions qui vont arriver, t’éloigne pas trop du forum :slight_smile: