Browse Source

Metadata for publishes is now a file instead of Base64.

.eslintrc renamed to cjs to fix error when using ESLint

Max file size is now global variable that coverts file size to binary byte format automatically.

Many small EsLint fixes.

Fixed no key warning in MultiplePublishAll.tsx
pull/29/head
Qortal Dev 3 months ago
parent
commit
0001ea250a
  1. 0
      .eslintrc.cjs
  2. 4
      package-lock.json
  3. 30
      src/components/Publish/EditVideo/EditVideo.tsx
  4. 1
      src/components/Publish/MultiplePublish/MultiplePublishAll.tsx
  5. 60
      src/components/Publish/PublishVideo/PublishVideo.tsx
  6. 2
      src/components/common/ContentButtons/LikeAndDislike-functions.ts
  7. 3
      src/constants/Misc.ts
  8. 17
      src/hooks/useFetchVideos.tsx
  9. 8
      src/utils/toBase64.ts

0
.eslintrc.js → .eslintrc.cjs

4
package-lock.json generated

@ -1,12 +1,12 @@
{ {
"name": "qtube", "name": "qtube",
"version": "0.0.0", "version": "2.0.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "qtube", "name": "qtube",
"version": "0.0.0", "version": "2.0.0",
"dependencies": { "dependencies": {
"@emotion/react": "^11.10.6", "@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6", "@emotion/styled": "^11.10.6",

30
src/components/Publish/EditVideo/EditVideo.tsx

@ -35,7 +35,7 @@ import AddBoxIcon from "@mui/icons-material/AddBox";
import { useDropzone } from "react-dropzone"; import { useDropzone } from "react-dropzone";
import { setNotification } from "../../../state/features/notificationsSlice.ts"; import { setNotification } from "../../../state/features/notificationsSlice.ts";
import { objectToBase64, uint8ArrayToBase64 } from "../../../utils/toBase64.ts"; import { objectToBase64, objectToFile, uint8ArrayToBase64 } from "../../../utils/toBase64.ts";
import { RootState } from "../../../state/store.ts"; import { RootState } from "../../../state/store.ts";
import { import {
upsertVideosBeginning, upsertVideosBeginning,
@ -53,7 +53,7 @@ import { extractTextFromHTML } from "../../common/TextEditor/utils.ts";
import { toBase64 } from "../PublishVideo/PublishVideo.tsx"; import { toBase64 } from "../PublishVideo/PublishVideo.tsx";
import { FrameExtractor } from "../../common/FrameExtractor/FrameExtractor.tsx"; import { FrameExtractor } from "../../common/FrameExtractor/FrameExtractor.tsx";
import { QTUBE_VIDEO_BASE } from "../../../constants/Identifiers.ts"; import { QTUBE_VIDEO_BASE } from "../../../constants/Identifiers.ts";
import { titleFormatter } from "../../../constants/Misc.ts"; import { maxSize, titleFormatter, videoMaxSize } from "../../../constants/Misc.ts";
const uid = new ShortUniqueId(); const uid = new ShortUniqueId();
const shortuid = new ShortUniqueId({ length: 5 }); const shortuid = new ShortUniqueId({ length: 5 });
@ -103,7 +103,7 @@ export const EditVideo = () => {
"video/*": [], "video/*": [],
}, },
maxFiles: 1, maxFiles: 1,
maxSize: 419430400, // 400 MB in bytes maxSize,
onDrop: (acceptedFiles, rejectedFiles) => { onDrop: (acceptedFiles, rejectedFiles) => {
const firstFile = acceptedFiles[0]; const firstFile = acceptedFiles[0];
@ -114,7 +114,7 @@ export const EditVideo = () => {
rejectedFiles.forEach(({ file, errors }) => { rejectedFiles.forEach(({ file, errors }) => {
errors.forEach(error => { errors.forEach(error => {
if (error.code === "file-too-large") { if (error.code === "file-too-large") {
errorString = "File must be under 400mb"; errorString = `File must be under ${videoMaxSize}MB`;
} }
console.log(`Error with file ${file.name}: ${error.message}`); console.log(`Error with file ${file.name}: ${error.message}`);
}); });
@ -248,7 +248,7 @@ export const EditVideo = () => {
); );
return; return;
} }
let listOfPublishes = []; const listOfPublishes = [];
const category = selectedCategoryVideos.id; const category = selectedCategoryVideos.id;
const subcategory = selectedSubCategoryVideos?.id || ""; const subcategory = selectedSubCategoryVideos?.id || "";
@ -259,14 +259,14 @@ export const EditVideo = () => {
fileExtension = fileExtensionSplit?.pop() || "mp4"; fileExtension = fileExtensionSplit?.pop() || "mp4";
} }
let filename = title.slice(0, 15); const filename = title.slice(0, 15);
// Step 1: Replace all white spaces with underscores // Step 1: Replace all white spaces with underscores
// Replace all forms of whitespace (including non-standard ones) with underscores // Replace all forms of whitespace (including non-standard ones) with underscores
let stringWithUnderscores = filename.replace(/[\s\uFEFF\xA0]+/g, "_"); const stringWithUnderscores = filename.replace(/[\s\uFEFF\xA0]+/g, "_");
// Remove all non-alphanumeric characters (except underscores) // Remove all non-alphanumeric characters (except underscores)
let alphanumericString = stringWithUnderscores.replace( const alphanumericString = stringWithUnderscores.replace(
/[^a-zA-Z0-9_]/g, /[^a-zA-Z0-9_]/g,
"" ""
); );
@ -287,17 +287,17 @@ export const EditVideo = () => {
filename: `${alphanumericString.trim()}.${fileExtension}`, filename: `${alphanumericString.trim()}.${fileExtension}`,
}; };
let metadescription = const metadescription =
`**category:${category};subcategory:${subcategory};code:${editVideoProperties.code}**` + `**category:${category};subcategory:${subcategory};code:${editVideoProperties.code}**` +
description.slice(0, 150); description.slice(0, 150);
const crowdfundObjectToBase64 = await objectToBase64(videoObject); const videoObjectToFile = objectToFile(videoObject);
// Description is obtained from raw data // Description is obtained from raw data
const requestBodyJson: any = { const requestBodyJson: any = {
action: "PUBLISH_QDN_RESOURCE", action: "PUBLISH_QDN_RESOURCE",
name: username, name: username,
service: "DOCUMENT", service: "DOCUMENT",
data64: crowdfundObjectToBase64, file: videoObjectToFile,
title: title.slice(0, 50), title: title.slice(0, 50),
description: metadescription, description: metadescription,
identifier: editVideoProperties.id, identifier: editVideoProperties.id,
@ -318,7 +318,7 @@ export const EditVideo = () => {
tag1: QTUBE_VIDEO_BASE, tag1: QTUBE_VIDEO_BASE,
filename: `${alphanumericString.trim()}.${fileExtension}`, filename: `${alphanumericString.trim()}.${fileExtension}`,
}; };
console.log('edit file is: ', file)
listOfPublishes.push(requestBodyVideo); listOfPublishes.push(requestBodyVideo);
} }
@ -377,7 +377,7 @@ export const EditVideo = () => {
const onFramesExtracted = async imgs => { const onFramesExtracted = async imgs => {
try { try {
let imagesExtracts = []; const imagesExtracts = [];
for (const img of imgs) { for (const img of imgs) {
try { try {
@ -395,7 +395,7 @@ export const EditVideo = () => {
compressedFile = file; compressedFile = file;
resolve(); resolve();
}, },
error(err) {}, error(error) {console.log(error)},
}); });
}); });
if (!compressedFile) continue; if (!compressedFile) continue;
@ -407,7 +407,7 @@ export const EditVideo = () => {
} }
setImageExtracts(imagesExtracts); setImageExtracts(imagesExtracts);
} catch (error) {} } catch (error) {console.log(error)}
}; };
return ( return (

1
src/components/Publish/MultiplePublish/MultiplePublishAll.tsx

@ -125,6 +125,7 @@ export const MultiplePublish = ({
); );
return ( return (
<Box <Box
key={publish?.identifier}
sx={{ sx={{
display: "flex", display: "flex",
gap: "20px", gap: "20px",

60
src/components/Publish/PublishVideo/PublishVideo.tsx

@ -38,7 +38,7 @@ import { useDropzone } from "react-dropzone";
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import { setNotification } from "../../../state/features/notificationsSlice.ts"; import { setNotification } from "../../../state/features/notificationsSlice.ts";
import { objectToBase64, uint8ArrayToBase64 } from "../../../utils/toBase64.ts"; import { objectToBase64, objectToFile, uint8ArrayToBase64 } from "../../../utils/toBase64.ts";
import { RootState } from "../../../state/store.ts"; import { RootState } from "../../../state/store.ts";
import { import {
upsertVideosBeginning, upsertVideosBeginning,
@ -65,7 +65,7 @@ import {
QTUBE_PLAYLIST_BASE, QTUBE_PLAYLIST_BASE,
QTUBE_VIDEO_BASE, QTUBE_VIDEO_BASE,
} from "../../../constants/Identifiers.ts"; } from "../../../constants/Identifiers.ts";
import { titleFormatter } from "../../../constants/Misc.ts"; import { maxSize, titleFormatter, videoMaxSize } from "../../../constants/Misc.ts";
import { getFileName } from "../../../utils/stringFunctions.ts"; import { getFileName } from "../../../utils/stringFunctions.ts";
export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> => export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
@ -137,13 +137,16 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
const [isCheckDescriptionIsTitle, setIsCheckDescriptionIsTitle] = const [isCheckDescriptionIsTitle, setIsCheckDescriptionIsTitle] =
useState(false); useState(false);
const [imageExtracts, setImageExtracts] = useState<any>({}); const [imageExtracts, setImageExtracts] = useState<any>({});
const { getRootProps, getInputProps } = useDropzone({ const { getRootProps, getInputProps } = useDropzone({
accept: { accept: {
"video/*": [], "video/*": [],
}, },
maxSize: 419430400, // 400 MB in bytes maxSize,
onDrop: (acceptedFiles, rejectedFiles) => { onDrop: (acceptedFiles, rejectedFiles) => {
const formatArray = acceptedFiles.map(item => { const formatArray = acceptedFiles.map(item => {
console.log('file: ', item)
let filteredTitle = ""; let filteredTitle = "";
if (isCheckTitleByFile) { if (isCheckTitleByFile) {
@ -164,7 +167,7 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
rejectedFiles.forEach(({ file, errors }) => { rejectedFiles.forEach(({ file, errors }) => {
errors.forEach(error => { errors.forEach(error => {
if (error.code === "file-too-large") { if (error.code === "file-too-large") {
errorString = "File must be under 400mb"; errorString = `File must be under ${videoMaxSize}MB`;
} }
console.log(`Error with file ${file.name}: ${error.message}`); console.log(`Error with file ${file.name}: ${error.message}`);
}); });
@ -180,10 +183,10 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
}, },
}); });
useEffect(() => { // useEffect(() => {
if (editContent) { // if (editContent) {
} // }
}, [editContent]); // }, [editContent]);
const onClose = () => { const onClose = () => {
setIsOpen(false); setIsOpen(false);
@ -238,7 +241,7 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
return; return;
} }
let listOfPublishes = []; const listOfPublishes = [];
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
const publish = files[i]; const publish = files[i];
@ -274,18 +277,18 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
fileExtension = fileExtensionSplit?.pop() || "mp4"; fileExtension = fileExtensionSplit?.pop() || "mp4";
} }
let filename = title.slice(0, 15); const filename = title.slice(0, 15);
// Step 1: Replace all white spaces with underscores // Step 1: Replace all white spaces with underscores
// Replace all forms of whitespace (including non-standard ones) with underscores // Replace all forms of whitespace (including non-standard ones) with underscores
let stringWithUnderscores = filename.replace(/[\s\uFEFF\xA0]+/g, "_"); const stringWithUnderscores = filename.replace(/[\s\uFEFF\xA0]+/g, "_");
// Remove all non-alphanumeric characters (except underscores) // Remove all non-alphanumeric characters (except underscores)
let alphanumericString = stringWithUnderscores.replace( const alphanumericString = stringWithUnderscores.replace(
/[^a-zA-Z0-9_]/g, /[^a-zA-Z0-9_]/g,
"" ""
); );
console.log('file size: ', file.size)
const videoObject: any = { const videoObject: any = {
title, title,
version: 1, version: 1,
@ -306,17 +309,16 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
filename: `${alphanumericString.trim()}.${fileExtension}`, filename: `${alphanumericString.trim()}.${fileExtension}`,
}; };
let metadescription = const metadescription =
`**category:${category};subcategory:${subcategory};code:${code}**` + `**category:${category};subcategory:${subcategory};code:${code}**` +
fullDescription.slice(0, 150); fullDescription.slice(0, 150);
const crowdfundObjectToBase64 = await objectToBase64(videoObject);
// Description is obtained from raw data // Description is obtained from raw data
const requestBodyJson: any = { const requestBodyJson: any = {
action: "PUBLISH_QDN_RESOURCE", action: "PUBLISH_QDN_RESOURCE",
name: name, name: name,
service: "DOCUMENT", service: "DOCUMENT",
data64: crowdfundObjectToBase64, file: objectToFile(videoObject),
title: title.slice(0, 50), title: title.slice(0, 50),
description: metadescription, description: metadescription,
identifier: identifier + "_metadata", identifier: identifier + "_metadata",
@ -392,17 +394,17 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
.slice(0, 10) .slice(0, 10)
.join(""); .join("");
let metadescription = const metadescription =
`**category:${category};subcategory:${subcategory};${codes}**` + `**category:${category};subcategory:${subcategory};${codes}**` +
stringDescription.slice(0, 120); stringDescription.slice(0, 120);
const crowdfundObjectToBase64 = await objectToBase64(playlistObject);
// Description is obtained from raw data // Description is obtained from raw data
const requestBodyJson: any = { const requestBodyJson: any = {
action: "PUBLISH_QDN_RESOURCE", action: "PUBLISH_QDN_RESOURCE",
name: name, name: name,
service: "PLAYLIST", service: "PLAYLIST",
data64: crowdfundObjectToBase64, file: objectToFile(playlistObject),
title: title.slice(0, 50), title: title.slice(0, 50),
description: metadescription, description: metadescription,
identifier: identifier + "_metadata", identifier: identifier + "_metadata",
@ -447,17 +449,17 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
.slice(0, 10) .slice(0, 10)
.join(""); .join("");
let metadescription = const metadescription =
`**category:${playlistObject.category};subcategory:${playlistObject.subcategory};${codes}**` + `**category:${playlistObject.category};subcategory:${playlistObject.subcategory};${codes}**` +
playlistObject.description.slice(0, 120); playlistObject.description.slice(0, 120);
const crowdfundObjectToBase64 = await objectToBase64(playlistObject);
// Description is obtained from raw data // Description is obtained from raw data
const requestBodyJson: any = { const requestBodyJson: any = {
action: "PUBLISH_QDN_RESOURCE", action: "PUBLISH_QDN_RESOURCE",
name: name, name: name,
service: "PLAYLIST", service: "PLAYLIST",
data64: crowdfundObjectToBase64, file: objectToFile( playlistObject),
title: playlistObject.title.slice(0, 50), title: playlistObject.title.slice(0, 50),
description: metadescription, description: metadescription,
identifier: selectExistingPlaylist.identifier, identifier: selectExistingPlaylist.identifier,
@ -480,24 +482,24 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
let notificationObj: any = null; let notificationObj: any = null;
if (typeof error === "string") { if (typeof error === "string") {
notificationObj = { notificationObj = {
msg: error || "Failed to publish crowdfund", msg: error || "Failed to publish video",
alertType: "error", alertType: "error",
}; };
} else if (typeof error?.error === "string") { } else if (typeof error?.error === "string") {
notificationObj = { notificationObj = {
msg: error?.error || "Failed to publish crowdfund", msg: error?.error || "Failed to publish video",
alertType: "error", alertType: "error",
}; };
} else { } else {
notificationObj = { notificationObj = {
msg: error?.message || "Failed to publish crowdfund", msg: error?.message || "Failed to publish video",
alertType: "error", alertType: "error",
}; };
} }
if (!notificationObj) return; if (!notificationObj) return;
dispatch(setNotification(notificationObj)); dispatch(setNotification(notificationObj));
throw new Error("Failed to publish crowdfund"); throw new Error("Failed to publish video");
} }
} }
@ -578,7 +580,7 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
const onFramesExtracted = async (imgs, index) => { const onFramesExtracted = async (imgs, index) => {
try { try {
let imagesExtracts = []; const imagesExtracts = [];
for (const img of imgs) { for (const img of imgs) {
try { try {
@ -596,7 +598,7 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
compressedFile = file; compressedFile = file;
resolve(); resolve();
}, },
error(err) {}, error(error) {console.log(error)},
}); });
}); });
if (!compressedFile) continue; if (!compressedFile) continue;
@ -613,7 +615,7 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
[index]: imagesExtracts, [index]: imagesExtracts,
}; };
}); });
} catch (error) {} } catch (error) {console.log(error)}
}; };
return ( return (

2
src/components/common/ContentButtons/LikeAndDislike-functions.ts

@ -14,7 +14,7 @@ export const getCurrentLikeType = async (
}); });
return response?.likeType; return response?.likeType;
} catch (e) { } catch (e) {
console.log("liketype error: ", e); // console.log("liketype error: ", e);
return NEUTRAL; return NEUTRAL;
} }
}; };

3
src/constants/Misc.ts

@ -1,3 +1,6 @@
export const minPriceSuperlike = 1; export const minPriceSuperlike = 1;
export const titleFormatter = /[^a-zA-Z0-9\s-_!?()&'",.;:|—~@#$%^*+=<>]/g; export const titleFormatter = /[^a-zA-Z0-9\s-_!?()&'",.;:|—~@#$%^*+=<>]/g;
export const titleFormatterOnSave = /[^a-zA-Z0-9\s-_!()&',.;—~@#$%^+=]/g; export const titleFormatterOnSave = /[^a-zA-Z0-9\s-_!()&',.;—~@#$%^+=]/g;
export const videoMaxSize = 400; // Size in Megabytes (decimal)
export const maxSize = videoMaxSize *1024*1024

17
src/hooks/useFetchVideos.tsx

@ -72,7 +72,7 @@ export const useFetchVideos = () => {
const getAvatar = React.useCallback(async (author: string) => { const getAvatar = React.useCallback(async (author: string) => {
try { try {
let url = await qortalRequest({ const url = await qortalRequest({
action: "GET_QDN_RESOURCE_URL", action: "GET_QDN_RESOURCE_URL",
name: author, name: author,
service: "THUMBNAIL", service: "THUMBNAIL",
@ -85,14 +85,14 @@ export const useFetchVideos = () => {
url, url,
}) })
); );
} catch (error) {} } catch (error) {console.log(error)}
}, []); }, []);
const getVideo = async ( const getVideo = async (
user: string, user: string,
videoId: string, videoId: string,
content: any, content: any,
retries: number = 0 retries = 0
) => { ) => {
try { try {
const res = await fetchAndEvaluateVideos({ const res = await fetchAndEvaluateVideos({
@ -183,7 +183,7 @@ export const useFetchVideos = () => {
} }
} }
} }
} catch (error) { } catch (error) {console.log(error)
} finally { } finally {
dispatch(setIsLoadingGlobal(false)); dispatch(setIsLoadingGlobal(false));
} }
@ -311,7 +311,6 @@ export const useFetchVideos = () => {
} }
} catch (error) { } catch (error) {
console.log({ error }); console.log({ error });
} finally {
} }
}, },
[videos, hashMapVideos] [videos, hashMapVideos]
@ -370,8 +369,7 @@ export const useFetchVideos = () => {
} }
} }
} }
} catch (error) { } catch (error) {console.log(error)
} finally {
} }
}, },
[filteredVideos, hashMapVideos] [filteredVideos, hashMapVideos]
@ -411,12 +409,12 @@ export const useFetchVideos = () => {
const newArray = responseData.slice(0, findVideo); const newArray = responseData.slice(0, findVideo);
dispatch(setCountNewVideos(newArray.length)); dispatch(setCountNewVideos(newArray.length));
return; return;
} catch (error) {} } catch (error) {console.log(error)}
}, [videos]); }, [videos]);
const getVideosCount = React.useCallback(async () => { const getVideosCount = React.useCallback(async () => {
try { try {
let url = `/arbitrary/resources/search?mode=ALL&includemetadata=false&limit=0&service=DOCUMENT&identifier=${QTUBE_VIDEO_BASE}`; const url = `/arbitrary/resources/search?mode=ALL&includemetadata=false&limit=0&service=DOCUMENT&identifier=${QTUBE_VIDEO_BASE}`;
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: "GET",
@ -436,7 +434,6 @@ export const useFetchVideos = () => {
dispatch(setVideosPerNamePublished(videosPerNamePublished)); dispatch(setVideosPerNamePublished(videosPerNamePublished));
} catch (error) { } catch (error) {
console.log({ error }); console.log({ error });
} finally {
} }
}, []); }, []);

8
src/utils/toBase64.ts

@ -46,6 +46,14 @@ export function objectToBase64(obj: any) {
}) })
} }
export function objectToFile(obj: any) {
// Step 1: Convert the object to a JSON string
const jsonString = JSON.stringify(obj)
// Step 2: Create a Blob from the JSON string
const blob = new Blob([jsonString], { type: 'application/json' })
return blob
}
export function objectToUint8Array(obj: any) { export function objectToUint8Array(obj: any) {
// Convert the object to a JSON string // Convert the object to a JSON string
const jsonString = JSON.stringify(obj) const jsonString = JSON.stringify(obj)

Loading…
Cancel
Save