file selector operational for thumbs, publishes, thumb generation, channel edits
This commit is contained in:
parent
bd1d32e5d5
commit
f8ea6164f7
11 changed files with 92 additions and 135 deletions
4
flow-typed/web-file.js
vendored
Normal file
4
flow-typed/web-file.js
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
declare type WebFile = {
|
||||
name: string,
|
||||
path?: string,
|
||||
}
|
|
@ -1,27 +1,18 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
// @if TARGET='app'
|
||||
// $FlowFixMe
|
||||
import { remote } from 'electron';
|
||||
// @endif
|
||||
|
||||
import Button from 'component/button';
|
||||
import { FormField } from 'component/common/form';
|
||||
import path from 'path';
|
||||
|
||||
type FileFilters = {
|
||||
name: string,
|
||||
extensions: string[],
|
||||
};
|
||||
|
||||
type Props = {
|
||||
type: string,
|
||||
currentPath?: ?string,
|
||||
onFileChosen: (string, string) => void,
|
||||
onFileChosen: WebFile => void,
|
||||
label?: string,
|
||||
placeholder?: string,
|
||||
fileLabel?: string,
|
||||
directoryLabel?: string,
|
||||
filters?: FileFilters[],
|
||||
accept?: string,
|
||||
};
|
||||
|
||||
class FileSelector extends React.PureComponent<Props> {
|
||||
|
@ -33,78 +24,53 @@ class FileSelector extends React.PureComponent<Props> {
|
|||
|
||||
constructor() {
|
||||
super();
|
||||
// @if TARGET='web'
|
||||
this.fileInput = React.createRef();
|
||||
// @endif
|
||||
this.handleFileInputSelection = this.handleFileInputSelection.bind(this);
|
||||
this.fileInputButton = this.fileInputButton.bind(this);
|
||||
}
|
||||
|
||||
handleButtonClick() {
|
||||
remote.dialog.showOpenDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
properties: this.props.type === 'file' ? ['openFile'] : ['openDirectory', 'createDirectory'],
|
||||
filters: this.props.filters,
|
||||
},
|
||||
paths => {
|
||||
if (!paths) {
|
||||
// User hit cancel, so do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = paths[0];
|
||||
|
||||
const extension = path.extname(filePath);
|
||||
const fileName = path.basename(filePath, extension);
|
||||
|
||||
if (this.props.onFileChosen) {
|
||||
this.props.onFileChosen(filePath, fileName);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Add this back for web publishing
|
||||
handleFileInputSelection() {
|
||||
handleFileInputSelection = () => {
|
||||
const { files } = this.fileInput.current;
|
||||
if (!files) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = files[0];
|
||||
const fileName = filePath.name;
|
||||
|
||||
const file = files[0];
|
||||
if (this.props.onFileChosen) {
|
||||
this.props.onFileChosen(filePath, fileName);
|
||||
this.props.onFileChosen(file);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fileInputButton = () => {
|
||||
this.fileInput.current.click();
|
||||
};
|
||||
|
||||
input: ?HTMLInputElement;
|
||||
|
||||
render() {
|
||||
const { type, currentPath, label, fileLabel, directoryLabel, placeholder } = this.props;
|
||||
const { type, currentPath, label, fileLabel, directoryLabel, placeholder, accept } = this.props;
|
||||
|
||||
const buttonLabel = type === 'file' ? fileLabel || __('Choose File') : directoryLabel || __('Choose Directory');
|
||||
|
||||
const placeHolder = currentPath || placeholder;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{/* @if TARGET='app' */}
|
||||
<FormField
|
||||
label={label}
|
||||
webkitdirectory="true"
|
||||
className="form-field--copyable"
|
||||
type="text"
|
||||
ref={this.fileInput}
|
||||
onFocus={() => {
|
||||
if (this.fileInput) this.fileInput.select();
|
||||
}}
|
||||
readOnly="readonly"
|
||||
value={currentPath || placeholder || __('Choose a file')}
|
||||
inputButton={<Button button="primary" onClick={() => this.handleButtonClick()} label={buttonLabel} />}
|
||||
value={placeHolder || __('Choose a file')}
|
||||
inputButton={<Button button="primary" onClick={this.fileInputButton} label={buttonLabel} />}
|
||||
/>
|
||||
<input
|
||||
type={'file'}
|
||||
style={{ display: 'none' }}
|
||||
accept={accept}
|
||||
ref={this.fileInput}
|
||||
onChange={() => this.handleFileInputSelection()}
|
||||
webkitdirectory={type === 'openDirectory' ? 'True' : null}
|
||||
/>
|
||||
{/* @endif */}
|
||||
{/* @if TARGET='web' */}
|
||||
<input type="file" ref={this.fileInput} onChange={() => this.handleFileInputSelection()} />
|
||||
{/* @endif */}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import Card from 'component/common/card';
|
|||
|
||||
type Props = {
|
||||
name: ?string,
|
||||
filePath: ?string,
|
||||
filePath: string | WebFile,
|
||||
isStillEditing: boolean,
|
||||
balance: number,
|
||||
updatePublishForm: ({}) => void,
|
||||
|
@ -18,13 +18,26 @@ type Props = {
|
|||
function PublishFile(props: Props) {
|
||||
const { name, balance, filePath, isStillEditing, updatePublishForm, disabled } = props;
|
||||
|
||||
function handleFileChange(filePath: string, fileName: string) {
|
||||
const publishFormParams: { filePath: string, name?: string } = { filePath };
|
||||
|
||||
if (!name) {
|
||||
const parsedFileName = fileName.replace(regexInvalidURI, '');
|
||||
publishFormParams.name = parsedFileName.replace(' ', '-');
|
||||
let currentFile = '';
|
||||
if (filePath) {
|
||||
if (typeof filePath === 'string') {
|
||||
currentFile = filePath;
|
||||
} else {
|
||||
currentFile = filePath.name;
|
||||
}
|
||||
}
|
||||
|
||||
function handleFileChange(file: WebFile) {
|
||||
// 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.
|
||||
const publishFormParams: { filePath: string | WebFile, name?: string } = {
|
||||
filePath: file.path || file,
|
||||
name: file.name,
|
||||
};
|
||||
const parsedFileName = file.name.replace(regexInvalidURI, '');
|
||||
|
||||
publishFormParams.name = parsedFileName.replace(' ', '-');
|
||||
|
||||
updatePublishForm(publishFormParams);
|
||||
}
|
||||
|
@ -39,7 +52,7 @@ function PublishFile(props: Props) {
|
|||
}
|
||||
actions={
|
||||
<React.Fragment>
|
||||
<FileSelector currentPath={filePath} onFileChosen={handleFileChange} />
|
||||
<FileSelector currentPath={currentFile} onFileChosen={handleFileChange} />
|
||||
{!isStillEditing && (
|
||||
<p className="help">
|
||||
{__('For video content, use MP4s in H264/AAC format for best compatibility.')}{' '}
|
||||
|
|
|
@ -4,18 +4,10 @@ import React, { useState } from 'react';
|
|||
import { FormField } from 'component/common/form';
|
||||
import FileSelector from 'component/common/file-selector';
|
||||
import Button from 'component/button';
|
||||
// @if TARGET='app'
|
||||
import fs from 'fs';
|
||||
// @endif
|
||||
import path from 'path';
|
||||
import { SPEECH_URLS } from 'lbry-redux';
|
||||
import uuid from 'uuid/v4';
|
||||
|
||||
const filters = [
|
||||
{
|
||||
name: __('Thumbnail Image'),
|
||||
extensions: ['png', 'jpg', 'jpeg', 'gif'],
|
||||
},
|
||||
];
|
||||
const accept = '.png, .jpg, .jpeg, .gif';
|
||||
|
||||
const SOURCE_URL = 'url';
|
||||
const SOURCE_UPLOAD = 'upload';
|
||||
|
@ -32,29 +24,10 @@ function SelectAsset(props: Props) {
|
|||
const { onUpdate, assetName, currentValue, recommended } = props;
|
||||
const [assetSource, setAssetSource] = useState(SOURCE_URL);
|
||||
const [pathSelected, setPathSelected] = useState('');
|
||||
const [fileSelected, setFileSelected] = useState(null);
|
||||
const [uploadStatus, setUploadStatus] = useState(SPEECH_READY);
|
||||
|
||||
function doUploadAsset(filePath, thumbnailBuffer) {
|
||||
let thumbnail, fileExt, fileName, fileType;
|
||||
if (IS_WEB) {
|
||||
console.error('no upload support for web');
|
||||
return;
|
||||
}
|
||||
|
||||
if (filePath) {
|
||||
thumbnail = fs.readFileSync(filePath);
|
||||
fileExt = path.extname(filePath);
|
||||
fileName = path.basename(filePath);
|
||||
fileType = `image/${fileExt.slice(1)}`;
|
||||
} else if (thumbnailBuffer) {
|
||||
thumbnail = thumbnailBuffer;
|
||||
fileExt = '.png';
|
||||
fileName = 'thumbnail.png';
|
||||
fileType = 'image/png';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
function doUploadAsset(file) {
|
||||
const uploadError = (error = '') => {
|
||||
console.log('error', error);
|
||||
};
|
||||
|
@ -69,11 +42,10 @@ function SelectAsset(props: Props) {
|
|||
|
||||
const data = new FormData();
|
||||
const name = uuid();
|
||||
const file = new File([thumbnail], fileName, { type: fileType });
|
||||
data.append('name', name);
|
||||
data.append('file', file);
|
||||
|
||||
return fetch('https://spee.ch/api/claim/publish', {
|
||||
return fetch(SPEECH_URLS.SPEECH_PUBLISH, {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
})
|
||||
|
@ -104,22 +76,26 @@ function SelectAsset(props: Props) {
|
|||
<FileSelector
|
||||
label={'File to upload'}
|
||||
name={'assetSelector'}
|
||||
onFileChosen={path => {
|
||||
setPathSelected(path);
|
||||
onFileChosen={file => {
|
||||
if (file.name) {
|
||||
setPathSelected(file.path);
|
||||
setFileSelected(file);
|
||||
}
|
||||
}}
|
||||
filters={filters}
|
||||
accept={accept}
|
||||
/>
|
||||
)}
|
||||
{pathSelected && (
|
||||
<div>
|
||||
{`...${pathSelected.slice(-18)}`} {uploadStatus}{' '}
|
||||
<Button button={'primary'} onClick={() => doUploadAsset(pathSelected)}>
|
||||
<Button button={'primary'} onClick={() => doUploadAsset(fileSelected)}>
|
||||
Upload
|
||||
</Button>{' '}
|
||||
<Button
|
||||
button={'secondary'}
|
||||
onClick={() => {
|
||||
setPathSelected('');
|
||||
setFileSelected(null);
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
|
|
|
@ -25,13 +25,6 @@ type State = {
|
|||
thumbnailError: boolean,
|
||||
};
|
||||
|
||||
const filters = [
|
||||
{
|
||||
name: __('Thumbnail Image'),
|
||||
extensions: ['png', 'jpg', 'jpeg', 'gif'],
|
||||
},
|
||||
];
|
||||
|
||||
class SelectThumbnail extends React.PureComponent<Props, State> {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -66,13 +59,19 @@ class SelectThumbnail extends React.PureComponent<Props, State> {
|
|||
} = this.props;
|
||||
|
||||
const { thumbnailError } = this.state;
|
||||
const accept = '.png, .jpg, .jpeg, .gif';
|
||||
|
||||
const outpoint = myClaimForUri ? `${myClaimForUri.txid}:${myClaimForUri.nout}` : undefined;
|
||||
const fileInfo = outpoint ? fileInfos[outpoint] : undefined;
|
||||
const downloadPath = fileInfo ? fileInfo.download_path : undefined;
|
||||
|
||||
const actualFilePath = filePath || downloadPath;
|
||||
const isSupportedVideo = Lbry.getMediaType(null, actualFilePath) === 'video';
|
||||
let isSupportedVideo = false;
|
||||
if (typeof filePath === 'string') {
|
||||
isSupportedVideo = Lbry.getMediaType(null, actualFilePath) === 'video';
|
||||
} else if (filePath && filePath.type) {
|
||||
isSupportedVideo = filePath.type.split('/')[0] === 'video';
|
||||
}
|
||||
|
||||
let thumbnailSrc;
|
||||
if (!thumbnail) {
|
||||
|
@ -132,8 +131,8 @@ class SelectThumbnail extends React.PureComponent<Props, State> {
|
|||
currentPath={thumbnailPath}
|
||||
label={__('Thumbnail')}
|
||||
placeholder={__('Choose a thumbnail')}
|
||||
filters={filters}
|
||||
onFileChosen={path => openModal(MODALS.CONFIRM_THUMBNAIL_UPLOAD, { path })}
|
||||
accept={accept}
|
||||
onFileChosen={file => openModal(MODALS.CONFIRM_THUMBNAIL_UPLOAD, { file })}
|
||||
/>
|
||||
)}
|
||||
{status === THUMBNAIL_STATUSES.COMPLETE && thumbnail && (
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doHideModal } from 'redux/actions/app';
|
||||
import { doToast, doUploadThumbnail } from 'lbry-redux';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import ModalAutoGenerateThumbnail from './view';
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doHideModal()),
|
||||
upload: buffer => dispatch(doUploadThumbnail(null, buffer, null, fs, path)),
|
||||
upload: file => dispatch(doUploadThumbnail(null, file, null, null, 'Generated')),
|
||||
showToast: options => dispatch(doToast(options)),
|
||||
});
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Modal } from 'modal/modal';
|
|||
import { formatPathForWeb } from 'util/uri';
|
||||
|
||||
type Props = {
|
||||
upload: Buffer => void,
|
||||
upload: WebFile => void,
|
||||
filePath: string,
|
||||
closeModal: () => void,
|
||||
showToast: ({}) => void,
|
||||
|
@ -13,12 +13,18 @@ type Props = {
|
|||
function ModalAutoGenerateThumbnail(props: Props) {
|
||||
const { closeModal, filePath, upload, showToast } = props;
|
||||
const playerRef = useRef();
|
||||
const videoSrc = formatPathForWeb(filePath);
|
||||
let videoSrc;
|
||||
if (typeof filePath === 'string') {
|
||||
videoSrc = formatPathForWeb(filePath);
|
||||
} else {
|
||||
videoSrc = URL.createObjectURL(filePath);
|
||||
}
|
||||
|
||||
function uploadImage() {
|
||||
const imageBuffer = captureSnapshot();
|
||||
const file = new File([imageBuffer], 'thumbnail.png', { type: 'image/png' });
|
||||
if (imageBuffer) {
|
||||
upload(imageBuffer);
|
||||
upload(file);
|
||||
closeModal();
|
||||
} else {
|
||||
onError();
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doHideModal } from 'redux/actions/app';
|
||||
import { doUploadThumbnail, doUpdatePublishForm } from 'lbry-redux';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import ModalConfirmThumbnailUpload from './view';
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doHideModal()),
|
||||
upload: filePath => dispatch(doUploadThumbnail(filePath, null, null, fs, path)),
|
||||
upload: file => dispatch(doUploadThumbnail(null, file, null, null, file.path)),
|
||||
updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
|
||||
});
|
||||
|
||||
|
|
|
@ -3,22 +3,22 @@ import React from 'react';
|
|||
import { Modal } from 'modal/modal';
|
||||
|
||||
type Props = {
|
||||
upload: string => void,
|
||||
path: string,
|
||||
upload: WebFile => void,
|
||||
file: WebFile,
|
||||
closeModal: () => void,
|
||||
updatePublishForm: ({}) => void,
|
||||
};
|
||||
|
||||
class ModalConfirmThumbnailUpload extends React.PureComponent<Props> {
|
||||
upload() {
|
||||
const { upload, updatePublishForm, closeModal, path } = this.props;
|
||||
upload(path);
|
||||
updatePublishForm({ thumbnailPath: path });
|
||||
const { upload, updatePublishForm, closeModal, file } = this.props;
|
||||
upload(file);
|
||||
updatePublishForm({ thumbnailPath: file.path });
|
||||
closeModal();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { closeModal, path } = this.props;
|
||||
const { closeModal, file } = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
@ -32,7 +32,7 @@ class ModalConfirmThumbnailUpload extends React.PureComponent<Props> {
|
|||
>
|
||||
<p>{__('Are you sure you want to upload this thumbnail to spee.ch')}?</p>
|
||||
|
||||
<blockquote>{path}</blockquote>
|
||||
<blockquote>{file.path || file.name}</blockquote>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -233,8 +233,8 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
<FileSelector
|
||||
type="openDirectory"
|
||||
currentPath={daemonSettings.download_dir}
|
||||
onFileChosen={(newDirectory: string) => {
|
||||
setDaemonSetting('download_dir', newDirectory);
|
||||
onFileChosen={(newDirectory: WebFile) => {
|
||||
setDaemonSetting('download_dir', newDirectory.path);
|
||||
}}
|
||||
/>
|
||||
<p className="help">{__('LBRY downloads will be saved here.')}</p>
|
||||
|
|
|
@ -710,9 +710,6 @@
|
|||
"You have %credit_amount% in unclaimed rewards.": "You have %credit_amount% in unclaimed rewards.",
|
||||
"URI does not include name.": "URI does not include name.",
|
||||
"to fix it. If that doesn't work, press CMD/CTRL-R to reset to the homepage.": "to fix it. If that doesn't work, press CMD/CTRL-R to reset to the homepage.",
|
||||
"Add Email": "Add Email",
|
||||
"Sign Out": "Sign Out",
|
||||
"Follow more tags": "Follow more tags",
|
||||
"In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this channel from our applications.": "In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this channel from our applications.",
|
||||
"Read More": "Read More",
|
||||
"Subscribers": "Subscribers",
|
||||
|
|
Loading…
Add table
Reference in a new issue