Skip to content

Commit

Permalink
Merge pull request #11 from iqbaladinur/fix/video-player
Browse files Browse the repository at this point in the history
Fix/video player
  • Loading branch information
iqbaladinur authored Jun 19, 2024
2 parents 8e81b8b + 06faded commit 4e5b693
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 33 deletions.
10 changes: 8 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"dexie": "^4.0.4",
"fix-webm-duration": "^1.0.5",
"lucide-vue-next": "^0.376.0",
"radix-vue": "^1.7.2",
"tailwind-merge": "^2.3.0",
Expand Down
60 changes: 34 additions & 26 deletions src/components/Recorder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
TooltipProvider
} from "@/components/ui/tooltip";
import { use60FPS } from '@/composables/videoSettingStore';
import fixWebmDuration from "fix-webm-duration";
interface Props {
mobile?: boolean;
Expand All @@ -31,6 +32,7 @@ const enableCameraView = ref<boolean>(false);
const disableCameraView = ref<boolean>(false);
const webcamStream = ref<MediaStream | null>(null);
const webcamSrc = ref<HTMLVideoElement | null>(null);
const startTime = ref<number>(0);
const checkCameraAvailability = async () => {
try {
Expand Down Expand Up @@ -91,16 +93,19 @@ const startRecordingWithAudioMic = async () => {
}
};
mediaRecorder.value.onstop = () => {
mediaRecorder.value.onstop = async() => {
isRecording.value = false;
const recordedBlob = new Blob(recordedChunks, { type: 'video/webm' });
const tracks = stream.getTracks();
tracks.forEach((tr) => tr.stop());
stopWebCam();
saveToIndexedDB(recordedBlob, true);
const duration = Date.now() - startTime.value;
const blobWithDuration = await fixWebmDuration(recordedBlob, duration, { logger: false });
saveToIndexedDB(blobWithDuration, true);
};
mediaRecorder.value.start(200);
startTime.value = Date.now();
isRecording.value = true;
} catch (error: any) {
stopWebCam();
Expand All @@ -112,11 +117,10 @@ const startRecordingWithAudioMic = async () => {
}
}
const startRecordingOnlyAudioMic = async () => {
const startRecording = async() => {
try {
recordedType.value = 'scr_mic';
const audioStream = await captureAudio();
const stream = new MediaStream([...audioStream.getTracks()]);
recordedType.value = 'scr';
const stream = await captureScreen(true);
mediaRecorder.value = new MediaRecorder(stream);
const recordedChunks: Blob[] = [];
Expand All @@ -126,29 +130,41 @@ const startRecordingOnlyAudioMic = async () => {
}
};
mediaRecorder.value.onstop = () => {
mediaRecorder.value.onstop = async () => {
isRecording.value = false;
const recordedBlob = new Blob(recordedChunks, { type: 'audio/wav' });
const recordedBlob = new Blob(recordedChunks, { type: 'video/webm' });
const tracks = stream.getTracks();
tracks.forEach((tr) => tr.stop());
saveToIndexedDB(recordedBlob, true);
let audioEnable = false;
tracks.forEach((tr) => {
if (tr.kind === 'audio') {
audioEnable = true;
};
tr.stop();
});
const duration = Date.now() - startTime.value;
const blobWithDuration = await fixWebmDuration(recordedBlob, duration, { logger: false });
stopWebCam();
saveToIndexedDB(blobWithDuration, audioEnable);
};
mediaRecorder.value.start(200);
startTime.value = Date.now();
isRecording.value = true;
} catch (error: any) {
stopWebCam();
toast({
title: 'Failed',
description: error?.message,
variant: 'destructive'
});
}
}
};
const startRecording = async() => {
const startRecordingOnlyAudioMic = async () => {
try {
recordedType.value = 'scr';
const stream = await captureScreen(true);
recordedType.value = 'scr_mic';
const audioStream = await captureAudio();
const stream = new MediaStream([...audioStream.getTracks()]);
mediaRecorder.value = new MediaRecorder(stream);
const recordedChunks: Blob[] = [];
Expand All @@ -160,30 +176,22 @@ const startRecording = async() => {
mediaRecorder.value.onstop = () => {
isRecording.value = false;
const recordedBlob = new Blob(recordedChunks, { type: 'video/webm' });
const recordedBlob = new Blob(recordedChunks, { type: 'audio/wav' });
const tracks = stream.getTracks();
let audioEnable = false;
tracks.forEach((tr) => {
if (tr.kind === 'audio') {
audioEnable = true;
};
tr.stop();
});
stopWebCam();
saveToIndexedDB(recordedBlob, audioEnable);
tracks.forEach((tr) => tr.stop());
saveToIndexedDB(recordedBlob, true);
};
mediaRecorder.value.start(200);
isRecording.value = true;
} catch (error: any) {
stopWebCam();
toast({
title: 'Failed',
description: error?.message,
variant: 'destructive'
});
}
};
}
async function startWebcam() {
if (!enableCameraView.value) {
Expand Down
84 changes: 84 additions & 0 deletions src/components/VideoCutter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<div>
<div class="relative min-h-[50px] rounded-lg border">
<ResizablePanelGroup direction="horizontal" class="w-full rounded-lg min-h-[50px] absolute">
<ResizablePanel :default-size="0" @resize="handleResize('left', $event)" />
<ResizableHandle with-handle />
<ResizablePanel class="bg-blue-300/30 flex items-center justify-center" />
<ResizableHandle with-handle />
<ResizablePanel :default-size="0" @resize="handleResize('right', $event)"/>
</ResizablePanelGroup>
<div ref="waveLength" class="w-full h-[50px] bg-white rounded-lg flex items-center justify-center px-4">
<AudioLines v-for="(i) in getWaveSoundlength" :key="`logo_${i}`" class="w-7 h-7"></AudioLines>
</div>
</div>
<div class="flex justify-between items-center mt-4">
<fieldset class="rounded-lg border px-3 py-1.5 w-[200px] text-xs">
<legend class="px-1 text-xs font-medium">Duration Cut</legend>
<div class="flex justify-between items-center">
<label>Start</label>
<span>{{ (timeStamp.start).toFixed(2) }}</span>
</div>
<div class="flex justify-between items-center text-red-500">
<label>End</label>
<span>{{ (timeStamp.end).toFixed(2) }}</span>
</div>
</fieldset>
<slot></slot>
</div>
</div>
</template>
<script setup lang="ts">
import {
ResizablePanelGroup,
ResizableHandle,
ResizablePanel
} from "@/components/ui/resizable";
import { computed, reactive, ref } from "vue";
import { AudioLines } from "lucide-vue-next";
const props = defineProps<{ duration: number }>();
const emit = defineEmits<{
(event: 'resize', payload: { start: number, end: number }): void
}>();
const waveLength = ref<HTMLDivElement | null>(null);
const getWaveSoundlength = computed(() => {
if (waveLength.value) {
const widthEl = waveLength.value;
if (widthEl) {
const width = widthEl.offsetWidth;
return Math.floor(width/28) - 2;
}
return 1;
}
return 1;
});
const percentageSize = reactive({
left: 0,
right: 0
});
const timeStamp = computed(() => {
if (!props.duration) {
return {
start: 0,
end: 0
}
}
return {
start: (percentageSize.left/100 * props.duration),
end: props.duration - (percentageSize.right/100 * props.duration)
}
})
function handleResize(side: 'left' | 'right', size: number) {
if (side === 'left') {
percentageSize.left = Math.round(size);
} else {
percentageSize.right = Math.round(size);
}
emit('resize', timeStamp.value);
}
</script>
Loading

0 comments on commit 4e5b693

Please sign in to comment.