photo_folder 1.0.1 → 1.0.2

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.
data/photo_folder.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "photo_folder"
3
- s.version = "1.0.1"
3
+ s.version = "1.0.2"
4
4
  s.date = "2010-05-11"
5
5
  s.summary = "JavaScript gallery"
6
6
  s.email = "ryan@syntacticx.com"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: photo_folder
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 1
10
- version: 1.0.1
9
+ - 2
10
+ version: 1.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ryan Johnson
@@ -61,11 +61,6 @@ extra_rdoc_files: []
61
61
  files:
62
62
  - README.markdown
63
63
  - photo_folder.gemspec
64
- - lib/models/photo.rb
65
- - lib/models/photo_collection.rb
66
- - lib/models/photo_tag.rb
67
- - lib/models/photo_tagging.rb
68
- - lib/photo_folder.rb
69
64
  - vendor/acts_as_list.rb
70
65
  - vendor/acts_as_tree.rb
71
66
  - vendor/mini_exiftool.rb
data/lib/models/photo.rb DELETED
@@ -1,115 +0,0 @@
1
- class Photo < ActiveRecord::Base
2
- belongs_to :photo_collection
3
- has_many :photo_taggings, :dependent => :destroy
4
- has_many :photo_tags, :through => :photo_taggings
5
- acts_as_list :scope => :photo_collection
6
- before_create :set_exif_data, :set_last_modified, :set_guid, :set_nsfw
7
- after_create :set_tags
8
-
9
- def self.guid(path,date)
10
- Digest::MD5.hexdigest([
11
- path,
12
- date
13
- ].join(''))
14
- end
15
-
16
- def full_path
17
- File.join(PhotoFolder.source_directory_location,path)
18
- end
19
-
20
- def update_meta
21
- set_exif_data
22
- set_last_modified
23
- set_guid
24
- set_nsfw
25
- save
26
- set_tags
27
- end
28
-
29
- def set_guid
30
- self.guid = Photo.guid(path,date)
31
- end
32
-
33
- def set_tags
34
- #@tags was set by set_exif_data, which is always called first
35
- self.photo_taggings.destroy_all
36
- @tags.each do |str|
37
- self.photo_taggings.create({
38
- :photo_tag_id => (PhotoTag.find_by_tag(str) || PhotoTag.create(:tag => str)).id,
39
- :photo_id => self.id
40
- })
41
- end
42
- end
43
-
44
- def set_nsfw
45
- self.nsfw = @tags.collect(&:downcase).include?('nsfw') ? 1 : 0
46
- end
47
-
48
- def set_last_modified
49
- self.last_modified = File.mtime(full_path)
50
- end
51
-
52
- def set_exif_data
53
- begin
54
- exif = EXIFData.new(MiniExiftool.new(full_path),logger)
55
-
56
- [:focal_length,:iso,:shutter_speed,:f_stop,:camera,:caption,:location,:height,:width].each do |key|
57
- self.send(key.to_s + '=',exif[key] || nil)
58
- end
59
-
60
- #date,title,lens and tags are special cases
61
- if exif[:date].is_a? Time
62
- self.date = exif[:date]
63
- else
64
- date_match = (exif[:date] || '').match(/([\d]+):([\d]+):([\d]+) ([\d]+):([\d]+):([\d]+)/)
65
- self.date = Time.mktime(date_match[1],date_match[2],date_match[3],date_match[4],date_match[5],date_match[6]) if date_match
66
- end
67
- self.name = exif[:title] || full_path.split('/').pop.gsub(/\.[a-zA-Z]{1,4}/,'')
68
- self.lens = exif[:lens] ? exif[:lens] : nil
69
- @tags = exif[:tags] || []
70
- rescue
71
- self.name = full_path.split('/').pop.gsub(/\.[a-zA-Z]{1,4}/,'')
72
- @tags = []
73
- end
74
- end
75
-
76
- class EXIFData
77
- EXIF_TRANSLATIONS = {
78
- :title => ['Title','ObjectName'],
79
- :focal_length => ['FocalLength'],
80
- :iso => ['ISO'],
81
- :height => ['ImageHeight'],
82
- :width => ['ImageWidth'],
83
- :shutter_speed => ['ShutterSpeedValue'],
84
- :f_stop => ['ApertureValue'],
85
- :lens => ['Lens','LensType'],
86
- :camera => ['Model'],
87
- :date => ['DateTimeOriginal'],
88
- :caption => ['Description','ImageDescription','Caption-Abstract'],
89
- :tags => ['Keywords','Subject'],
90
- :location => ['Location'],
91
- }
92
-
93
- EXIF_TRANSFORMATIONS = {
94
- #:date => lambda{|exif_value| exif_value.to_s.split('-').shift},
95
- :shutter_speed => lambda{|exif_value| exif_value.to_s.gsub(/1\//,'')},
96
- :focal_length => lambda{|exif_value| exif_value.to_s.gsub(/\.([\d]{1,})?\s?.+$/,'')},
97
- :tags => lambda{|exif_value| (exif_value.is_a?(Array) ? exif_value.join(',') : exif_value).gsub(/["']+/,'').gsub(/\s+?,\s+?/,',').split(',').collect(&:strip).collect(&:downcase)}
98
- }
99
-
100
- def initialize(exif_data,logger)
101
- @exif_data = exif_data
102
- @logger = logger
103
- end
104
-
105
- def [](key)
106
- EXIF_TRANSLATIONS[key.to_sym].each do |exif_key_name|
107
- exif_value = @exif_data[exif_key_name.to_s]
108
- if !exif_value.nil? && exif_value != false && exif_value != '' && exif_value != 'false' && exif_value != 'nil'
109
- return EXIF_TRANSFORMATIONS[key] ? EXIF_TRANSFORMATIONS[key].call(exif_value) : exif_value
110
- end
111
- end
112
- false
113
- end
114
- end
115
- end
@@ -1,96 +0,0 @@
1
- class PhotoCollection < ActiveRecord::Base
2
- has_many :photos, :order => 'position'
3
- acts_as_list :scope => :parent_id
4
- acts_as_tree :order => :name
5
- after_create :set_children_parent_id
6
- before_create :set_name
7
-
8
- def self.set_correct_positions(collections,is_callback = false)
9
- collections.sort_by(&:name).each_with_index do |collection,i|
10
- collection.update_attribute :position, i
11
- set_correct_positions(PhotoCollection.find_all_by_parent_id(collection.id),true)
12
- end
13
- if self.positions && !is_callback
14
- self.positions.each_with_index do |file_name,i|
15
- photo_collection = PhotoCollection.find_by_path(file_name,{
16
- :conditions => {
17
- :parent_id => 0
18
- }
19
- })
20
- if photo_collection
21
- photo_collection.insert_at i + 1
22
- end
23
- end
24
- end
25
- end
26
-
27
- def self.positions_file_path
28
- File.join(PhotoFolder.source_directory_location,'positions.yml')
29
- end
30
-
31
- def self.positions
32
- return @positions if !@positions.nil?
33
- if File.exists?(self.positions_file_path)
34
- @positions = YAML.load(File.read(self.positions_file_path))
35
- else
36
- nil
37
- end
38
- end
39
-
40
- def set_children_parent_id
41
- PhotoFolder::all_paths_in_database.select{|path| self.path.underscore.gsub(/\s/,'_') == path.gsub(/\/[^\/]+$/,'').underscore.gsub(/\s/,'_')}.each do |path|
42
- Photo.update_all("photo_collection_id = #{self.id}","path = '#{path}'")
43
- end
44
- end
45
-
46
- def set_parent_id
47
- update_attribute :parent_id, parent_from_path ? parent_from_path.id : 0
48
- end
49
-
50
- def set_name
51
- self.name = path.split('/').pop.gsub(/[\_\-]+/,' ').split(/\s+/).each(&:capitalize!).join(' ')
52
- end
53
-
54
- def parent_from_path
55
- self.path.count('/') > 0 ? PhotoCollection.find_by_path(self.path.gsub(/\/[^\/]+$/,'')) : false
56
- end
57
-
58
- def full_path
59
- File.join(PhotoFolder.source_directory_location,path)
60
- end
61
-
62
- def positions_file_path
63
- File.join(full_path,'positions.yml')
64
- end
65
-
66
- def positions
67
- return @positions if !@positions.nil?
68
- if File.exists?(positions_file_path)
69
- @positions = YAML.load(File.read(positions_file_path))
70
- else
71
- nil
72
- end
73
- end
74
-
75
- def meta_file_path
76
- File.join(full_path,'meta.json')
77
- end
78
-
79
- def set_meta
80
- meta_data = File.exists?(meta_file_path) ? File.read(meta_file_path) : false
81
- write_attribute :meta, meta_data
82
- meta_data
83
- end
84
-
85
- def set_correct_positions
86
- positions.each_with_index do |file_name,i|
87
- photo = photos.find_by_path("#{path}/#{file_name}")
88
- photo_collection = children.find_by_path("#{path}/#{file_name}")
89
- if photo
90
- photo.insert_at i + 1
91
- elsif photo_collection
92
- photo_collection.insert_at i + 1
93
- end
94
- end
95
- end
96
- end
@@ -1,3 +0,0 @@
1
- class PhotoTag < ActiveRecord::Base
2
- has_many :photos, :through => :photo_taggings
3
- end
@@ -1,4 +0,0 @@
1
- class PhotoTagging < ActiveRecord::Base
2
- belongs_to :photo_tag
3
- belongs_to :photo
4
- end
data/lib/photo_folder.rb DELETED
@@ -1,296 +0,0 @@
1
- #external libraries
2
- require 'rubygems'
3
- gem 'sqlite3-ruby'
4
- gem 'activerecord'
5
- require 'activerecord'
6
- require 'fileutils'
7
- require 'tempfile'
8
- require 'pstore'
9
- require 'set'
10
- require 'ftools'
11
- require 'digest/md5'
12
- require 'yaml'
13
-
14
- #internal libraries
15
- require '../vendor/acts_as_tree'
16
- require '../vendor/acts_as_list'
17
- require '../vendor/mini_exiftool'
18
- require 'models/photo'
19
- require 'models/photo_tag'
20
- require 'models/photo_tagging'
21
- require 'models/photo_collection'
22
-
23
- unless Numeric.instance_methods.include? 'to_a'
24
- class Numeric
25
- def to_a
26
- [self]
27
- end
28
- end
29
- end
30
-
31
- module PhotoFolder
32
- def self.setup_database(database_location)
33
- ActiveRecord::Base.establish_connection(
34
- :adapter => "sqlite3",
35
- :database => database_location,
36
- :timeout => 5000
37
- )
38
- PhotoFolder::create_schema
39
- end
40
-
41
- def self.create_schema
42
- if !Photo.table_exists?
43
- ActiveRecord::Base.connection.create_table("photos") do |t|
44
- t.integer "photo_collection_id"
45
- t.integer "position"
46
- t.integer "height"
47
- t.integer "width"
48
- t.string "guid"
49
- t.string "path"
50
- t.string "name"
51
- t.datetime "date"
52
- t.string "location"
53
- t.string "f_stop"
54
- t.string "shutter_speed"
55
- t.string "focal_length"
56
- t.string "iso"
57
- t.string "camera"
58
- t.string "lens"
59
- t.text "caption"
60
- t.datetime "last_modified"
61
- t.integer "nsfw"
62
- t.integer "featured"
63
- end
64
- end
65
-
66
- if !PhotoCollection.table_exists?
67
- ActiveRecord::Base.connection.create_table("photo_collections") do |t|
68
- t.string "name"
69
- t.string "path"
70
- t.integer "parent_id"
71
- t.integer "position"
72
- t.text "meta"
73
- end
74
- end
75
-
76
- if !PhotoTag.table_exists?
77
- ActiveRecord::Base.connection.create_table("photo_tags") do |t|
78
- t.string "tag"
79
- end
80
- end
81
-
82
- if !PhotoTagging.table_exists?
83
- ActiveRecord::Base.connection.create_table("photo_taggings") do |t|
84
- t.integer "photo_tag_id"
85
- t.integer "photo_id"
86
- end
87
- end
88
- end
89
-
90
- def self.photos_list
91
- return @photos_list if !@photos_list.nil?
92
- @photos_list = Dir[PhotoFolder.source_directory_location + '/**/*.{png,jpg}']
93
- end
94
-
95
- def self.all_paths_in_database
96
- return @all_paths_in_database if !@all_paths_in_database.nil?
97
- @all_paths_in_database = Photo.find(:all).collect(&:path)
98
- end
99
-
100
- def self.all_paths_in_filesystem
101
- return @all_paths_in_filesystem if !@all_paths_in_filesystem.nil?
102
- @all_paths_in_filesystem = PhotoFolder::photos_list.collect{|file| file.gsub(PhotoFolder.source_directory_location + '/','')}
103
- end
104
-
105
- def self.flush_cached_path_information
106
- @all_paths_in_filesystem = nil
107
- @all_paths_in_database = nil
108
- end
109
-
110
- def self.scan_photos
111
- to_add = []
112
- to_update = []
113
- to_remove = []
114
-
115
- PhotoFolder::all_paths_in_filesystem.each do |path_in_filesystem|
116
- to_add.push(path_in_filesystem) if !PhotoFolder::all_paths_in_database.select{|path_in_database| path_in_database == path_in_filesystem}[0]
117
- end
118
-
119
- PhotoFolder::all_paths_in_database.each do |path_in_database|
120
- to_remove.push(path_in_database) if !PhotoFolder::all_paths_in_filesystem.select{|path_in_filesystem| path_in_filesystem == path_in_database}[0]
121
- end
122
-
123
- PhotoFolder::all_paths_in_filesystem.each do |path_in_filesystem|
124
- if !to_add.include?(path_in_filesystem) && !to_remove.include?(path_in_filesystem)
125
- entry = Photo.find_by_path(path_in_filesystem)
126
- if entry && File.mtime(entry.full_path) > entry.last_modified
127
- to_update.push(path_in_filesystem)
128
- end
129
- elsif to_add.include?(path_in_filesystem)
130
- #cleaned = Photo.clean_filename(path_in_filesystem)
131
- entry = Photo.find_by_path(path_in_filesystem)
132
- if entry
133
- to_update.push(path_in_filesystem)
134
- end
135
- end
136
- end
137
-
138
- to_add.reject!{|item| to_update.include?(item)}
139
-
140
- [to_add,to_update,to_remove]
141
- end
142
-
143
- def self.scan_photo_collections
144
- collection_paths_in_database = PhotoCollection.find(:all).collect(&:path)
145
-
146
- collection_paths_in_filesystem = []
147
- PhotoFolder::all_paths_in_database.each do |path|
148
- collection = path.gsub(/\/[^\/]+$/,'')
149
- collection_bits = collection.split('/')
150
- collection_bits.each_with_index do |part,i|
151
- joined = collection_bits[0,i].join('/')
152
- collection_paths_in_filesystem.push(joined) if !collection_paths_in_filesystem.include?(joined) && joined != ''
153
- end
154
- collection_paths_in_filesystem.push(collection) if !collection_paths_in_filesystem.include?(collection)
155
- end
156
-
157
- to_add = []
158
- to_remove = []
159
-
160
- collection_paths_in_filesystem.each do |path_in_filesystem|
161
- to_add.push(path_in_filesystem) if !collection_paths_in_database.include?(path_in_filesystem)
162
- end
163
-
164
- collection_paths_in_database.each do |path_in_database|
165
- to_remove.push(path_in_database) if !collection_paths_in_filesystem.include?(path_in_database)
166
- end
167
-
168
- [to_add,to_remove]
169
- end
170
-
171
- def self.hash_from_database
172
- {
173
- :photo_collections => PhotoCollection,
174
- :photos => Photo,
175
- :photo_tags => PhotoTag,
176
- :photo_taggings => PhotoTagging
177
- }.inject(Hash.new) do |result,key_name_and_model|
178
- key_name = key_name_and_model[0]
179
- model = key_name_and_model[1]
180
- result[key_name] = model.find(:all).inject(Hash.new) do |inner_result,item|
181
- inner_result[item.id.to_s] = item.attributes.inject(Hash.new) do |attribute_result,key_and_value|
182
- key = key_and_value[0]
183
- value = key_and_value[1]
184
- attribute_result[key.to_s] = value.to_s
185
- attribute_result
186
- end
187
- inner_result
188
- end
189
- result
190
- end
191
- end
192
-
193
- def self.write_database_to_file(path)
194
- File.open(path,'w'){|f| f.write(PhotoFolder::hash_from_database.to_json) }
195
- end
196
-
197
- def self.source_directory_location
198
- @source_directory_location
199
- end
200
-
201
- def self.generate(source_directory_location,database_location,json_location,include_nsfw = false,exif_tool_location = 'exiftool')
202
- Photo.send(:with_scope, :find => {:conditions => include_nsfw ? "nsfw = 1 OR nsfw = 0" : "nsfw = 0"}) do
203
-
204
- @source_directory_location = source_directory_location
205
-
206
- begin
207
- MiniExiftool.configure(exif_tool_location)
208
- rescue
209
- puts "WARNING: Could not configure MiniExifTool, EXIF data will not be parsed."
210
- end
211
-
212
- puts "PhotoFolder Database Generator"
213
- puts "=============================="
214
-
215
- PhotoFolder::setup_database(database_location)
216
- photos_list = PhotoFolder::photos_list
217
- puts "Scanning #{photos_list.length} photos in #{source_directory_location}"
218
-
219
- photos_to_add, photos_to_update, photos_to_remove = PhotoFolder::scan_photos
220
-
221
- Photo.transaction do
222
- if photos_to_add.length > 0
223
- photos_to_add.each do |path|
224
- puts "Adding photo: #{path}"
225
- Photo.create :path => path
226
- end
227
- else
228
- puts "No photos to add."
229
- end
230
-
231
- if photos_to_update.length > 0
232
- photos_to_update.each do |path|
233
- puts "Updating photo: #{path}"
234
- photo = Photo.find_by_path(path)
235
- photo.update_meta if photo
236
- end
237
- else
238
- puts "No photos to update."
239
- end
240
-
241
- if photos_to_remove.length > 0
242
- photos_to_remove.each do |path|
243
- puts "Removing photo: #{path}"
244
- Photo.find_all_by_path(path).each(&:destroy)
245
- end
246
- else
247
- puts "No photos to remove."
248
- end
249
- end
250
-
251
- PhotoFolder::flush_cached_path_information
252
- collections_to_add, collections_to_remove = PhotoFolder::scan_photo_collections
253
-
254
- PhotoCollection.transaction do
255
- if collections_to_add.length > 0
256
- collections_to_add.sort_by{|path| path.split('/').pop}.each do |path|
257
- puts "Adding collection: #{path}"
258
- PhotoCollection.create :path => path
259
- end
260
- else
261
- puts "No photo collections to add."
262
- end
263
-
264
- if collections_to_remove.length > 0
265
- collections_to_remove.each do |path|
266
- puts "Removing collection: #{path}"
267
- PhotoCollection.find_by_path(path).destroy if PhotoCollection.find_by_path(path)
268
- end
269
- else
270
- puts "No photo collections to remove."
271
- end
272
-
273
- puts "Ensuring correct tree structure of photo collections."
274
- PhotoCollection.find(:all).each(&:set_parent_id)
275
-
276
- puts "Ensuring correct position of photos and photo collections."
277
- if PhotoCollection.positions
278
- puts "Setting positions for root collections from #{PhotoCollection.positions_file_path}"
279
- end
280
- PhotoCollection.set_correct_positions(PhotoCollection.find_all_by_parent_id(0))
281
- PhotoCollection.find(:all).each do |photo_collection|
282
- if photo_collection.positions
283
- puts "Setting positions for #{photo_collection.name} from #{photo_collection.positions_file_path}"
284
- photo_collection.set_correct_positions
285
- end
286
- if photo_collection.set_meta
287
- puts "Setting meta data for #{photo_collection.name} from #{photo_collection.meta_file_path}"
288
- end
289
- end
290
- end
291
-
292
- puts "Writing database to #{json_location}"
293
- PhotoFolder::write_database_to_file(json_location)
294
- end
295
- end
296
- end