make transcoding work
appstrings provide optimize checkbox on publish fix missing status no crash on web cleanup better settings ui add help and time estimate to publish transcoding messaging fix: Special SDK + fix config name fix: older SDK build fix app string, style tweak whoops, and looks better to me this way. bump SDK
This commit is contained in:
parent
eb54d899fb
commit
e35fbdd86a
15 changed files with 279 additions and 50 deletions
2
flow-typed/web-file.js
vendored
2
flow-typed/web-file.js
vendored
|
@ -1,7 +1,7 @@
|
||||||
declare type WebFile = {
|
declare type WebFile = {
|
||||||
name: string,
|
name: string,
|
||||||
title?: string,
|
title?: string,
|
||||||
path?: string,
|
path: string,
|
||||||
size: string,
|
size: string,
|
||||||
type: string,
|
type: string,
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,7 +209,7 @@
|
||||||
"yarn": "^1.3"
|
"yarn": "^1.3"
|
||||||
},
|
},
|
||||||
"lbrySettings": {
|
"lbrySettings": {
|
||||||
"lbrynetDaemonVersion": "0.64.0",
|
"lbrynetDaemonVersion": "0.65.0",
|
||||||
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-OSNAME.zip",
|
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-OSNAME.zip",
|
||||||
"lbrynetDaemonDir": "static/daemon",
|
"lbrynetDaemonDir": "static/daemon",
|
||||||
"lbrynetDaemonFileName": "lbrynet"
|
"lbrynetDaemonFileName": "lbrynet"
|
||||||
|
|
|
@ -1027,6 +1027,12 @@
|
||||||
"For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.": "For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.",
|
"For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.": "For video content, use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.",
|
||||||
"Your video may not be the best format. Use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.": "Your video may not be the best format. Use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.",
|
"Your video may not be the best format. Use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.": "Your video may not be the best format. Use MP4s in H264/AAC format and a friendly bitrate (720p) for more reliable streaming.",
|
||||||
"Your video has a bitrate over 6 mbps. We suggest transcoding to provide viewers the best experience.": "Your video has a bitrate over 6 mbps. We suggest transcoding to provide viewers the best experience.",
|
"Your video has a bitrate over 6 mbps. We suggest transcoding to provide viewers the best experience.": "Your video has a bitrate over 6 mbps. We suggest transcoding to provide viewers the best experience.",
|
||||||
|
"Transcoding": "Transcoding",
|
||||||
|
"Optimize and transcode video": "Optimize and transcode video",
|
||||||
|
"FFmpeg not found": "FFmpeg not found",
|
||||||
|
"FFmpeg is correctly configured": "FFmpeg is correctly configured",
|
||||||
|
"ffmpeg not found": "ffmpeg not found",
|
||||||
|
"Known Tags": "Known Tags",
|
||||||
"Your video has a bitrate over 5 mbps. We suggest transcoding to provide viewers the best experience.": "Your video has a bitrate over 5 mbps. We suggest transcoding to provide viewers the best experience.",
|
"Your video has a bitrate over 5 mbps. We suggest transcoding to provide viewers the best experience.": "Your video has a bitrate over 5 mbps. We suggest transcoding to provide viewers the best experience.",
|
||||||
"Almost there": "Almost there",
|
"Almost there": "Almost there",
|
||||||
"More Channels": "More Channels",
|
"More Channels": "More Channels",
|
||||||
|
|
|
@ -7,14 +7,17 @@ import {
|
||||||
doToast,
|
doToast,
|
||||||
doClearPublish,
|
doClearPublish,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
|
import { selectFfmpegStatus } from 'redux/selectors/settings';
|
||||||
import PublishPage from './view';
|
import PublishPage from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
name: makeSelectPublishFormValue('name')(state),
|
name: makeSelectPublishFormValue('name')(state),
|
||||||
filePath: makeSelectPublishFormValue('filePath')(state),
|
filePath: makeSelectPublishFormValue('filePath')(state),
|
||||||
|
optimize: makeSelectPublishFormValue('optimize')(state),
|
||||||
isStillEditing: selectIsStillEditing(state),
|
isStillEditing: selectIsStillEditing(state),
|
||||||
balance: selectBalance(state),
|
balance: selectBalance(state),
|
||||||
publishing: makeSelectPublishFormValue('publishing')(state),
|
publishing: makeSelectPublishFormValue('publishing')(state),
|
||||||
|
ffmpegStatus: selectFfmpegStatus(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -5,7 +5,9 @@ import { regexInvalidURI } from 'lbry-redux';
|
||||||
import FileSelector from 'component/common/file-selector';
|
import FileSelector from 'component/common/file-selector';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
|
import { FormField } from 'component/common/form';
|
||||||
import Spinner from 'component/spinner';
|
import Spinner from 'component/spinner';
|
||||||
|
import I18nMessage from '../i18nMessage';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
name: ?string,
|
name: ?string,
|
||||||
|
@ -18,6 +20,8 @@ type Props = {
|
||||||
showToast: string => void,
|
showToast: string => void,
|
||||||
inProgress: boolean,
|
inProgress: boolean,
|
||||||
clearPublish: () => void,
|
clearPublish: () => void,
|
||||||
|
ffmpegStatus: any,
|
||||||
|
optimize: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function PublishFile(props: Props) {
|
function PublishFile(props: Props) {
|
||||||
|
@ -31,8 +35,11 @@ function PublishFile(props: Props) {
|
||||||
publishing,
|
publishing,
|
||||||
inProgress,
|
inProgress,
|
||||||
clearPublish,
|
clearPublish,
|
||||||
|
optimize,
|
||||||
|
ffmpegStatus = {},
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const { available } = ffmpegStatus;
|
||||||
const [duration, setDuration] = useState(0);
|
const [duration, setDuration] = useState(0);
|
||||||
const [size, setSize] = useState(0);
|
const [size, setSize] = useState(0);
|
||||||
const [oversized, setOversized] = useState(false);
|
const [oversized, setOversized] = useState(false);
|
||||||
|
@ -40,6 +47,12 @@ function PublishFile(props: Props) {
|
||||||
const RECOMMENDED_BITRATE = 6000000;
|
const RECOMMENDED_BITRATE = 6000000;
|
||||||
const TV_PUBLISH_SIZE_LIMIT: number = 1073741824;
|
const TV_PUBLISH_SIZE_LIMIT: number = 1073741824;
|
||||||
const UPLOAD_SIZE_MESSAGE = 'Lbrytv uploads are limited to 1 GB. Download the app for unrestricted publishing.';
|
const UPLOAD_SIZE_MESSAGE = 'Lbrytv uploads are limited to 1 GB. Download the app for unrestricted publishing.';
|
||||||
|
const PROCESSING_MB_PER_SECOND = 0.5;
|
||||||
|
const MINUTES_THRESHOLD = 30;
|
||||||
|
const HOURS_THRESHOLD = MINUTES_THRESHOLD * 60;
|
||||||
|
|
||||||
|
const sizeInMB = Number(size) / 1000000;
|
||||||
|
const secondsToProcess = sizeInMB / PROCESSING_MB_PER_SECOND;
|
||||||
|
|
||||||
// clear warnings
|
// clear warnings
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -70,6 +83,29 @@ function PublishFile(props: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (secondsToProcess > 1) return 'seconds';
|
||||||
|
return 'second';
|
||||||
|
} else if (s >= MINUTES_THRESHOLD && s < HOURS_THRESHOLD) {
|
||||||
|
if (Math.floor(secondsToProcess / 60) > 1) return 'minutes';
|
||||||
|
return 'minute';
|
||||||
|
} else {
|
||||||
|
if (Math.floor(secondsToProcess / 3600) > 1) return 'hours';
|
||||||
|
return 'hour';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getMessage() {
|
function getMessage() {
|
||||||
// @if TARGET='web'
|
// @if TARGET='web'
|
||||||
if (oversized) {
|
if (oversized) {
|
||||||
|
@ -189,7 +225,7 @@ function PublishFile(props: Props) {
|
||||||
}
|
}
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
const publishFormParams: { filePath: string | WebFile, name?: string } = {
|
const publishFormParams: { filePath: string | WebFile, name?: string, optimize?: boolean } = {
|
||||||
filePath: file.path || file,
|
filePath: file.path || file,
|
||||||
};
|
};
|
||||||
// Strip off extention and replace invalid characters
|
// Strip off extention and replace invalid characters
|
||||||
|
@ -232,6 +268,40 @@ function PublishFile(props: Props) {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<FileSelector disabled={disabled} currentPath={currentFile} onFileChosen={handleFileChange} />
|
<FileSelector disabled={disabled} currentPath={currentFile} onFileChosen={handleFileChange} />
|
||||||
{getMessage()}
|
{getMessage()}
|
||||||
|
{/* @if TARGET='app' */}
|
||||||
|
<FormField
|
||||||
|
type="checkbox"
|
||||||
|
checked={isVid && available && optimize}
|
||||||
|
disabled={!isVid || !available}
|
||||||
|
onChange={e => updatePublishForm({ optimize: e.target.checked })}
|
||||||
|
label={__('Optimize and transcode video')}
|
||||||
|
name="optimize"
|
||||||
|
/>
|
||||||
|
{!available && (
|
||||||
|
<p className="help">
|
||||||
|
<I18nMessage
|
||||||
|
tokens={{
|
||||||
|
settings_link: <Button button="link" navigate="/$/settings" label={__('Settings')} />,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
FFmpeg not configured. More in %settings_link%.
|
||||||
|
</I18nMessage>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{Boolean(size) && available && 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 */}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -143,9 +143,10 @@ function PublishForm(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<PublishFile disabled={disabled || publishing} inProgress={isInProgress} />
|
<PublishFile disabled={disabled || publishing} inProgress={isInProgress} />
|
||||||
<div className={classnames({ 'card--disabled': formDisabled })}>
|
{!publishing && (
|
||||||
<PublishText disabled={formDisabled} />
|
<div className={classnames({ 'card--disabled': formDisabled })}>
|
||||||
<Card actions={<SelectThumbnail />} />
|
<PublishText disabled={formDisabled} />
|
||||||
|
<Card actions={<SelectThumbnail />} />
|
||||||
|
|
||||||
<TagsSelect
|
<TagsSelect
|
||||||
suggestMature
|
suggestMature
|
||||||
|
@ -174,40 +175,40 @@ function PublishForm(props: Props) {
|
||||||
tagsChosen={tags}
|
tagsChosen={tags}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
actions={
|
actions={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<SelectChannel channel={channel} onChannelChange={channel => updatePublishForm({ channel })} />
|
<SelectChannel channel={channel} onChannelChange={channel => updatePublishForm({ channel })} />
|
||||||
<p className="help">
|
<p className="help">
|
||||||
{__('This is a username or handle that your content can be found under.')}{' '}
|
{__('This is a username or handle that your content can be found under.')}{' '}
|
||||||
{__('Ex. @Marvel, @TheBeatles, @BooksByJoe')}
|
{__('Ex. @Marvel, @TheBeatles, @BooksByJoe')}
|
||||||
</p>
|
</p>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PublishName disabled={formDisabled} />
|
<PublishName disabled={formDisabled} />
|
||||||
<PublishPrice disabled={formDisabled} />
|
<PublishPrice disabled={formDisabled} />
|
||||||
<PublishAdditionalOptions disabled={formDisabled} />
|
<PublishAdditionalOptions disabled={formDisabled} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<section>
|
||||||
|
{!formDisabled && !formValid && <PublishFormErrors />}
|
||||||
|
|
||||||
<section>
|
<div className="card__actions">
|
||||||
{!formDisabled && !formValid && <PublishFormErrors />}
|
<Button
|
||||||
|
button="primary"
|
||||||
<div className="card__actions">
|
onClick={() => publish(filePath)}
|
||||||
<Button
|
label={submitLabel}
|
||||||
button="primary"
|
disabled={formDisabled || !formValid || uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS}
|
||||||
onClick={() => publish(filePath)}
|
/>
|
||||||
label={submitLabel}
|
<Button button="link" onClick={clearPublish} label={__('Cancel')} />
|
||||||
disabled={formDisabled || !formValid || uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS}
|
</div>
|
||||||
/>
|
<p className="help">
|
||||||
<Button button="link" onClick={clearPublish} label={__('Cancel')} />
|
{__('By continuing, you accept the')}{' '}
|
||||||
</div>
|
<Button button="link" href="https://www.lbry.com/termsofservice" label={__('LBRY Terms of Service')} />.
|
||||||
<p className="help">
|
</p>
|
||||||
{__('By continuing, you accept the')}{' '}
|
</section>
|
||||||
<Button button="link" href="https://www.lbry.com/termsofservice" label={__('LBRY Terms of Service')} />.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,6 +116,8 @@ export const UPDATE_SEARCH_SUGGESTIONS = 'UPDATE_SEARCH_SUGGESTIONS';
|
||||||
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
||||||
export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
|
export const CLIENT_SETTING_CHANGED = 'CLIENT_SETTING_CHANGED';
|
||||||
export const UPDATE_IS_NIGHT = 'UPDATE_IS_NIGHT';
|
export const UPDATE_IS_NIGHT = 'UPDATE_IS_NIGHT';
|
||||||
|
export const FINDING_FFMPEG_STARTED = 'FINDING_FFMPEG_STARTED';
|
||||||
|
export const FINDING_FFMPEG_COMPLETED = 'FINDING_FFMPEG_COMPLETED';
|
||||||
|
|
||||||
// User
|
// User
|
||||||
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
|
export const AUTHENTICATION_STARTED = 'AUTHENTICATION_STARTED';
|
||||||
|
|
|
@ -7,9 +7,21 @@ import {
|
||||||
doToggle3PAnalytics,
|
doToggle3PAnalytics,
|
||||||
} from 'redux/actions/app';
|
} from 'redux/actions/app';
|
||||||
import { selectAllowAnalytics } from 'redux/selectors/app';
|
import { selectAllowAnalytics } from 'redux/selectors/app';
|
||||||
import { doSetDaemonSetting, doSetClientSetting, doSetDarkTime } from 'redux/actions/settings';
|
import {
|
||||||
|
doSetDaemonSetting,
|
||||||
|
doClearDaemonSetting,
|
||||||
|
doSetClientSetting,
|
||||||
|
doSetDarkTime,
|
||||||
|
doFindFFmpeg,
|
||||||
|
} from 'redux/actions/settings';
|
||||||
import { doSetPlayingUri } from 'redux/actions/content';
|
import { doSetPlayingUri } from 'redux/actions/content';
|
||||||
import { makeSelectClientSetting, selectDaemonSettings, selectosNotificationsEnabled } from 'redux/selectors/settings';
|
import {
|
||||||
|
makeSelectClientSetting,
|
||||||
|
selectDaemonSettings,
|
||||||
|
selectFfmpegStatus,
|
||||||
|
selectosNotificationsEnabled,
|
||||||
|
selectFindingFFmpeg,
|
||||||
|
} from 'redux/selectors/settings';
|
||||||
import { doWalletStatus, selectWalletIsEncrypted, selectBlockedChannelsCount, SETTINGS } from 'lbry-redux';
|
import { doWalletStatus, selectWalletIsEncrypted, selectBlockedChannelsCount, SETTINGS } from 'lbry-redux';
|
||||||
import SettingsPage from './view';
|
import SettingsPage from './view';
|
||||||
import { selectUserVerifiedEmail } from 'lbryinc';
|
import { selectUserVerifiedEmail } from 'lbryinc';
|
||||||
|
@ -34,10 +46,13 @@ const select = state => ({
|
||||||
floatingPlayer: makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state),
|
floatingPlayer: makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state),
|
||||||
showReposts: makeSelectClientSetting(SETTINGS.SHOW_REPOSTS)(state),
|
showReposts: makeSelectClientSetting(SETTINGS.SHOW_REPOSTS)(state),
|
||||||
darkModeTimes: makeSelectClientSetting(SETTINGS.DARK_MODE_TIMES)(state),
|
darkModeTimes: makeSelectClientSetting(SETTINGS.DARK_MODE_TIMES)(state),
|
||||||
|
ffmpegStatus: selectFfmpegStatus(state),
|
||||||
|
findingFFmpeg: selectFindingFFmpeg(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
|
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
|
||||||
|
clearDaemonSetting: key => dispatch(doClearDaemonSetting(key)),
|
||||||
toggle3PAnalytics: allow => dispatch(doToggle3PAnalytics(allow)),
|
toggle3PAnalytics: allow => dispatch(doToggle3PAnalytics(allow)),
|
||||||
clearCache: () => dispatch(doClearCache()),
|
clearCache: () => dispatch(doClearCache()),
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
|
@ -47,6 +62,7 @@ const perform = dispatch => ({
|
||||||
confirmForgetPassword: modalProps => dispatch(doNotifyForgetPassword(modalProps)),
|
confirmForgetPassword: modalProps => dispatch(doNotifyForgetPassword(modalProps)),
|
||||||
clearPlayingUri: () => dispatch(doSetPlayingUri(null)),
|
clearPlayingUri: () => dispatch(doSetPlayingUri(null)),
|
||||||
setDarkTime: (time, options) => dispatch(doSetDarkTime(time, options)),
|
setDarkTime: (time, options) => dispatch(doSetDarkTime(time, options)),
|
||||||
|
findFFmpeg: () => dispatch(doFindFFmpeg()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -17,6 +17,7 @@ import SyncToggle from 'component/syncToggle';
|
||||||
import { SETTINGS } from 'lbry-redux';
|
import { SETTINGS } from 'lbry-redux';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
import { getPasswordFromCookie } from 'util/saved-passwords';
|
import { getPasswordFromCookie } from 'util/saved-passwords';
|
||||||
|
import Spinner from 'component/spinner';
|
||||||
|
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
export const IS_MAC = process.platform === 'darwin';
|
export const IS_MAC = process.platform === 'darwin';
|
||||||
|
@ -46,10 +47,12 @@ type DaemonSettings = {
|
||||||
max_connections_per_download?: number,
|
max_connections_per_download?: number,
|
||||||
save_files: boolean,
|
save_files: boolean,
|
||||||
save_blobs: boolean,
|
save_blobs: boolean,
|
||||||
|
ffmpeg_path: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
setDaemonSetting: (string, ?SetDaemonSettingArg) => void,
|
setDaemonSetting: (string, ?SetDaemonSettingArg) => void,
|
||||||
|
clearDaemonSetting: string => void,
|
||||||
setClientSetting: (string, SetDaemonSettingArg) => void,
|
setClientSetting: (string, SetDaemonSettingArg) => void,
|
||||||
toggle3PAnalytics: boolean => void,
|
toggle3PAnalytics: boolean => void,
|
||||||
clearCache: () => Promise<any>,
|
clearCache: () => Promise<any>,
|
||||||
|
@ -78,6 +81,9 @@ type Props = {
|
||||||
clearPlayingUri: () => void,
|
clearPlayingUri: () => void,
|
||||||
darkModeTimes: DarkModeTimes,
|
darkModeTimes: DarkModeTimes,
|
||||||
setDarkTime: (string, {}) => void,
|
setDarkTime: (string, {}) => void,
|
||||||
|
ffmpegStatus: { available: boolean, which: string },
|
||||||
|
findingFFmpeg: boolean,
|
||||||
|
findFFmpeg: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -105,7 +111,17 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { isAuthenticated } = this.props;
|
const { isAuthenticated, ffmpegStatus, daemonSettings, findFFmpeg } = this.props;
|
||||||
|
// @if TARGET='app'
|
||||||
|
const { available } = ffmpegStatus;
|
||||||
|
const { ffmpeg_path: ffmpegPath } = daemonSettings;
|
||||||
|
if (!available) {
|
||||||
|
if (ffmpegPath) {
|
||||||
|
this.clearDaemonSetting('ffmpeg_path');
|
||||||
|
}
|
||||||
|
findFFmpeg();
|
||||||
|
}
|
||||||
|
// @endif
|
||||||
if (isAuthenticated || !IS_WEB) {
|
if (isAuthenticated || !IS_WEB) {
|
||||||
this.props.updateWalletStatus();
|
this.props.updateWalletStatus();
|
||||||
getPasswordFromCookie().then(p => {
|
getPasswordFromCookie().then(p => {
|
||||||
|
@ -116,6 +132,11 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFFmpegFolder(path: string) {
|
||||||
|
this.setDaemonSetting('ffmpeg_path', path);
|
||||||
|
this.findFFmpeg();
|
||||||
|
}
|
||||||
|
|
||||||
onKeyFeeChange(newValue: Price) {
|
onKeyFeeChange(newValue: Price) {
|
||||||
this.setDaemonSetting('max_key_fee', newValue);
|
this.setDaemonSetting('max_key_fee', newValue);
|
||||||
}
|
}
|
||||||
|
@ -187,9 +208,18 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
this.props.setDaemonSetting(name, value);
|
this.props.setDaemonSetting(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearDaemonSetting(name: string): void {
|
||||||
|
this.props.clearDaemonSetting(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
findFFmpeg(): void {
|
||||||
|
this.props.findFFmpeg();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
daemonSettings,
|
daemonSettings,
|
||||||
|
ffmpegStatus,
|
||||||
allowAnalytics,
|
allowAnalytics,
|
||||||
showNsfw,
|
showNsfw,
|
||||||
instantPurchaseEnabled,
|
instantPurchaseEnabled,
|
||||||
|
@ -213,11 +243,13 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
clearPlayingUri,
|
clearPlayingUri,
|
||||||
darkModeTimes,
|
darkModeTimes,
|
||||||
clearCache,
|
clearCache,
|
||||||
|
findingFFmpeg,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { storedPassword } = this.state;
|
const { storedPassword } = this.state;
|
||||||
|
|
||||||
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
|
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
|
||||||
|
// @if TARGET='app'
|
||||||
|
const { available: ffmpegAvailable, which: ffmpegPath } = ffmpegStatus;
|
||||||
|
// @endif
|
||||||
const defaultMaxKeyFee = { currency: 'USD', amount: 50 };
|
const defaultMaxKeyFee = { currency: 'USD', amount: 50 };
|
||||||
|
|
||||||
const disableMaxKeyFee = !(daemonSettings && daemonSettings.max_key_fee);
|
const disableMaxKeyFee = !(daemonSettings && daemonSettings.max_key_fee);
|
||||||
|
@ -622,7 +654,69 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{/* @if TARGET='app' */}
|
||||||
|
<Card
|
||||||
|
title={
|
||||||
|
<span>
|
||||||
|
{__('Experimental Transcoding')}
|
||||||
|
{findingFFmpeg && <Spinner type="small" />}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
<React.Fragment>
|
||||||
|
<FileSelector
|
||||||
|
type="openDirectory"
|
||||||
|
placeholder={__('A Folder containing FFmpeg')}
|
||||||
|
currentPath={ffmpegPath || daemonSettings.ffmpeg_path}
|
||||||
|
onFileChosen={(newDirectory: WebFile) => {
|
||||||
|
this.onFFmpegFolder(newDirectory.path);
|
||||||
|
}}
|
||||||
|
disabled={Boolean(ffmpegPath)}
|
||||||
|
/>
|
||||||
|
<p className="help">
|
||||||
|
{ffmpegAvailable ? (
|
||||||
|
<I18nMessage
|
||||||
|
tokens={{
|
||||||
|
learn_more: (
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
label={__('Learn more')}
|
||||||
|
href="https://lbry.com/faq/video-publishing-guide#automatic"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
FFmpeg is correctly configured. %learn_more%
|
||||||
|
</I18nMessage>
|
||||||
|
) : (
|
||||||
|
<I18nMessage
|
||||||
|
tokens={{
|
||||||
|
check_again: (
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
label={__('Check again')}
|
||||||
|
onClick={() => this.findFFmpeg()}
|
||||||
|
disabled={findingFFmpeg}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
learn_more: (
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
label={__('Learn more')}
|
||||||
|
href="https://lbry.com/faq/video-publishing-guide#automatic"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
FFmpeg could not be found. Navigate to it or Install, Then %check_again% or quit and restart the
|
||||||
|
app. %learn_more%
|
||||||
|
</I18nMessage>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{/* @endif */}
|
||||||
{(!IS_WEB || isAuthenticated) && (
|
{(!IS_WEB || isAuthenticated) && (
|
||||||
<Card
|
<Card
|
||||||
title={__('Experimental Settings')}
|
title={__('Experimental Settings')}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import {
|
||||||
doFetchDaemonSettings,
|
doFetchDaemonSettings,
|
||||||
doSetAutoLaunch,
|
doSetAutoLaunch,
|
||||||
// doSetDaemonSetting
|
// doSetDaemonSetting
|
||||||
|
doFindFFmpeg,
|
||||||
|
doGetDaemonStatus,
|
||||||
} from 'redux/actions/settings';
|
} from 'redux/actions/settings';
|
||||||
import {
|
import {
|
||||||
selectIsUpgradeSkipped,
|
selectIsUpgradeSkipped,
|
||||||
|
@ -347,6 +349,8 @@ export function doDaemonReady() {
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
dispatch(doBalanceSubscribe());
|
dispatch(doBalanceSubscribe());
|
||||||
dispatch(doSetAutoLaunch());
|
dispatch(doSetAutoLaunch());
|
||||||
|
dispatch(doFindFFmpeg());
|
||||||
|
dispatch(doGetDaemonStatus());
|
||||||
dispatch(doFetchDaemonSettings());
|
dispatch(doFetchDaemonSettings());
|
||||||
dispatch(doFetchFileInfosAndPublishedClaims());
|
dispatch(doFetchFileInfosAndPublishedClaims());
|
||||||
if (!selectIsUpgradeSkipped(state)) {
|
if (!selectIsUpgradeSkipped(state)) {
|
||||||
|
|
|
@ -22,6 +22,19 @@ export function doFetchDaemonSettings() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doFindFFmpeg() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: LOCAL_ACTIONS.FINDING_FFMPEG_STARTED,
|
||||||
|
});
|
||||||
|
return Lbry.ffmpeg_find().then(done => {
|
||||||
|
dispatch({
|
||||||
|
type: LOCAL_ACTIONS.FINDING_FFMPEG_COMPLETED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function doGetDaemonStatus() {
|
export function doGetDaemonStatus() {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
return Lbry.status().then(status => {
|
return Lbry.status().then(status => {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
// deleted, moved to lbry-redux
|
|
|
@ -14,11 +14,12 @@ settingLanguage.push('en');
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
isNight: false,
|
isNight: false,
|
||||||
|
findingFFmpeg: false,
|
||||||
loadedLanguages: [...Object.keys(window.i18n_messages), 'en'] || ['en'],
|
loadedLanguages: [...Object.keys(window.i18n_messages), 'en'] || ['en'],
|
||||||
customWalletServers: [],
|
customWalletServers: [],
|
||||||
sharedPreferences: {},
|
sharedPreferences: {},
|
||||||
daemonSettings: {},
|
daemonSettings: {},
|
||||||
daemonStatus: {},
|
daemonStatus: { ffmpeg_status: {} },
|
||||||
clientSettings: {
|
clientSettings: {
|
||||||
// UX
|
// UX
|
||||||
[SETTINGS.NEW_USER_ACKNOWLEDGED]: false,
|
[SETTINGS.NEW_USER_ACKNOWLEDGED]: false,
|
||||||
|
@ -59,6 +60,16 @@ const defaultState = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.FINDING_FFMPEG_STARTED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
findingFFmpeg: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
reducers[ACTIONS.FINDING_FFMPEG_COMPLETED] = state =>
|
||||||
|
Object.assign({}, state, {
|
||||||
|
findingFFmpeg: false,
|
||||||
|
});
|
||||||
|
|
||||||
reducers[LBRY_REDUX_ACTIONS.DAEMON_SETTINGS_RECEIVED] = (state, action) =>
|
reducers[LBRY_REDUX_ACTIONS.DAEMON_SETTINGS_RECEIVED] = (state, action) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
daemonSettings: action.data.settings,
|
daemonSettings: action.data.settings,
|
||||||
|
|
|
@ -14,6 +14,16 @@ export const selectDaemonStatus = createSelector(
|
||||||
state => state.daemonStatus
|
state => state.daemonStatus
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectFfmpegStatus = createSelector(
|
||||||
|
selectDaemonStatus,
|
||||||
|
status => status.ffmpeg_status
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectFindingFFmpeg = createSelector(
|
||||||
|
selectState,
|
||||||
|
state => state.findingFFmpeg || false
|
||||||
|
);
|
||||||
|
|
||||||
export const selectClientSettings = createSelector(
|
export const selectClientSettings = createSelector(
|
||||||
selectState,
|
selectState,
|
||||||
state => state.clientSettings || {}
|
state => state.clientSettings || {}
|
||||||
|
@ -62,8 +72,7 @@ export const makeSelectSharedPreferencesForKey = key =>
|
||||||
export const selectHasWalletServerPrefs = createSelector(
|
export const selectHasWalletServerPrefs = createSelector(
|
||||||
makeSelectSharedPreferencesForKey(SHARED_PREFERENCES.WALLET_SERVERS),
|
makeSelectSharedPreferencesForKey(SHARED_PREFERENCES.WALLET_SERVERS),
|
||||||
servers => {
|
servers => {
|
||||||
if (servers && servers.length) return true;
|
return !!(servers && servers.length);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,8 @@ img {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-small);
|
||||||
color: var(--color-text-help);
|
color: var(--color-text-help);
|
||||||
margin-top: var(--spacing-small);
|
margin-top: var(--spacing-miniscule);
|
||||||
|
margin-bottom: var(--spacing-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.help--warning {
|
.help--warning {
|
||||||
|
|
Loading…
Add table
Reference in a new issue