Skip to content
Snippets Groups Projects
Unverified Commit 057b6eea authored by Lutz Helm's avatar Lutz Helm Committed by GitHub
Browse files

Merge pull request #3379 from ProjectMirador/fix-av

Fixes an issue with A/V to allow configurable element attribution and crossOrigin options
parents 533239be b99e9902
No related branches found
No related tags found
No related merge requests found
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
manifestId: 'https://iiif.io/api/cookbook/recipe/0014-accompanyingcanvas/manifest.json' manifestId: 'https://iiif.io/api/cookbook/recipe/0014-accompanyingcanvas/manifest.json'
}, },
{ {
manifestId: 'https://iiif-commons.github.io/iiif-av-component/examples/data/iiif/lunchroom-manners.json' manifestId: 'https://preview.iiif.io/cookbook/0219-using-caption-file/recipe/0219-using-caption-file/manifest.json'
} }
], ],
}); });
......
...@@ -7,6 +7,7 @@ function createWrapper(props, suspenseFallback) { ...@@ -7,6 +7,7 @@ function createWrapper(props, suspenseFallback) {
return shallow( return shallow(
<AudioViewer <AudioViewer
classes={{}} classes={{}}
audioOptions={{ crossOrigin: 'anonymous' }}
{...props} {...props}
/>, />,
); );
...@@ -22,8 +23,16 @@ describe('AudioViewer', () => { ...@@ -22,8 +23,16 @@ describe('AudioViewer', () => {
{ getFormat: () => 'video/mp4', id: 2 }, { getFormat: () => 'video/mp4', id: 2 },
], ],
}, true); }, true);
expect(wrapper.contains(<source src="1" type="video/mp4" />)); expect(wrapper.contains(<source src={1} type="video/mp4" />)).toBe(true);
expect(wrapper.contains(<source src="2" type="video/mp4" />)); expect(wrapper.contains(<source src={2} type="video/mp4" />)).toBe(true);
});
it('passes through configurable options', () => {
wrapper = createWrapper({
audioResources: [
{ getFormat: () => 'audio/mp3', id: 1 },
],
}, true);
expect(wrapper.exists('audio[crossOrigin="anonymous"]')).toBe(true); // eslint-disable-line jsx-a11y/media-has-caption
}); });
it('captions', () => { it('captions', () => {
wrapper = createWrapper({ wrapper = createWrapper({
...@@ -31,12 +40,12 @@ describe('AudioViewer', () => { ...@@ -31,12 +40,12 @@ describe('AudioViewer', () => {
{ getFormat: () => 'video/mp4', id: 1 }, { getFormat: () => 'video/mp4', id: 1 },
], ],
captions: [ captions: [
{ getLabel: () => 'English', getProperty: () => 'en', id: 1 }, { getDefaultLabel: () => 'English', getProperty: () => 'en', id: 1 },
{ getLabel: () => 'French', getProperty: () => 'fr', id: 2 }, { getDefaultLabel: () => 'French', getProperty: () => 'fr', id: 2 },
], ],
}, true); }, true);
expect(wrapper.contains(<track src="1" label="English" srcLang="en" />)); expect(wrapper.contains(<track src={1} label="English" srcLang="en" />)).toBe(true);
expect(wrapper.contains(<track src="2" label="French" srcLang="fr" />)); expect(wrapper.contains(<track src={2} label="French" srcLang="fr" />)).toBe(true);
}); });
}); });
}); });
...@@ -7,6 +7,7 @@ function createWrapper(props, suspenseFallback) { ...@@ -7,6 +7,7 @@ function createWrapper(props, suspenseFallback) {
return shallow( return shallow(
<VideoViewer <VideoViewer
classes={{}} classes={{}}
videoOptions={{ crossOrigin: 'anonymous' }}
{...props} {...props}
/>, />,
); );
...@@ -22,21 +23,29 @@ describe('VideoViewer', () => { ...@@ -22,21 +23,29 @@ describe('VideoViewer', () => {
{ getFormat: () => 'video/mp4', id: 2 }, { getFormat: () => 'video/mp4', id: 2 },
], ],
}, true); }, true);
expect(wrapper.contains(<source src="1" type="video/mp4" />)); expect(wrapper.contains(<source src={1} type="video/mp4" />)).toBe(true);
expect(wrapper.contains(<source src="2" type="video/mp4" />)); expect(wrapper.contains(<source src={2} type="video/mp4" />)).toBe(true);
});
it('passes through configurable options', () => {
wrapper = createWrapper({
videoResources: [
{ getFormat: () => 'video/mp4', id: 1 },
],
}, true);
expect(wrapper.exists('video[crossOrigin="anonymous"]')).toBe(true); // eslint-disable-line jsx-a11y/media-has-caption
}); });
it('captions', () => { it('captions', () => {
wrapper = createWrapper({ wrapper = createWrapper({
captions: [ captions: [
{ getLabel: () => 'English', getProperty: () => 'en', id: 1 }, { getDefaultLabel: () => 'English', getProperty: () => 'en', id: 1 },
{ getLabel: () => 'French', getProperty: () => 'fr', id: 2 }, { getDefaultLabel: () => 'French', getProperty: () => 'fr', id: 2 },
], ],
videoResources: [ videoResources: [
{ getFormat: () => 'video/mp4', id: 1 }, { getFormat: () => 'video/mp4', id: 1 },
], ],
}, true); }, true);
expect(wrapper.contains(<track src="1" label="English" srcLang="en" />)); expect(wrapper.contains(<track src={1} label="English" srcLang="en" />)).toBe(true);
expect(wrapper.contains(<track src="2" label="French" srcLang="fr" />)); expect(wrapper.contains(<track src={2} label="French" srcLang="fr" />)).toBe(true);
}); });
}); });
}); });
...@@ -6,16 +6,23 @@ export class AudioViewer extends Component { ...@@ -6,16 +6,23 @@ export class AudioViewer extends Component {
/* eslint-disable jsx-a11y/media-has-caption */ /* eslint-disable jsx-a11y/media-has-caption */
/** */ /** */
render() { render() {
const { classes, audioResources } = this.props; const {
captions, classes, audioOptions, audioResources,
} = this.props;
return ( return (
<div className={classes.container}> <div className={classes.container}>
<audio controls className={classes.audio}> <audio className={classes.audio} {...audioOptions}>
{audioResources.map(audio => ( {audioResources.map(audio => (
<Fragment key={audio.id}> <Fragment key={audio.id}>
<source src={audio.id} type={audio.getFormat()} /> <source src={audio.id} type={audio.getFormat()} />
</Fragment> </Fragment>
))} ))}
{captions.map(caption => (
<Fragment key={caption.id}>
<track src={caption.id} label={caption.getDefaultLabel()} srcLang={caption.getProperty('language')} />
</Fragment>
))}
</audio> </audio>
</div> </div>
); );
...@@ -24,10 +31,14 @@ export class AudioViewer extends Component { ...@@ -24,10 +31,14 @@ export class AudioViewer extends Component {
} }
AudioViewer.propTypes = { AudioViewer.propTypes = {
audioOptions: PropTypes.object, // eslint-disable-line react/forbid-prop-types
audioResources: PropTypes.arrayOf(PropTypes.object), audioResources: PropTypes.arrayOf(PropTypes.object),
captions: PropTypes.arrayOf(PropTypes.object),
classes: PropTypes.objectOf(PropTypes.string).isRequired, classes: PropTypes.objectOf(PropTypes.string).isRequired,
}; };
AudioViewer.defaultProps = { AudioViewer.defaultProps = {
audioOptions: {},
audioResources: [], audioResources: [],
captions: [],
}; };
...@@ -6,10 +6,12 @@ export class VideoViewer extends Component { ...@@ -6,10 +6,12 @@ export class VideoViewer extends Component {
/* eslint-disable jsx-a11y/media-has-caption */ /* eslint-disable jsx-a11y/media-has-caption */
/** */ /** */
render() { render() {
const { captions, classes, videoResources } = this.props; const {
captions, classes, videoOptions, videoResources,
} = this.props;
return ( return (
<div className={classes.container}> <div className={classes.container}>
<video controls className={classes.video}> <video className={classes.video} {...videoOptions}>
{videoResources.map(video => ( {videoResources.map(video => (
<Fragment key={video.id}> <Fragment key={video.id}>
<source src={video.id} type={video.getFormat()} /> <source src={video.id} type={video.getFormat()} />
...@@ -17,7 +19,7 @@ export class VideoViewer extends Component { ...@@ -17,7 +19,7 @@ export class VideoViewer extends Component {
))} ))}
{captions.map(caption => ( {captions.map(caption => (
<Fragment key={caption.id}> <Fragment key={caption.id}>
<track src={caption.id} label={caption.getLabel()} srcLang={caption.getProperty('language')} /> <track src={caption.id} label={caption.getDefaultLabel()} srcLang={caption.getProperty('language')} />
</Fragment> </Fragment>
))} ))}
</video> </video>
...@@ -30,10 +32,12 @@ export class VideoViewer extends Component { ...@@ -30,10 +32,12 @@ export class VideoViewer extends Component {
VideoViewer.propTypes = { VideoViewer.propTypes = {
captions: PropTypes.arrayOf(PropTypes.object), captions: PropTypes.arrayOf(PropTypes.object),
classes: PropTypes.objectOf(PropTypes.string).isRequired, classes: PropTypes.objectOf(PropTypes.string).isRequired,
videoOptions: PropTypes.object, // eslint-disable-line react/forbid-prop-types
videoResources: PropTypes.arrayOf(PropTypes.object), videoResources: PropTypes.arrayOf(PropTypes.object),
}; };
VideoViewer.defaultProps = { VideoViewer.defaultProps = {
captions: [], captions: [],
videoOptions: {},
videoResources: [], videoResources: [],
}; };
...@@ -344,6 +344,14 @@ export default { ...@@ -344,6 +344,14 @@ export default {
windows: true, windows: true,
workspace: true, workspace: true,
}, },
audioOptions: { // Additional props passed to <audio> element
controls: true,
crossOrigin: 'anonymous',
},
videoOptions: { // Additional props passed to <audio> element
controls: true,
crossOrigin: 'anonymous',
},
auth: { auth: {
serviceProfiles: [ serviceProfiles: [
{ profile: 'http://iiif.io/api/auth/1/external', external: true }, { profile: 'http://iiif.io/api/auth/1/external', external: true },
......
...@@ -4,12 +4,14 @@ import { withTranslation } from 'react-i18next'; ...@@ -4,12 +4,14 @@ import { withTranslation } from 'react-i18next';
import { withStyles } from '@material-ui/core'; import { withStyles } from '@material-ui/core';
import { withPlugins } from '../extend/withPlugins'; import { withPlugins } from '../extend/withPlugins';
import { AudioViewer } from '../components/AudioViewer'; import { AudioViewer } from '../components/AudioViewer';
import { getVisibleCanvasAudioResources } from '../state/selectors'; import { getConfig, getVisibleCanvasAudioResources, getVisibleCanvasCaptions } from '../state/selectors';
/** */ /** */
const mapStateToProps = (state, { windowId }) => ( const mapStateToProps = (state, { windowId }) => (
{ {
audioOptions: getConfig(state).audioOptions,
audioResources: getVisibleCanvasAudioResources(state, { windowId }) || [], audioResources: getVisibleCanvasAudioResources(state, { windowId }) || [],
captions: getVisibleCanvasCaptions(state, { windowId }) || [],
} }
); );
......
...@@ -4,12 +4,13 @@ import { withTranslation } from 'react-i18next'; ...@@ -4,12 +4,13 @@ import { withTranslation } from 'react-i18next';
import { withStyles } from '@material-ui/core'; import { withStyles } from '@material-ui/core';
import { withPlugins } from '../extend/withPlugins'; import { withPlugins } from '../extend/withPlugins';
import { VideoViewer } from '../components/VideoViewer'; import { VideoViewer } from '../components/VideoViewer';
import { getVisibleCanvasCaptions, getVisibleCanvasVideoResources } from '../state/selectors'; import { getConfig, getVisibleCanvasCaptions, getVisibleCanvasVideoResources } from '../state/selectors';
/** */ /** */
const mapStateToProps = (state, { windowId }) => ( const mapStateToProps = (state, { windowId }) => (
{ {
captions: getVisibleCanvasCaptions(state, { windowId }) || [], captions: getVisibleCanvasCaptions(state, { windowId }) || [],
videoOptions: getConfig(state).videoOptions,
videoResources: getVisibleCanvasVideoResources(state, { windowId }) || [], videoResources: getVisibleCanvasVideoResources(state, { windowId }) || [],
} }
); );
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment