Katal, catalogue de fichiers

29
26
oct.
2015
Ligne de commande

Katal est un projet GPLv3/Python3/CLI/Linux-Windows : il permet de créer un catalogue de fichiers à partir de différentes sources, en filtrant les fichiers à récupérer, en les renommant à la volée; on peut alors étiqueter les fichiers obtenus.

Le répertoire de destination contient les fichiers du catalogue, une base de données, un dossier .trash et un dossier pour les logs. J'ai l'habitude de placer dans le répertoire de destination le script katal.py; ainsi, je déplace le catalogue en même temps que l'outil qui me permet de le gérer.

De l'aide est disponible dans le fichier README.md : voyez ici.
Voyez aussi le résultat de $ katal -h .

Sommaire

L'histoire

Un copain m'avait demandé de l'aider à classer des dizaines de milliers de photos tirées de milliers de répertoires différents, mélangées avec toutes sortes de fichiers (textes, vidéos); beaucoup de photos identiques portaient des noms différents.

Je voulais obtenir un catalogue des fichiers :

  1. qui avaient l'extension de fichiers image (.jpg, .tiff, …) ;
  2. qui avaient une taille supérieure à 2 Mo pour éliminer les vignettes ;
  3. sans doublon (=aucun fichier ayant le même contenu qu'un autre) ;
  4. avec la possibilité d'étiqueter (=avec des tags) les images ;
  5. avec la possibilité de renommer les fichiers ;
  6. avec des fichiers de logs verbeux pour garder la trace de mes essais et de mes erreurs ;
  7. le tout sur Linux/Mac/Windows puisque je passais d'un ordinateur à l'autre.

Installation

$ pip install katal

katal est en effet hébergé sur Pypi.

mais aussi :

$ wget https://raw.githubusercontent.com/suizokukan/katal/master/katal/katal.py

… puisque le projet Katal tient en un seul fichier, katal.py, qui peut être placé dans le répertoire de destination.

Utilisation

Créer le répertoire de destination, qui contiendra les fichiers du catalogue :

$ katal --new=destdir

… et accepter de télécharger le fichier de configuration par défaut qui sera placé dans le répertoire de destination (dans un sous répertoire nommé .katal)

Placez-vous ensuite à l'intérieur du répertoire de destination.

$ cd destdir

Vous pouvez voir ce que pense Katal de ce répertoire en tapant :

$ katal --infos

À l'intérieur du répertoire de destination, modifier le fichier de configuration ./katal/katal.ini :

Changer la source, par exemple :

[source]
path : ~/masource/photos/

Katal recherchera de manière récursive dans ce répertoire.

Changer le nom des fichiers qui seront copiés dans le répertoire de destination, par exemple :

[target]
name of the target files : INTTIMESTAMP_SIZE__DATABASE_INDEX.SOURCE_EXTENSION2

… pour avoir des fichiers du type 1445584562_123__1.jpg (=timestamp du fichier source, taille du fichier source, index dans la base de données du répertoire de destination)

Changer la fonction d'évaluation permettant de faire le tri entre les fichiers de la source, par exemple :

… si vous voulez les fichiers python du répertoire source :
[source]
eval : sieve1
[source.sieve1]
name : .*\.py$
… si vous voulez les fichiers python du répertoire source faisant plus d'un mégaoctet :
[source]
eval : sieve1
[source.sieve1]
name : .*\.py$
size : >1000000
… si vous voulez les fichiers python du répertoire source faisant plus d'un mégaoctet OU les .jpg :
[source]
eval : sieve1 or sieve2
[source.sieve1]
name : .*\.py$
size : >1000000
[source.sieve2]
name : .*\.jpg$

Tester le fichier de configuration ./katal/katal.ini :

$ katal --select

Katal vous montre alors ce qui se passerait si vous acceptiez la copie de la source vers la destination. Par exemple :

  +  selected /home/suizokukan/projets/dysodos/dysodos.py (file selected #1)
  -  discarded "/home/suizokukan/projets/dysodos/.git/description" : incompatibility with the sieves
  -  discarded "/home/suizokukan/projets/dysodos/.git/info/exclude" : incompatibility with the sieves
[...]
  o  everything ok : no anomaly detected. See details above.
  o size of the selected file(s) : 3877 bytes
  o number of selected files (after discarding 36 file(s)) : 1, 2.70% of the source files.
  o required space : 3877 bytes; available space on disk : ~35.62 Go (35619852288 bytes) (ok)
  o e.g. … "/home/suizokukan/projets/dysodos/dysodos.py" would be copied as "/home/suizokukan/projets/katal/target2/5474c904__0.py" .
  Do you want to add the selected files to the target dictionary (".") ? (y/N) 

Si vous êtes d'accord, répondez oui en tapant 'y' + entrée :

    … (1/1) copying "/home/suizokukan/projets/dysodos/dysodos.py" to "/home/suizokukan/projets/katal/target2/5474c904__0.py" .
    = all files have been copied, let's update the database… =
    = … database updated =

Katal affiche automatiquement le contenu du catalogue (ici, un seul fichier) :

   = informations about the "." (path: "/home/suizokukan/projets/katal/target2") target directory =
+--------------------+--------------------+------+--------------------+------------------+
| hashid/base64      | name               | tags | source name        | source date      |
+--------------------+--------------------+------+--------------------+------------------+
| […]f8SfE3Tzc+UP0s= | […]/5474c904__0.py |      | […]odos/dysodos.py | 2014-11-25 19:23 |
+--------------------+--------------------+------+--------------------+------------------+

Vous pouvez à tout moment afficher ces informations sur le catalogue à l'aide de l'option -ti :

$ katal -ti

(ti : target informations)

=======================================================
=== Katal v.0.1.3 (launched at 2015-10-25 17:19:06) ===
=======================================================
  = target directory given as parameter : "." (path : "/home/suizokukan/projets/katal/target2")
  = no config file specified on the command line : let's search a config file in the current directory…
  * config file name : "/home/suizokukan/projets/katal/target2/.katal/katal.ini" (path : "/home/suizokukan/projets/katal/target2/.katal/katal.ini")
    … config file found and read (ok)
  = source directory : "~/projets/dysodos" (path : "/home/suizokukan/projets/dysodos")
  = informations about the "." (path: "/home/suizokukan/projets/katal/target2") target directory =
+--------------------+--------------------+------+--------------------+------------------+
| hashid/base64      | name               | tags | source name        | source date      |
+--------------------+--------------------+------+--------------------+------------------+
| […]f8SfE3Tzc+UP0s= | […]/5474c904__0.py |      | […]odos/dysodos.py | 2014-11-25 19:23 |
+--------------------+--------------------+------+--------------------+------------------+
=== exit (stopped at 2015-10-25 17:19; total duration time : 0:00:00.004414) ===

Vous pouvez enfin étiqueter un ou plusieurs fichiers :

Si vous voulez ajouter le tag "mytag" aux fichiers Python :

$ katal --addtag=mytag --to=*.py

Si vous voulez ajouter le tag "mytag" à un seul fichier :

$ katal --addtag=mytag --to=5474c904__0.py

Vous pouvez aussi :

  • supprimer toutes les étiquettes d'un ou plusieurs fichiers (--rmnotags) ;
  • supprimer un fichier ou plusieurs fichiers du catalogue (--targetkill) ;
  • supprimer les fichiers qui n'ont pas de tags (--rmnotags) ;
  • supprimer de la base de données les fichiers qui n'existent pas dans le répertoire de destination (--cleandbrm).

Et d'autres choses encore, accessibles dans le fichier README.md ou en consultant le résultat de $ katal -h .

Et la suite ?

J'ai essayé d'écrire du code lisible par tous : pylint à 10, documentation abondante, commentaires et documentation en anglais…
Je serais ravi de vos commentaires et de vos suggestions d'amélioration !

  • # Suggestion d'amélioration

    Posté par . Évalué à 3.

    Google Translate s'est pris les pieds dans le tapis : "doublons" peut se traduire par "doubloons", mais uniquement s'il est question de trésor, ou d'épave de galion. Dans le cas de fichiers, "duplicates" serait plus approprié.

  • # Questions

    Posté par (page perso) . Évalué à 3.

    Hello,

    Alors déjà super idée ! Et merci pour ce projet !

    Ça m'intéresse beaucoup (je précise que j'ai pas encore joué avec) alors j'ai des questions :
    - Comment gères-tu les doublons ? Car en plus tu précises (même contenu)
    - Des limitations connues (taille de fichiers, caractères qui ne passent pas…) ?
    - Vu que tu es parti de 0, il n'y a donc aucun projet que tu pourrais recommander comme "alternative" au tien ? Ceci afin de comparer
    - Le logiciel est bien-sûr capable de bosser avec un montage (genre je monte un NAS et je balance le tri en local) ?
    - Tu parles d'une base de données, c'est quoi comme base ?
    - T'as une roadmap ? C'est quoi la prochaine feature, la prochaine grosse étape ?

    Merci, Tcho !

    • [^] # Re: Questions

      Posté par (page perso) . Évalué à 4. Dernière modification le 26/10/15 à 16:50.

      Merci pour les encouragements :

      • le programme calcule un checksum de chaque fichier (ce que j'appelle son hashid), et il conserve cette somme de contrôle dans la base de données. J'utilise SHA256 (confer la fonction hashfile64()). Si tu ajoutes des fichiers vers le répertoire source, le checksum de chaque fichier source est d'abord comparé à ceux stockés dans la base de données, ce qui évite les doublons. Dans les faits, ça n'évite pas que deux images identiques mais encodées différemment se retrouvent dans le répertoire de destination.
      • j'ai testé le programme dans tous les sens, mais hélas toujours sur les mêmes fichiers; pas de limitation connue. J'ai tenu compte des spécificités de certains systèmes de fichiers (cf get_disk_free_space(), remove_illegal_characters()) et de la console de Windows (cf la fin de remove_illegal_characters()).
      • s'agissant des alternatives, je n'en connais pas mais avec un bon desktop manager, un système de fichiers avec métadonnées et quelques programmes spécifiques, il y a sans doute moyen de mieux faire. Mon projet n'est intéressant que si tu préfères la ligne de commande à tout le reste et si tu veux quelque chose de rapide à installer/utiliser sur des systèmes Linux/OSX/Windows.
      • pour le "montage", je prie saint-Python de savoir faire avec… mais je n'ai pas essayé. Mon code étant relativement "de haut niveau" (shutils), ça devrait passer.
      • la base de données est du sqlite3, la seule à être disponible (à ma connaissance) out-of-the-box avec Python.
      • la prochaine étape : je me suis dis que si quelqu'un d'autre était intéressé, je rendrais Katal résistant aux arrêts intempestifs; autrement dit, il serait capable de reprendre une tâche là où il l'avait laissée. C'est un peu compliqué pour moi mais c'est surtout complexe et donc chronophage.

      Si tu es intéressé, fais-le-moi savoir !

      Trust the Python !

      • [^] # Re: Questions

        Posté par (page perso) . Évalué à 1.

        Grâce à Cascador, je peux rectifier une erreur quant à l'utilisation de wget pour télécharger Katal :

        $wget https://raw.githubusercontent.com/suizokukan/katal/master/katal/katal.py

        … est la bonne adresse; celle que je donnais encapsulait le code Python dans du HTML.

        Si un admin' pouvait rectifier ma dépêche : merci d'avance !

        Trust the Python !

      • [^] # Re: Questions

        Posté par . Évalué à 3.

        Pour les doublons le fait de ne vérifier que les hash n'est pas suffisant, les hash peuvent avoir des collisions.

        J'en ai déjà fait les frais, des fichiers effacés par erreur lors d'une déduplication,
        merci liten2, https://code.google.com/p/liten2/
        En effet, chez liten2 c'est encore pire que de comparer les hash complets, il ne compare que les hash des 16 premiers Ko du chaque fichier.

        Je pense que le mieux est:
        1. Vérification de la taille des fichiers
        2. Si même taille, vérification du hash
        3. Si même hash faire une vraie comparaison bit à bit.

        • [^] # Re: Questions

          Posté par . Évalué à 2.

          La probabilité de trouver une collision en utilisant SHA256 est minuscule.
          Même avec un milliard de fichiers elle est inférieure à 1 sur 1060.
          La comparaison bit à bit ajouterait beaucoup de lourdeur inutile.

          • [^] # Re: Questions

            Posté par (page perso) . Évalué à 2.

            Je pense aussi que la probabilité d'une collision est quasi nulle. Pour mettre tout le monde d'accord, je vais modifier mon code en laissant le choix à l'utilisateur d'exiger ou non une comparaison bit à bit si deux hashs sont identiques.

            Merci à tous de ces remarques qui m'aident à améliorer mon code !

            Trust the Python !

          • [^] # Re: Questions

            Posté par . Évalué à 3.

            On parle de données (ex: photos de famille), je pense qu'un risque, même très faible, n'est pas acceptable pour des données.

            De plus la comparaison bit à bit ne se ferait qu'en cas très rare d'hash identique. Donc, la perte de performance sera très faible et justifiée.

            Par contre vérifier la taille des fichiers me semble être vraiment le strict minimum. Bien avant de calculer et comparer un hash.

            Pour optimiser le tout, je ne calculerais les "hash"s que si au moins 2 fichiers ont la même taille. Puis évidement ils seraient stockés dans la DB.

            • [^] # Re: Questions

              Posté par (page perso) . Évalué à 2.

              Voici que ce j'envisage de faire : stocker dans la base de données la taille, le hash partiel, le hash complet de chaque fichier.
              Pour tout nouveau fichier, je vérifie si sa taille correspond à qqc de connu; si c'est le cas, je calcule un hash partiel (sur 1 Mo ?) sur les fichiers qui ont la même taille; si ce hash partiel correspond à qqc dans la base de données, je calcule alors le hash complet. Si le hash complet correspond, normalement j'élimine le fichier source comme doublon; si une option "strict cmp" est activée, je vérifie bit à bit.

              Trust the Python !

              • [^] # Re: Questions

                Posté par . Évalué à 1.

                Voici une "alternative" en java et libre, si tu veux de l'inspiration : http://evrignaud.github.io/fim/#_why_do_you_need_fim
                Par contre ton logiciel a plus de fonctionnalités (tags, recherche)

              • [^] # Re: Questions

                Posté par . Évalué à 4.

                Sous unix, commence par regarder les inodes au cas où l'utilisateur a juste créé des liens durs.

                Les logiciels sous licence GPL forcent leurs utilisateurs à respecter la GPL (et oui, l'eau, ça mouille).

                • [^] # Re: Questions

                  Posté par (page perso) . Évalué à 1.

                  Merci de cette suggestion, hélas valable uniquement pour Linux. Je vais y réfléchir.

                  Trust the Python !

      • [^] # Re: Questions

        Posté par (page perso) . Évalué à 1.

        Toujours grâce à Cascador, je préciser que sur la majorité des systèmes Linux il faut écrire :
        $pip3 install katal

        à la place de
        $pip install katal

        Merci.

        Trust the Python !

  • # Merci !

    Posté par (page perso) . Évalué à 3.

    Je note la chose bien précieusement : je vais devoir faire du tri dans pas mal de photos également, avec plein de doublons.
    Je ne sais pas encore quand je m'y collerais, mais ça va m'être bien utile !

    • [^] # Re: Merci !

      Posté par (page perso) . Évalué à 2.

      N'hésite pas à me poser des questions si la doc' n'est pas assez claire. Ca me ferait très plaisir de t'aider… ou d'incorporer tes suggestions.

      Trust the Python !

  • # Splendide !

    Posté par . Évalué à 4.

    Mais c'est maaaaaaagnifique, ça! Ça colle pile-poil à mes besoins.

    J'ai une énorme opération de tri/étiquetage/chasse aux doublons à faire au début de l'année prochaine, je m'étais lancé dans l'écriture d'un bidule similaire, mais Katal va bien plus loin…

    Merci!

    • [^] # Re: Splendide !

      Posté par (page perso) . Évalué à 2.

      Merci de tes encouragements; je pensais être le seul à utiliser Katal mais si d'autres que moi en ont besoin, je vais continuer à travailler dessus. Prochaine petite étape, sans doute pour la fin de la semaine : la possibilité de trouver les fichiers ayant un certain tag.

      Si vous avez des suggestions, je suis preneur !

      Trust the Python !

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.