Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix slowness problem on the Electron version #1636

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@
</v-app>
<About v-if="showAboutDialog" @update:show-about-dialog="showAboutDialog = $event" />
<Tutorial :show-tutorial="interfaceStore.isTutorialVisible" />
<VideoLibraryModal :open-modal="interfaceStore.isVideoLibraryVisible" />
<VideoLibraryModal v-if="interfaceStore.isVideoLibraryVisible" :open-modal="interfaceStore.isVideoLibraryVisible" />
<VehicleDiscoveryDialog v-model="showDiscoveryDialog" show-auto-search-option />
<UpdateNotification v-if="isElectron()" />
</template>
Expand Down
147 changes: 63 additions & 84 deletions src/components/VideoLibraryModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,18 @@
<div class="flex flex-col w-full h-full px-4 overflow-auto align-center">
<div v-for="video in availableVideos" :key="video.fileName" class="mb-4 video-container">
<div class="relative video-wrapper">
<video
:id="`video-library-${video.fileName}`"
class="border-4 border-white rounded-md cursor-pointer border-opacity-[0.1] hover:border-opacity-[0.4] transition duration-75 hover:ease-in"
<div
:id="`video-library-thumbnail-${video.fileName}`"
class="border-4 border-white rounded-md cursor-pointer border-opacity-[0.1] hover:border-opacity-[0.4] transition duration-75 hover:ease-in aspect-video"
:class="
selectedVideos.find((v) => v.fileName === video.fileName)
? ['border-opacity-[0.4]', 'w-[220px]']
: ['border-opacity-[0.1]', 'w-[190px]']
"
preload="auto"
:poster="!video.isProcessed ? video.thumbnail : undefined"
:src="video.thumbnail ?? undefined"
>
<source :src="video.url" />
</video>
<img v-if="video.thumbnail" :src="video.thumbnail" />
</div>
<div
v-if="selectedVideos.find((v) => v.fileName === video.fileName) && !isMultipleSelectionMode"
class="play-button"
Expand Down Expand Up @@ -201,9 +200,16 @@
<!-- Video Player -->
<div v-if="availableVideos.length > 0" class="flex flex-col justify-between mt-5 align-center w-[720px]">
<div>
<div v-if="loadingVideoBlob" class="text-center w-full aspect-video flex justify-center items-center">
Loading video...
</div>
<video
v-if="
!isMultipleSelectionMode && selectedVideos.length === 1 && !isMultipleSelectionMode && !loadingData
v-show="
!isMultipleSelectionMode &&
selectedVideos.length === 1 &&
!isMultipleSelectionMode &&
!loadingData &&
!loadingVideoBlob
"
id="video-player"
ref="videoPlayerRef"
Expand All @@ -212,9 +218,7 @@
:preload="selectedVideos[0].isProcessed ? 'auto' : 'none'"
:poster="selectedVideos[0]?.thumbnail || undefined"
class="border-[14px] border-white border-opacity-10 rounded-lg min-h-[382px] aspect-video"
>
<source :src="selectedVideos[0]?.url || undefined" />
</video>
></video>
<v-btn
v-if="
!loadingData &&
Expand Down Expand Up @@ -314,7 +318,7 @@
</div>
<div class="flex flex-row mt-2">
<button
v-for="button in fileActionButtons"
v-for="button in fileActionButtons.filter((b) => b.show)"
:key="button.name"
class="flex flex-col justify-center ml-6 align-center"
:disabled="button.disabled"
Expand Down Expand Up @@ -496,17 +500,9 @@ const videoStore = useVideoStore()
const interfaceStore = useAppInterfaceStore()
const { showSnackbar } = useSnackbar()

const props = defineProps({
openModal: Boolean,
})
const emits = defineEmits(['update:openModal'])

const { showDialog, closeDialog } = useInteractionDialog()
const { width: windowWidth } = useWindowSize()

// Track the blob URLs to revoke them when the modal is closed
const blobURLs = ref<string[]>([])

/* eslint-disable jsdoc/require-jsdoc */
interface CustomHammerInstance {
destroy(): void
Expand All @@ -519,7 +515,7 @@ interface HammerInstances {
/* eslint-enable jsdoc/require-jsdoc */
const availableVideos = ref<VideoLibraryFile[]>([])
const availableLogFiles = ref<VideoLibraryLogFile[]>([])
const isVisible = ref(props.openModal)
const isVisible = ref(true)
const selectedVideos = ref<VideoLibraryFile[]>([])
const videoPlayerRef = ref<HTMLVideoElement | null>(null)
const currentTab = ref('videos')
Expand All @@ -546,6 +542,8 @@ const showOnScreenProgress = ref(false)
const lastSelectedVideo = ref<VideoLibraryFile | null>(null)
const errorProcessingVideos = ref(false)
const deleteButtonLoading = ref(false)
const videoBlobURL = ref<string | null>(null)
const loadingVideoBlob = ref(false)

const dialogStyle = computed(() => {
const scale = interfaceStore.isOnSmallScreen ? windowWidth.value / 1100 : 1
Expand Down Expand Up @@ -577,7 +575,7 @@ const fileActionButtons = computed(() => [
size: 28,
tooltip: 'Download selected videos with logs',
confirmAction: false,
show: true,
show: !isElectron(),
disabled: showOnScreenProgress.value === true || isPreparingDownload.value === true,
action: () => downloadVideoAndTelemetryFiles(),
},
Expand Down Expand Up @@ -608,10 +606,7 @@ const openVideoFolder = (): void => {

const closeModal = (): void => {
isVisible.value = false
emits('update:openModal', false)
currentTab.value = 'videos'
blobURLs.value.forEach((url) => URL.revokeObjectURL(url))
blobURLs.value = []
deselectAllVideos()
lastSelectedVideo.value = null
isMultipleSelectionMode.value = false
Expand All @@ -635,9 +630,8 @@ const parseMissionAndDateFromTitle = (title: string): string => {
const playVideo = (): void => {
if (selectedVideos.value.length === 1 && !isMultipleSelectionMode.value) {
const videoPlayer = document.getElementById(`video-player`) as HTMLVideoElement
if (videoPlayer) {
videoPlayer.play().catch((e: Error) => console.error('Error auto-playing video:', e))
}
if (!videoPlayer) return
videoPlayer.play().catch((e: Error) => console.error('Error auto-playing video:', e))
}
}

Expand Down Expand Up @@ -916,44 +910,17 @@ const fetchVideosAndLogData = async (): Promise<void> => {
const keys = await videoStore.videoStorage.keys()
for (const key of keys) {
if (videoStore.isVideoFilename(key)) {
videoFilesOperations.push(
(async () => {
const videoBlob = await videoStore.videoStorage.getItem(key)
let url = ''
let isProcessed = true
if (videoBlob instanceof Blob) {
url = URL.createObjectURL(videoBlob)
blobURLs.value.push(url)
} else {
console.error('Video data is not a Blob:', videoBlob)
}
const size = (await videoStore.videoStorageFileSize(key)) ?? 0
return { fileName: key, size, url, isProcessed }
})()
)
videoFilesOperations.push({ fileName: key, isProcessed: true })
}
if (key.endsWith('.ass')) {
logFileOperations.push(
(async () => {
const videoBlob = await videoStore.videoStorage.getItem(key)
let url = ''
if (videoBlob instanceof Blob) {
url = URL.createObjectURL(videoBlob)
blobURLs.value.push(url)
} else {
console.error('Video data is not a Blob:', videoBlob)
}
const size = (await videoStore.videoStorageFileSize(key)) ?? 0
return { fileName: key, url, size }
})()
)
logFileOperations.push({ fileName: key })
}
}

// Fetch unprocessed videos
const unprocessedVideos = await videoStore.unprocessedVideos
const unprocessedVideoOperations = Object.entries(unprocessedVideos).map(async ([hash, videoInfo]) => {
return { ...videoInfo, ...{ hash: hash, url: '', isProcessed: false } }
return { ...videoInfo, ...{ hash: hash, isProcessed: false } }
})

const videos = await Promise.all(videoFilesOperations)
Expand Down Expand Up @@ -998,7 +965,6 @@ watch(
)

watch(isVisible, (newValue) => {
emits('update:openModal', newValue)
if (!newValue) {
resetProgressBars()
isMultipleSelectionMode.value = false
Expand All @@ -1008,34 +974,46 @@ watch(isVisible, (newValue) => {
}
})

watch(
() => props.openModal,
async (newVal) => {
isVisible.value = newVal
if (newVal === true) {
await fetchVideosAndLogData()
showOnScreenProgress.value = false
const loadVideoBlobIntoPlayer = async (videoFileName: string): Promise<void> => {
loadingVideoBlob.value = true

try {
const videoPlayer = document.getElementById(`video-player`) as HTMLVideoElement
const videoBlob = await videoStore.videoStorage.getItem(videoFileName)

if (videoBlob instanceof Blob && videoPlayer) {
videoBlobURL.value = URL.createObjectURL(videoBlob)
videoPlayer.src = videoBlobURL.value
videoPlayer.load()
}
} catch (error) {
const msg = 'Error loading video blob into player'
showSnackbar({ message: msg, duration: 3000, variant: 'error', closeButton: true })
} finally {
loadingVideoBlob.value = false
}
)
}

const unloadVideoBlob = (): void => {
if (!videoBlobURL.value) return
URL.revokeObjectURL(videoBlobURL.value)
videoBlobURL.value = null
}

watch(
selectedVideos,
(newVal) => {
async (newVal) => {
if (newVal.length === 1) {
lastSelectedVideo.value = newVal[0]
await loadVideoBlobIntoPlayer(newVal[0].fileName)
if (errorProcessingVideos.value) {
resetProgressBars()
}
lastSelectedVideo.value = newVal[0]
const videoSrc = newVal[0].url
const videoPlayer = videoPlayerRef.value
if (videoPlayer) {
videoPlayer.src = videoSrc
videoPlayer.load()
}
} else {
unloadVideoBlob()
}
},
{ immediate: true, deep: true }
{ deep: true }
)

// Keep last processed video selected after refresh
Expand Down Expand Up @@ -1067,11 +1045,11 @@ watch(
async () => {
await nextTick()
availableVideos.value.forEach((video) => {
const videoElement = document.getElementById(`video-library-${video.fileName}`)
if (videoElement) {
const videoThumbnailElement = document.getElementById(`video-library-thumbnail-${video.fileName}`)
if (videoThumbnailElement) {
hammerInstances.value[video.fileName]?.destroy()

const hammerManager = new Hammer.Manager(videoElement)
const hammerManager = new Hammer.Manager(videoThumbnailElement)
hammerManager.add(new Hammer.Tap())
hammerManager.add(new Hammer.Press({ time: 500 }))

Expand Down Expand Up @@ -1118,20 +1096,21 @@ watch(
)

onMounted(async () => {
loadingData.value = true
await fetchVideosAndLogData()
if (availableVideos.value.length > 0) {
await loadVideoBlobIntoPlayer(availableVideos.value[0].fileName)
}
showOnScreenProgress.value = false
})

onBeforeUnmount(() => {
currentTab.value = 'videos'
// Revoke each blob URL
blobURLs.value.forEach((url) => URL.revokeObjectURL(url))
blobURLs.value = []
// Properly destroy Hammer instances
Object.values(hammerInstances.value).forEach((instance) => {
instance.destroy()
})
interfaceStore.videoLibraryVisibility = false
unloadVideoBlob()
})
</script>

Expand Down
2 changes: 1 addition & 1 deletion electron/main.ts → src/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function createWindow(): void {
y: store.get('windowBounds')?.y ?? screen.getPrimaryDisplay().bounds.y,
})

mainWindow.on('close', () => {
mainWindow.on('move', () => {
const windowBounds = mainWindow!.getBounds()
const { x, y, width, height } = windowBounds
store.set('windowBounds', { x, y, width, height })
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ipcMain } from 'electron'
import { networkInterfaces } from 'os'

import { NetworkInfo } from '../../src/types/network'
import { NetworkInfo } from '../../types/network'
/**
* Get the network information
* @returns {NetworkInfo} The network information
Expand Down
File renamed without changes.
8 changes: 1 addition & 7 deletions src/types/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,11 @@ export interface UnprocessedVideoInfo extends CommonVideoInfo {
}

export interface VideoLibraryFile extends CommonVideoInfo {
size?: number
url: string
hash?: string
isProcessed: boolean
}

export interface VideoLibraryLogFile extends CommonVideoInfo {
size?: number
url?: string
hash?: string
}
export interface VideoLibraryLogFile extends CommonVideoInfo {}

export interface VideoProgress {
filename: string
Expand Down
4 changes: 2 additions & 2 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const baseConfig = {
(isElectron || isBuilding) &&
electron([
{
entry: 'electron/main.ts',
entry: 'src/electron/main.ts',
vite: {
build: {
outDir: 'dist/electron',
Expand All @@ -34,7 +34,7 @@ const baseConfig = {
},
},
{
entry: 'electron/preload.ts',
entry: 'src/electron/preload.ts',
vite: {
build: {
outDir: 'dist/electron',
Expand Down