lbry-desktop/ui/component/publishFile/view.jsx
jessopb 815ab193aa
update publishing messages: (#3794)
increase lbrytv limit until tv transcoding
warn about bitrate
warn about strange types
more reliably convey size limit message
2020-03-06 18:11:16 -05:00

241 lines
7.1 KiB
JavaScript

// @flow
import * as ICONS from 'constants/icons';
import React, { useState, useEffect } from 'react';
import { regexInvalidURI } from 'lbry-redux';
import FileSelector from 'component/common/file-selector';
import Button from 'component/button';
import Card from 'component/common/card';
import Spinner from 'component/spinner';
type Props = {
name: ?string,
filePath: string | WebFile,
isStillEditing: boolean,
balance: number,
updatePublishForm: ({}) => void,
disabled: boolean,
publishing: boolean,
showToast: string => void,
inProgress: boolean,
clearPublish: () => void,
};
function PublishFile(props: Props) {
const {
name,
balance,
filePath,
isStillEditing,
updatePublishForm,
disabled,
publishing,
inProgress,
clearPublish,
} = props;
const [duration, setDuration] = useState(0);
const [size, setSize] = useState(0);
const [oversized, setOversized] = useState(false);
const [isVid, setIsVid] = useState(false);
const RECOMMENDED_BITRATE = 6000000;
const TV_PUBLISH_SIZE_LIMIT: number = 1073741824;
const UPLOAD_SIZE_MESSAGE = 'Lbrytv uploads are limited to 1 GB. Download the app for unrestricted publishing.';
// clear warnings
useEffect(() => {
if (!filePath || filePath === '' || filePath.name === '') {
setDuration(0);
setSize(0);
setIsVid(false);
setOversized(false);
}
}, [filePath]);
let currentFile = '';
if (filePath) {
if (typeof filePath === 'string') {
currentFile = filePath;
} else {
currentFile = filePath.name;
}
}
function getBitrate(size, duration) {
const s = Number(size);
const d = Number(duration);
if (s && d) {
return (s * 8) / d;
} else {
return 0;
}
}
function getMessage() {
// @if TARGET='web'
if (oversized) {
return (
<p className="help--error">
{__(UPLOAD_SIZE_MESSAGE)}{' '}
<Button button="link" label={__('Publishing Guide')} href="https://lbry.com/faq/video-publishing-guide" />
</p>
);
}
// @endif
if (isVid && duration && getBitrate(size, duration) > RECOMMENDED_BITRATE) {
return (
<p className="help--warning">
{__('Your video has a bitrate over 5 mbps. We suggest transcoding to provide viewers the best experience.')}{' '}
<Button button="link" label={__('Publishing Guide')} href="https://lbry.com/faq/video-publishing-guide" />
</p>
);
}
if (isVid && !duration) {
return (
<p className="help--warning">
{__(
'Your video may not be the best format. Use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.'
)}{' '}
<Button button="link" label={__('Publishing Guide')} href="https://lbry.com/faq/video-publishing-guide" />
</p>
);
}
if (!!isStillEditing && name) {
return (
<p className="help">
{__("If you don't choose a file, the file from your existing claim %name% will be used", { name: name })}
</p>
);
}
// @if TARGET='web'
if (!isStillEditing) {
return (
<p className="help">
{__(
'For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming. Lbrytv uploads are restricted to 1GB.'
)}{' '}
<Button button="link" label={__('Publishing Guide')} href="https://lbry.com/faq/video-publishing-guide" />
</p>
);
}
// @endif
// @if TARGET='app'
if (!isStillEditing) {
return (
<p className="help">
{__(
'For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.'
)}{' '}
<Button button="link" label={__('Publishing Guide')} href="https://lbry.com/faq/video-publishing-guide" />
</p>
);
}
// @endif
}
function handleFileChange(file: WebFile) {
const { showToast } = props;
window.URL = window.URL || window.webkitURL;
// if electron, we'll set filePath to the path string because SDK is handling publishing.
// if web, we set the filePath (dumb name) to the File() object
// File.path will be undefined from web due to browser security, so it will default to the File Object.
setSize(file ? file.size : 0);
setDuration(0);
setOversized(false);
// select file, start to select a new one, then cancel
if (!file) {
updatePublishForm({ filePath: '', name: '' });
return;
}
// if video, extract duration so we can warn about bitrate
const contentType = file.type.split('/');
const isVideo = contentType[0] === 'video';
const isMp4 = contentType[1] === 'mp4';
if (isVideo) {
if (isMp4) {
const video = document.createElement('video');
video.preload = 'metadata';
video.onloadedmetadata = function() {
setDuration(video.duration);
setSize(file.size);
setIsVid(isVideo);
window.URL.revokeObjectURL(video.src);
};
video.onerror = function() {
setDuration(0);
setSize(file.size);
setIsVid(isVideo);
};
video.src = window.URL.createObjectURL(file);
} else {
setSize(file.size);
setDuration(0);
setIsVid(isVideo);
}
}
// @if TARGET='web'
// we only need to enforce file sizes on 'web'
if (typeof file !== 'string') {
if (file && file.size && Number(file.size) > TV_PUBLISH_SIZE_LIMIT) {
setOversized(true);
showToast(__(UPLOAD_SIZE_MESSAGE));
updatePublishForm({ filePath: '', name: '' });
return;
}
}
// @endif
const publishFormParams: { filePath: string | WebFile, name?: string } = {
filePath: file.path || file,
};
// Strip off extention and replace invalid characters
let fileName = name || file.name.substr(0, file.name.lastIndexOf('.')) || file.name;
let INVALID_URI_CHARS = new RegExp(regexInvalidURI, 'gu');
let parsedFileName = fileName.replace(INVALID_URI_CHARS, '-');
if (!isStillEditing) {
publishFormParams.name = parsedFileName;
}
updatePublishForm(publishFormParams);
}
let title;
if (publishing) {
title = (
<span>
{__('Publishing')}
<Spinner type={'small'} />
</span>
);
} else {
title = isStillEditing ? __('Edit') : __('Publish');
}
return (
<Card
actionIconPadding={false}
icon={ICONS.PUBLISH}
disabled={disabled || balance === 0}
title={
<React.Fragment>
{title}{' '}
{inProgress && <Button button="close" label={__('Cancel')} icon={ICONS.REMOVE} onClick={clearPublish} />}
</React.Fragment>
}
subtitle={
isStillEditing ? __('You are currently editing a claim.') : __('Publish something totally wacky and wild.')
}
actions={
<React.Fragment>
<FileSelector disabled={disabled} currentPath={currentFile} onFileChosen={handleFileChange} />
{getMessage()}
</React.Fragment>
}
/>
);
}
export default PublishFile;