Skip to content

Commit

Permalink
Merge pull request #272 from openzim/add-es6-support
Browse files Browse the repository at this point in the history
Update Vite configuration in zimui for native ES6 support
  • Loading branch information
benoit74 authored Jul 23, 2024
2 parents 76305b7 + ed62be8 commit 3e161d0
Show file tree
Hide file tree
Showing 19 changed files with 1,487 additions and 48 deletions.
3 changes: 3 additions & 0 deletions zimui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

<body>
<div id="app"></div>
<noscript>
<p>JavaScript is disabled in your browser. Please enable JavaScript to view this ZIM.</p>
</noscript>
<script type="module" src="./src/main.ts"></script>
</body>
</html>
5 changes: 4 additions & 1 deletion zimui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"axios": "^1.7.2",
"dayjs": "^1.11.11",
"pinia": "^2.1.7",
"resize-observer-polyfill": "^1.5.1",
"video.js": "^8.12.0",
"vite-plugin-vuetify": "^2.0.3",
"vue": "^3.4.21",
Expand All @@ -32,6 +33,7 @@
"@tsconfig/node20": "^20.1.4",
"@types/jsdom": "^21.1.6",
"@types/node": "^20.12.5",
"@vitejs/plugin-legacy": "^5.4.1",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0",
Expand All @@ -42,8 +44,9 @@
"eslint-plugin-vue": "^9.23.0",
"jsdom": "^24.0.0",
"npm-run-all2": "^6.1.2",
"ogv": "^1.9.0",
"ogv": "1.8.9",
"prettier": "^3.2.5",
"terser": "^5.31.3",
"typescript": "~5.4.0",
"vite": "^5.2.8",
"vite-plugin-static-copy": "^1.0.5",
Expand Down
6 changes: 0 additions & 6 deletions zimui/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { RouterView } from 'vue-router'
import { useMainStore } from '@/stores/main'
import { triggerWebpPolyfill } from './plugins/webp-hero'
import ErrorDisplay from '@/components/common/ErrorDisplay.vue'
const main = useMainStore()
onMounted(() => {
triggerWebpPolyfill()
})
</script>

<template>
Expand Down
Binary file added zimui/src/assets/images/thumbnail-placeholder.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed zimui/src/assets/images/thumbnail-placeholder.webp
Binary file not shown.
17 changes: 17 additions & 0 deletions zimui/src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,20 @@ a {
U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
U+FFFD;
}

.v-btn {
display: flex !important;
}

.v-slide-group__content {
align-items: center;
justify-content: center;
}

.v-avatar img {
border-radius: 100%;
}

.v-btn__content > .v-icon--start {
margin-right: 0.5rem;
}
2 changes: 2 additions & 0 deletions zimui/src/assets/vjs-youtube.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
.vjs-youtube,
.vjs-youtube video {
border-radius: 8px;
width: 100%;
height: auto;
}

.vjs-youtube .vjs-control-bar {
Expand Down
8 changes: 5 additions & 3 deletions zimui/src/components/channel/ChannelHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,11 @@ const tab = ref<number>(tabs[0].id)

<!-- Tabs Navigation -->
<v-tabs v-if="!hideTabs" v-model="tab" align-tabs="center">
<v-tab v-for="item in tabs" :key="item.id" :to="item.to">
{{ item.title }}
</v-tab>
<router-link v-for="item in tabs" :key="item.id" :to="item.to" class="text-black">
<v-tab>
{{ item.title }}
</v-tab>
</router-link>
</v-tabs>
</v-card>
</v-container>
Expand Down
17 changes: 13 additions & 4 deletions zimui/src/components/playlist/PlaylistCard.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { computed } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { useDisplay } from 'vuetify'
import type { PlaylistPreview } from '@/types/Playlists'
import { truncateText } from '@/utils/format-utils'
import thumbnailPlaceholder from '@/assets/images/thumbnail-placeholder.webp'
import thumbnailPlaceholder from '@/assets/images/thumbnail-placeholder.jpg'
import { polyfillThumbnail } from '@/plugins/webp-hero'
const { smAndDown } = useDisplay()
Expand All @@ -30,6 +31,14 @@ const truncatedTitle = computed<string>(() => {
const videoCount = computed<string>(() => {
return props.playlist.videosCount === 1 ? '1 video' : `${props.playlist.videosCount} videos`
})
const thumbnailSrc = ref(props.playlist.thumbnailPath)
// Polyfill the thumbnail if the browser doesn't support WebP
onMounted(async () => {
if (!thumbnailSrc.value) return
thumbnailSrc.value = await polyfillThumbnail(thumbnailSrc.value)
})
</script>

<template>
Expand All @@ -54,9 +63,9 @@ const videoCount = computed<string>(() => {
</div>
<div class="position-relative">
<v-img
class="border-thin rounded-lg"
class="d-block border-thin rounded-lg"
:lazy-src="thumbnailPlaceholder"
:src="props.playlist.thumbnailPath"
:src="thumbnailSrc"
min-width="125"
max-width="400"
></v-img>
Expand Down
6 changes: 2 additions & 4 deletions zimui/src/components/playlist/panel/PlaylistPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ const windowHeight = ref(
// Calculate the height of the playlist panel container
const panelContainerHeight = computed<string>(() => {
if (smAndDown.value) return '350px'
const panelHeight = Math.min(windowHeight.value - 150, 800)
const panelHeight = Math.min(windowHeight.value - 150, smAndDown.value ? 350 : 800)
const totalItemsHeight = props.playlist.videos.length * 90
return totalItemsHeight < panelHeight ? '100%' : `${panelHeight}px`
Expand Down Expand Up @@ -79,7 +77,7 @@ const emit = defineEmits(['shuffle', 'loop', 'hide-panel'])
</v-col>
</v-row>
<v-row dense no-gutters>
<v-col>
<v-col class="d-flex">
<v-btn
class="pa-2"
size="md"
Expand Down
17 changes: 13 additions & 4 deletions zimui/src/components/playlist/panel/PlaylistPanelItem.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { computed } from 'vue'
import { computed, onMounted, ref } from 'vue'
import type { VideoPreview } from '@/types/Videos'
import { formatTimestamp, truncateText } from '@/utils/format-utils'
import { useDisplay } from 'vuetify'
import thumbnailPlaceholder from '@/assets/images/thumbnail-placeholder.webp'
import thumbnailPlaceholder from '@/assets/images/thumbnail-placeholder.jpg'
import { polyfillThumbnail } from '@/plugins/webp-hero'
const { smAndDown } = useDisplay()
Expand All @@ -27,6 +28,14 @@ const titleLength = computed<number>(() => {
const truncatedTitle = computed<string>(() => {
return truncateText(props.video.title, titleLength.value)
})
const thumbnailSrc = ref(props.video.thumbnailPath)
// Polyfill the thumbnail if the browser doesn't support WebP
onMounted(async () => {
if (!thumbnailSrc.value) return
thumbnailSrc.value = await polyfillThumbnail(thumbnailSrc.value)
})
</script>

<template>
Expand All @@ -48,9 +57,9 @@ const truncatedTitle = computed<string>(() => {
<span v-else class="mx-2">{{ order }}</span>
</div>
<v-img
class="border-thin rounded-lg"
class="d-block border-thin rounded-lg"
:lazy-src="thumbnailPlaceholder"
:src="props.video.thumbnailPath"
:src="thumbnailSrc"
min-width="50"
max-width="300"
></v-img>
Expand Down
17 changes: 13 additions & 4 deletions zimui/src/components/video/VideoCard.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { computed } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { useDisplay } from 'vuetify'
import type { VideoPreview } from '@/types/Videos'
import { formatTimestamp, truncateText } from '@/utils/format-utils'
import { polyfillThumbnail } from '@/plugins/webp-hero'
import thumbnailPlaceholder from '@/assets/images/thumbnail-placeholder.webp'
import thumbnailPlaceholder from '@/assets/images/thumbnail-placeholder.jpg'
const { smAndDown } = useDisplay()
Expand All @@ -32,6 +33,14 @@ const truncatedTitle = computed<string>(() => {
const duration = computed<string>(() => {
return formatTimestamp(props.video.duration)
})
const thumbnailSrc = ref(props.video.thumbnailPath)
// Polyfill the thumbnail if the browser doesn't support WebP
onMounted(async () => {
if (!thumbnailSrc.value) return
thumbnailSrc.value = await polyfillThumbnail(thumbnailSrc.value)
})
</script>

<template>
Expand All @@ -47,9 +56,9 @@ const duration = computed<string>(() => {
<v-col cols="5" md="12">
<div class="position-relative">
<v-img
class="rounded-lg border-thin"
class="d-block rounded-lg border-thin"
:lazy-src="thumbnailPlaceholder"
:src="props.video.thumbnailPath"
:src="thumbnailSrc"
min-width="125"
max-width="400"
></v-img>
Expand Down
24 changes: 23 additions & 1 deletion zimui/src/components/video/VideoPlayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,28 @@ onBeforeUnmount(() => {

<template>
<div>
<video ref="videoPlayer" class="video-js vjs-youtube"></video>
<video
ref="videoPlayer"
class="video-js vjs-youtube"
:controls="props.options.controls"
:preload="props.options.preload"
:autoplay="props.options.autoplay"
:poster="props.options.poster"
>
<source
v-for="(source, idx) in props.options.sources"
:key="idx"
:src="source.src"
:type="source.type"
/>
<track
v-for="(track, idx) in props.options.tracks"
:key="idx"
:kind="track.kind"
:src="track.src"
:srclang="track.code"
:label="track.label"
/>
</video>
</div>
</template>
7 changes: 7 additions & 0 deletions zimui/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import App from './App.vue'
import router from './router'
import loadVuetify from './plugins/vuetify'

import ResizeObserver from 'resize-observer-polyfill'

if (typeof window.ResizeObserver === 'undefined') {
console.debug('Polyfilling ResizeObserver')
window.ResizeObserver = ResizeObserver
}

loadVuetify()
.then((vuetify) => {
const app = createApp(App)
Expand Down
7 changes: 4 additions & 3 deletions zimui/src/plugins/vuetify.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'vuetify/styles'
import '@mdi/font/css/materialdesignicons.css'
import axios from 'axios'
import { createVuetify } from 'vuetify'
import type { Config } from '@/types/Channel'

Expand All @@ -9,9 +10,9 @@ async function loadVuetify() {

// Load primary and secondary colors from config.json
try {
const response = await fetch('./config.json')
if (response.ok) {
const config: Config = await response.json()
const response = await axios.get('./config.json')
if (response.status === axios.HttpStatusCode.Ok) {
const config: Config = response.data
primaryColor = config.mainColor || primaryColor
secondaryColor = config.secondaryColor || secondaryColor
} else {
Expand Down
29 changes: 22 additions & 7 deletions zimui/src/plugins/webp-hero.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import { WebpMachine, detectWebpSupport } from 'webp-hero'
import {
WebpMachine,
detectWebpSupport,
loadBinaryData,
convertDataURIToBinary,
isBase64Url
} from 'webp-hero'
import { Webp } from 'webp-hero/libwebp/dist/webp'
import 'webp-hero/dist-cjs/polyfills.js'

export const triggerWebpPolyfill = () => {
detectWebpSupport().then((support_webp) => {
if (!support_webp) {
const webpMachine = new WebpMachine()
webpMachine.polyfillDocument()
const webp = new Webp()

export async function polyfillThumbnail(src: string): Promise<string> {
const isWebpSupport = await detectWebpSupport()
try {
if (!isWebpSupport) {
const webpMachine = new WebpMachine({ webp })
const webpData = isBase64Url(src) ? convertDataURIToBinary(src) : await loadBinaryData(src)
const pngData = await webpMachine.decode(webpData)
if (pngData) return pngData
}
})
} catch (error) {
console.error(`Error polyfilling thumbnail "${src}": `, error)
}
return src
}
16 changes: 13 additions & 3 deletions zimui/src/views/PlaylistView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import type { Playlist } from '@/types/Playlists'
import { formatDate } from '@/utils/format-utils'
import PlaylistPanelItem from '@/components/playlist/panel/PlaylistPanelItem.vue'
import thumbnailPlaceholder from '@/assets/images/thumbnail-placeholder.webp'
import thumbnailPlaceholder from '@/assets/images/thumbnail-placeholder.jpg'
import { polyfillThumbnail } from '@/plugins/webp-hero'
const main = useMainStore()
const route = useRoute()
const router = useRouter()
const slug: string = route.params.slug as string
const playlist: Ref<Playlist> = ref<Playlist>() as Ref<Playlist>
const thumbnailSrc = ref('')
// Fetch playlist data
const fetchPlaylistData = async function () {
Expand Down Expand Up @@ -54,6 +56,14 @@ const onShuffleClick = function () {
// Fetch the data on component mount
onMounted(() => {
fetchPlaylistData()
.then(() => {
if (!playlist.value.thumbnailPath) return
thumbnailSrc.value = playlist.value.thumbnailPath
})
.then(async () => {
if (!thumbnailSrc.value) return
thumbnailSrc.value = await polyfillThumbnail(thumbnailSrc.value)
})
})
const { mdAndDown } = useDisplay()
Expand All @@ -67,11 +77,11 @@ const { mdAndDown } = useDisplay()
<v-card flat class="header-card rounded-lg border-thin pa-5">
<v-img
:lazy-src="thumbnailPlaceholder"
:src="playlist.thumbnailPath"
:src="thumbnailSrc"
min-width="125"
max-width="400"
aspect-ratio="16/9"
class="rounded-lg"
class="d-block rounded-lg"
/>

<p class="playlist-title text-h5 font-weight-bold mt-4">{{ playlist.title }}</p>
Expand Down
Loading

0 comments on commit 3e161d0

Please sign in to comment.