gallerist 0.1.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 +7 -0
- data/.gitignore +2 -0
- data/CONTRIBUTING.md +53 -0
- data/Gemfile +8 -0
- data/LICENSE +25 -0
- data/README.md +57 -0
- data/Rakefile +16 -0
- data/assets/stylesheets/main.scss +17 -0
- data/bin/gallerist +59 -0
- data/config.ru +21 -0
- data/gallerist.gemspec +32 -0
- data/lib/gallerist.rb +46 -0
- data/lib/gallerist/app.rb +230 -0
- data/lib/gallerist/errors.rb +12 -0
- data/lib/gallerist/helpers.rb +72 -0
- data/lib/gallerist/library.rb +100 -0
- data/lib/gallerist/middleware/raise_warmup_exceptions.rb +24 -0
- data/lib/gallerist/middleware/show_exceptions.rb +40 -0
- data/lib/gallerist/models/admin_data.rb +10 -0
- data/lib/gallerist/models/album.rb +30 -0
- data/lib/gallerist/models/album_photo.rb +20 -0
- data/lib/gallerist/models/base_model.rb +32 -0
- data/lib/gallerist/models/image_proxies_model.rb +11 -0
- data/lib/gallerist/models/image_proxy_state.rb +22 -0
- data/lib/gallerist/models/master.rb +25 -0
- data/lib/gallerist/models/model_resource.rb +22 -0
- data/lib/gallerist/models/person.rb +46 -0
- data/lib/gallerist/models/person_model.rb +10 -0
- data/lib/gallerist/models/person_photo.rb +39 -0
- data/lib/gallerist/models/photo.rb +100 -0
- data/lib/gallerist/models/tag.rb +31 -0
- data/lib/gallerist/models/tag_photo.rb +20 -0
- data/lib/rack/handler/unicorn.rb +34 -0
- data/views/500.erb +5 -0
- data/views/album.erb +9 -0
- data/views/index.erb +24 -0
- data/views/layout.erb +34 -0
- data/views/partials/thumbnail.erb +13 -0
- data/views/person.erb +9 -0
- data/views/photos.erb +9 -0
- data/views/tag.erb +9 -0
- metadata +183 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
class Gallerist::LibraryInUseError < StandardError
|
7
|
+
|
8
|
+
def initialize(library_path)
|
9
|
+
super "The library '%s' is currently in use." % [ library_path ]
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
module Gallerist::Helpers
|
7
|
+
|
8
|
+
def library
|
9
|
+
settings.library
|
10
|
+
end
|
11
|
+
|
12
|
+
def link_to(obj, classes = nil)
|
13
|
+
url = url_for obj
|
14
|
+
current = (url == request.path)
|
15
|
+
|
16
|
+
classes = [ classes ].compact
|
17
|
+
|
18
|
+
case obj
|
19
|
+
when Gallerist::Album
|
20
|
+
link = obj.name
|
21
|
+
when Gallerist::Person
|
22
|
+
classes << 'label' << 'tag'
|
23
|
+
classes << (current ? 'label-info' : 'label-primary')
|
24
|
+
link = obj.name
|
25
|
+
when Gallerist::Photo
|
26
|
+
classes << 'thumbnail'
|
27
|
+
link = '<img src="/thumbs/%s">' % [ obj.id ]
|
28
|
+
when Gallerist::Tag
|
29
|
+
classes << 'label' << 'tag'
|
30
|
+
classes << (current ? 'label-info' : 'label-primary')
|
31
|
+
link = obj.name
|
32
|
+
end
|
33
|
+
|
34
|
+
classes = classes.empty? ? '' : ' class="%s"' % [ classes.join(' ') ]
|
35
|
+
|
36
|
+
if current
|
37
|
+
'<span%s>%s</span>' % [ classes, link ]
|
38
|
+
else
|
39
|
+
'<a href="%s"%s>%s</a>' % [ url_for(obj), classes, link ]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def navbar
|
44
|
+
@navbar
|
45
|
+
end
|
46
|
+
|
47
|
+
def partial(partial, *options)
|
48
|
+
erb :"partials/#{partial}", *options
|
49
|
+
end
|
50
|
+
|
51
|
+
def route_exists(url)
|
52
|
+
settings.routes['GET'].map(&:first).any? { |route| route =~ url }
|
53
|
+
end
|
54
|
+
|
55
|
+
def title
|
56
|
+
'%s – Gallerist' % [ @title ]
|
57
|
+
end
|
58
|
+
|
59
|
+
def url_for(obj)
|
60
|
+
case obj
|
61
|
+
when Gallerist::Album
|
62
|
+
'/albums/%s' % [ obj.id ]
|
63
|
+
when Gallerist::Person
|
64
|
+
'/persons/%s' % [ obj.id ]
|
65
|
+
when Gallerist::Photo
|
66
|
+
'/photos/%s' % [ obj.id ]
|
67
|
+
when Gallerist::Tag
|
68
|
+
'/tags/%s' % [ URI.encode(obj.simple_name) ]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
require 'sqlite3'
|
7
|
+
|
8
|
+
class Gallerist::Library
|
9
|
+
|
10
|
+
attr_reader :app
|
11
|
+
attr_reader :name
|
12
|
+
attr_reader :path
|
13
|
+
|
14
|
+
def initialize(library_path)
|
15
|
+
@name = File.basename(library_path).rpartition('.').first
|
16
|
+
@path = File.expand_path library_path
|
17
|
+
@db_path = File.dirname File.realpath(File.join @path, 'Database', 'Library.apdb')
|
18
|
+
end
|
19
|
+
|
20
|
+
def albums
|
21
|
+
Gallerist::Album.all
|
22
|
+
end
|
23
|
+
|
24
|
+
def app_id
|
25
|
+
@app_id ||= Gallerist::AdminData.
|
26
|
+
where(propertyArea: 'database', propertyName: 'applicationIdentifier').
|
27
|
+
pluck(:propertyValue).first
|
28
|
+
end
|
29
|
+
|
30
|
+
def db_path
|
31
|
+
@temp_path || @db_path
|
32
|
+
end
|
33
|
+
|
34
|
+
def copy_base_db
|
35
|
+
@temp_path = Dir.mktmpdir 'gallerist'
|
36
|
+
temp_path = @temp_path.dup
|
37
|
+
at_exit { FileUtils.rm_rf temp_path }
|
38
|
+
|
39
|
+
copy_tmp_db 'Library.apdb'
|
40
|
+
end
|
41
|
+
|
42
|
+
def copy_extra_dbs
|
43
|
+
copy_tmp_db 'ImageProxies.apdb'
|
44
|
+
|
45
|
+
if iphoto?
|
46
|
+
copy_tmp_db 'Faces.db'
|
47
|
+
else
|
48
|
+
copy_tmp_db 'Person.db'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def copy_tmp_db(db_name)
|
53
|
+
source_path = File.join @db_path, db_name
|
54
|
+
dest_path = File.join @temp_path, db_name
|
55
|
+
|
56
|
+
db = SQLite3::Database.new source_path
|
57
|
+
db.transaction :immediate do |_|
|
58
|
+
FileUtils.cp source_path, dest_path, preserve: true
|
59
|
+
end
|
60
|
+
ensure
|
61
|
+
db.close unless db.nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
def iphoto?
|
65
|
+
app_id == 'com.apple.iPhoto'
|
66
|
+
end
|
67
|
+
|
68
|
+
def tags
|
69
|
+
Gallerist::Tag.all
|
70
|
+
end
|
71
|
+
|
72
|
+
def library_db
|
73
|
+
File.join db_path, 'Library.apdb'
|
74
|
+
end
|
75
|
+
|
76
|
+
def image_proxies_db
|
77
|
+
File.join db_path, 'ImageProxies.apdb'
|
78
|
+
end
|
79
|
+
|
80
|
+
def inspect
|
81
|
+
"#<%s path='%s'>" % [ self.class, path ]
|
82
|
+
end
|
83
|
+
|
84
|
+
def person_db
|
85
|
+
File.join db_path, iphoto? ? 'Faces.db' : 'Person.db'
|
86
|
+
end
|
87
|
+
|
88
|
+
def persons
|
89
|
+
Gallerist::Person.all
|
90
|
+
end
|
91
|
+
|
92
|
+
def photos
|
93
|
+
Gallerist::Photo.all
|
94
|
+
end
|
95
|
+
|
96
|
+
def type
|
97
|
+
iphoto? ? :iphoto : :photos
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
class Gallerist::RaiseWarmupExceptions
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
@app.call env
|
14
|
+
rescue Exception
|
15
|
+
if env['rack.warmup']
|
16
|
+
if $!.is_a? SQLite3::BusyException
|
17
|
+
raise Gallerist::LibraryInUseError, Gallerist::App.library_path
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
raise $!
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
# Monkey-patch for Sinatra 1.4 and Rack 1.6
|
7
|
+
#
|
8
|
+
# See https://github.com/sinatra/sinatra/issues/951
|
9
|
+
class Gallerist::ShowExceptions < Sinatra::ShowExceptions
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@app.call(env)
|
13
|
+
rescue Exception => e
|
14
|
+
raise if env['rack.warmup']
|
15
|
+
|
16
|
+
errors, env["rack.errors"] = env["rack.errors"], @@eats_errors
|
17
|
+
|
18
|
+
if prefers_plain_text?(env)
|
19
|
+
content_type = "text/plain"
|
20
|
+
exception_string = dump_exception(e)
|
21
|
+
else
|
22
|
+
content_type = "text/html"
|
23
|
+
exception_string = pretty(env, e)
|
24
|
+
end
|
25
|
+
|
26
|
+
env["rack.errors"] = errors
|
27
|
+
|
28
|
+
# Post 893a2c50 in rack/rack, the #pretty method above, implemented in
|
29
|
+
# Rack::ShowExceptions, returns a String instead of an array.
|
30
|
+
body = Array(exception_string)
|
31
|
+
|
32
|
+
[
|
33
|
+
500,
|
34
|
+
{"Content-Type" => content_type,
|
35
|
+
"Content-Length" => Rack::Utils.bytesize(body.join).to_s},
|
36
|
+
body
|
37
|
+
]
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
class Gallerist::Album < Gallerist::BaseModel
|
7
|
+
|
8
|
+
self.table_name = 'RKAlbum'
|
9
|
+
|
10
|
+
has_many :album_photos, primary_key: 'modelId', foreign_key: 'albumId'
|
11
|
+
has_many :photos, through: :album_photos
|
12
|
+
|
13
|
+
alias_attribute :date, :createDate
|
14
|
+
alias_attribute :hidden, :isHidden
|
15
|
+
alias_attribute :trashed, :isInTrash
|
16
|
+
|
17
|
+
default_scope { select(:createDate, :isHidden, :isInTrash, :modelId, :name) }
|
18
|
+
scope :nonempty, -> {
|
19
|
+
joins(:album_photos).
|
20
|
+
select('count(RKAlbumVersion.albumId) as photos_count').
|
21
|
+
group('RKAlbumVersion.albumId').
|
22
|
+
having('photos_count > 0')
|
23
|
+
}
|
24
|
+
scope :visible, -> { where(trashed: false, hidden: false) }
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
"#<%s id=%d uuid=%s name='%s'>" % [ self.class, id, uuid, name ]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
class Gallerist::AlbumPhoto < Gallerist::BaseModel
|
7
|
+
|
8
|
+
self.table_name = 'RKAlbumVersion'
|
9
|
+
|
10
|
+
has_one :album, primary_key: 'albumId', foreign_key: 'modelId'
|
11
|
+
has_one :photo, primary_key: 'versionId', foreign_key: 'modelId'
|
12
|
+
|
13
|
+
alias_attribute :album_id, :albumId
|
14
|
+
alias_attribute :photo_id, :versionId
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
'#<%s album_id=%d photo_id=%d>' % [ self.class, album_id, photo_id ]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
class Gallerist::BaseModel < ActiveRecord::Base
|
7
|
+
|
8
|
+
self.abstract_class = true
|
9
|
+
self.inheritance_column = nil
|
10
|
+
self.primary_key = 'modelId'
|
11
|
+
|
12
|
+
def self.iphoto(&block)
|
13
|
+
store_setup :iphoto, &block
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.photos(&block)
|
17
|
+
store_setup :photos, &block
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.setup_for(app)
|
21
|
+
((@setups || {})[app] || []).each do |setup|
|
22
|
+
setup.call
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.store_setup(app, &block)
|
27
|
+
@setups ||= {}
|
28
|
+
@setups[app] ||= []
|
29
|
+
@setups[app] << block
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
|
7
|
+
class Gallerist::ImageProxiesModel < Gallerist::BaseModel
|
8
|
+
|
9
|
+
self.abstract_class = true
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
class Gallerist::ImageProxyState < Gallerist::ImageProxiesModel
|
7
|
+
|
8
|
+
self.table_name = 'RKImageProxyState'
|
9
|
+
|
10
|
+
belongs_to :photo
|
11
|
+
|
12
|
+
alias_attribute :photo_id, :versionId
|
13
|
+
alias_attribute :small_thumbnail_path, :miniThumbnailPath
|
14
|
+
alias_attribute :thumbnail_available, :thumbnailsCurrent
|
15
|
+
|
16
|
+
default_scope { select(:miniThumbnailPath, :modelId, :thumbnailsCurrent, :versionId) }
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
'#<%s id=%d photo=%s>' % [ self.class, id, photo_id ]
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
class Gallerist::Master < Gallerist::BaseModel
|
7
|
+
|
8
|
+
self.table_name = 'RKMaster'
|
9
|
+
|
10
|
+
alias_attribute :file_name, :fileName
|
11
|
+
alias_attribute :path, :imagePath
|
12
|
+
|
13
|
+
photos do
|
14
|
+
default_scope { select(:fileName, :imagePath, :modelId, :uuid) }
|
15
|
+
end
|
16
|
+
|
17
|
+
iphoto do
|
18
|
+
default_scope { select(:fileName, :imagePath, :modelId, :type, :uuid) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#<%s id=%d uuid=%s name='%s'>" % [ self.class, id, uuid, file_name ]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
class Gallerist::ModelResource < Gallerist::ImageProxiesModel
|
7
|
+
|
8
|
+
self.table_name = 'RKModelResource'
|
9
|
+
|
10
|
+
belongs_to :photo
|
11
|
+
|
12
|
+
alias_attribute :file_name, :filename
|
13
|
+
alias_attribute :model_type, :attachedModelType
|
14
|
+
alias_attribute :uuid, :resourceUuid
|
15
|
+
|
16
|
+
default_scope { select(:attachedModelType, :filename, :modelId, :resourceUuid) }
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
'#<%s id=%d uuid=%s file_name=%s>' % [ self.class, id, uuid, file_name ]
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2015, Sebastian Staudt
|
5
|
+
|
6
|
+
class Gallerist::Person < Gallerist::PersonModel
|
7
|
+
|
8
|
+
iphoto do
|
9
|
+
self.table_name = 'RKFaceName'
|
10
|
+
|
11
|
+
has_many :person_photos, primary_key: 'faceKey', foreign_key: 'faceKey'
|
12
|
+
|
13
|
+
default_scope { select(:name, :faceKey, :keyVersionUuid).order(:manual_order) }
|
14
|
+
end
|
15
|
+
|
16
|
+
photos do
|
17
|
+
self.table_name = 'RKPerson'
|
18
|
+
|
19
|
+
has_many :person_photos, primary_key: 'modelId', foreign_key: 'personId'
|
20
|
+
|
21
|
+
alias_attribute :person_type, :personType
|
22
|
+
alias_attribute :simple_name, :searchName
|
23
|
+
|
24
|
+
default_scope do
|
25
|
+
select(:modelId, :name, :representativeFaceId).
|
26
|
+
order(person_type: :desc, manual_order: :asc)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
alias_attribute :manual_order, :manualOrder
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
"#<%s id=%d name='%s'>" % [ self.class, id, name ]
|
34
|
+
end
|
35
|
+
|
36
|
+
# ActiveRecord does not support has_many-through associations across
|
37
|
+
# different databases, so we have to query the photos manually
|
38
|
+
def photos
|
39
|
+
Gallerist::Photo.where modelId: person_photos.map(&:photo_id)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
name
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|