hyrax-iiif_av 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +33 -0
  5. data/.travis.yml +23 -0
  6. data/Gemfile +41 -0
  7. data/LICENSE +201 -0
  8. data/README.md +43 -0
  9. data/Rakefile +47 -0
  10. data/app/controllers/concerns/hyrax/iiif_av/controller_behavior.rb +63 -0
  11. data/app/helpers/hyrax/iiif_av/iiif_av_helper.rb +40 -0
  12. data/app/presenters/concerns/hyrax/iiif_av/displays_content.rb +123 -0
  13. data/app/presenters/concerns/hyrax/iiif_av/displays_iiif_av.rb +87 -0
  14. data/app/presenters/hyrax/iiif_av/iiif_file_set_presenter.rb +14 -0
  15. data/app/views/hyrax/base/_representative_media.html.erb +27 -0
  16. data/app/views/hyrax/base/iiif_viewers/_universal_viewer.html.erb +4 -0
  17. data/bin/rails +15 -0
  18. data/hyrax-iiif_av.gemspec +33 -0
  19. data/lib/generators/hyrax/iiif_av/add_to_work_type_generator.rb +40 -0
  20. data/lib/generators/hyrax/iiif_av/install_avalon_player_generator.rb +40 -0
  21. data/lib/generators/hyrax/iiif_av/install_generator.rb +12 -0
  22. data/lib/generators/hyrax/iiif_av/templates/AvalonIiifPlayer.js +28 -0
  23. data/lib/generators/hyrax/iiif_av/templates/_avalon.html.erb +23 -0
  24. data/lib/hyrax/iiif_av.rb +8 -0
  25. data/lib/hyrax/iiif_av/engine.rb +12 -0
  26. data/lib/hyrax/iiif_av/manifest_range.rb +13 -0
  27. data/lib/hyrax/iiif_av/spec/shared_specs.rb +4 -0
  28. data/lib/hyrax/iiif_av/spec/shared_specs/controller_behavior.rb +208 -0
  29. data/lib/hyrax/iiif_av/spec/shared_specs/displays_content.rb +166 -0
  30. data/lib/hyrax/iiif_av/spec/shared_specs/displays_iiif_av.rb +129 -0
  31. data/lib/hyrax/iiif_av/version.rb +6 -0
  32. data/lib/tasks/hyrax/iiif_av_tasks.rake +5 -0
  33. metadata +221 -0
@@ -0,0 +1,28 @@
1
+ import React from "react"
2
+ import PropTypes from "prop-types"
3
+ import IIIFPlayer from 'react-iiif-media-player'
4
+
5
+ const config = {
6
+ fetch: {
7
+ options: {
8
+ credentials: 'include'
9
+ }
10
+ }
11
+ };
12
+
13
+ class AvalonIiifPlayer extends React.Component {
14
+ render () {
15
+ return (
16
+ <div>
17
+ <div id="iiif-manifest-url" data-manifest-url={this.props.manifestUrl}></div>
18
+ <IIIFPlayer config={config} />
19
+ </div>
20
+ );
21
+ }
22
+ }
23
+
24
+ AvalonIiifPlayer.propTypes = {
25
+ manifestUrl: PropTypes.string,
26
+ credentials: PropTypes.string
27
+ };
28
+ export default AvalonIiifPlayer
@@ -0,0 +1,23 @@
1
+ <%#
2
+ Copyright 2011-2018, The Trustees of Indiana University and Northwestern
3
+ University. Additional copyright may be held by others, as reflected in
4
+ the commit history.
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ --- END LICENSE_HEADER BLOCK ---
18
+ %>
19
+ <% content_for(:head) do %>
20
+ <%= javascript_pack_tag 'application' %>
21
+ <%= stylesheet_pack_tag 'application' %>
22
+ <% end %>
23
+ <%= react_component("AvalonIiifPlayer", { manifestUrl: main_app.polymorphic_path([main_app, :manifest, presenter], { locale: nil }) }) %>
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require "hyrax/iiif_av/engine"
3
+
4
+ module Hyrax
5
+ module IiifAv
6
+ # Your code goes here...
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module IiifAv
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Hyrax::IiifAv
6
+
7
+ def self.view_path
8
+ paths['app/views'].existent
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module IiifAv
4
+ class ManifestRange
5
+ attr_reader :label, :items
6
+
7
+ def initialize(label:, items: [])
8
+ @label = label
9
+ @items = items
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ require 'hyrax/iiif_av/spec/shared_specs/controller_behavior'
3
+ require 'hyrax/iiif_av/spec/shared_specs/displays_content'
4
+ require 'hyrax/iiif_av/spec/shared_specs/displays_iiif_av'
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2011-2018, The Trustees of Indiana University and Northwestern
3
+ # University. Additional copyright may be held by others, as reflected in
4
+ # the commit history.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # --- END LICENSE_HEADER BLOCK ---
18
+
19
+ RSpec.shared_examples "IiifAv::ControllerBehavior" do
20
+ routes { Rails.application.routes }
21
+ let(:main_app) { Rails.application.routes.url_helpers }
22
+ let(:hyrax) { Hyrax::Engine.routes.url_helpers }
23
+
24
+ describe '#manifest' do
25
+ let(:iiif_version) { 2 }
26
+ let(:presenter) { double("presenter", iiif_version: iiif_version) }
27
+ let(:mime2) { Hyrax::IiifAv::ControllerBehavior::IIIF_PRESENTATION_2_MIME }
28
+ let(:mime3) { Hyrax::IiifAv::ControllerBehavior::IIIF_PRESENTATION_3_MIME }
29
+ let(:manifest_factory2) { instance_double("IIIFManifest::ManifestBuilder", to_h: { test: 'manifest2' }) }
30
+ let(:manifest_factory3) { instance_double("IIIFManifest::V3::ManifestBuilder", to_h: { test: 'manifest3' }) }
31
+
32
+ before do
33
+ allow(controller).to receive(:presenter).and_return(presenter)
34
+ allow(IIIFManifest::ManifestFactory).to receive(:new).with(presenter).and_return(manifest_factory2)
35
+ allow(IIIFManifest::V3::ManifestFactory).to receive(:new).with(presenter).and_return(manifest_factory3)
36
+ request.headers['Accept'] = mime
37
+ end
38
+
39
+ context 'without an enabled presenter' do
40
+ let(:mime) { mime2 }
41
+ let(:presenter) { double("presenter") }
42
+
43
+ it 'returns IIIF V2 manifest' do
44
+ get :manifest, params: { id: 'testwork', format: :json }
45
+ expect(response.headers['Content-Type']).to eq mime
46
+ expect(response.body).to eq "{\"test\":\"manifest2\"}"
47
+ end
48
+ end
49
+
50
+ context 'for request accepting IIIF V2' do
51
+ let(:mime) { mime2 }
52
+
53
+ it 'returns IIIF V2 manifest' do
54
+ get :manifest, params: { id: 'testwork', format: :json }
55
+ expect(response.headers['Content-Type']).to eq mime
56
+ expect(response.body).to eq "{\"test\":\"manifest2\"}"
57
+ end
58
+ end
59
+
60
+ context 'for request accepting IIIF V3' do
61
+ let(:iiif_version) { 3 }
62
+ let(:mime) { mime3 }
63
+
64
+ it 'returns IIIF V3 manifest' do
65
+ get :manifest, params: { id: 'testwork', format: :json }
66
+ expect(response.headers['Content-Type']).to eq mime
67
+ expect(response.body).to eq "{\"test\":\"manifest3\"}"
68
+ end
69
+ end
70
+
71
+ context 'for request without IIIF profile' do
72
+ let(:mime) { "application/json;" }
73
+
74
+ it 'returns IIIF default version manifest' do
75
+ get :manifest, params: { id: 'testwork', format: :json }
76
+ expect(response.headers['Content-Type']).to eq Hyrax::IiifAv::ControllerBehavior::IIIF_PRESENTATION_2_MIME
77
+ # the following code assumes Hyrax::GenericWorksController::IIIF_DEFAULT_VERSION is 2;
78
+ # if this constant changes, this code also needs to be changed accordingly
79
+ expect(response.body).to eq "{\"test\":\"manifest2\"}"
80
+ end
81
+ end
82
+
83
+ context 'for request accepting both IIIF V2 and V3' do
84
+ let(:mime) { "#{mime2},#{mime3}" }
85
+ let(:iiif_version) { 3 }
86
+
87
+ it 'returns manifest with the highest accepted IIIF version' do
88
+ get :manifest, params: { id: 'testwork', format: :json }
89
+ expect(response.headers['Content-Type']).to eq mime3
90
+ expect(response.body).to eq "{\"test\":\"manifest3\"}"
91
+ end
92
+ end
93
+
94
+ context 'for request without accept header' do
95
+ let(:mime) { nil }
96
+
97
+ it 'returns manifest with the highest accepted IIIF version' do
98
+ get :manifest, params: { id: 'testwork', format: :json }
99
+ expect(response.headers['Content-Type']).to eq Hyrax::IiifAv::ControllerBehavior::IIIF_PRESENTATION_2_MIME
100
+ expect(response.body).to eq "{\"test\":\"manifest2\"}"
101
+ end
102
+
103
+ context 'for work with audio representative media' do
104
+ let(:mime) { nil }
105
+ let(:iiif_version) { 3 }
106
+ let(:rep_presenter) { instance_double("Hyrax::FileSetPresenter", audio?: true, video?: false) }
107
+
108
+ before do
109
+ allow(presenter).to receive(:representative_presenter).and_return(rep_presenter)
110
+ end
111
+
112
+ it 'returns manifest with the highest accepted IIIF version' do
113
+ get :manifest, params: { id: 'testwork', format: :json }
114
+ expect(response.headers['Content-Type']).to eq mime3
115
+ expect(response.body).to eq "{\"test\":\"manifest3\"}"
116
+ end
117
+ end
118
+
119
+ context 'for work with video representative media' do
120
+ let(:mime) { nil }
121
+ let(:iiif_version) { 3 }
122
+ let(:rep_presenter) { instance_double("Hyrax::FileSetPresenter", audio?: false, video?: true) }
123
+
124
+ before do
125
+ allow(presenter).to receive(:representative_presenter).and_return(rep_presenter)
126
+ end
127
+
128
+ it 'returns manifest with the highest accepted IIIF version' do
129
+ get :manifest, params: { id: 'testwork', format: :json }
130
+ expect(response.headers['Content-Type']).to eq mime3
131
+ expect(response.body).to eq "{\"test\":\"manifest3\"}"
132
+ end
133
+ end
134
+
135
+ context 'for work with non-av representative media' do
136
+ let(:mime) { nil }
137
+ let(:rep_presenter) { instance_double("Hyrax::FileSetPresenter", audio?: false, video?: false) }
138
+
139
+ before do
140
+ allow(presenter).to receive(:representative_presenter).and_return(rep_presenter)
141
+ end
142
+
143
+ it 'returns manifest with the highest accepted IIIF version' do
144
+ get :manifest, params: { id: 'testwork', format: :json }
145
+ expect(response.headers['Content-Type']).to eq mime2
146
+ expect(response.body).to eq "{\"test\":\"manifest2\"}"
147
+ end
148
+ end
149
+ end
150
+
151
+ context 'for request with default browser accept header' do
152
+ let(:mime) { "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" }
153
+
154
+ it 'returns manifest with the highest accepted IIIF version' do
155
+ get :manifest, params: { id: 'testwork', format: :json }
156
+ expect(response.headers['Content-Type']).to eq Hyrax::IiifAv::ControllerBehavior::IIIF_PRESENTATION_2_MIME
157
+ expect(response.body).to eq "{\"test\":\"manifest2\"}"
158
+ end
159
+
160
+ context 'for work with audio representative media' do
161
+ let(:mime) { nil }
162
+ let(:iiif_version) { 3 }
163
+ let(:rep_presenter) { instance_double("Hyrax::FileSetPresenter", audio?: true, video?: false) }
164
+
165
+ before do
166
+ allow(presenter).to receive(:representative_presenter).and_return(rep_presenter)
167
+ end
168
+
169
+ it 'returns manifest with the highest accepted IIIF version' do
170
+ get :manifest, params: { id: 'testwork', format: :json }
171
+ expect(response.headers['Content-Type']).to eq mime3
172
+ expect(response.body).to eq "{\"test\":\"manifest3\"}"
173
+ end
174
+ end
175
+
176
+ context 'for work with video representative media' do
177
+ let(:mime) { nil }
178
+ let(:iiif_version) { 3 }
179
+ let(:rep_presenter) { instance_double("Hyrax::FileSetPresenter", audio?: false, video?: true) }
180
+
181
+ before do
182
+ allow(presenter).to receive(:representative_presenter).and_return(rep_presenter)
183
+ end
184
+
185
+ it 'returns manifest with the highest accepted IIIF version' do
186
+ get :manifest, params: { id: 'testwork', format: :json }
187
+ expect(response.headers['Content-Type']).to eq mime3
188
+ expect(response.body).to eq "{\"test\":\"manifest3\"}"
189
+ end
190
+ end
191
+
192
+ context 'for work with non-av representative media' do
193
+ let(:mime) { nil }
194
+ let(:rep_presenter) { instance_double("Hyrax::FileSetPresenter", audio?: false, video?: false) }
195
+
196
+ before do
197
+ allow(presenter).to receive(:representative_presenter).and_return(rep_presenter)
198
+ end
199
+
200
+ it 'returns manifest with the highest accepted IIIF version' do
201
+ get :manifest, params: { id: 'testwork', format: :json }
202
+ expect(response.headers['Content-Type']).to eq mime2
203
+ expect(response.body).to eq "{\"test\":\"manifest2\"}"
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples "IiifAv::DisplaysContent" do
4
+ let(:id) { '12345' }
5
+ let(:solr_document) { SolrDocument.new(id: id) }
6
+ let(:request) { instance_double("Request", base_url: 'http://test.host') }
7
+ let(:ability) { instance_double("Ability") }
8
+ let(:presenter) { described_class.new(solr_document, ability, request) }
9
+ let(:read_permission) { true }
10
+ let(:parent_presenter) { double("Hyrax::GenericWorkPresenter", iiif_version: 2) }
11
+ let(:first_title) { 'File Set Title' }
12
+ let(:title) { [first_title] }
13
+
14
+ before do
15
+ allow(ability).to receive(:can?).with(:read, solr_document.id).and_return(read_permission)
16
+ allow(presenter).to receive(:parent).and_return(parent_presenter)
17
+ allow(presenter).to receive(:title).and_return(title)
18
+ end
19
+
20
+ describe '#display_content' do
21
+ it 'responds to #display_content' do
22
+ expect(presenter.respond_to?(:display_content)).to be true
23
+ end
24
+
25
+ let(:content) { presenter.display_content }
26
+
27
+ context 'without a file' do
28
+ let(:id) { 'bogus' }
29
+
30
+ it 'is nil' do
31
+ expect(content).to be_nil
32
+ end
33
+ end
34
+
35
+ context 'with a file' do
36
+ context "when the file is not a known file" do
37
+ it 'is nil' do
38
+ expect(content).to be_nil
39
+ end
40
+ end
41
+
42
+ context "when the file is a sound recording" do
43
+ let(:solr_document) { SolrDocument.new(id: '12345', duration_tesim: 1000) }
44
+ let(:mp3_url) { "http://test.host/downloads/#{solr_document.id}?file=mp3" }
45
+ let(:ogg_url) { "http://test.host/downloads/#{solr_document.id}?file=ogg" }
46
+
47
+ before do
48
+ allow(solr_document).to receive(:audio?).and_return(true)
49
+ end
50
+
51
+ it 'creates an array of content objects' do
52
+ expect(content).to all(be_instance_of IIIFManifest::V3::DisplayContent)
53
+ expect(content.length).to eq 2
54
+ expect(content.map(&:type)).to all(eq 'Sound')
55
+ expect(content.map(&:duration)).to all(eq 1.000)
56
+ expect(content.map(&:label)).to match_array(['mp3', 'ogg'])
57
+ expect(content.map(&:url)).to match_array([mp3_url, ogg_url])
58
+ end
59
+ end
60
+
61
+ context "when the file is a video" do
62
+ let(:solr_document) { SolrDocument.new(id: '12345', width_is: 640, height_is: 480, duration_tesim: 1000) }
63
+ let(:mp4_url) { "http://test.host/downloads/#{id}?file=mp4" }
64
+ let(:webm_url) { "http://test.host/downloads/#{id}?file=webm" }
65
+
66
+ before do
67
+ allow(solr_document).to receive(:video?).and_return(true)
68
+ end
69
+
70
+ it 'creates an array of content objects' do
71
+ expect(content).to all(be_instance_of IIIFManifest::V3::DisplayContent)
72
+ expect(content.length).to eq 2
73
+ expect(content.map(&:type)).to all(eq 'Video')
74
+ expect(content.map(&:width)).to all(eq 640)
75
+ expect(content.map(&:height)).to all(eq 480)
76
+ expect(content.map(&:duration)).to all(eq 1.000)
77
+ expect(content.map(&:label)).to match_array(['mp4', 'webm'])
78
+ expect(content.map(&:url)).to match_array([mp4_url, webm_url])
79
+ end
80
+ end
81
+
82
+ context 'when the file is an audio derivative with metadata' do
83
+ let(:derivatives_metadata) do
84
+ [
85
+ { id: '1', label: 'high', external_file_uri: 'http://test.com/high.mp3' },
86
+ { id: '2', label: 'medium', external_file_uri: 'http://test.com/medium.mp3' }
87
+ ]
88
+ end
89
+ let(:solr_document) { SolrDocument.new(id: '12345', duration_tesim: 1000, derivatives_metadata_ssi: derivatives_metadata.to_json) }
90
+
91
+ before do
92
+ allow(solr_document).to receive(:audio?).and_return(true)
93
+ end
94
+
95
+ it 'creates an array of content objects with metadata' do
96
+ expect(content).to all(be_instance_of IIIFManifest::V3::DisplayContent)
97
+ expect(content.length).to eq 2
98
+ expect(content.map(&:label)).to match_array(['high', 'medium'])
99
+ expect(content.map(&:url)).to match_array(['http://test.com/high.mp3', 'http://test.com/medium.mp3'])
100
+ end
101
+ end
102
+
103
+ context "when the file is an image" do
104
+ before do
105
+ allow(solr_document).to receive(:image?).and_return(true)
106
+ end
107
+
108
+ it 'creates a content object' do
109
+ expect(content).to be_instance_of IIIFManifest::DisplayImage
110
+ expect(content.url).to eq "http://test.host/#{id}/full/600,/0/default.jpg"
111
+ end
112
+
113
+ context 'with custom image size default' do
114
+ let(:custom_image_size) { '666,' }
115
+
116
+ around do |example|
117
+ default_image_size = Hyrax.config.iiif_image_size_default
118
+ Hyrax.config.iiif_image_size_default = custom_image_size
119
+ example.run
120
+ Hyrax.config.iiif_image_size_default = default_image_size
121
+ end
122
+
123
+ it 'creates a content object' do
124
+ expect(content).to be_instance_of IIIFManifest::DisplayImage
125
+ expect(content.url).to eq "http://test.host/#{id}/full/#{custom_image_size}/0/default.jpg"
126
+ end
127
+ end
128
+
129
+ context 'with custom image url builder' do
130
+ let(:custom_builder) do
131
+ ->(file_id, base_url, _size) { "#{base_url}/downloads/#{file_id.split('/').first}" }
132
+ end
133
+
134
+ around do |example|
135
+ default_builder = Hyrax.config.iiif_image_url_builder
136
+ Hyrax.config.iiif_image_url_builder = custom_builder
137
+ example.run
138
+ Hyrax.config.iiif_image_url_builder = default_builder
139
+ end
140
+
141
+ it 'creates a content object' do
142
+ expect(content).to be_instance_of IIIFManifest::DisplayImage
143
+ expect(content.url).to eq "http://test.host/downloads/#{id.split('/').first}"
144
+ end
145
+ end
146
+
147
+ context "when the user doesn't have permission to view the image" do
148
+ let(:read_permission) { false }
149
+
150
+ it 'is nil' do
151
+ expect(content).to be_nil
152
+ end
153
+ end
154
+
155
+ context "when the parent presenter's iiif_version is 3" do
156
+ let(:parent_presenter) { double("Hyrax::GenericWorkPresenter", iiif_version: 3) }
157
+
158
+ it 'creates a V3 content object' do
159
+ expect(content).to be_instance_of IIIFManifest::V3::DisplayContent
160
+ expect(content.url).to eq "http://test.host/#{id}/full/600,/0/default.jpg"
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end