jon 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 880c52018870d05e4d10fd370690086f86eef18ff326bd8fbe1e910f1c4334c9
4
+ data.tar.gz: 3c558c63986bbba5ac7415e0bb17ac590d75e8d52389f46f76d7a3d3420f3fa8
5
+ SHA512:
6
+ metadata.gz: 2aec4de1e112fff7d879c4911a0228b2535d61ddef92346d4b77c4b1d88ea56eebf396ef6246c080f8e667b9e117eeeec891facf984baef3e369962d6e53b97b
7
+ data.tar.gz: 4eb453336031ec0b6a35eb2f7fb39bf7b4a0d6c05ab99479d53794067bed7f9e20095ba5698d17e3aa78637a3f0b774cfe9fe15ab02e14890e202fbe1e091e48
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in jon.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
11
+
12
+ gem "rubocop", "~> 1.21"
data/Gemfile.lock ADDED
@@ -0,0 +1,128 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ jon (1.0.0)
5
+ logging (~> 2.3.0)
6
+ mini_exiftool_vendored (~> 9.2.7.v1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (2.4.2)
12
+ backport (1.2.0)
13
+ benchmark (0.2.0)
14
+ binding_ninja (0.2.3)
15
+ coderay (1.1.3)
16
+ debase (0.2.4.1)
17
+ debase-ruby_core_source (>= 0.10.2)
18
+ debase-ruby_core_source (0.10.14)
19
+ diff-lcs (1.5.0)
20
+ e2mmap (0.1.0)
21
+ jaro_winkler (1.5.4)
22
+ kramdown (2.3.2)
23
+ rexml
24
+ kramdown-parser-gfm (1.1.0)
25
+ kramdown (~> 2.0)
26
+ little-plugger (1.1.4)
27
+ logging (2.3.0)
28
+ little-plugger (~> 1.1)
29
+ multi_json (~> 1.14)
30
+ mini_exiftool (2.10.2)
31
+ mini_exiftool_vendored (9.2.7.v1)
32
+ mini_exiftool (>= 1.6.0)
33
+ mocha (1.13.0)
34
+ multi_json (1.15.0)
35
+ nokogiri (1.13.3-x86_64-linux)
36
+ racc (~> 1.4)
37
+ parallel (1.22.1)
38
+ parser (3.1.1.0)
39
+ ast (~> 2.4.1)
40
+ proc_to_ast (0.1.0)
41
+ coderay
42
+ parser
43
+ unparser
44
+ racc (1.6.0)
45
+ rainbow (3.1.1)
46
+ rake (13.0.6)
47
+ regexp_parser (2.2.1)
48
+ reverse_markdown (2.1.1)
49
+ nokogiri
50
+ rexml (3.2.5)
51
+ rspec (3.11.0)
52
+ rspec-core (~> 3.11.0)
53
+ rspec-expectations (~> 3.11.0)
54
+ rspec-mocks (~> 3.11.0)
55
+ rspec-core (3.11.0)
56
+ rspec-support (~> 3.11.0)
57
+ rspec-expectations (3.11.0)
58
+ diff-lcs (>= 1.2.0, < 2.0)
59
+ rspec-support (~> 3.11.0)
60
+ rspec-mocks (3.11.0)
61
+ diff-lcs (>= 1.2.0, < 2.0)
62
+ rspec-support (~> 3.11.0)
63
+ rspec-parameterized (0.5.1)
64
+ binding_ninja (>= 0.2.3)
65
+ parser
66
+ proc_to_ast
67
+ rspec (>= 2.13, < 4)
68
+ unparser
69
+ rspec-support (3.11.0)
70
+ rubocop (1.26.1)
71
+ parallel (~> 1.10)
72
+ parser (>= 3.1.0.0)
73
+ rainbow (>= 2.2.2, < 4.0)
74
+ regexp_parser (>= 1.8, < 3.0)
75
+ rexml
76
+ rubocop-ast (>= 1.16.0, < 2.0)
77
+ ruby-progressbar (~> 1.7)
78
+ unicode-display_width (>= 1.4.0, < 3.0)
79
+ rubocop-ast (1.16.0)
80
+ parser (>= 3.1.1.0)
81
+ rubocop-rspec (2.9.0)
82
+ rubocop (~> 1.19)
83
+ ruby-debug-ide (0.7.3)
84
+ rake (>= 0.8.1)
85
+ ruby-progressbar (1.11.0)
86
+ solargraph (0.44.3)
87
+ backport (~> 1.2)
88
+ benchmark
89
+ bundler (>= 1.17.2)
90
+ diff-lcs (~> 1.4)
91
+ e2mmap
92
+ jaro_winkler (~> 1.5)
93
+ kramdown (~> 2.3)
94
+ kramdown-parser-gfm (~> 1.1)
95
+ parser (~> 3.0)
96
+ reverse_markdown (>= 1.0.5, < 3)
97
+ rubocop (>= 0.52)
98
+ thor (~> 1.0)
99
+ tilt (~> 2.0)
100
+ yard (~> 0.9, >= 0.9.24)
101
+ thor (1.2.1)
102
+ tilt (2.0.10)
103
+ unicode-display_width (2.1.0)
104
+ unparser (0.6.4)
105
+ diff-lcs (~> 1.3)
106
+ parser (>= 3.1.0)
107
+ webrick (1.7.0)
108
+ yard (0.9.27)
109
+ webrick (~> 1.7.0)
110
+
111
+ PLATFORMS
112
+ x86_64-linux
113
+
114
+ DEPENDENCIES
115
+ debase (~> 0.2.4.1)
116
+ jon!
117
+ mocha (~> 1.13.0)
118
+ rake (~> 13.0)
119
+ rspec (~> 3.0)
120
+ rspec-core (~> 3.11.0)
121
+ rspec-parameterized (~> 0.5.1)
122
+ rubocop (~> 1.21)
123
+ rubocop-rspec (~> 2.9.0)
124
+ ruby-debug-ide (~> 0.7.3)
125
+ solargraph (~> 0.44.3)
126
+
127
+ BUNDLED WITH
128
+ 2.3.8
data/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # Jon
2
+
3
+ ## Installation
4
+
5
+ Exécuter :
6
+
7
+ $ bundle install
8
+
9
+ ## Développement
10
+
11
+ Aprés avoir récupéré le repository, exécuter `bin/setup` pour installer les dépendances. Puis lancer `rake spec` pour exécuter les tests automatisés.
12
+
13
+ Pour installer cette gem sur la machine, exécuter `bundle exec rake install`.
14
+ Pour release une nouvelle version, mettre à jour le numéro de version dans `version.rb`, puis exécuter `bundle exec rake release` ce qui crée un nouveau tag git pour la version, push les commit et crée un tag et push le `.gem` vers [rubygems.org](https://rubygems.org).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/jon.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/jon/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "jon"
7
+ spec.version = Jon::VERSION
8
+ spec.authors = ["JordanC0TT"]
9
+ spec.email = ["jordan.content@pm.me"]
10
+
11
+ spec.summary = "Catalogue des fichiers"
12
+ spec.description = "Permet de cataloguer, restaurer des fichiers photos et vidéos"
13
+ spec.homepage = "https://github.com/JordanC0TT/jon"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
+ `git ls-files -z`.split("\x0").reject do |f|
22
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
23
+ end
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ # Uncomment to register a new dependency of your gem
30
+ spec.add_development_dependency "debase", "~> 0.2.4.1"
31
+ spec.add_development_dependency "mocha", "~> 1.13.0"
32
+ spec.add_development_dependency "rspec-core", "~> 3.11.0"
33
+ spec.add_development_dependency "rspec-parameterized", "~> 0.5.1"
34
+ spec.add_development_dependency "rubocop-rspec", "~> 2.9.0"
35
+ spec.add_development_dependency "ruby-debug-ide", "~> 0.7.3"
36
+ spec.add_development_dependency "solargraph", "~> 0.44.3"
37
+ spec.add_dependency "logging", "~> 2.3.0"
38
+ spec.add_dependency "mini_exiftool_vendored", "~> 9.2.7.v1"
39
+
40
+ # For more information and examples about making a new gem, check out our
41
+ # guide at: https://bundler.io/guides/creating_gem.html
42
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Catalogage
4
+ # Cataloger
5
+ class Catalog
6
+ def initialize(analyse, nom_attributeur, application, verificateur)
7
+ @analyse = analyse
8
+ @nom_attributeur = nom_attributeur
9
+ @application = application
10
+ @verificateur = verificateur
11
+ @log = Logging.logger["Cataloguer"]
12
+ end
13
+
14
+ def process(dossier, apply, destination)
15
+ @analyse.analyser(dossier)
16
+ @nom_attributeur.attribut(@analyse.fichiers_analyses)
17
+ @verificateur.index(@analyse.fichiers_analyses, destination)
18
+ @log.info "Fichiers en conflits : #{@verificateur.conflit.keys}"
19
+ if apply && !@verificateur.conflit.size.positive?
20
+ @application.applique(@analyse.fichiers_analyses, destination)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/helpers/regex_helpers"
4
+
5
+ require "images/catalogage/fichier"
6
+
7
+ module Catalogage
8
+ module Etape
9
+ # Analyse
10
+ class Analyse
11
+ attr_reader :fichiers_analyses
12
+
13
+ def initialize(extracteur, exif_manipulateur, fichiers_analyses = {})
14
+ @extracteur = extracteur
15
+ @exif_manipulateur = exif_manipulateur
16
+ @fichiers_analyses = fichiers_analyses
17
+ @log = Logging.logger["Analyse"]
18
+ end
19
+
20
+ def analyser(dossier)
21
+ @log.debug "Parcours du dossier '#{dossier}'"
22
+ Dir.each_child(dossier) do |nom_fichier|
23
+ fichier = "#{dossier}/#{nom_fichier}"
24
+ if File.file?(fichier)
25
+ if File.extname(fichier) =~ RegexHelpers::EXTENSIONS_EXCLUS
26
+ date_extraite = nil
27
+ exif = false
28
+ if @exif_manipulateur.datetimeoriginal?(fichier)
29
+ date_extraite = @exif_manipulateur.get_datetimeoriginal(fichier)
30
+ exif = true
31
+ elsif @extracteur.extirpabilite(fichier)
32
+ date_extraite = @extracteur.extraction_du(File.basename(fichier, File.extname(fichier)))
33
+ else
34
+ @log.warn "Aucune date extraire pour le fichier '#{fichier}'"
35
+ end
36
+ @log.debug "Le fichier '#{fichier}' à une date extraite au #{date_extraite}"
37
+ @log.debug "La date extraite viens des metadata 'datetimeoriginal' : #{exif ? "oui" : "non"}"
38
+ ajoute_analyse Fichier.new(fichier, File.extname(fichier), date_extraite, exif)
39
+ else
40
+ @log.warn "Le fichier '#{fichier}' ne sera pas analysé"
41
+ end
42
+ else
43
+ analyser(fichier)
44
+ next
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def ajoute_analyse(fichier_analyse)
52
+ path_destination = fichier_analyse.path_destination
53
+ if @fichiers_analyses.key?(path_destination)
54
+ @fichiers_analyses.merge!({ path_destination =>
55
+ @fichiers_analyses
56
+ .fetch(path_destination)
57
+ .push(fichier_analyse) })
58
+ else
59
+ @fichiers_analyses.store(path_destination, [].push(fichier_analyse))
60
+ end
61
+ @log.info "Le fichier '#{fichier_analyse.path}' sera versé vers #{path_destination}"
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Catalogage
4
+ module Etape
5
+ class Application
6
+ def initialize(exif_manipulateur)
7
+ @exif_manipulateur = exif_manipulateur
8
+ @log = Logging.logger["Application"]
9
+ end
10
+
11
+ def applique(fichiers_par_dossier, destination)
12
+ fichiers_par_dossier.each_pair do |dossier, fichiers|
13
+ @log.debug "Application sur le dossier '#{dossier}'"
14
+ destination_par_dossier = "#{destination}/#{dossier}"
15
+ @log.info "destination '#{destination_par_dossier}'"
16
+ fichiers.each do |fichier|
17
+ @log.debug "Application sur le fichier '#{fichier.path}'"
18
+ @exif_manipulateur.set_datetimeoriginal(fichier.path, fichier.date_extraite) unless fichier.exif
19
+ nouveau_nom = fichier.path_nouveau_nom(File.dirname(fichier.path))
20
+ File.rename(fichier.path, nouveau_nom)
21
+ FileUtils.mkdir_p(destination_par_dossier)
22
+ FileUtils.move(nouveau_nom, fichier.path_nouveau_nom(destination_par_dossier))
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/nommage/nom_attributeur"
4
+ require "images/nommage/dedoublonneur"
5
+
6
+ module Catalogage
7
+ module Etape
8
+ # Dedoublonne et attribut un nom
9
+ class NomAttribuer
10
+ def attribut(fichiers_analyses_par_dossier)
11
+ fichiers_analyses_par_dossier.each_value do |dossier|
12
+ dedoublonneur = Dedoublonneur.new
13
+ dossier.each do |fichier_analyse|
14
+ if fichier_analyse.date_extraite.nil?
15
+ fichier_analyse.nom_attribue = dedoublonneur.dedoublonne_par_numerotation(File.basename(
16
+ fichier_analyse.path, fichier_analyse.extension
17
+ ))
18
+ else
19
+ fichier_analyse.nom_attribue = dedoublonneur.dedoublonne_par_numerotation(
20
+ NomAttributeur.attribut_par(fichier_analyse.extension,
21
+ fichier_analyse.date_extraite)
22
+ )
23
+
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/helpers/regex_helpers"
4
+
5
+ module Catalogage
6
+ module Etape
7
+ # Permet de vérifier les fichiers en conflit dans la destination
8
+ class Verificateur
9
+ def initialize(index_fichiers_destination = [], index_fichiers_analyses = [], fichiers_conflit = {})
10
+ @index_fichiers_destination = index_fichiers_destination
11
+ @index_fichiers_analyses = index_fichiers_analyses
12
+ @fichiers_conflit = fichiers_conflit
13
+ @log = Logging.logger["Verificateur"]
14
+ end
15
+
16
+ def index(fichiers_analyses, path_destination)
17
+ index_destination(path_destination)
18
+ index_fichiers_analyses(fichiers_analyses, path_destination)
19
+ @index_fichiers_analyses.each do |fichier_analyse|
20
+ @fichiers_conflit.store(fichier_analyse, @index_fichiers_destination.include?(fichier_analyse))
21
+ end
22
+ @fichiers_conflit
23
+ end
24
+
25
+ def conflit
26
+ @fichiers_conflit.select { |_k, v| v == true }
27
+ end
28
+
29
+ private
30
+
31
+ def index_fichiers_analyses(fichiers_analyses, path_destination)
32
+ fichiers_analyses.each_value do |fichiers|
33
+ fichiers.each do |fichier|
34
+ @index_fichiers_analyses.push fichier.path_nouveau_nom("#{path_destination}/#{fichier.path_destination}")
35
+ end
36
+ end
37
+ end
38
+
39
+ def index_destination(destination)
40
+ Dir.each_child(destination) do |nom_fichier|
41
+ fichier = "#{destination}/#{nom_fichier}"
42
+ if File.file?(fichier)
43
+ @index_fichiers_destination.push fichier if File.extname(fichier) =~ RegexHelpers::EXTENSIONS_EXCLUS
44
+ else
45
+ index_destination(fichier)
46
+ next
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/helpers/directory_helpers"
4
+
5
+ require "images/classification/classification"
6
+
7
+ module Catalogage
8
+ # Fichier
9
+ class Fichier
10
+ attr_writer :nom_attribue
11
+ attr_reader :extension, :date_extraite, :exif, :path
12
+
13
+ def initialize(path, extension, date_extraite, exif, nom_attribue = nil)
14
+ @date_extraite = date_extraite
15
+ @path = path
16
+ @extension = extension
17
+ @type = Classification.get_type(extension)
18
+ @exif = exif
19
+ @nom_attribue = nom_attribue
20
+ end
21
+
22
+ def path_destination
23
+ if !@date_extraite.nil?
24
+ "#{@date_extraite.year}/#{format("%02d",
25
+ @date_extraite.month)}/#{DirectoryHelpers.defini_dossier_par(@extension)}"
26
+ else
27
+ "none/#{DirectoryHelpers.defini_dossier_par(@extension)}"
28
+ end
29
+ end
30
+
31
+ def path_nouveau_nom(destination)
32
+ "#{destination}/#{@nom_attribue}#{@extension.downcase}"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Classification
4
+ module Classification
5
+ PHOTO = "photo"
6
+ VIDEO = "video"
7
+ OTHER = "other"
8
+
9
+ EXTENSIONS_PAR_PREFIXE = {
10
+ /jpg|jpeg|png|raw/ => PHOTO,
11
+ /mp4|mov/ => VIDEO
12
+ }.freeze
13
+
14
+ def self.get_type(extension)
15
+ type = OTHER
16
+ EXTENSIONS_PAR_PREFIXE.each_pair do |key, value|
17
+ type = value if key =~ extension[/[a-zA-Z0-9]+/].downcase
18
+ end
19
+ type
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Exif Manipulateur
4
+ module ExifManipulateur
5
+ # Exif Manipulateur erreur
6
+ class ExifManipulateurErreur < StandardError; end
7
+
8
+ def set_datetimeoriginal(_fichier, _datetimeoriginal)
9
+ raise "Non implémenté"
10
+ end
11
+
12
+ def datetimeoriginal?(_fichier)
13
+ raise "Non implémenté"
14
+ end
15
+
16
+ def get_datetimeoriginal(_fichier)
17
+ raise "Non implémenté"
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mini_exiftool"
4
+
5
+ require "images/exif/exif_manipulateur"
6
+
7
+ # Exif Manipulateur
8
+ class MiniExiftoolManipulateur
9
+ include ExifManipulateur
10
+ def set_datetimeoriginal(fichier, datetimeoriginal)
11
+ exif = MiniExiftool.new(fichier)
12
+ exif.datetimeoriginal = datetimeoriginal
13
+ exif.save
14
+ exif
15
+ rescue MiniExiftool::Error => e
16
+ raise ExifManipulateur::ExifManipulateurErreur, e
17
+ end
18
+
19
+ def datetimeoriginal?(fichier)
20
+ !MiniExiftool.new(fichier).datetimeoriginal.nil?
21
+ rescue MiniExiftool::Error => e
22
+ raise ExifManipulateur::ExifManipulateurErreur, e
23
+ end
24
+
25
+ def get_datetimeoriginal(fichier)
26
+ DateTime.parse(MiniExiftool.new(fichier).datetimeoriginal.to_s)
27
+ rescue MiniExiftool::Error => e
28
+ raise ExifManipulateur::ExifManipulateurErreur, e
29
+ end
30
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ # Erreur d'extraction
6
+ class ExtractionErreur < StandardError; end
7
+
8
+ # Extracteur par date
9
+ class ExtracteurParDate
10
+ attr_accessor :regex_patterns
11
+
12
+ def initialize
13
+ @regex_patterns = {
14
+ /(IMG|VID|PANO)_([0-9]{8})_([0-9]{6})/ => Extraction.new.method(:extract01),
15
+ /(IMG|VID|PANO)_([0-9]{8})_([0-9]{4})/ => Extraction.new.method(:extract02),
16
+ /(IMG|VID)_([0-9]{8})_([0-9]{4})-([0-9]{2})/ => Extraction.new.method(:extract02),
17
+ /(IMG|VID)_([0-9]{14})/ => Extraction.new.method(:extract03),
18
+ /([0-9]{8})_([0-9]{6})_([0-9]{3})/ => Extraction.new.method(:extract04),
19
+ /([0-9]{8})_([0-9]{6})_([0-9]{3}) (\([0-9]\))/ => Extraction.new.method(:extract04),
20
+ /([0-9]{8})_([0-9]{6})/ => Extraction.new.method(:extract04),
21
+ /^(Resized)_([0-9]{8})_([0-9]{6})_([0-9]{5})$/ => Extraction.new.method(:extract04),
22
+ /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}\.[0-9]{2}\.[0-9]{2}/ => Extraction.new.method(:extract05),
23
+ /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2})\.([0-9]{2})\.([0-9]{2})-([0-9]{1})$/ => Extraction.new.method(:extract05),
24
+ /([0-9]{2})-([0-9]{2})-([0-9]{4}) ([0-9]{2})-([0-9]{2})-([0-9]{2})/ => Extraction.new.method(:extract08),
25
+ /(PHOTO|Photo|photo|VIDEO|Video|video)-([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{2})-([0-9]{2})-([0-9]{2})/ => Extraction.new.method(:extract06),
26
+ /(photo)_([0-9]{4})_([0-9]{2})_([0-9]{2})-([0-9]{2})_([0-9]{2})_([0-9]{2})/ => Extraction.new.method(:extract07),
27
+ /(photo)_([0-9]{4})_([0-9]{2})_([0-9]{2})-([0-9]{2})_([0-9]{2})_([0-9]{2})-([0-9]{2})/ => Extraction.new.method(:extract07)
28
+ }
29
+ end
30
+
31
+ def extraction_du(nom)
32
+ regex_patterns.each_pair do |key, value|
33
+ next unless key =~ nom
34
+
35
+ return value.call(nom)
36
+ end
37
+ raise ExtractionErreur, "Aucune date ne peux être extraite sur '#{nom}'"
38
+ end
39
+
40
+ def extirpabilite(nom)
41
+ est_extirpable = false
42
+ regex_patterns.each_key do |key|
43
+ est_extirpable = true if key =~ nom
44
+ end
45
+ est_extirpable
46
+ end
47
+ end
48
+
49
+ # Extraction
50
+ class Extraction
51
+ def extract01(nom)
52
+ nom_split = nom.split("_")
53
+ date = nom_split[1]
54
+ instant = nom_split[2]
55
+ DateTime.parse("#{date}T#{instant}")
56
+ end
57
+
58
+ def extract02(nom)
59
+ nom_split = nom.split("_")
60
+ date = nom_split[1]
61
+ annee = date[4..7]
62
+ mois = date[2..3]
63
+ jour = date[0..1]
64
+ instant = nom_split[2].split("-")[0]
65
+ DateTime.parse("#{annee}#{mois}#{jour}T#{instant}00")
66
+ end
67
+
68
+ def extract03(nom)
69
+ nom_split = nom.split("_")
70
+ date = nom_split[1][0..7]
71
+ instant = nom_split[1][8..13]
72
+ DateTime.parse("#{date}T#{instant}")
73
+ end
74
+
75
+ def extract04(nom)
76
+ nom_split = nom.split("_")
77
+ if nom =~ /Resized/
78
+ DateTime.parse("#{nom_split[1]}T#{nom_split[2]}")
79
+ else
80
+ DateTime.parse("#{nom_split[0]}T#{nom_split[1]}")
81
+ end
82
+ end
83
+
84
+ def extract05(nom)
85
+ nom_split = nom.split(" ")
86
+ date = nom_split[0].gsub("-", "")
87
+ instant = nom_split[1].gsub(".", "").split("-")[0]
88
+ DateTime.parse("#{date}T#{instant}")
89
+ end
90
+
91
+ def extract06(nom)
92
+ nom_split = nom.split("-")
93
+ date = "#{nom_split[1]}#{nom_split[2]}#{nom_split[3]}"
94
+ seconde = nom_split[6][0..1]
95
+ instant = "#{nom_split[4]}#{nom_split[5]}#{seconde}"
96
+ DateTime.parse("#{date}T#{instant}")
97
+ end
98
+
99
+ def extract07(nom)
100
+ nom_split = nom.split("-")
101
+ date_split = nom_split[0].split("_")
102
+ date = "#{date_split[1]}#{date_split[2]}#{date_split[3]}"
103
+ instant_split = nom_split[1].split("_")
104
+ seconde = instant_split[2][0..1]
105
+ instant = "#{instant_split[0]}#{instant_split[1]}#{seconde}"
106
+ DateTime.parse("#{date}T#{instant}")
107
+ end
108
+
109
+ def extract08(nom)
110
+ nom_split = nom.split(" ")
111
+ date_split = nom_split[0].split("-")
112
+ date = "#{date_split[2]}#{date_split[1]}#{date_split[0]}"
113
+ instant = nom_split[1].gsub("-", "")
114
+ DateTime.parse("#{date}T#{instant}")
115
+ end
116
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "date"
5
+
6
+ # Directory
7
+ module DirectoryHelpers
8
+ def self.get_date(nom_dossier)
9
+ chemins_dossier = nom_dossier.split("/")
10
+ annee = chemins_dossier.select { |v| v =~ /^([0-9]{4})$/ }
11
+ mois = chemins_dossier.select { |v| v =~ /^([0-9]{2})$/ }
12
+ DateTime.new(annee[0].to_i, mois[0].to_i, 1, 0, 0, 0)
13
+ end
14
+
15
+ def self.cree_le(dossier)
16
+ FileUtils.mkdir_p(dossier) if dossier != "" && !Dir.exist?(dossier)
17
+ dossier
18
+ end
19
+
20
+ def self.defini_dossier_par(extension)
21
+ if extension.downcase == ".jpeg"
22
+ "JPG"
23
+ else
24
+ extension[/[a-zA-Z0-9]+/].upcase
25
+ end
26
+ end
27
+
28
+ def self.nombre_fichiers(dossier)
29
+ Dir.glob(File.join(dossier, "**", "*")).select { |file| File.file?(file) }.count
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RegexHelpers
4
+ EXTENSIONS_EXCLUS = /\.(?!(ini|log|1|db-shm|db-wal|db)$)([^.]+$)/.freeze
5
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/classification/classification"
4
+
5
+ # Dedoublonneur
6
+ class Dedoublonneur
7
+ attr_accessor :noms_attribues
8
+
9
+ def initialize(noms_attribues = [], noms_attribues_par_extension = {})
10
+ @noms_attribues = noms_attribues
11
+ @noms_attribues_par_extension = noms_attribues_par_extension
12
+ end
13
+
14
+ def dedoublonne_par_numerotation(nom)
15
+ i = 0
16
+ nom_numerote = nom
17
+ @noms_attribues.each do |nom_attribue|
18
+ if nom_numerote == nom_attribue
19
+ i += 1
20
+ nom_numerote = format("%s-%02d", nom, i)
21
+ end
22
+ end
23
+ @noms_attribues.push(nom_numerote)
24
+ nom_numerote
25
+ end
26
+
27
+ def attribution_par_numero(extension)
28
+ type = Classification.get_type(extension)
29
+ nom_numerote = ""
30
+ if @noms_attribues_par_extension.key?(type)
31
+ numeros_attribues = @noms_attribues_par_extension.fetch(type)
32
+ numero = numeros_attribues.length + 1
33
+ nom_numerote = format("%03d", numero)
34
+ numeros_attribues.push(nom_numerote)
35
+ @noms_attribues_par_extension.merge!({ type => numeros_attribues })
36
+ else
37
+ numero = 1
38
+ nom_numerote = format("%03d", numero)
39
+ numeros_attribues = []
40
+ numeros_attribues.push(nom_numerote)
41
+ @noms_attribues_par_extension.store(type, numeros_attribues)
42
+ end
43
+ nom_numerote
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/classification/classification"
4
+
5
+ # NomAttributeur
6
+ module NomAttributeur
7
+ def self.attribut_par(extension, date)
8
+ prefixe = Classification.get_type(extension)
9
+ date.strftime("#{prefixe}_%Y_%m_%d-%H_%M_%S")
10
+ end
11
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logging"
4
+
5
+ require "images/helpers/regex_helpers"
6
+
7
+
8
+ module Restauration
9
+ module Etape
10
+ # Definit l'étape d'analyse
11
+ class Analyse
12
+ attr_reader :dossiers_analyses, :nombre_fichiers_analyses
13
+
14
+ def initialize(extracteur, noms_extirpable_par_dossier = {}, dossiers_analyses = {})
15
+ @extracteur = extracteur
16
+ @noms_extirpable_par_dossier = noms_extirpable_par_dossier
17
+ @dossiers_analyses = dossiers_analyses
18
+ @log = Logging.logger[self]
19
+ @nombre_fichiers_analyses = 0
20
+ end
21
+
22
+ def parcours(dossier)
23
+ @log.debug "Parcours du dossier '#{dossier}'"
24
+ Dir.each_child(dossier) do |nom_fichier|
25
+ fichier = "#{dossier}/#{nom_fichier}"
26
+ if File.file?(fichier)
27
+ if File.extname(fichier) =~ RegexHelpers::EXTENSIONS_EXCLUS
28
+ extirpabilite_par(dossier, fichier)
29
+ @nombre_fichiers_analyses += 1
30
+ else
31
+ @log.warn "Le fichier '#{fichier}' ne sera pas analysé"
32
+ end
33
+ else
34
+ parcours(fichier)
35
+ next
36
+ end
37
+ end
38
+ if @noms_extirpable_par_dossier.key?(dossier)
39
+ taux = calcul_taux_d_extirpabilite_par(dossier)
40
+ @dossiers_analyses.store(dossier, taux)
41
+ @log.info "Le dossier '#{dossier}' posséde un taux de #{taux}% d'extirpabilité"
42
+ end
43
+ end
44
+
45
+ def calcul_taux_d_extirpabilite_par(nom_dossier)
46
+ noms_extirpable = @noms_extirpable_par_dossier.fetch(nom_dossier)
47
+ noms_extirpable_possible = @noms_extirpable_par_dossier.fetch(nom_dossier).tally
48
+ if noms_extirpable_possible.key?(true)
49
+ ((noms_extirpable_possible[true] * 100) / noms_extirpable.length)
50
+ else
51
+ 0
52
+ end
53
+ end
54
+
55
+ def extirpabilite_par(path_dossier, nom_fichier)
56
+ extirpable = @extracteur.extirpabilite(nom_fichier)
57
+ nom_extirpable_par_dossier = if @noms_extirpable_par_dossier.key?(path_dossier)
58
+ @noms_extirpable_par_dossier.merge!({ path_dossier => @noms_extirpable_par_dossier
59
+ .fetch(path_dossier)
60
+ .push(extirpable) })
61
+ else
62
+ @noms_extirpable_par_dossier.merge!({ path_dossier => [].push(extirpable) })
63
+ end
64
+ @log.debug "Le fichier '#{nom_fichier}' est extirpable : #{extirpable}"
65
+ nom_extirpable_par_dossier
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logging"
4
+
5
+ module Restauration
6
+ module Etape
7
+ # Définit l'étape d'application
8
+ class Application
9
+ def initialize(exif_manipulateur)
10
+ @exif_manipulateur = exif_manipulateur
11
+ @log = Logging.logger[self]
12
+ end
13
+
14
+ def parcours(fichiers)
15
+ fichiers.each_pair do |key, value|
16
+ @log.debug "Application sur le fichier '#{key}'"
17
+ if File.file?(key)
18
+ begin
19
+ @exif_manipulateur.set_datetimeoriginal(key, value.date)
20
+ File.rename(key, value.path_nouveau_nom)
21
+ FileUtils.mkdir_p(File.dirname(value.path_destination))
22
+ FileUtils.move(value.path_nouveau_nom, value.path_destination)
23
+ rescue ExifManipulateur::ExifManipulateurErreur => e
24
+ @log.fatal e.message
25
+ rescue SystemCallError => e
26
+ @log.fatal e.message
27
+ end
28
+ else
29
+ @log.warn "le fichier '#{key}' ne sera pas traite"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logging"
4
+
5
+ require "images/restauration/fichier"
6
+
7
+ require "images/nommage/dedoublonneur"
8
+ require "images/nommage/nom_attributeur"
9
+
10
+ module Restauration
11
+ module Etape
12
+ # Définit l'étape de traitement de dossier extirpable
13
+ class TraitementDossierExtirpable
14
+ attr_reader :fichiers
15
+
16
+ def initialize(extracteur, fichiers = {})
17
+ @extracteur = extracteur
18
+ @fichiers = fichiers
19
+ @log = Logging.logger[self]
20
+ end
21
+
22
+ def parcours(dossiers)
23
+ dossiers.each do |dossier|
24
+ @log.info "Traitement sur le dossier '#{dossier}'"
25
+ dedoublonneur = Dedoublonneur.new
26
+ Dir.each_child(dossier) do |nom_fichier|
27
+ fichier = "#{dossier}/#{nom_fichier}"
28
+ @log.debug "Traitement sur le fichier '#{fichier}'"
29
+ date_extraite = @extracteur.extraction_du(File.basename(fichier, File.extname(fichier)))
30
+ nom_attribue = dedoublonneur.dedoublonne_par_numerotation(NomAttributeur.attribut_par(File.extname(fichier),
31
+ date_extraite))
32
+ @fichiers.store(fichier,
33
+ Fichier.new(nom_attribue, date_extraite, File.dirname(fichier), File.extname(fichier)))
34
+ rescue ExtractionErreur => e
35
+ @log.error e.message
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logging"
4
+
5
+ require "images/helpers/directory_helpers"
6
+
7
+ require "images/nommage/dedoublonneur"
8
+
9
+ require "images/restauration/fichier"
10
+
11
+ module Restauration
12
+ module Etape
13
+ # Définit l'étape de traitement de dossier non extirpable
14
+ class TraitementDossierNonExtirpable
15
+ attr_reader :fichiers
16
+
17
+ def initialize(fichiers = {})
18
+ @fichiers = fichiers
19
+ @log = Logging.logger[self]
20
+ end
21
+
22
+ def parcours(dossiers)
23
+ dossiers.each do |dossier|
24
+ @log.info "Traitement sur le dossier '#{dossier}'"
25
+ dedoublonneur = Dedoublonneur.new
26
+ Dir.each_child(dossier) do |nom_fichier|
27
+ fichier = "#{dossier}/#{nom_fichier}"
28
+ @log.debug "Traitement sur le fichier '#{fichier}'"
29
+ if File.file?(fichier)
30
+ numero_attribue = dedoublonneur.attribution_par_numero(File.extname(fichier))
31
+ fichiers.store(
32
+ fichier,
33
+ Fichier.new(
34
+ numero_attribue, DirectoryHelpers.get_date(File.dirname(fichier)),
35
+ File.dirname(fichier), File.extname(fichier)
36
+ )
37
+ )
38
+ else
39
+ @log.warn "le fichier '#{fichier}' ne sera pas traite"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "images/helpers/directory_helpers"
4
+ require "images/classification/classification"
5
+
6
+
7
+ module Restauration
8
+ # Fichier
9
+ class Fichier
10
+ attr_reader :nom_attribue, :date, :path, :extension, :type
11
+
12
+ def initialize(nom_attribue, date, path, extension)
13
+ @nom_attribue = nom_attribue
14
+ @date = date
15
+ @path = path
16
+ @extension = extension
17
+ @type = Classification.get_type(extension)
18
+ end
19
+
20
+ def path_nouveau_nom
21
+ "#{@path}/#{@nom_attribue}#{@extension.downcase}"
22
+ end
23
+
24
+ def path_destination
25
+ "#{@path}/#{DirectoryHelpers.defini_dossier_par(@extension)}/#{@nom_attribue}#{@extension.downcase}"
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Restauration
4
+ # Restauration
5
+ class Restore
6
+ def initialize(analyse, traitement_dossier_extirpable, traitement_dossier_non_extirpable, application)
7
+ @analyse = analyse
8
+ @traitement_dossier_extirpable = traitement_dossier_extirpable
9
+ @traitement_dossier_non_extirpable = traitement_dossier_non_extirpable
10
+ @application_photos = application
11
+ @log = Logging.logger[self]
12
+ end
13
+
14
+ def process(dossier, apply)
15
+ @analyse.parcours(dossier)
16
+
17
+ dossiers_extirpable = @analyse.dossiers_analyses.select { |_key, value| value == 100 }.keys
18
+ @traitement_dossier_extirpable.parcours(dossiers_extirpable)
19
+
20
+ dossiers_non_extirpable = @analyse.dossiers_analyses.select { |_key, value| value < 100 }.keys
21
+ @traitement_dossier_non_extirpable.parcours(dossiers_non_extirpable)
22
+
23
+ all_fichiers = @traitement_dossier_extirpable.fichiers.merge(@traitement_dossier_non_extirpable.fichiers)
24
+
25
+ @log.info "Nombre de fichiers analysés : #{@analyse.nombre_fichiers_analyses}"
26
+ @log.info "Nombre de fichiers traités : #{all_fichiers.length}"
27
+
28
+ if apply && all_fichiers.length == @analyse.nombre_fichiers_analyses
29
+ @application_photos.parcours(all_fichiers)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jon
4
+ VERSION = "1.0.0"
5
+ end
data/sig/jon.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Jon
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,198 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jon
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - JordanC0TT
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-03-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: debase
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.4.1
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.4.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: mocha
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.13.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.13.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-core
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.11.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.11.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-parameterized
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 2.9.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 2.9.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: ruby-debug-ide
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.7.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.7.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: solargraph
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.44.3
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.44.3
111
+ - !ruby/object:Gem::Dependency
112
+ name: logging
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 2.3.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 2.3.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: mini_exiftool_vendored
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 9.2.7.v1
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 9.2.7.v1
139
+ description: Permet de cataloguer, restaurer des fichiers photos et vidéos
140
+ email:
141
+ - jordan.content@pm.me
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".rspec"
147
+ - ".rubocop.yml"
148
+ - Gemfile
149
+ - Gemfile.lock
150
+ - README.md
151
+ - Rakefile
152
+ - jon.gemspec
153
+ - lib/images/catalogage/catalog.rb
154
+ - lib/images/catalogage/etape/analyse.rb
155
+ - lib/images/catalogage/etape/application.rb
156
+ - lib/images/catalogage/etape/nom_attribuer.rb
157
+ - lib/images/catalogage/etape/verificateur.rb
158
+ - lib/images/catalogage/fichier.rb
159
+ - lib/images/classification/classification.rb
160
+ - lib/images/exif/exif_manipulateur.rb
161
+ - lib/images/exif/mini_exiftool_manipulateur.rb
162
+ - lib/images/extraction/extracteur_par_date.rb
163
+ - lib/images/helpers/directory_helpers.rb
164
+ - lib/images/helpers/regex_helpers.rb
165
+ - lib/images/nommage/dedoublonneur.rb
166
+ - lib/images/nommage/nom_attributeur.rb
167
+ - lib/images/restauration/etape/analyse.rb
168
+ - lib/images/restauration/etape/application.rb
169
+ - lib/images/restauration/etape/traitement_dossier_extirpable.rb
170
+ - lib/images/restauration/etape/traitement_dossier_non_extirpable.rb
171
+ - lib/images/restauration/fichier.rb
172
+ - lib/images/restauration/restore.rb
173
+ - lib/jon/version.rb
174
+ - sig/jon.rbs
175
+ homepage: https://github.com/JordanC0TT/jon
176
+ licenses: []
177
+ metadata:
178
+ homepage_uri: https://github.com/JordanC0TT/jon
179
+ post_install_message:
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: 2.6.0
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ requirements: []
194
+ rubygems_version: 3.1.4
195
+ signing_key:
196
+ specification_version: 4
197
+ summary: Catalogue des fichiers
198
+ test_files: []