photo_folder 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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