hyrax-iiif_av 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.
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