asset_host_core 2.0.0.beta

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.
Files changed (199) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.markdown +138 -0
  3. data/Rakefile +11 -0
  4. data/app/assets/images/asset_host_core/alert-overlay.png +0 -0
  5. data/app/assets/images/asset_host_core/arrow-left.gif +0 -0
  6. data/app/assets/images/asset_host_core/arrow-right.gif +0 -0
  7. data/app/assets/images/asset_host_core/fallback-img-rect.png +0 -0
  8. data/app/assets/images/asset_host_core/videoplayer-play.png +0 -0
  9. data/app/assets/images/asset_host_core/x.png +0 -0
  10. data/app/assets/javascripts/asset_host_core/admin/assets.js.coffee +221 -0
  11. data/app/assets/javascripts/asset_host_core/application.js +20 -0
  12. data/app/assets/javascripts/asset_host_core/assetadmin.js.coffee +56 -0
  13. data/app/assets/javascripts/asset_host_core/assethost.js.coffee.erb +17 -0
  14. data/app/assets/javascripts/asset_host_core/browserui.js.coffee +139 -0
  15. data/app/assets/javascripts/asset_host_core/chooserui.js.coffee +381 -0
  16. data/app/assets/javascripts/asset_host_core/client.js.coffee +29 -0
  17. data/app/assets/javascripts/asset_host_core/clients/BrightcoveVideo.js.coffee +64 -0
  18. data/app/assets/javascripts/asset_host_core/clients/templates/brightcove_embed.jst.eco +18 -0
  19. data/app/assets/javascripts/asset_host_core/clients/templates/vimeo_embed.jst.eco +1 -0
  20. data/app/assets/javascripts/asset_host_core/clients/templates/youtube_embed.jst.eco +1 -0
  21. data/app/assets/javascripts/asset_host_core/clients/vimeo_video.js.coffee +21 -0
  22. data/app/assets/javascripts/asset_host_core/clients/youtube_video.js.coffee +21 -0
  23. data/app/assets/javascripts/asset_host_core/cmsplugin.js.coffee +235 -0
  24. data/app/assets/javascripts/asset_host_core/models.js.coffee +586 -0
  25. data/app/assets/javascripts/asset_host_core/railsCMS.js.coffee +141 -0
  26. data/app/assets/javascripts/asset_host_core/slideshow.js.coffee +428 -0
  27. data/app/assets/javascripts/asset_host_core/templates/after_upload_button.jst.eco +3 -0
  28. data/app/assets/javascripts/asset_host_core/templates/asset_drop_asset.jst.eco +4 -0
  29. data/app/assets/javascripts/asset_host_core/templates/asset_modal.jst.eco +13 -0
  30. data/app/assets/javascripts/asset_host_core/templates/asset_preview.jst.eco +35 -0
  31. data/app/assets/javascripts/asset_host_core/templates/asset_search.jst.eco +2 -0
  32. data/app/assets/javascripts/asset_host_core/templates/browser_asset.jst.eco +1 -0
  33. data/app/assets/javascripts/asset_host_core/templates/browser_asset_tip.jst.eco +3 -0
  34. data/app/assets/javascripts/asset_host_core/templates/edit_modal.jst.eco +40 -0
  35. data/app/assets/javascripts/asset_host_core/templates/import_help.jst.eco +59 -0
  36. data/app/assets/javascripts/asset_host_core/templates/pagination_link.jst.eco +1 -0
  37. data/app/assets/javascripts/asset_host_core/templates/pagination_links.jst.eco +13 -0
  38. data/app/assets/javascripts/asset_host_core/templates/queued_file.jst.eco +11 -0
  39. data/app/assets/javascripts/asset_host_core/templates/save_and_close_view.jst.eco +4 -0
  40. data/app/assets/javascripts/asset_host_core/templates/upload_all_button.jst.eco +4 -0
  41. data/app/assets/javascripts/asset_host_core/templates/url_input.jst.eco +8 -0
  42. data/app/assets/stylesheets/asset_host_core/application.css.scss +384 -0
  43. data/app/assets/stylesheets/asset_host_core/jquery-ui.css +105 -0
  44. data/app/assets/stylesheets/asset_host_core/public.css.scss +204 -0
  45. data/app/assets/stylesheets/asset_host_core/slidetest.css.scss +93 -0
  46. data/app/controllers/asset_host_core/admin/api_users_controller.rb +72 -0
  47. data/app/controllers/asset_host_core/admin/assets_controller.rb +140 -0
  48. data/app/controllers/asset_host_core/admin/base_controller.rb +36 -0
  49. data/app/controllers/asset_host_core/admin/home_controller.rb +13 -0
  50. data/app/controllers/asset_host_core/admin/outputs_controller.rb +55 -0
  51. data/app/controllers/asset_host_core/api/assets_controller.rb +110 -0
  52. data/app/controllers/asset_host_core/api/base_controller.rb +43 -0
  53. data/app/controllers/asset_host_core/api/outputs_controller.rb +33 -0
  54. data/app/controllers/asset_host_core/application_controller.rb +43 -0
  55. data/app/controllers/asset_host_core/public_controller.rb +104 -0
  56. data/app/models/asset_host_core/api_user.rb +44 -0
  57. data/app/models/asset_host_core/api_user_permission.rb +6 -0
  58. data/app/models/asset_host_core/asset.rb +265 -0
  59. data/app/models/asset_host_core/asset_output.rb +69 -0
  60. data/app/models/asset_host_core/brightcove_video.rb +20 -0
  61. data/app/models/asset_host_core/output.rb +52 -0
  62. data/app/models/asset_host_core/permission.rb +19 -0
  63. data/app/models/asset_host_core/video.rb +8 -0
  64. data/app/models/asset_host_core/vimeo_video.rb +17 -0
  65. data/app/models/asset_host_core/youtube_video.rb +17 -0
  66. data/app/views/asset_host_core/admin/api_users/_form_fields.html.erb +5 -0
  67. data/app/views/asset_host_core/admin/api_users/edit.html.erb +26 -0
  68. data/app/views/asset_host_core/admin/api_users/index.html.erb +31 -0
  69. data/app/views/asset_host_core/admin/api_users/new.html.erb +17 -0
  70. data/app/views/asset_host_core/admin/api_users/show.html.erb +23 -0
  71. data/app/views/asset_host_core/admin/assets/index.html.erb +19 -0
  72. data/app/views/asset_host_core/admin/assets/metadata.html.erb +24 -0
  73. data/app/views/asset_host_core/admin/assets/show.html.erb +86 -0
  74. data/app/views/asset_host_core/admin/home/chooser.html.erb +49 -0
  75. data/app/views/asset_host_core/admin/outputs/_form_fields.html.erb +5 -0
  76. data/app/views/asset_host_core/admin/outputs/edit.html.erb +26 -0
  77. data/app/views/asset_host_core/admin/outputs/index.html.erb +27 -0
  78. data/app/views/asset_host_core/admin/outputs/new.html.erb +13 -0
  79. data/app/views/asset_host_core/admin/outputs/show.html.erb +17 -0
  80. data/app/views/asset_host_core/shared/_footerjs.html.erb +3 -0
  81. data/app/views/asset_host_core/shared/_navbar.html.erb +28 -0
  82. data/app/views/kaminari/_first_page.html.erb +3 -0
  83. data/app/views/kaminari/_gap.html.erb +3 -0
  84. data/app/views/kaminari/_last_page.html.erb +3 -0
  85. data/app/views/kaminari/_next_page.html.erb +3 -0
  86. data/app/views/kaminari/_page.html.erb +3 -0
  87. data/app/views/kaminari/_paginator.html.erb +17 -0
  88. data/app/views/kaminari/_prev_page.html.erb +3 -0
  89. data/app/views/layouts/asset_host_core/application.html.erb +54 -0
  90. data/app/views/layouts/asset_host_core/full_width.html.erb +32 -0
  91. data/app/views/layouts/asset_host_core/minimal.html.erb +45 -0
  92. data/config/initializers/simple_form.rb +142 -0
  93. data/config/initializers/simple_form_bootstrap.rb +45 -0
  94. data/config/locales/simple_form.en.yml +26 -0
  95. data/config/routes.rb +49 -0
  96. data/lib/asset_host_core.rb +38 -0
  97. data/lib/asset_host_core/config.rb +39 -0
  98. data/lib/asset_host_core/engine.rb +94 -0
  99. data/lib/asset_host_core/loaders.rb +34 -0
  100. data/lib/asset_host_core/loaders/asset_host.rb +30 -0
  101. data/lib/asset_host_core/loaders/base.rb +22 -0
  102. data/lib/asset_host_core/loaders/brightcove.rb +67 -0
  103. data/lib/asset_host_core/loaders/flickr.rb +114 -0
  104. data/lib/asset_host_core/loaders/url.rb +59 -0
  105. data/lib/asset_host_core/loaders/vimeo.rb +76 -0
  106. data/lib/asset_host_core/loaders/youtube.rb +90 -0
  107. data/lib/asset_host_core/model_methods.rb +61 -0
  108. data/lib/asset_host_core/paperclip.rb +4 -0
  109. data/lib/asset_host_core/paperclip/asset_thumbnail.rb +92 -0
  110. data/lib/asset_host_core/paperclip/attachment.rb +206 -0
  111. data/lib/asset_host_core/paperclip/trimmer.rb +33 -0
  112. data/lib/asset_host_core/resque_job.rb +13 -0
  113. data/lib/asset_host_core/version.rb +3 -0
  114. data/lib/tasks/asset_host_core_tasks.rake +4 -0
  115. data/spec/controllers/admin/api_users_controller_spec.rb +21 -0
  116. data/spec/controllers/admin/assets_controller_spec.rb +59 -0
  117. data/spec/controllers/admin/home_controller_spec.rb +4 -0
  118. data/spec/controllers/admin/outputs_controller_spec.rb +4 -0
  119. data/spec/controllers/api/assets_controller_spec.rb +133 -0
  120. data/spec/controllers/api/outputs_controller_spec.rb +51 -0
  121. data/spec/controllers/public_controller_spec.rb +4 -0
  122. data/spec/factories.rb +39 -0
  123. data/spec/features/api_users_spec.rb +78 -0
  124. data/spec/fixtures/api/brightcove/video.json +137 -0
  125. data/spec/fixtures/api/flickr/photos_getInfo.json +78 -0
  126. data/spec/fixtures/api/flickr/photos_getSizes.json +82 -0
  127. data/spec/fixtures/api/flickr/photos_licenses_getInfo.json +52 -0
  128. data/spec/fixtures/api/vimeo/video.json +28 -0
  129. data/spec/fixtures/api/youtube/discovery.json +5190 -0
  130. data/spec/fixtures/api/youtube/video.json +44 -0
  131. data/spec/fixtures/images/chipmunk.jpg +0 -0
  132. data/spec/fixtures/images/dude.jpg +0 -0
  133. data/spec/fixtures/images/ernie.jpg +0 -0
  134. data/spec/fixtures/images/fry.png +0 -0
  135. data/spec/fixtures/images/hat.jpg +0 -0
  136. data/spec/fixtures/images/spongebob.png +0 -0
  137. data/spec/fixtures/images/stars.jpg +0 -0
  138. data/spec/internal/app/controllers/application_controller.rb +16 -0
  139. data/spec/internal/app/controllers/sessions_controller.rb +24 -0
  140. data/spec/internal/app/models/user.rb +10 -0
  141. data/spec/internal/app/views/sessions/new.html.erb +14 -0
  142. data/spec/internal/config/database.yml +3 -0
  143. data/spec/internal/config/initializers/assethost_config.rb +57 -0
  144. data/spec/internal/config/routes.rb +7 -0
  145. data/spec/internal/db/combustion_test.sqlite +0 -0
  146. data/spec/internal/db/schema.rb +106 -0
  147. data/spec/internal/log/test.log +14769 -0
  148. data/spec/internal/public/favicon.ico +0 -0
  149. data/spec/internal/public/images/1_27f7745237849975ca90591c1fba5934_original. +0 -0
  150. data/spec/internal/public/images/1_7d33319deca787d5bb3f62ff06563ad2_original. +0 -0
  151. data/spec/internal/public/images/1_b6d48c8b1286104ce76649731e09645f_original. +0 -0
  152. data/spec/internal/public/images/1_b6d48c8b1286104ce76649731e09645f_original.jpg +0 -0
  153. data/spec/internal/public/images/1_b6d48c8b1286104ce76649731e09645f_original.txt +0 -0
  154. data/spec/internal/public/images/1_e179cbd27e07cb55042d0db36cdac095_original. +0 -0
  155. data/spec/internal/public/images/1_e669edd3dfd74be66fc38416e82e3a37_original. +0 -0
  156. data/spec/lib/asset_host_core/loaders/asset_host_spec.rb +33 -0
  157. data/spec/lib/asset_host_core/loaders/brightcove_spec.rb +51 -0
  158. data/spec/lib/asset_host_core/loaders/flickr_spec.rb +72 -0
  159. data/spec/lib/asset_host_core/loaders/url_spec.rb +42 -0
  160. data/spec/lib/asset_host_core/loaders/vimeo_spec.rb +51 -0
  161. data/spec/lib/asset_host_core/loaders/youtube_spec.rb +73 -0
  162. data/spec/lib/asset_host_core/loaders_spec.rb +4 -0
  163. data/spec/lib/asset_host_core/model_methods_spec.rb +4 -0
  164. data/spec/lib/asset_host_core/paperclip/asset_thumbnail_spec.rb +4 -0
  165. data/spec/lib/asset_host_core/paperclip/attachment_spec.rb +4 -0
  166. data/spec/lib/asset_host_core/resque_job_spec.rb +4 -0
  167. data/spec/lib/asset_host_core_spec.rb +4 -0
  168. data/spec/models/api_user_spec.rb +58 -0
  169. data/spec/models/asset_output_spec.rb +4 -0
  170. data/spec/models/asset_spec.rb +4 -0
  171. data/spec/models/output_spec.rb +4 -0
  172. data/spec/models/permission_spec.rb +4 -0
  173. data/spec/spec_helper.rb +30 -0
  174. data/spec/support/fixture_loader.rb +9 -0
  175. data/spec/support/param_helper.rb +14 -0
  176. data/spec/support/permission_matcher.rb +17 -0
  177. data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  178. data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  179. data/vendor/assets/images/jquery-ui/ui-bg_flat_10_000000_40x100.png +0 -0
  180. data/vendor/assets/images/jquery-ui/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  181. data/vendor/assets/images/jquery-ui/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  182. data/vendor/assets/images/jquery-ui/ui-bg_glass_65_ffffff_1x400.png +0 -0
  183. data/vendor/assets/images/jquery-ui/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  184. data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  185. data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  186. data/vendor/assets/images/jquery-ui/ui-icons_222222_256x240.png +0 -0
  187. data/vendor/assets/images/jquery-ui/ui-icons_228ef1_256x240.png +0 -0
  188. data/vendor/assets/images/jquery-ui/ui-icons_ef8c08_256x240.png +0 -0
  189. data/vendor/assets/images/jquery-ui/ui-icons_ffd27a_256x240.png +0 -0
  190. data/vendor/assets/images/jquery-ui/ui-icons_ffffff_256x240.png +0 -0
  191. data/vendor/assets/javascripts/backbone.js +1158 -0
  192. data/vendor/assets/javascripts/backbone.modelbinding.js +475 -0
  193. data/vendor/assets/javascripts/exif.js +695 -0
  194. data/vendor/assets/javascripts/jquery-ui.js +5614 -0
  195. data/vendor/assets/javascripts/simplemodal.js +698 -0
  196. data/vendor/assets/javascripts/spin.jquery.js +81 -0
  197. data/vendor/assets/javascripts/spin.min.js +1 -0
  198. data/vendor/assets/javascripts/underscore.min.js +1 -0
  199. metadata +658 -0
@@ -0,0 +1,59 @@
1
+ require 'mime/types'
2
+ require 'net/http'
3
+ require 'cgi'
4
+
5
+ module AssetHostCore
6
+ module Loaders
7
+ class URL < Base
8
+ SOURCE = "URL"
9
+
10
+ #-----------
11
+
12
+ def self.build_from_url(url)
13
+ uri = URI.parse(url)
14
+ return nil unless uri.is_a?(URI::HTTP)
15
+
16
+ response = Net::HTTP.get_response(uri)
17
+
18
+ # Check that it's actually an image we're grabbing
19
+ if response.content_type.match(/image/)
20
+ self.new(url: url, id: url)
21
+ else
22
+ nil
23
+ end
24
+ end
25
+
26
+ #----------
27
+
28
+ def load
29
+ filename = File.basename(@url)
30
+
31
+ # build asset
32
+ asset = AssetHostCore::Asset.new(
33
+ :title => filename,
34
+ :url => @url,
35
+ :notes => "Fetched from URL: #{@url}",
36
+ :image => image_file
37
+ )
38
+
39
+ asset.save!
40
+ asset
41
+ end
42
+
43
+ #----------
44
+
45
+ private
46
+
47
+ def image_file
48
+ @image_file ||= begin
49
+ response = open(@url)
50
+
51
+ tempfile = Tempfile.new('ah-brightcove', encoding: "ascii-8bit")
52
+ tempfile.write response.read
53
+
54
+ tempfile
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,76 @@
1
+ module AssetHostCore
2
+ module Loaders
3
+ class Vimeo < Base
4
+ SOURCE = "Vimeo"
5
+
6
+ #----------------
7
+
8
+ def self.build_from_url(url)
9
+ # Don't need a key just to read from the public API
10
+ url.match(/vimeo\.com\/(?<id>\d+)/i) do |m|
11
+ self.new(url: url, id: m[:id])
12
+ end
13
+ end
14
+
15
+ #----------------
16
+
17
+ def load
18
+ data = fetch_data
19
+
20
+ video = data[0]
21
+ return nil if !video
22
+
23
+ native = AssetHostCore::VimeoVideo.create(
24
+ :videoid => video["id"]
25
+ )
26
+
27
+ asset = AssetHostCore::Asset.new(
28
+ :image => image_file(video["thumbnail_large"]),
29
+ :title => video["title"],
30
+ :caption => video["title"], # Description is too long
31
+ :url => @url,
32
+ :owner => "#{video["user_name"]} (via Vimeo)",
33
+ :notes => "Imported from Vimeo: #{@url}",
34
+ :image_taken => video["upload_date"],
35
+ :native => native
36
+ )
37
+
38
+ asset.save!
39
+ asset
40
+ end
41
+
42
+ #----------------
43
+
44
+ def fetch_data
45
+ response = connection.get do |request|
46
+ request.url "video/#{@id}.json"
47
+ end
48
+
49
+ response.body
50
+ end
51
+
52
+
53
+ private
54
+
55
+ def image_file(url)
56
+ @image_file ||= begin
57
+ response = open(url)
58
+
59
+ tempfile = Tempfile.new('ah-vimeo', encoding: "ascii-8bit")
60
+ tempfile.write response.read
61
+
62
+ tempfile
63
+ end
64
+ end
65
+
66
+ def connection
67
+ @connection ||= begin
68
+ Faraday.new('http://vimeo.com/api/v2') do |conn|
69
+ conn.response :json
70
+ conn.adapter Faraday.default_adapter
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,90 @@
1
+ module AssetHostCore
2
+ module Loaders
3
+ class YouTube < Base
4
+ SOURCE = "YouTube"
5
+
6
+ #----------------
7
+
8
+ def self.build_from_url(url)
9
+ return nil if AssetHostCore.config.google_api_key.blank?
10
+
11
+ url.match(/youtube\.com\/watch\?.*v=(?<id>[\w-]+)/i) do |m|
12
+ self.new(url: url, id: m[:id])
13
+ end
14
+ end
15
+
16
+ #----------------
17
+
18
+ def load
19
+ data = fetch_data
20
+
21
+ video = data["items"][0]
22
+ return nil if !video
23
+
24
+ snippet = video["snippet"]
25
+ return nil if !snippet
26
+
27
+ native = AssetHostCore::YoutubeVideo.create(
28
+ :videoid => video["id"]
29
+ )
30
+
31
+ thumbnail = snippet["thumbnails"]["maxres"] ||
32
+ snippet["thumbnails"]["high"]
33
+
34
+ asset = AssetHostCore::Asset.new(
35
+ :image => image_file(thumbnail["url"]),
36
+ :title => snippet["title"],
37
+ :caption => snippet["description"],
38
+ :url => @url,
39
+ :owner => "#{snippet["channelTitle"]} (via YouTube)",
40
+ :notes => "Imported from YouTube: #{@url}",
41
+ :image_taken => snippet["publishedAt"],
42
+ :native => native
43
+ )
44
+
45
+ asset.save!
46
+ asset
47
+ end
48
+
49
+ #----------------
50
+
51
+ def fetch_data
52
+ response = client.execute!(
53
+ :api_method => youtube.videos.list,
54
+ :parameters => {
55
+ :id => @id,
56
+ :part => 'id,snippet,contentDetails,player'
57
+ }
58
+ )
59
+
60
+ JSON.parse(response.body)
61
+ end
62
+
63
+ #----------------
64
+
65
+ private
66
+
67
+ def image_file(url)
68
+ @image_file ||= begin
69
+ response = open(url)
70
+
71
+ tempfile = Tempfile.new('ah-youtube', encoding: "ascii-8bit")
72
+ tempfile.write response.read
73
+
74
+ @url.match(/ah-noTrim/) ? tempfile : Paperclip::Trimmer.make(tempfile)
75
+ end
76
+ end
77
+
78
+ def client
79
+ @client ||= Google::APIClient.new(
80
+ :key => AssetHostCore.config.google_api_key,
81
+ :authorization => nil
82
+ )
83
+ end
84
+
85
+ def youtube
86
+ @youtube ||= client.discovered_api('youtube', 'v3')
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,61 @@
1
+ require 'mini_exiftool'
2
+
3
+ module AssetHostCore
4
+ module ModelMethods
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def treat_as_image_asset(name)
9
+ include InstanceMethodsOnActivation
10
+
11
+ attachment_definitions[name][:delayed] = true
12
+
13
+ define_method "write_exif_data_for_#{name}" do
14
+ if self.send("#{name}").dirty?
15
+ # need to extract dimensions from the attachment
16
+ self.attachment_for(name).write_exif_data
17
+ end
18
+ end
19
+
20
+ define_method "enqueue_delayed_processing_for_#{name}" do
21
+ # we render on two things: image fingerprint changed, or image gravity changed
22
+ if self.previous_changes.include?("image_fingerprint") || self.previous_changes.include?("image_gravity")
23
+ self.attachment_for(name).enqueue
24
+ end
25
+ end
26
+
27
+ # register our event handler
28
+ before_save :"write_exif_data_for_#{name}"
29
+
30
+ if respond_to?(:after_commit)
31
+ after_commit :"enqueue_delayed_processing_for_#{name}"
32
+ else
33
+ after_save :"enqueue_delayed_processing_for_#{name}"
34
+ end
35
+
36
+ # -- Style fingerprint interpolation -- #
37
+
38
+ ::Paperclip.interpolates "sprint" do |attachment, style_name|
39
+ if style_name == :original
40
+ 'original'
41
+ elsif ao = attachment.instance.output_by_style(style_name)
42
+ ao.fingerprint
43
+ else
44
+ nil
45
+ end
46
+ end
47
+ end
48
+
49
+
50
+ module InstanceMethodsOnActivation
51
+ # borrowed from delayed_paperclip... combines with [:delayed] above to turn off the inline processing
52
+ def attachment_for(name)
53
+ @_paperclip_attachments ||= {}
54
+ @_paperclip_attachments[name] ||= ::Paperclip::Attachment.new(name, self, self.class.attachment_definitions[name]).tap do |a|
55
+ a.post_processing = false if self.class.attachment_definitions[name][:delayed]
56
+ end
57
+ end
58
+ end
59
+ end # ClassMethods
60
+ end # Paperclip
61
+ end # AssetHostCore
@@ -0,0 +1,4 @@
1
+ require 'paperclip'
2
+ require 'asset_host_core/paperclip/attachment'
3
+ require 'asset_host_core/paperclip/asset_thumbnail'
4
+ require 'asset_host_core/paperclip/trimmer'
@@ -0,0 +1,92 @@
1
+ module Paperclip
2
+ class AssetThumbnail < Paperclip::Thumbnail
3
+ attr_accessor :prerender, :output, :asset
4
+
5
+ def initialize(file, options={}, attachment=nil)
6
+ @prerender = options[:prerender]
7
+ @size = options[:size]
8
+ @output = options[:output]
9
+ @asset = attachment ? attachment.instance : nil
10
+ @attachment = attachment
11
+
12
+ super
13
+
14
+ gravity = @asset.image_gravity? ? @asset.image_gravity : "Center"
15
+
16
+ @convert_options = [
17
+ "-gravity #{gravity}",
18
+ "-strip",
19
+ "-quality 95",
20
+ @convert_options
21
+ ].flatten.compact
22
+ end
23
+
24
+ # Perform processing, if prerender == true or we've had to render
25
+ # this output before. Afterward, update our AssetOutput entry
26
+ def make
27
+ # do we have an AssetOutput already?
28
+ ao = @asset.outputs.where(output_id: @output).first
29
+
30
+ dst = nil
31
+
32
+ if @prerender || ao
33
+ if !ao
34
+ # register empty AssetObject to denote processing
35
+ ao = @asset.outputs.create(
36
+ :output_id => @output,
37
+ :image_fingerprint => @asset.image_fingerprint
38
+ )
39
+ end
40
+
41
+ if @size =~ /(\d+)?x?(\d+)?([\#>])?$/ && $~[3] == "#"
42
+ # crop... scale using dimensions as minimums, then crop to dimensions
43
+ scale = "-scale #{$~[1]}x#{$~[2]}^"
44
+ crop = "-crop #{$~[1]}x#{$~[2]}+0+0"
45
+
46
+ @convert_options = [
47
+ @convert_options.shift,
48
+ scale,
49
+ crop,
50
+ @convert_options
51
+ ].flatten
52
+ else
53
+ # don't crop
54
+ scale = "-scale '#{$~[1]}x#{$~[2]}#{$~[3]}'"
55
+ @convert_options = [scale, @convert_options].flatten
56
+ end
57
+
58
+ # call thumbnail generator
59
+ dst = super
60
+
61
+ # need to get dimensions
62
+ width = height = nil
63
+
64
+ begin
65
+ geo = Geometry.from_file(dst.path)
66
+ width = geo.width.to_i
67
+ height = geo.height.to_i
68
+ rescue NotIdentifiedByImageMagickError => e
69
+ # hmmm... do nothing?
70
+ end
71
+
72
+ # get fingerprint
73
+ print = Digest::MD5.hexdigest(dst.read)
74
+ dst.rewind if dst.respond_to?(:rewind)
75
+
76
+ ao.attributes = {
77
+ :fingerprint => print,
78
+ :width => width,
79
+ :height => height,
80
+ :image_fingerprint => @asset.image_fingerprint
81
+ }
82
+
83
+ ao.save
84
+
85
+ # just to be safe...
86
+ @asset.outputs(true)
87
+ end
88
+
89
+ dst
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,206 @@
1
+ module Paperclip
2
+ class Attachment
3
+ # Overwrite styles loader to allow caching despite dynamic loading
4
+ def styles
5
+ styling_option = @options[:styles]
6
+
7
+ if !@normalized_styles
8
+ @normalized_styles = ActiveSupport::OrderedHash.new
9
+ styling_option.call(self).each do |name, args|
10
+ @normalized_styles[name] = Paperclip::Style.new(name, args.dup, self)
11
+ end
12
+ end
13
+ @normalized_styles
14
+ end
15
+
16
+
17
+ # overwrite to only delete original when clear() is called. styles will
18
+ # be deleted by the thumbnailer
19
+ def queue_existing_for_delete #:nodoc:
20
+ return unless file?
21
+
22
+ @queued_for_delete = [path(:original)]
23
+
24
+ instance_write(:file_name, nil)
25
+ instance_write(:content_type, nil)
26
+ instance_write(:file_size, nil)
27
+ instance_write(:updated_at, nil)
28
+ end
29
+
30
+
31
+ #----------
32
+
33
+ def delete_path(path)
34
+ @queued_for_delete = [ path ]
35
+ self.flush_deletes
36
+ end
37
+
38
+ #----------
39
+
40
+ def enqueue
41
+ # queue up any outputs that a) already exist or b) are set to prerender
42
+ styles = [
43
+ AssetHostCore::Output.where(prerender: true).map(&:code_sym),
44
+ self.instance.outputs.map { |ao| ao.output.code_sym }
45
+ ].flatten.uniq
46
+
47
+ enqueue_styles(*styles)
48
+ end
49
+
50
+ def enqueue_styles(*styles)
51
+ Resque.enqueue(
52
+ AssetHostCore::ResqueJob,
53
+ self.instance.class.name,
54
+ self.instance.id,
55
+ self.name,
56
+ styles
57
+ )
58
+ end
59
+
60
+ #----------
61
+
62
+ def width(style = default_style)
63
+ return nil if !self.instance_read("width")
64
+
65
+ if s = self.styles[style]
66
+ # load dimensions
67
+ if ao = self.instance.output_by_style(style)
68
+ return ao.width
69
+ else
70
+ # TODO: Need to add code to guess dimensions if we don't yet have an output
71
+ g = Paperclip::Geometry.parse(s.processor_options[:size])
72
+ if g.modifier == '#'
73
+ if g.square?
74
+ # match w/h from style
75
+ return g.width.to_i
76
+ else
77
+ return g.width.to_i
78
+ end
79
+ end
80
+
81
+ factor = self._compute_style_ratio(s)
82
+ width = ((self.instance_read("width") || 0) * factor).round
83
+ return width < self.instance_read("width") ? width : self.instance_read("width")
84
+ end
85
+ end
86
+
87
+ nil
88
+ end
89
+
90
+ #----------
91
+
92
+ def height(style = default_style)
93
+ return nil if !self.instance_read("height")
94
+
95
+ if s = self.styles[style]
96
+ # load dimensions
97
+ if ao = self.instance.output_by_style(style)
98
+ return ao.height
99
+ else
100
+ # TODO: Need to add code to guess dimensions if we don't yet have an output
101
+ g = Paperclip::Geometry.parse(s.processor_options[:size])
102
+ if g.modifier == '#'
103
+ # match w/h from style
104
+ return g.height.to_i
105
+ end
106
+
107
+ factor = self._compute_style_ratio(s)
108
+ height = ((self.instance_read("height") || 0) * factor).round
109
+
110
+ return height < self.instance_read("height") ? height : self.instance_read("height")
111
+
112
+ end
113
+ end
114
+
115
+ nil
116
+ end
117
+
118
+ #----------
119
+
120
+ def isPortrait?
121
+ w = self.instance_read("width")
122
+ h = self.instance_read("height")
123
+
124
+ h > w
125
+ end
126
+
127
+ #----------
128
+
129
+ def _compute_style_ratio(style)
130
+ w = self.instance_read("width")
131
+ h = self.instance_read("height")
132
+
133
+ return 0 if !w || !h
134
+
135
+ g = Paperclip::Geometry.parse(style.processor_options[:size])
136
+ ratio = Paperclip::Geometry.new( g.width/w, g.height/h )
137
+
138
+ # we need to compute off the smaller number
139
+ factor = (ratio.width > ratio.height) ? ratio.height : ratio.width
140
+ end
141
+
142
+ #----------
143
+
144
+ def tags(args = {})
145
+ tags = {}
146
+
147
+ self.styles.each do |style,v|
148
+ tags[style] = self.tag(style,args)
149
+ end
150
+
151
+ tags
152
+ end
153
+
154
+ #----------
155
+
156
+ def tag(style = default_style, args={})
157
+ s = self.styles[style.to_sym]
158
+ return nil if !s
159
+
160
+ if (s.instance_variable_get :@other_args)[:rich] && self.instance.native
161
+ args = args.merge(self.instance.native.attrs)
162
+ end
163
+
164
+ htmlargs = args.collect { |k,v| %Q!#{k}="#{v}"! }.join(" ")
165
+
166
+ %Q(<img src="#{self.url(style)}" width="#{self.width(style)}" height="#{self.height(style)}" alt="#{self.instance.title.to_s.gsub('"', ERB::Util::HTML_ESCAPE['"'])}" #{htmlargs}/>).html_safe
167
+ end
168
+
169
+ #----------
170
+
171
+ def write_exif_data
172
+ return unless @queued_for_write[:original]
173
+
174
+ p = ::MiniExiftool.new(@queued_for_write[:original].path)
175
+
176
+ # -- determine metadata -- #
177
+
178
+ if p.credit =~ /Getty Images/
179
+ # smart import for Getty Images photos
180
+ copyright = [p.by_line,p.credit].join("/")
181
+ title = p.headline
182
+ description = p.description
183
+
184
+ elsif p.credit =~ /AP/
185
+ # smart import for AP photos
186
+ copyright = [p.by_line,p.credit].join("/")
187
+ title = p.title
188
+ description = p.description
189
+
190
+ else
191
+ copyright = p.byline || p.credit
192
+ title = p.title
193
+ description = p.description
194
+ end
195
+
196
+ instance_write(:width, p.image_width)
197
+ instance_write(:height, p.image_height)
198
+ instance_write(:title, title)
199
+ instance_write(:description, description)
200
+ instance_write(:copyright, copyright)
201
+ instance_write(:taken, p.datetime_original)
202
+
203
+ true
204
+ end
205
+ end
206
+ end