Skip to content
This repository has been archived by the owner on Jan 15, 2025. It is now read-only.

Commit

Permalink
Merge pull request #1353 from ecency/feat/3speak-video-upload
Browse files Browse the repository at this point in the history
Feat/3speak video upload
  • Loading branch information
feruzm authored Aug 3, 2023
2 parents 7723af4 + fa0854d commit 7d17eb1
Show file tree
Hide file tree
Showing 17 changed files with 1,618 additions and 23 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
"@loadable/server": "^5.15.2",
"@tanstack/react-query": "^4.29.7",
"@tanstack/react-query-devtools": "^4.29.7",
"@types/tus-js-client": "^2.1.0",
"@webscopeio/react-textarea-autocomplete": "^4.8.1",
"axios": "^0.21.2",
"axios-cookiejar-support": "^4.0.6",
"bs58": "^4.0.1",
"connected-react-router": "^6.8.0",
"cookie-parser": "^1.4.5",
Expand All @@ -35,7 +37,7 @@
"express": "^4.17.3",
"history": "^4.7.2",
"hive-uri": "^0.2.3",
"hivesigner": "^3.2.8",
"hivesigner": "^3.3.4",
"html-react-parser": "^1.2.1",
"i18next": "^19.4.4",
"i18next-browser-languagedetector": "^4.2.0",
Expand Down Expand Up @@ -83,6 +85,8 @@
"serialize-javascript": "^3.1.0",
"sortablejs": "^1.13.0",
"speakingurl": "^14.0.1",
"tough-cookie": "^4.1.2",
"tus-js-client": "^3.1.0",
"use-async-effect": "^2.2.6",
"use-indexeddb": "^2.0.2",
"uuid": "^9.0.0",
Expand Down
3 changes: 3 additions & 0 deletions src/common/api/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import hs from "hivesigner";

import {
AccountUpdateOperation,
Authority,
CustomJsonOperation,
KeyRole,
Operation,
OperationName,
VirtualOperationName,
PrivateKey,
TransactionConfirmation
} from "@hiveio/dhive";
Expand Down
195 changes: 195 additions & 0 deletions src/common/api/threespeak.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import * as tus from "tus-js-client";
import axios from "axios";
import { getDecodedMemo } from "../helper/hive-signer";

const studioEndPoint = "https://studio.3speak.tv";
const tusEndPoint = "https://uploads.3speak.tv/files/";
const client = axios.create({});

export const threespeakAuth = async (username: string) => {
try {
let response = await client.get(
`${studioEndPoint}/mobile/login?username=${username}&hivesigner=true`,
{
withCredentials: false,
headers: {
"Content-Type": "application/json"
}
}
);
const memo_string = response.data.memo;
let { memoDecoded } = await getDecodedMemo(username, memo_string);

memoDecoded = memoDecoded.replace("#", "");
const user = await getTokenValidated(memoDecoded, username);
return memoDecoded;
} catch (err) {
console.log(err);
throw err;
}
};

export const getTokenValidated = async (jwt: string, username: string) => {
try {
let response = await client.get(
`${studioEndPoint}/mobile/login?username=${username}&access_token=${jwt}`,
{
withCredentials: false,
headers: {
"Content-Type": "application/json"
}
}
);
return response.data;
} catch (err) {
console.log(err);
throw err;
}
};

export const uploadVideoInfo = async (
oFilename: string,
fileSize: number,
videoUrl: string,
thumbnailUrl: string,
username: string,
duration: string
) => {
const token = await threespeakAuth(username);
try {
const { data } = await axios.post(
`${studioEndPoint}/mobile/api/upload_info?app=ecency`,
{
filename: videoUrl,
oFilename: oFilename,
size: fileSize,
duration,
thumbnail: thumbnailUrl,
isReel: false,
owner: username
},
{
withCredentials: false,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
}
}
);
return data;
} catch (e) {
console.error(e);
throw e;
}
};

export const getAllVideoStatuses = async (username: string) => {
const token = await threespeakAuth(username);
try {
let response = await client.get(`${studioEndPoint}/mobile/api/my-videos`, {
withCredentials: false,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
}
});
return response.data;
} catch (err) {
console.log(err);
throw err;
}
};

export const updateSpeakVideoInfo = async (
username: string,
postBody: string,
videoId: string,
title: string,
tags: string[],
isNsfwC: boolean
) => {
const token = await threespeakAuth(username);

const data = {
videoId: videoId,
title: title,
description: postBody,
isNsfwContent: isNsfwC,
tags_v2: tags
};

const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
};

axios
.post(`${studioEndPoint}/mobile/api/update_info`, data, { headers })
.then((response) => {})
.catch((error) => {
console.error("Error:", error);
});
};

export const markAsPublished = async (username: string, videoId: string) => {
const token = await threespeakAuth(username);
const data = {
videoId
};

const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
};

axios
.post(`${studioEndPoint}/mobile/api/my-videos/iPublished`, data, { headers })
.then((response) => {})
.catch((error) => {
console.error("Error:", error);
});
};

export const uploadFile = async (file: File, type: string, progressCallback: (percentage: number) => void) => {

return new Promise<{
fileUrl: string,
fileName: string,
fileSize: number
}>((resolve, reject) => {

let vPercentage = 0;
let tPercentage = 0;

const upload: any = new tus.Upload(file, {
endpoint: tusEndPoint,
retryDelays: [0, 3000, 5000, 10000, 20000],
metadata: {
filename: file.name,
filetype: file.type
},
onError: function (error: Error) {
reject(error);
},
onProgress: function (bytesUploaded: number, bytesTotal: number) {
if (type === "video") {
vPercentage = Number(((bytesUploaded / bytesTotal) * 100).toFixed(2));
progressCallback(vPercentage);
} else {
tPercentage = Number(((bytesUploaded / bytesTotal) * 100).toFixed(2));
progressCallback(tPercentage);
}
},
onSuccess: function () {
let fileUrl = upload?.url.replace(tusEndPoint, "");
const result = {
fileUrl,
fileName: upload.file?.name || '',
fileSize: upload.file?.size || 0,
};
resolve(result);
}
});
upload.start();
});
};
1 change: 1 addition & 0 deletions src/common/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Announcement from "./components/announcement";
import FloatingFAQ from "./components/floating-faq";
import { useMappedStore } from "./store/use-mapped-store";
import { EntriesCacheManager } from "./core";

import { UserActivityRecorder } from "./components/user-activity-recorder";

// Define lazy pages
Expand Down
2 changes: 1 addition & 1 deletion src/common/components/comment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export class Comment extends Component<Props, State> {
!showEmoji && !showGif && this.setState({ showEmoji: true, showGif: true })
}
>
{EditorToolbar({ ...this.props, sm: true, showEmoji })}
{EditorToolbar({ ...this.props, sm: true, showEmoji, comment: true })}
<div className="comment-body" onKeyDown={this.handleShortcuts} ref={this.commentBodyRef}>
<TextareaAutocomplete
className={`the-editor accepts-emoji ${text?.length > 20 ? "expanded" : ""}`}
Expand Down
30 changes: 27 additions & 3 deletions src/common/components/editor-toolbar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Component } from "react";
import React, { Component, ChangeEvent } from "react";
import isEqual from "react-fast-compare";
import axios from "axios";

Expand Down Expand Up @@ -38,6 +38,7 @@ import {
textShortSvg,
gifIcon
} from "../../img/svg";
import { VideoUpload } from "../video-upload-threespeak";

interface Props {
global: Global;
Expand All @@ -46,6 +47,9 @@ interface Props {
sm?: boolean;
showEmoji?: boolean;
showGif?: boolean;
setVideoEncoderBeneficiary?: (video: any) => void;
toggleNsfwC?: () => void;
comment: boolean;
}

interface State {
Expand Down Expand Up @@ -79,6 +83,7 @@ export class EditorToolbar extends Component<Props> {

holder = React.createRef<HTMLDivElement>();
fileInput = React.createRef<HTMLInputElement>();
videoInput = React.createRef<HTMLInputElement>();

shouldComponentUpdate(nextProps: Readonly<Props>, nextState: Readonly<State>): boolean {
return (
Expand Down Expand Up @@ -477,7 +482,9 @@ export class EditorToolbar extends Component<Props> {
onClick={(e: React.MouseEvent<HTMLElement>) => {
e.stopPropagation();
const el = this.fileInput.current;
if (el) el.click();
if (el) {
el.click();
}
}}
>
{_t("editor-toolbar.upload")}
Expand Down Expand Up @@ -555,6 +562,20 @@ export class EditorToolbar extends Component<Props> {
</div>
</Tooltip>
)}

{!this.props.comment && (
<Tooltip content={_t("video-upload.upload-video")}>
<div className="editor-tool" role="none">
<VideoUpload
activeUser={activeUser}
global={global}
insertText={this.insertText}
setVideoEncoderBeneficiary={this.props.setVideoEncoderBeneficiary}
toggleNsfwC={this.props.toggleNsfwC}
/>
</div>
</Tooltip>
)}
</div>
<input
onChange={this.fileInputChanged}
Expand Down Expand Up @@ -635,7 +656,10 @@ export default (props: Props) => {
activeUser: props.activeUser,
sm: props.sm,
showEmoji: props.showEmoji,
showGif: props.showGif
showGif: props.showGif,
setVideoEncoderBeneficiary: props.setVideoEncoderBeneficiary,
toggleNsfwC: props.toggleNsfwC,
comment: props.comment
};
return <EditorToolbar {...p} />;
};
Loading

0 comments on commit 7d17eb1

Please sign in to comment.