1
Fork 0

Start on video integration with web ui

This commit is contained in:
viktorstrate 2020-07-11 16:42:27 +02:00
parent f537b1d608
commit b34115cab0
10 changed files with 169 additions and 89 deletions

View File

@ -101,6 +101,10 @@ func (r *mediaResolver) Downloads(ctx context.Context, obj *models.Media) ([]*mo
title = "Small"
case url.Purpose == models.PhotoHighRes:
title = "Large"
case url.Purpose == models.VideoThumbnail:
title = "Video thumbnail"
case url.Purpose == models.VideoWeb:
title = "Web optimized video"
}
downloads = append(downloads, &models.MediaDownload{
@ -158,8 +162,12 @@ func (r *mediaResolver) VideoWeb(ctx context.Context, obj *models.Media) (*model
url, err := models.NewMediaURLFromRow(row)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
} else {
return nil, errors.Wrapf(err, "could not query video web-format url (%s)", obj.Path)
}
}
return url, nil
}

View File

@ -20,6 +20,7 @@ const albumQuery = gql`
}
media(filter: { order_by: "title", order_direction: DESC }) {
id
type
thumbnail {
url
width
@ -28,6 +29,9 @@ const albumQuery = gql`
highRes {
url
}
videoWeb {
url
}
favorite
}
}

View File

@ -15,6 +15,7 @@ const photoQuery = gql`
) {
id
title
type
thumbnail {
url
width
@ -23,6 +24,9 @@ const photoQuery = gql`
highRes {
url
}
videoWeb {
url
}
favorite
}
}

View File

@ -4,7 +4,7 @@ import styled from 'styled-components'
import Layout from '../../Layout'
import ProtectedImage from '../../components/photoGallery/ProtectedImage'
import { SidebarConsumer } from '../../components/sidebar/Sidebar'
import PhotoSidebar from '../../components/sidebar/PhotoSidebar'
import MediaSidebar from '../../components/sidebar/MediaSidebar'
const DisplayPhoto = styled(ProtectedImage)`
width: 100%;
@ -22,7 +22,7 @@ const AlbumSharePage = ({ photo }) => {
<DisplayPhoto
src={photo.highRes.url}
onLoad={() => {
updateSidebar(<PhotoSidebar photo={photo} hidePreview />)
updateSidebar(<MediaSidebar media={photo} hidePreview />)
}}
/>
</>

View File

@ -5,7 +5,7 @@ import { Photo, PhotoThumbnail } from './Photo'
import PresentView from './presentView/PresentView'
import PropTypes from 'prop-types'
import { SidebarConsumer } from '../sidebar/Sidebar'
import PhotoSidebar from '../sidebar/PhotoSidebar'
import MediaSidebar from '../sidebar/MediaSidebar'
const Gallery = styled.div`
display: flex;
@ -79,7 +79,7 @@ const PhotoGallery = ({
key={photo.id}
photo={photo}
onSelectImage={index => {
updateSidebar(<PhotoSidebar photo={photo} />)
updateSidebar(<MediaSidebar media={photo} />)
onSelectImage(index)
}}
setPresenting={setPresenting}
@ -109,7 +109,7 @@ const PhotoGallery = ({
</Gallery>
{presenting && (
<PresentView
photo={activeImage}
media={activeImage}
{...{ nextImage, previousImage, setPresenting }}
/>
)}

View File

@ -0,0 +1,59 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import ProtectedImage from '../ProtectedImage'
const StyledPhoto = styled(ProtectedImage)`
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
object-fit: contain;
object-position: center;
`
const StyledVideo = styled.video`
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
`
const PresentMedia = ({ media, imageLoaded, ...otherProps }) => {
if (media.type == 'photo') {
return (
<div {...otherProps}>
<StyledPhoto src={media.thumbnail.url} />
<StyledPhoto
style={{ display: 'none' }}
src={media.highRes.url}
onLoad={e => {
e.target.style.display = 'initial'
imageLoaded && imageLoaded()
}}
/>
</div>
)
}
if (media.type == 'video') {
return (
<div {...otherProps}>
<StyledVideo controls key={media.id}>
<source src={media.videoWeb.url} type="video/mp4" />
</StyledVideo>
</div>
)
}
throw new Error(`Unknown media type '${media.type}'`)
}
PresentMedia.propTypes = {
media: PropTypes.object.isRequired,
imageLoaded: PropTypes.func,
}
export default PresentMedia

View File

@ -64,6 +64,7 @@ const NavigationButton = styled(OverlayButton)`
`
const PresentNavigationOverlay = ({
children,
nextImage,
previousImage,
setPresenting,
@ -92,6 +93,7 @@ const PresentNavigationOverlay = ({
onMouseMove.current()
}}
>
{children}
<NavigationButton
className={hide && 'hide'}
float="left"
@ -117,6 +119,7 @@ const PresentNavigationOverlay = ({
}
PresentNavigationOverlay.propTypes = {
children: PropTypes.element,
nextImage: PropTypes.func.isRequired,
previousImage: PropTypes.func.isRequired,
setPresenting: PropTypes.func.isRequired,

View File

@ -1,35 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import ProtectedImage from '../ProtectedImage'
const StyledPhoto = styled(ProtectedImage)`
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
object-fit: contain;
object-position: center;
`
const PresentPhoto = ({ photo, imageLoaded, ...otherProps }) => (
<div {...otherProps}>
<StyledPhoto src={photo.thumbnail.url} />
<StyledPhoto
style={{ display: 'none' }}
src={photo.highRes.url}
onLoad={e => {
e.target.style.display = 'initial'
imageLoaded && imageLoaded()
}}
/>
</div>
)
PresentPhoto.propTypes = {
photo: PropTypes.object.isRequired,
imageLoaded: PropTypes.func,
}
export default PresentPhoto

View File

@ -2,7 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import styled, { createGlobalStyle } from 'styled-components'
import PresentNavigationOverlay from './PresentNavigationOverlay'
import PresentPhoto from './PresentPhoto'
import PresentMedia from './PresentMedia'
const StyledContainer = styled.div`
position: fixed;
@ -23,7 +23,7 @@ const PreventScroll = createGlobalStyle`
const PresentView = ({
className,
photo,
media,
imageLoaded,
nextImage,
previousImage,
@ -31,15 +31,14 @@ const PresentView = ({
}) => (
<StyledContainer {...className}>
<PreventScroll />
<PresentPhoto photo={photo} imageLoaded={imageLoaded} />
<PresentNavigationOverlay
{...{ nextImage, previousImage, setPresenting }}
/>
<PresentNavigationOverlay {...{ nextImage, previousImage, setPresenting }}>
<PresentMedia media={media} imageLoaded={imageLoaded} />
</PresentNavigationOverlay>
</StyledContainer>
)
PresentView.propTypes = {
photo: PropTypes.object.isRequired,
media: PropTypes.object.isRequired,
imageLoaded: PropTypes.func,
className: PropTypes.string,
nextImage: PropTypes.func.isRequired,

View File

@ -1,23 +1,34 @@
import React, { Component } from 'react'
import React, { Component, useEffect } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { Query } from 'react-apollo'
import { useLazyQuery } from 'react-apollo'
import gql from 'graphql-tag'
import SidebarItem from './SidebarItem'
import ProtectedImage from '../photoGallery/ProtectedImage'
import SidebarShare from './Sharing'
import SidebarDownload from './SidebarDownload'
const photoQuery = gql`
const mediaQuery = gql`
query sidebarPhoto($id: Int!) {
media(id: $id) {
id
title
type
highRes {
url
width
height
}
thumbnail {
url
width
height
}
videoWeb {
url
width
height
}
exif {
camera
maker
@ -50,6 +61,35 @@ const PreviewImage = styled(ProtectedImage)`
object-fit: contain;
`
const PreviewVideo = styled.video`
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
`
const PreviewMedia = ({ media, previewImage }) => {
if (media.type == null || media.type == 'photo') {
return <PreviewImage src={previewImage.url} />
}
if (media.type == 'video') {
return (
<PreviewVideo controls key={media.id}>
<source src={media.videoWeb.url} type="video/mp4" />
</PreviewVideo>
)
}
throw new Error('Unknown media type')
}
PreviewMedia.propTypes = {
media: PropTypes.object.isRequired,
previewImage: PropTypes.object.isRequired,
}
const Name = styled.div`
text-align: center;
font-size: 1.2rem;
@ -85,18 +125,18 @@ const exposurePrograms = {
'8': 'Landscape mode ',
}
const SidebarContent = ({ photo, hidePreview }) => {
const SidebarContent = ({ media, hidePreview }) => {
let exifItems = []
if (photo && photo.exif) {
if (media && media.exif) {
let exifKeys = Object.keys(exifNameLookup).filter(
x => !!photo.exif[x] && x != '__typename'
x => !!media.exif[x] && x != '__typename'
)
let exif = exifKeys.reduce(
(prev, curr) => ({
...prev,
[curr]: photo.exif[curr],
[curr]: media.exif[curr],
}),
{}
)
@ -120,9 +160,9 @@ const SidebarContent = ({ photo, hidePreview }) => {
}
let previewImage = null
if (photo) {
if (photo.highRes) previewImage = photo.highRes
else if (photo.thumbnail) previewImage = photo.thumbnail
if (media) {
if (media.highRes) previewImage = media.highRes
else if (media.thumbnail) previewImage = media.thumbnail
}
return (
@ -131,55 +171,53 @@ const SidebarContent = ({ photo, hidePreview }) => {
<PreviewImageWrapper
imageAspect={previewImage.height / previewImage.width}
>
<PreviewImage src={previewImage.url} />
<PreviewMedia previewImage={previewImage} media={media} />
</PreviewImageWrapper>
)}
<Name>{photo && photo.title}</Name>
<Name>{media && media.title}</Name>
<ExifInfo>{exifItems}</ExifInfo>
<SidebarDownload photo={photo} />
<SidebarShare photo={photo} />
<SidebarDownload photo={media} />
<SidebarShare photo={media} />
</div>
)
}
SidebarContent.propTypes = {
photo: PropTypes.object,
media: PropTypes.object,
hidePreview: PropTypes.bool,
}
class PhotoSidebar extends Component {
render() {
const { photo, hidePreview } = this.props
const MediaSidebar = ({ media, hidePreview }) => {
const [loadMedia, { loading, error, data }] = useLazyQuery(mediaQuery)
if (!photo) return null
useEffect(() => {
if (media != null && localStorage.getItem('token')) {
loadMedia({
variables: {
id: media.id,
},
})
}
}, [media])
if (!media) return null
if (!localStorage.getItem('token')) {
return <SidebarContent photo={photo} hidePreview={hidePreview} />
return <SidebarContent media={media} hidePreview={hidePreview} />
}
return (
<div>
<Query query={photoQuery} variables={{ id: photo.id }}>
{({ loading, error, data }) => {
if (error) return error
if (loading) {
return <SidebarContent photo={photo} hidePreview={hidePreview} />
if (loading || data == null) {
return <SidebarContent media={media} hidePreview={hidePreview} />
}
return (
<SidebarContent photo={data.media} hidePreview={hidePreview} />
)
}}
</Query>
</div>
)
}
return <SidebarContent media={data.media} hidePreview={hidePreview} />
}
PhotoSidebar.propTypes = {
photo: PropTypes.object.isRequired,
MediaSidebar.propTypes = {
media: PropTypes.object.isRequired,
hidePreview: PropTypes.bool,
}
export default PhotoSidebar
export default MediaSidebar