feat: stream video upload progress (#210)
* working on the streaming progress * connected video upload progress from frontend to server to bucket * cleanup * cleanup * added the progress bar * finished the supabase upload-progress using supabse realtime * use only progress bar * updating the helpertext * Merge branch 'upload-progress' of https://github.com/rotimi-best/classroomio into upload-progress * updated the supabase import * Update LocalVideo.svelte * update lock file --------- Co-authored-by: Rotimi Best <[email protected]>
This commit is contained in:
@@ -13,7 +13,8 @@ const {
|
|||||||
// GetObjectCommand,
|
// GetObjectCommand,
|
||||||
} = require('@aws-sdk/client-s3');
|
} = require('@aws-sdk/client-s3');
|
||||||
|
|
||||||
// const { getSupabase } = require('../utils/supabase');
|
const { getSupabase } = require('../utils/supabase');
|
||||||
|
|
||||||
const genUniqueId = require('../utils/genUniqueId');
|
const genUniqueId = require('../utils/genUniqueId');
|
||||||
const upload = require('../utils/upload');
|
const upload = require('../utils/upload');
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ const S3 = new S3Client({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// const supabase = getSupabase();
|
const supabase = getSupabase();
|
||||||
|
|
||||||
// const museUploadQueue = new Queue('video', process.env.REDIS_API_KEY, {
|
// const museUploadQueue = new Queue('video', process.env.REDIS_API_KEY, {
|
||||||
// redis: { tls: { rejectUnauthorized: false } },
|
// redis: { tls: { rejectUnauthorized: false } },
|
||||||
@@ -96,7 +97,18 @@ router.post('/', upload.single('videoFile'), async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
parallelUploads3.on('httpUploadProgress', (progress) => {
|
parallelUploads3.on('httpUploadProgress', (progress) => {
|
||||||
console.log(progress);
|
const uploadProgress = Math.round(((progress.loaded / progress.total) * 100) / 2);
|
||||||
|
console.log({ uploadProgress });
|
||||||
|
const channel = supabase.channel('upload-progress');
|
||||||
|
channel.subscribe((status) => {
|
||||||
|
if (status === 'SUBSCRIBED') {
|
||||||
|
channel.send({
|
||||||
|
type: 'broadcast',
|
||||||
|
event: lessonId,
|
||||||
|
payload: uploadProgress
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await parallelUploads3.done();
|
await parallelUploads3.done();
|
||||||
|
|||||||
@@ -9,22 +9,19 @@ const getSupabase = (config) => {
|
|||||||
|
|
||||||
config = {
|
config = {
|
||||||
supabaseConfig: {
|
supabaseConfig: {
|
||||||
bucketPath:
|
bucketPath: 'https://koxqonvbkeakwvmdegcf.supabase.in/storage/v1/object/sign/',
|
||||||
'https://koxqonvbkeakwvmdegcf.supabase.in/storage/v1/object/sign/',
|
|
||||||
url: PUBLIC_SUPABASE_URL || '',
|
url: PUBLIC_SUPABASE_URL || '',
|
||||||
anonKey: PUBLIC_SUPABASE_ANON_KEY || '',
|
anonKey: PUBLIC_SUPABASE_ANON_KEY || ''
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
supabase = createClient(
|
supabase = createClient(config.supabaseConfig.url, config.supabaseConfig.anonKey, {
|
||||||
config.supabaseConfig.url,
|
auth: { persistSession: false }
|
||||||
config.supabaseConfig.anonKey,
|
});
|
||||||
{ auth: { persistSession: false } }
|
|
||||||
);
|
|
||||||
|
|
||||||
return supabase;
|
return supabase;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getSupabase,
|
getSupabase
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,7 +80,6 @@
|
|||||||
"postcss": "^8.4.24",
|
"postcss": "^8.4.24",
|
||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.1",
|
||||||
"sass": "^1.64.2",
|
"sass": "^1.64.2",
|
||||||
"supabase": "^1.112.0",
|
|
||||||
"svelte": "^4.1.2",
|
"svelte": "^4.1.2",
|
||||||
"svelte-calendar": "^3.1.6",
|
"svelte-calendar": "^3.1.6",
|
||||||
"svelte-dnd-action": "^0.9.24",
|
"svelte-dnd-action": "^0.9.24",
|
||||||
|
|||||||
+37
-29
@@ -7,7 +7,14 @@
|
|||||||
deleteLessonVideo,
|
deleteLessonVideo,
|
||||||
uploadCourseVideoStore
|
uploadCourseVideoStore
|
||||||
} from '$lib/components/Course/components/Lesson/store/lessons';
|
} from '$lib/components/Course/components/Lesson/store/lessons';
|
||||||
import { Moon } from 'svelte-loading-spinners';
|
import { onMount, onDestroy } from 'svelte';
|
||||||
|
import { ProgressBar } from 'carbon-components-svelte';
|
||||||
|
import { supabase } from '$lib/utils/functions/supabase';
|
||||||
|
|
||||||
|
let value = 0;
|
||||||
|
let max = 100;
|
||||||
|
let status = 'active';
|
||||||
|
let fileSize;
|
||||||
|
|
||||||
export let lessonId = '';
|
export let lessonId = '';
|
||||||
|
|
||||||
@@ -17,16 +24,8 @@
|
|||||||
let submit;
|
let submit;
|
||||||
let uploadedFileUrl = '';
|
let uploadedFileUrl = '';
|
||||||
let isLoading = false;
|
let isLoading = false;
|
||||||
let timeoutkey;
|
let prevProgress = 0;
|
||||||
|
let uploadChannel;
|
||||||
const uploadingTexts = [
|
|
||||||
'Sending your video to our server...',
|
|
||||||
'Breaking video into chunks',
|
|
||||||
'Compressing the video...',
|
|
||||||
'Generating a link to your video...',
|
|
||||||
'Wrapping things up'
|
|
||||||
];
|
|
||||||
let uploadingLoadingText = uploadingTexts[0];
|
|
||||||
|
|
||||||
function isVideoAdded(link) {
|
function isVideoAdded(link) {
|
||||||
return $lesson.materials?.videos?.find((v) => v.link === link);
|
return $lesson.materials?.videos?.find((v) => v.link === link);
|
||||||
@@ -42,6 +41,7 @@
|
|||||||
|
|
||||||
console.log({ size: videoFile?.size });
|
console.log({ size: videoFile?.size });
|
||||||
|
|
||||||
|
fileSize = videoFile?.size / (1024 * 1024);
|
||||||
try {
|
try {
|
||||||
const response = await axios({
|
const response = await axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -51,6 +51,9 @@
|
|||||||
maxBodyLength: Infinity,
|
maxBodyLength: Infinity,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data; boundary=MyBoundary'
|
'Content-Type': 'multipart/form-data; boundary=MyBoundary'
|
||||||
|
},
|
||||||
|
onUploadProgress: (progressEvent) => {
|
||||||
|
value = Math.round((progressEvent.loaded * 100) / progressEvent?.total / 2);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,23 +93,25 @@
|
|||||||
isLoading = false;
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
onMount(async () => {
|
||||||
if (isLoading) {
|
uploadChannel = supabase
|
||||||
timeoutkey = setTimeout(() => {
|
.channel('upload-progress')
|
||||||
console.log('timeout');
|
.on('broadcast', { event: lessonId }, (payload) => {
|
||||||
const i = uploadingTexts.findIndex((text) => text === uploadingLoadingText);
|
console.log('The progress of the upload', payload);
|
||||||
const next = uploadingTexts[i + 1];
|
value = value + payload?.payload - prevProgress;
|
||||||
|
prevProgress = payload.payload;
|
||||||
|
})
|
||||||
|
.subscribe();
|
||||||
|
});
|
||||||
|
|
||||||
if (next) {
|
onDestroy(() => {
|
||||||
console.log('uploadingLoadingText', next);
|
supabase.removeChannel(uploadChannel);
|
||||||
uploadingLoadingText = next;
|
});
|
||||||
}
|
|
||||||
}, 30000);
|
$: helperText = value + '% of ' + Math.round(fileSize) + 'MB';
|
||||||
} else if (timeoutkey) {
|
$: if (value === max) {
|
||||||
console.log('clearTimeout');
|
helperText = 'Done';
|
||||||
clearTimeout(timeoutkey);
|
status = 'finished';
|
||||||
uploadingLoadingText = uploadingTexts[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: isDoneUploading(formRes);
|
$: isDoneUploading(formRes);
|
||||||
@@ -124,8 +129,11 @@
|
|||||||
on:submit|preventDefault={onUpload}
|
on:submit|preventDefault={onUpload}
|
||||||
>
|
>
|
||||||
{#if isLoading}
|
{#if isLoading}
|
||||||
<Moon size="40" color="#1d4ed8" unit="px" duration="1s" />
|
<div class="flex flex-col gap-5 max-w-[500px] w-[60%] justify-center">
|
||||||
<p class="mt-5">{uploadingLoadingText}</p>
|
<p class="mt-5 text-center">Uploading...</p>
|
||||||
|
<ProgressBar class="w-full" {value} {max} {status} />
|
||||||
|
<p class="text-sm">{helperText}</p>
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<img src="/upload-video.svg" alt="upload" />
|
<img src="/upload-video.svg" alt="upload" />
|
||||||
<span class="pt-3">
|
<span class="pt-3">
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@
|
|||||||
"prettier-plugin-svelte": "^3.1.2",
|
"prettier-plugin-svelte": "^3.1.2",
|
||||||
"prettier-plugin-tailwindcss": "^0.5.7",
|
"prettier-plugin-tailwindcss": "^0.5.7",
|
||||||
"standard-version": "^9.5.0",
|
"standard-version": "^9.5.0",
|
||||||
"supabase": "^1.112.0",
|
"supabase": "^1.151.1",
|
||||||
"tsconfig": "workspace:*",
|
"tsconfig": "workspace:*",
|
||||||
"turbo": "^1.10.16",
|
"turbo": "^1.10.16",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
|
|||||||
Generated
+3416
-9014
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user