mirror of https://github.com/perkeep/perkeep.git
server/perkeepd/ui: Implement blob detail audio
Summary: Implement audio player using web ui. Add new blob aspect for audio files. Test Plan: 1. Upload browser compatible audio to perkeep 2. Navigate to web UI 3. Open the audio blob's detail page 4. Play audio Before: Audio cannot be played using web ui After: Compatible audio can be played and paused from the detail page Audio title and artist are shown if available Issue: #202 Change-Id: Icd450aaa16e9e622a677c23f4f1f699784453dbc
This commit is contained in:
parent
a838f6e15a
commit
99f2e2dcb4
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright 2019 The Perkeep Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.cam-detail-audio {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
background: black;
|
||||
|
||||
&-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&-player {
|
||||
padding: 12px;
|
||||
font-size: 0;
|
||||
flex-grow: 1;
|
||||
|
||||
audio {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-meta {
|
||||
padding: 12px;
|
||||
|
||||
&-title {
|
||||
color: white;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
&-artist {
|
||||
color: lightgrey;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright 2019 The Perkeep Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
goog.provide('cam.AudioDetail');
|
||||
|
||||
goog.require('cam.BlobItemAudioContent');
|
||||
|
||||
cam.AudioDetail = React.createClass({
|
||||
displayName: 'AudioDetail',
|
||||
|
||||
propTypes: {
|
||||
permanodeMeta: React.PropTypes.object,
|
||||
resolvedMeta: React.PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return React.DOM.div({
|
||||
className: 'cam-detail-audio',
|
||||
},
|
||||
this.getControls_(),
|
||||
);
|
||||
},
|
||||
|
||||
getControls_: function() {
|
||||
var mediaTags = this.props.resolvedMeta.mediaTags;
|
||||
return React.DOM.div({
|
||||
className: 'cam-detail-audio-controls',
|
||||
},
|
||||
React.DOM.div({
|
||||
className: 'cam-detail-audio-controls-meta'
|
||||
},
|
||||
mediaTags.title != null && React.DOM.div({
|
||||
className: 'cam-detail-audio-controls-meta-title',
|
||||
}, mediaTags.title),
|
||||
|
||||
mediaTags.artist != null && React.DOM.div({
|
||||
className: 'cam-detail-audio-controls-meta-artist',
|
||||
}, mediaTags.artist),
|
||||
),
|
||||
|
||||
React.DOM.div({
|
||||
className: 'cam-detail-audio-controls-player',
|
||||
},
|
||||
React.DOM.audio({
|
||||
controls: true,
|
||||
key: this.props.resolvedMeta.blobRef,
|
||||
src: goog.string.subs('%s%s/%s', goog.global.CAMLISTORE_CONFIG.downloadHelper, this.props.resolvedMeta.blobRef, this.props.resolvedMeta.file.fileName),
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
cam.AudioDetail.getAspect = function(blobref, searchSession) {
|
||||
if (!blobref) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var rm = searchSession.getResolvedMeta(blobref);
|
||||
var pm = searchSession.getMeta(blobref);
|
||||
|
||||
if (!pm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (pm.camliType != 'permanode') {
|
||||
pm = null;
|
||||
}
|
||||
|
||||
if (rm && cam.BlobItemAudioContent.isAudio(rm)) {
|
||||
return {
|
||||
fragment: 'audio',
|
||||
title: 'Audio',
|
||||
createContent: function(size, backwardPiggy) {
|
||||
return React.createElement(cam.AudioDetail, {
|
||||
key: 'audio',
|
||||
permanodeMeta: pm,
|
||||
resolvedMeta: rm,
|
||||
});
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
|
@ -54,6 +54,7 @@ limitations under the License.
|
|||
<link rel="stylesheet/less" href="index.css" type="text/css">
|
||||
<link rel="stylesheet/less" href="header.css" type="text/css">
|
||||
<link rel="stylesheet/less" href="detail.css" type="text/css">
|
||||
<link rel="stylesheet/less" href="audio_detail.css" type="text/css">
|
||||
|
||||
<link rel="stylesheet/less" href="blob_item.css" type="text/css">
|
||||
<link rel="stylesheet/less" href="blob_item_audio.css" type="text/css">
|
||||
|
|
|
@ -33,6 +33,8 @@ goog.require('goog.string');
|
|||
goog.require('goog.Uri');
|
||||
|
||||
goog.require('cam.BlobDetail');
|
||||
goog.require('cam.ImageDetail');
|
||||
goog.require('cam.AudioDetail');
|
||||
goog.require('cam.BlobItemContainerReact');
|
||||
goog.require('cam.DirContainer');
|
||||
goog.require('cam.BlobItemDemoContent');
|
||||
|
@ -249,9 +251,10 @@ cam.IndexPage = React.createClass({
|
|||
|
||||
var specificAspects = [
|
||||
cam.ImageDetail.getAspect,
|
||||
cam.AudioDetail.getAspect,
|
||||
cam.PdfDetail.getAspect,
|
||||
this.getDirAspect_.bind(null),
|
||||
].map(getAspect).filter(goog.functions.identity);
|
||||
];
|
||||
|
||||
var generalAspects = [
|
||||
this.getSearchAspect_.bind(null, specificAspects),
|
||||
|
@ -260,9 +263,12 @@ cam.IndexPage = React.createClass({
|
|||
this.props.availWidth, this.props.availHeight - this.HEADER_HEIGHT_,
|
||||
this.updateSearchBarOnMap_, this.setPendingQuery_, this.childSearchSession_),
|
||||
cam.BlobDetail.getAspect.bind(null, this.getDetailURL_, this.props.serverConnection),
|
||||
].map(getAspect).filter(goog.functions.identity);
|
||||
];
|
||||
|
||||
return specificAspects.concat(generalAspects);
|
||||
return specificAspects
|
||||
.concat(generalAspects)
|
||||
.map(getAspect)
|
||||
.filter(goog.functions.identity);
|
||||
},
|
||||
|
||||
getSearchAspect_: function(specificAspects, blobref, targetSearchSession) {
|
||||
|
|
Loading…
Reference in New Issue