2019-06-28 09:27:55 +02:00
// @flow
2021-04-14 19:45:45 +02:00
import { SITE _NAME , WEB _PUBLISH _SIZE _LIMIT _GB , SIMPLE _SITE } from 'config' ;
2020-08-05 19:19:15 +02:00
import type { Node } from 'react' ;
2019-09-27 20:56:15 +02:00
import * as ICONS from 'constants/icons' ;
2020-03-07 00:11:16 +01:00
import React , { useState , useEffect } from 'react' ;
2021-10-17 10:36:14 +02:00
import { regexInvalidURI } from 'util/lbryURI' ;
2020-07-29 22:30:26 +02:00
import PostEditor from 'component/postEditor' ;
2019-06-28 09:27:55 +02:00
import FileSelector from 'component/common/file-selector' ;
2019-07-17 05:23:45 +02:00
import Button from 'component/button' ;
2019-09-27 20:56:15 +02:00
import Card from 'component/common/card' ;
2020-03-24 18:57:17 +01:00
import { FormField } from 'component/common/form' ;
2019-10-11 02:37:18 +02:00
import Spinner from 'component/spinner' ;
2020-07-01 17:35:05 +02:00
import I18nMessage from 'component/i18nMessage' ;
import usePersistedState from 'effects/use-persisted-state' ;
2020-07-28 01:12:59 +02:00
import * as PUBLISH _MODES from 'constants/publish_types' ;
2021-02-09 17:05:56 +01:00
import PublishName from 'component/publishName' ;
2021-04-14 06:06:11 +02:00
import CopyableText from 'component/copyableText' ;
import Empty from 'component/common/empty' ;
import moment from 'moment' ;
import classnames from 'classnames' ;
import ReactPaginate from 'react-paginate' ;
2021-12-29 23:10:19 +01:00
import { SOURCE _NONE , SOURCE _SELECT , SOURCE _UPLOAD } from 'constants/publish_sources' ;
2019-06-28 09:27:55 +02:00
type Props = {
2020-07-28 01:12:59 +02:00
uri : ? string ,
mode : ? string ,
2019-06-28 09:27:55 +02:00
name : ? string ,
2020-07-28 01:12:59 +02:00
title : ? string ,
2019-10-07 22:02:32 +02:00
filePath : string | WebFile ,
2020-07-29 04:56:07 +02:00
fileMimeType : ? string ,
2019-06-28 09:27:55 +02:00
isStillEditing : boolean ,
balance : number ,
2022-05-19 16:41:32 +02:00
doUpdatePublishForm : ( { } ) => void ,
2019-09-27 20:56:15 +02:00
disabled : boolean ,
2019-10-11 02:37:18 +02:00
publishing : boolean ,
2022-05-19 16:41:32 +02:00
doToast : ( { message : string , isError ? : boolean } ) => void ,
2019-12-26 16:37:26 +01:00
inProgress : boolean ,
2022-05-19 16:41:32 +02:00
doClearPublish : ( ) => void ,
2020-03-24 18:57:17 +01:00
ffmpegStatus : any ,
optimize : boolean ,
2020-03-30 20:19:32 +02:00
size : number ,
duration : number ,
isVid : boolean ,
2021-04-14 06:06:11 +02:00
subtitle : string ,
2021-02-16 03:33:34 +01:00
setPublishMode : ( string ) => void ,
setPrevFileText : ( string ) => void ,
2020-08-05 19:19:15 +02:00
header : Node ,
2021-04-14 06:06:11 +02:00
livestreamData : LivestreamReplayData ,
isLivestreamClaim : boolean ,
2022-04-20 21:48:45 +02:00
checkLivestreams : ( string , string ) => void ,
channelName : string ,
2021-04-14 06:06:11 +02:00
channelId : string ,
isCheckingLivestreams : boolean ,
setWaitForFile : ( boolean ) => void ,
2022-02-01 23:02:30 +01:00
setOverMaxBitrate : ( boolean ) => void ,
2021-12-31 21:48:07 +01:00
fileSource : string ,
changeFileSource : ( string ) => void ,
inEditMode : boolean ,
2019-06-28 09:27:55 +02:00
} ;
function PublishFile ( props : Props ) {
2019-12-26 16:37:26 +01:00
const {
2020-07-28 01:12:59 +02:00
uri ,
mode ,
2019-12-26 16:37:26 +01:00
name ,
2020-07-28 01:12:59 +02:00
title ,
2019-12-26 16:37:26 +01:00
balance ,
filePath ,
2020-07-29 04:56:07 +02:00
fileMimeType ,
2019-12-26 16:37:26 +01:00
isStillEditing ,
2022-05-19 16:41:32 +02:00
doUpdatePublishForm : updatePublishForm ,
doToast ,
2019-12-26 16:37:26 +01:00
disabled ,
publishing ,
inProgress ,
2022-05-19 16:41:32 +02:00
doClearPublish ,
2020-03-24 18:57:17 +01:00
optimize ,
ffmpegStatus = { } ,
2020-03-30 20:19:32 +02:00
size ,
duration ,
isVid ,
2020-07-28 01:12:59 +02:00
setPublishMode ,
setPrevFileText ,
2020-08-05 19:06:24 +02:00
header ,
2021-04-14 06:06:11 +02:00
livestreamData ,
isLivestreamClaim ,
subtitle ,
checkLivestreams ,
channelId ,
2022-04-20 21:48:45 +02:00
channelName ,
2021-04-14 06:06:11 +02:00
isCheckingLivestreams ,
setWaitForFile ,
2022-02-01 23:02:30 +01:00
setOverMaxBitrate ,
2021-12-31 21:48:07 +01:00
fileSource ,
changeFileSource ,
inEditMode ,
2019-12-26 16:37:26 +01:00
} = props ;
2019-10-11 02:37:18 +02:00
2022-06-02 20:47:50 +02:00
const RECOMMENDED _BITRATE = 8500000 ;
const MAX _BITRATE = 16500000 ;
2021-04-14 06:06:11 +02:00
const TV _PUBLISH _SIZE _LIMIT _BYTES = WEB _PUBLISH _SIZE _LIMIT _GB * 1073741824 ;
const TV _PUBLISH _SIZE _LIMIT _GB _STR = String ( WEB _PUBLISH _SIZE _LIMIT _GB ) ;
const PROCESSING _MB _PER _SECOND = 0.5 ;
const MINUTES _THRESHOLD = 30 ;
const HOURS _THRESHOLD = MINUTES _THRESHOLD * 60 ;
const MARKDOWN _FILE _EXTENSIONS = [ 'txt' , 'md' , 'markdown' ] ;
const sizeInMB = Number ( size ) / 1000000 ;
const secondsToProcess = sizeInMB / PROCESSING _MB _PER _SECOND ;
2020-07-01 16:17:07 +02:00
const ffmpegAvail = ffmpegStatus . available ;
2020-03-07 00:11:16 +01:00
const [ oversized , setOversized ] = useState ( false ) ;
2020-05-25 16:27:36 +02:00
const [ currentFile , setCurrentFile ] = useState ( null ) ;
2020-07-28 01:12:59 +02:00
const [ currentFileType , setCurrentFileType ] = useState ( null ) ;
2020-07-01 17:35:05 +02:00
const [ optimizeAvail , setOptimizeAvail ] = useState ( false ) ;
const [ userOptimize , setUserOptimize ] = usePersistedState ( 'publish-file-user-optimize' , false ) ;
2021-11-11 03:16:31 +01:00
const UPLOAD _SIZE _MESSAGE = _ _ ( '%SITE_NAME% uploads are limited to %limit% GB.' , {
SITE _NAME ,
limit : TV _PUBLISH _SIZE _LIMIT _GB _STR ,
} ) ;
2020-03-24 18:57:17 +01:00
2022-05-19 16:41:32 +02:00
const bitRate = getBitrate ( size , duration ) ;
const bitRateIsOverMax = bitRate > MAX _BITRATE ;
2021-04-14 06:06:11 +02:00
const fileSelectorModes = [
{ label : _ _ ( 'Upload' ) , actionName : SOURCE _UPLOAD , icon : ICONS . PUBLISH } ,
2021-04-14 17:56:45 +02:00
{ label : _ _ ( 'Choose Replay' ) , actionName : SOURCE _SELECT , icon : ICONS . MENU } ,
2021-12-22 22:45:47 +01:00
{ label : isLivestreamClaim ? _ _ ( 'Edit / Update' ) : _ _ ( 'None' ) , actionName : SOURCE _NONE } ,
2021-04-14 06:06:11 +02:00
] ;
const livestreamDataStr = JSON . stringify ( livestreamData ) ;
const hasLivestreamData = livestreamData && Boolean ( livestreamData . length ) ;
2021-12-31 21:48:07 +01:00
const [ showSourceSelector , setShowSourceSelector ] = useState ( false ) ;
2021-04-14 06:06:11 +02:00
// const [showFileUpdate, setShowFileUpdate] = useState(false);
const [ selectedFileIndex , setSelectedFileIndex ] = useState ( null ) ;
const PAGE _SIZE = 4 ;
const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
const totalPages =
hasLivestreamData && livestreamData . length > PAGE _SIZE ? Math . ceil ( livestreamData . length / PAGE _SIZE ) : 1 ;
2020-03-07 00:11:16 +01:00
2020-07-28 01:12:59 +02:00
// Reset filePath if publish mode changed
useEffect ( ( ) => {
2020-07-29 22:30:26 +02:00
if ( mode === PUBLISH _MODES . POST ) {
2020-07-28 04:19:00 +02:00
if ( currentFileType !== 'text/markdown' && ! isStillEditing ) {
2020-07-30 00:55:48 +02:00
updatePublishForm ( { filePath : '' } ) ;
2020-07-28 01:12:59 +02:00
}
2021-04-14 06:06:11 +02:00
} else if ( mode === PUBLISH _MODES . LIVESTREAM ) {
updatePublishForm ( { filePath : '' } ) ;
2020-07-28 01:12:59 +02:00
}
2020-07-28 04:19:00 +02:00
} , [ currentFileType , mode , isStillEditing , updatePublishForm ] ) ;
2020-07-28 01:12:59 +02:00
2022-06-03 15:28:12 +02:00
// Reset title when form gets cleared
useEffect ( ( ) => {
updatePublishForm ( { title : title } ) ;
} , [ filePath ] ) ;
2021-12-31 21:48:07 +01:00
// Initialize default file source state for each mode.
2021-04-14 06:06:11 +02:00
useEffect ( ( ) => {
2021-12-31 21:48:07 +01:00
setShowSourceSelector ( false ) ;
switch ( mode ) {
case PUBLISH _MODES . LIVESTREAM :
if ( inEditMode ) {
changeFileSource ( SOURCE _SELECT ) ;
setShowSourceSelector ( true ) ;
} else {
changeFileSource ( SOURCE _NONE ) ;
}
break ;
case PUBLISH _MODES . POST :
changeFileSource ( SOURCE _NONE ) ;
break ;
case PUBLISH _MODES . FILE :
if ( hasLivestreamData ) setShowSourceSelector ( true ) ;
changeFileSource ( SOURCE _UPLOAD ) ;
break ;
default :
changeFileSource ( SOURCE _UPLOAD ) ;
2021-12-31 17:38:11 +01:00
}
2021-12-31 21:48:07 +01:00
} , [ mode , hasLivestreamData ] ) ; // eslint-disable-line react-hooks/exhaustive-deps
2021-04-14 06:06:11 +02:00
const normalizeUrlForProtocol = ( url ) => {
if ( url . startsWith ( 'https://' ) ) {
return url ;
} else {
if ( url . startsWith ( 'http://' ) ) {
return url ;
2022-04-20 21:48:45 +02:00
} else if ( url ) {
2021-04-14 06:06:11 +02:00
return ` https:// ${ url } ` ;
2022-04-20 21:48:45 +02:00
} else return _ _ ( 'Click Check for Replays to update...' ) ;
2021-04-14 06:06:11 +02:00
}
} ;
// update remoteUrl when replay selected
useEffect ( ( ) => {
const livestreamData = JSON . parse ( livestreamDataStr ) ;
if ( selectedFileIndex !== null && livestreamData && livestreamData . length ) {
updatePublishForm ( {
remoteFileUrl : normalizeUrlForProtocol ( livestreamData [ selectedFileIndex ] . data . fileLocation ) ,
} ) ;
}
} , [ selectedFileIndex , updatePublishForm , livestreamDataStr ] ) ;
2020-03-07 00:11:16 +01:00
useEffect ( ( ) => {
2020-03-30 20:19:32 +02:00
if ( ! filePath || filePath === '' ) {
2020-05-25 16:27:36 +02:00
setCurrentFile ( '' ) ;
2020-03-07 00:11:16 +01:00
setOversized ( false ) ;
2022-02-01 23:02:30 +01:00
setOverMaxBitrate ( false ) ;
2020-07-01 16:17:07 +02:00
updateFileInfo ( 0 , 0 , false ) ;
2020-05-25 16:27:36 +02:00
} else if ( typeof filePath !== 'string' ) {
// Update currentFile file
if ( filePath . name !== currentFile && filePath . path !== currentFile ) {
handleFileChange ( filePath ) ;
}
2020-03-07 00:11:16 +01:00
}
2022-05-19 16:41:32 +02:00
} , [ filePath , currentFile , doToast , updatePublishForm ] ) ;
2019-10-07 22:02:32 +02:00
2020-07-01 17:35:05 +02:00
useEffect ( ( ) => {
const isOptimizeAvail = currentFile && currentFile !== '' && isVid && ffmpegAvail ;
const finalOptimizeState = isOptimizeAvail && userOptimize ;
setOptimizeAvail ( isOptimizeAvail ) ;
updatePublishForm ( { optimize : finalOptimizeState } ) ;
2020-07-28 01:12:59 +02:00
} , [ currentFile , filePath , isVid , ffmpegAvail , userOptimize , updatePublishForm ] ) ;
2020-07-01 17:35:05 +02:00
2022-05-19 16:41:32 +02:00
useEffect ( ( ) => {
setOverMaxBitrate ( bitRateIsOverMax ) ;
} , [ bitRateIsOverMax ] ) ;
2020-07-01 16:17:07 +02:00
function updateFileInfo ( duration , size , isvid ) {
2020-03-30 20:19:32 +02:00
updatePublishForm ( { fileDur : duration , fileSize : size , fileVid : isvid } ) ;
}
2021-04-14 06:06:11 +02:00
function handlePaginateReplays ( page ) {
setCurrentPage ( page ) ;
}
2020-03-07 00:11:16 +01:00
function getBitrate ( size , duration ) {
const s = Number ( size ) ;
const d = Number ( duration ) ;
if ( s && d ) {
return ( s * 8 ) / d ;
} else {
return 0 ;
}
}
2020-03-24 18:57:17 +01:00
function getTimeForMB ( s ) {
if ( s < MINUTES _THRESHOLD ) {
return Math . floor ( secondsToProcess ) ;
} else if ( s >= MINUTES _THRESHOLD && s < HOURS _THRESHOLD ) {
return Math . floor ( secondsToProcess / 60 ) ;
} else {
return Math . floor ( secondsToProcess / 60 / 60 ) ;
}
}
function getUnitsForMB ( s ) {
if ( s < MINUTES _THRESHOLD ) {
2020-05-28 22:38:54 +02:00
if ( secondsToProcess > 1 ) return _ _ ( 'seconds' ) ;
2020-07-01 16:17:07 +02:00
return _ _ ( 'second' ) ;
2020-03-24 18:57:17 +01:00
} else if ( s >= MINUTES _THRESHOLD && s < HOURS _THRESHOLD ) {
2020-05-28 22:38:54 +02:00
if ( Math . floor ( secondsToProcess / 60 ) > 1 ) return _ _ ( 'minutes' ) ;
return _ _ ( 'minute' ) ;
2020-03-24 18:57:17 +01:00
} else {
2020-05-28 22:38:54 +02:00
if ( Math . floor ( secondsToProcess / 3600 ) > 1 ) return _ _ ( 'hours' ) ;
return _ _ ( 'hour' ) ;
2020-03-24 18:57:17 +01:00
}
}
2021-04-14 06:06:11 +02:00
function getUploadMessage ( ) {
2020-03-07 00:11:16 +01:00
// @if TARGET='web'
if ( oversized ) {
return (
< p className = "help--error" >
2020-10-05 20:24:57 +02:00
{ UPLOAD _SIZE _MESSAGE } { ' ' }
2022-01-31 17:38:57 +01:00
< Button button = "link" label = { _ _ ( 'Upload Guide' ) } href = "https://odysee.com/@OdyseeHelp:b/uploadguide:1" / >
2020-03-07 00:11:16 +01:00
< / p >
) ;
}
// @endif
2022-02-01 23:02:30 +01:00
if ( isVid && duration && bitRate > RECOMMENDED _BITRATE ) {
2020-03-07 00:11:16 +01:00
return (
< p className = "help--warning" >
2022-05-19 16:41:32 +02:00
{ bitRateIsOverMax
2022-02-01 23:02:30 +01:00
? _ _ (
2022-06-02 20:47:50 +02:00
'Your video has a bitrate over ~16 Mbps and cannot be processed at this time. We suggest transcoding to provide viewers the best experience.'
2022-02-01 23:02:30 +01:00
)
: _ _ (
2022-06-02 20:47:50 +02:00
'Your video has a bitrate over 8 Mbps. We suggest transcoding to provide viewers the best experience.'
2022-02-01 23:02:30 +01:00
) } { ' ' }
2022-01-31 17:38:57 +01:00
< Button
button = "link"
label = { _ _ ( 'Upload Guide' ) }
href = "https://odysee.com/@OdyseeHelp:b/uploadguide:1?lc=e280f6e6fdec3f5fd4043954c71add50b3fd2d6a9f3ddba979b459da6ae4a1f4"
/ >
2020-03-07 00:11:16 +01:00
< / p >
) ;
}
if ( isVid && ! duration ) {
return (
< p className = "help--warning" >
{ _ _ (
2022-06-02 20:47:50 +02:00
'Your video may not be the best format. Use MP4s in H264/AAC format and a friendly bitrate (under 8 Mbps) for more reliable streaming.'
2020-03-07 00:11:16 +01:00
) } { ' ' }
2022-01-31 17:38:57 +01:00
< Button
button = "link"
label = { _ _ ( 'Upload Guide' ) }
href = "https://odysee.com/@OdyseeHelp:b/uploadguide:1?lc=e280f6e6fdec3f5fd4043954c71add50b3fd2d6a9f3ddba979b459da6ae4a1f4"
/ >
2020-03-07 00:11:16 +01:00
< / p >
) ;
}
if ( ! ! isStillEditing && name ) {
2021-04-14 06:06:11 +02:00
if ( isLivestreamClaim ) {
return (
< p className = "help" > { _ _ ( 'You can upload your own recording or select a replay when your stream is over' ) } < / p >
) ;
}
2020-03-07 00:11:16 +01:00
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" >
{ _ _ (
2022-06-02 20:47:50 +02:00
'For video content, use MP4s in H264/AAC format and a friendly bitrate (under 8 Mbps) for more reliable streaming. %SITE_NAME% uploads are restricted to %limit% GB.' ,
2021-04-14 06:06:11 +02:00
{ SITE _NAME , limit : TV _PUBLISH _SIZE _LIMIT _GB _STR }
2020-03-07 00:11:16 +01:00
) } { ' ' }
2022-01-31 17:38:57 +01:00
< Button
button = "link"
label = { _ _ ( 'Upload Guide' ) }
href = "https://odysee.com/@OdyseeHelp:b/uploadguide:1?lc=e280f6e6fdec3f5fd4043954c71add50b3fd2d6a9f3ddba979b459da6ae4a1f4"
/ >
2020-03-07 00:11:16 +01:00
< / p >
) ;
}
// @endif
}
2020-07-30 00:55:48 +02:00
function parseName ( newName ) {
let INVALID _URI _CHARS = new RegExp ( regexInvalidURI , 'gu' ) ;
return newName . replace ( INVALID _URI _CHARS , '-' ) ;
}
2021-04-14 06:06:11 +02:00
function handleFileSource ( source ) {
if ( source === SOURCE _NONE ) {
// clear files and remotes...
// https://github.com/lbryio/lbry-desktop/issues/5855
// publish is trying to use one field to share html file blob and string and such
// $FlowFixMe
2021-04-23 18:57:51 +02:00
handleFileChange ( false , false ) ;
2021-04-14 06:06:11 +02:00
updatePublishForm ( { remoteFileUrl : undefined } ) ;
} else if ( source === SOURCE _UPLOAD ) {
updatePublishForm ( { remoteFileUrl : undefined } ) ;
} else if ( source === SOURCE _SELECT ) {
// $FlowFixMe
2021-04-23 18:57:51 +02:00
handleFileChange ( false , false ) ;
2021-04-14 06:06:11 +02:00
if ( selectedFileIndex !== null ) {
updatePublishForm ( { remoteFileUrl : livestreamData [ selectedFileIndex ] . data . fileLocation } ) ;
}
}
2021-12-31 21:48:07 +01:00
changeFileSource ( source ) ;
2021-04-14 06:06:11 +02:00
setWaitForFile ( source !== SOURCE _NONE ) ;
}
2020-07-30 06:03:56 +02:00
function handleTitleChange ( event ) {
2022-06-03 15:28:12 +02:00
updatePublishForm ( { title : event . target . value } ) ;
2020-07-30 00:55:48 +02:00
}
2020-08-11 04:08:03 +02:00
function handleFileReaderLoaded ( event : ProgressEvent ) {
// See: https://github.com/facebook/flow/issues/3470
if ( event . target instanceof FileReader ) {
const text = event . target . result ;
updatePublishForm ( { fileText : text } ) ;
setPublishMode ( PUBLISH _MODES . POST ) ;
}
}
2021-04-23 18:57:51 +02:00
function handleFileChange ( file : WebFile , clearName = true ) {
2020-03-07 00:11:16 +01:00
window . URL = window . URL || window . webkitURL ;
setOversized ( false ) ;
2022-02-01 23:02:30 +01:00
setOverMaxBitrate ( false ) ;
2020-03-07 00:11:16 +01:00
// select file, start to select a new one, then cancel
2020-05-28 16:45:56 +02:00
if ( ! file ) {
2021-04-23 18:57:51 +02:00
if ( isStillEditing || ! clearName ) {
2021-04-14 06:06:11 +02:00
updatePublishForm ( { filePath : '' } ) ;
} else {
updatePublishForm ( { filePath : '' , name : '' } ) ;
}
2020-03-07 00:11:16 +01:00
return ;
}
2020-05-25 16:27:36 +02:00
// if video, extract duration so we can warn about bitrateif (typeof file !== 'string') {
2020-05-28 16:45:56 +02:00
const contentType = file . type && file . type . split ( '/' ) ;
const isVideo = contentType && contentType [ 0 ] === 'video' ;
const isMp4 = contentType && contentType [ 1 ] === 'mp4' ;
2020-07-28 01:12:59 +02:00
2020-08-24 22:45:08 +02:00
let isTextPost = false ;
2020-07-28 01:12:59 +02:00
2020-08-24 22:45:08 +02:00
if ( contentType && contentType [ 0 ] === 'text' ) {
isTextPost = contentType [ 1 ] === 'plain' || contentType [ 1 ] === 'markdown' ;
2020-07-28 01:12:59 +02:00
setCurrentFileType ( contentType ) ;
} else if ( file . name ) {
// If user's machine is missign a valid content type registration
// for markdown content: text/markdown, file extension will be used instead
const extension = file . name . split ( '.' ) . pop ( ) ;
2020-08-24 22:45:08 +02:00
isTextPost = MARKDOWN _FILE _EXTENSIONS . includes ( extension ) ;
2020-07-28 01:12:59 +02:00
}
2020-03-07 00:11:16 +01:00
if ( isVideo ) {
if ( isMp4 ) {
const video = document . createElement ( 'video' ) ;
video . preload = 'metadata' ;
2021-02-16 03:33:34 +01:00
video . onloadedmetadata = ( ) => {
2020-07-01 16:17:07 +02:00
updateFileInfo ( video . duration , file . size , isVideo ) ;
2020-03-07 00:11:16 +01:00
window . URL . revokeObjectURL ( video . src ) ;
} ;
2021-02-16 03:33:34 +01:00
video . onerror = ( ) => {
2020-07-01 16:17:07 +02:00
updateFileInfo ( 0 , file . size , isVideo ) ;
2020-03-07 00:11:16 +01:00
} ;
video . src = window . URL . createObjectURL ( file ) ;
} else {
2020-07-01 16:17:07 +02:00
updateFileInfo ( 0 , file . size , isVideo ) ;
2020-03-07 00:11:16 +01:00
}
2020-07-14 12:32:08 +02:00
} else {
updateFileInfo ( 0 , file . size , isVideo ) ;
2020-03-07 00:11:16 +01:00
}
2019-10-11 02:37:18 +02:00
2022-06-03 15:28:12 +02:00
// Strip off extention and replace invalid characters
let fileName = name || ( file . name && file . name . substr ( 0 , file . name . lastIndexOf ( '.' ) ) ) || '' ;
autofillTitle ( file ) ;
2020-08-24 22:45:08 +02:00
if ( isTextPost ) {
2020-07-28 01:12:59 +02:00
// Create reader
const reader = new FileReader ( ) ;
// Handler for file reader
2020-08-11 04:08:03 +02:00
reader . addEventListener ( 'load' , handleFileReaderLoaded ) ;
2020-07-28 01:12:59 +02:00
// Read file contents
reader . readAsText ( file ) ;
setCurrentFileType ( 'text/markdown' ) ;
} else {
setPublishMode ( PUBLISH _MODES . FILE ) ;
}
2019-10-11 02:37:18 +02:00
// @if TARGET='web'
// we only need to enforce file sizes on 'web'
2021-04-14 06:06:11 +02:00
if ( file . size && Number ( file . size ) > TV _PUBLISH _SIZE _LIMIT _BYTES ) {
2020-05-25 16:27:36 +02:00
setOversized ( true ) ;
2022-05-19 16:41:32 +02:00
doToast ( { message : _ _ ( UPLOAD _SIZE _MESSAGE ) , isError : true } ) ;
2022-02-01 23:02:30 +01:00
updatePublishForm ( { filePath : '' } ) ;
2020-05-25 16:27:36 +02:00
return ;
2019-10-11 02:37:18 +02:00
}
// @endif
2019-12-09 19:51:00 +01:00
2020-03-24 18:57:17 +01:00
const publishFormParams : { filePath : string | WebFile , name ? : string , optimize ? : boolean } = {
2020-05-25 16:27:36 +02:00
// if electron, we'll set filePath to the path string because SDK is handling publishing.
// File.path will be undefined from web due to browser security, so it will default to the File Object.
2019-10-07 22:02:32 +02:00
filePath : file . path || file ,
} ;
2020-07-30 00:55:48 +02:00
2019-12-14 20:35:55 +01:00
if ( ! isStillEditing ) {
2020-07-30 00:55:48 +02:00
publishFormParams . name = parseName ( fileName ) ;
2019-12-14 20:35:55 +01:00
}
2020-07-30 06:03:56 +02:00
2020-05-25 16:27:36 +02:00
// File path is not supported on web for security reasons so we use the name instead.
setCurrentFile ( file . path || file . name ) ;
2019-06-28 09:27:55 +02:00
updatePublishForm ( publishFormParams ) ;
}
2022-06-03 15:28:12 +02:00
function autofillTitle ( file ) {
const newTitle = ( file && file . name && file . name . substr ( 0 , file . name . lastIndexOf ( '.' ) ) ) || name || '' ;
if ( ! title ) updatePublishForm ( { title : newTitle } ) ;
}
2021-12-22 22:45:47 +01:00
const showFileUpload = mode === PUBLISH _MODES . FILE || PUBLISH _MODES . LIVESTREAM ;
2020-07-29 22:30:26 +02:00
const isPublishPost = mode === PUBLISH _MODES . POST ;
2020-07-28 01:12:59 +02:00
2019-06-28 09:27:55 +02:00
return (
2019-09-27 20:56:15 +02:00
< Card
2022-06-03 15:28:12 +02:00
className = { classnames ( {
'card--disabled' : disabled || balance === 0 ,
} ) }
2019-12-26 16:37:26 +01:00
title = {
2020-08-05 19:19:15 +02:00
< div >
2021-04-14 06:06:11 +02:00
{ header } { /* display mode buttons from parent */ }
2020-08-05 19:06:24 +02:00
{ publishing && < Spinner type = { 'small' } / > }
2020-08-05 19:19:15 +02:00
{ inProgress && (
< div >
2021-09-21 10:47:45 +02:00
< Button
2022-06-03 15:28:12 +02:00
button = "alt"
label = { _ _ ( 'Clear --[clears Publish Form]--' ) }
2021-09-21 10:47:45 +02:00
icon = { ICONS . REFRESH }
2022-05-19 16:41:32 +02:00
onClick = { doClearPublish }
2021-09-21 10:47:45 +02:00
/ >
2020-08-05 19:19:15 +02:00
< / div >
) }
< / div >
2019-12-26 16:37:26 +01:00
}
2021-04-14 06:06:11 +02:00
subtitle = { subtitle || ( isStillEditing && _ _ ( 'You are currently editing your upload.' ) ) }
2019-09-27 20:56:15 +02:00
actions = {
2022-06-03 15:28:12 +02:00
< >
{ /* <h2 className="card__title">{__('File')}</h2> */ }
< div className = "card--file" >
< React.Fragment >
{ /* Decide whether to show file upload or replay selector */ }
{ /* @if TARGET='web' */ }
< >
{ showSourceSelector && (
< fieldset - section >
< div className = "section__actions--between section__actions--align-bottom" >
< div >
< label > { _ _ ( 'Replay video available' ) } < / label >
< div className = "button-group" >
{ fileSelectorModes . map ( ( fmode ) => (
< Button
key = { fmode . label }
icon = { fmode . icon || undefined }
iconSize = { 18 }
label = { fmode . label }
button = "alt"
onClick = { ( ) => {
// $FlowFixMe
handleFileSource ( fmode . actionName ) ;
} }
className = { classnames ( 'button-toggle' , {
'button-toggle--active' : fileSource === fmode . actionName ,
} ) }
/ >
) ) }
< / div >
< / div >
{ fileSource === SOURCE _SELECT && (
2021-04-14 06:06:11 +02:00
< Button
2022-06-03 15:28:12 +02:00
button = "secondary"
label = { _ _ ( 'Check for Replays' ) }
disabled = { isCheckingLivestreams }
icon = { ICONS . REFRESH }
onClick = { ( ) => checkLivestreams ( channelId , channelName ) }
2021-04-14 06:06:11 +02:00
/ >
2022-06-03 15:28:12 +02:00
) }
2021-04-14 06:06:11 +02:00
< / div >
2022-06-03 15:28:12 +02:00
< / f i e l d s e t - s e c t i o n >
) }
{ fileSource === SOURCE _UPLOAD && showFileUpload && (
< >
< FileSelector
disabled = { disabled }
currentPath = { currentFile }
onFileChosen = { handleFileChange }
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
accept = { SIMPLE _SITE ? 'video/mp4,video/x-m4v,video/*,audio/*' : undefined }
placeholder = {
SIMPLE _SITE ? _ _ ( 'Select video or audio file to upload' ) : _ _ ( 'Select a file to upload' )
}
2021-04-14 06:06:11 +02:00
/ >
2022-06-03 15:28:12 +02:00
{ getUploadMessage ( ) }
< / >
) }
{ fileSource === SOURCE _SELECT && showFileUpload && hasLivestreamData && ! isCheckingLivestreams && (
< >
< fieldset - section >
< label > { _ _ ( 'Select Replay' ) } < / label >
< div className = "table__wrapper" >
< table className = "table table--livestream-data" >
< tbody >
{ livestreamData
. slice ( ( currentPage - 1 ) * PAGE _SIZE , currentPage * PAGE _SIZE )
. map ( ( item , i ) => (
< tr
onClick = { ( ) => setSelectedFileIndex ( ( currentPage - 1 ) * PAGE _SIZE + i ) }
key = { item . id }
className = { classnames ( 'livestream__data-row' , {
'livestream__data-row--selected' :
selectedFileIndex === ( currentPage - 1 ) * PAGE _SIZE + i ,
} ) }
>
< td >
< FormField
type = "radio"
checked = { selectedFileIndex === ( currentPage - 1 ) * PAGE _SIZE + i }
label = { null }
onClick = { ( ) => setSelectedFileIndex ( ( currentPage - 1 ) * PAGE _SIZE + i ) }
className = "livestream__data-row-radio"
/ >
< / td >
< td >
< div className = "livestream_thumb_container" >
{ item . data . thumbnails . slice ( 0 , 3 ) . map ( ( thumb ) => (
< img key = { thumb } className = "livestream___thumb" src = { thumb } / >
) ) }
< / div >
< / td >
< td >
{ item . data . fileDuration && isNaN ( item . data . fileDuration )
? item . data . fileDuration
: ` ${ Math . floor ( item . data . fileDuration / 60 ) } ${
Math . floor ( item . data . fileDuration / 60 ) > 1 ? _ _ ( 'minutes' ) : _ _ ( 'minute' )
} ` }
< div className = "table__item-label" >
{ ` ${ moment ( item . data . uploadedAt ) . from ( moment ( ) ) } ` }
< / div >
< / td >
< td >
< CopyableText
primaryButton
copyable = { normalizeUrlForProtocol ( item . data . fileLocation ) }
snackMessage = { _ _ ( 'Url copied.' ) }
/ >
< / td >
< / tr >
) ) }
< / tbody >
< / table >
< / div >
< / f i e l d s e t - s e c t i o n >
< fieldset - group class = "fieldset-group--smushed fieldgroup--paginate" >
< fieldset - section >
< ReactPaginate
pageCount = { totalPages }
pageRangeDisplayed = { 2 }
previousLabel = "‹ "
nextLabel = "› "
activeClassName = "pagination__item--selected"
pageClassName = "pagination__item"
previousClassName = "pagination__item pagination__item--previous"
nextClassName = "pagination__item pagination__item--next"
breakClassName = "pagination__item pagination__item--break"
marginPagesDisplayed = { 2 }
onPageChange = { ( e ) => handlePaginateReplays ( e . selected + 1 ) }
forcePage = { currentPage - 1 }
initialPage = { currentPage - 1 }
containerClassName = "pagination"
/ >
< / f i e l d s e t - s e c t i o n >
< / f i e l d s e t - g r o u p >
< / >
) }
{ fileSource === SOURCE _SELECT && showFileUpload && ! hasLivestreamData && ! isCheckingLivestreams && (
< div className = "main--empty empty" >
< Empty text = { _ _ ( 'No replays found.' ) } / >
< / div >
) }
{ fileSource === SOURCE _SELECT && showFileUpload && isCheckingLivestreams && (
< div className = "main--empty empty" >
< Spinner small / >
< / div >
) }
< / >
< FormField
type = "text"
name = "content_title"
label = { _ _ ( 'Title' ) }
placeholder = { _ _ ( 'Descriptive titles work best' ) }
disabled = { disabled }
value = { title }
onChange = { handleTitleChange }
className = "fieldset-group"
/ >
< PublishName uri = { uri } / >
2021-04-14 06:06:11 +02:00
2022-06-03 15:28:12 +02:00
{ /* @endif */ }
{ /* @if TARGET='app' */ }
{ showFileUpload && (
2021-04-14 06:06:11 +02:00
< FileSelector
2021-07-21 22:15:56 +02:00
label = { _ _ ( 'File' ) }
2021-04-14 06:06:11 +02:00
disabled = { disabled }
currentPath = { currentFile }
onFileChosen = { handleFileChange }
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
2022-06-03 15:28:12 +02:00
placeholder = { _ _ ( 'Select file to upload' ) }
2021-04-14 06:06:11 +02:00
/ >
2022-06-03 15:28:12 +02:00
) }
{ showFileUpload && (
< FormField
type = "checkbox"
checked = { userOptimize }
disabled = { ! optimizeAvail }
onChange = { ( ) => setUserOptimize ( ! userOptimize ) }
label = { _ _ ( 'Optimize and transcode video' ) }
name = "optimize"
/ >
) }
{ showFileUpload && ! ffmpegAvail && (
< p className = "help" >
< I18nMessage
tokens = { {
settings _link : < Button button = "link" navigate = "/$/settings" label = { _ _ ( 'Settings' ) } / > ,
} }
>
FFmpeg not configured . More in % settings _link % .
< / I18nMessage >
< / p >
) }
{ showFileUpload && Boolean ( size ) && ffmpegAvail && optimize && isVid && (
< p className = "help" >
< I18nMessage
tokens = { {
size : Math . ceil ( sizeInMB ) ,
processTime : getTimeForMB ( sizeInMB ) ,
units : getUnitsForMB ( sizeInMB ) ,
} }
>
Transcoding this % size % MB file should take under % processTime % % units % .
< / I18nMessage >
< / p >
) }
{ /* @endif */ }
{ isPublishPost && (
< PostEditor
label = { _ _ ( 'Post --[noun, markdown post tab button]--' ) }
uri = { uri }
disabled = { disabled }
fileMimeType = { fileMimeType }
setPrevFileText = { setPrevFileText }
setCurrentFileType = { setCurrentFileType }
/ >
) }
< / React.Fragment >
< / div >
< / >
2019-09-27 20:56:15 +02:00
}
/ >
2019-06-28 09:27:55 +02:00
) ;
}
export default PublishFile ;