feat: ✨ chapters based on timecodes in description (fix #69)
This commit is contained in:
parent
eb6a4b2d9b
commit
e43adc79a1
@ -1,22 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-sanitize="source" class="html" />
|
<div ref="html" v-sanitize="source" class="html" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import dompurify from 'dompurify'
|
import dompurify from 'dompurify'
|
||||||
import linkifyHtml from 'linkify-html'
|
import linkifyHtml from 'linkify-html'
|
||||||
|
import { mapActions } from 'pinia'
|
||||||
|
import { timeToSeconds } from '../../utils/time.ts'
|
||||||
|
import { usePlayer } from '../../store/player.ts'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SafeHtml',
|
name: 'SafeHtml',
|
||||||
directives: {
|
directives: {
|
||||||
sanitize: {
|
sanitize: {
|
||||||
mounted(el, binding) {
|
mounted(el, binding) {
|
||||||
el.innerHTML = dompurify.sanitize(
|
el.innerHTML = dompurify
|
||||||
linkifyHtml(binding.value, {
|
.sanitize(
|
||||||
nl2br: true,
|
linkifyHtml(binding.value, {
|
||||||
target: '_blank',
|
nl2br: true,
|
||||||
}),
|
target: '_blank',
|
||||||
)
|
}),
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
/(([0-9]?[0-9]):)?([0-5]?[0-9]):([0-5][0-9])/g,
|
||||||
|
(
|
||||||
|
match,
|
||||||
|
noop: string,
|
||||||
|
hours: string,
|
||||||
|
minutes: string,
|
||||||
|
seconds: string,
|
||||||
|
) =>
|
||||||
|
`<seekable time="${timeToSeconds(
|
||||||
|
parseInt(hours),
|
||||||
|
parseInt(minutes),
|
||||||
|
parseInt(seconds),
|
||||||
|
)}">${match}</seekable>`,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -26,11 +45,31 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
const seekables = (this.$refs.html as HTMLElement).querySelectorAll(
|
||||||
|
'seekable',
|
||||||
|
)
|
||||||
|
for (const seekable of seekables) {
|
||||||
|
seekable.addEventListener('click', (event) => {
|
||||||
|
this.seek(
|
||||||
|
parseInt(
|
||||||
|
(event.target as HTMLElement).getAttribute('time') || '',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
this.play()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(usePlayer, ['play', 'seek']),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.html a {
|
.html a,
|
||||||
|
seekable {
|
||||||
|
cursor: pointer;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -51,3 +51,16 @@ export const durationToSeconds = (duration: string): number => {
|
|||||||
seconds += splitDuration.length > 2 ? parseInt(splitDuration[2]) * 60 * 60 : 0
|
seconds += splitDuration.length > 2 ? parseInt(splitDuration[2]) * 60 * 60 : 0
|
||||||
return seconds
|
return seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert splitted time to seconds
|
||||||
|
* @param {number} hours The number of seconds
|
||||||
|
* @param {number} minutes The number of seconds
|
||||||
|
* @param {number} seconds The number of seconds
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
export const timeToSeconds = (
|
||||||
|
hours: number,
|
||||||
|
minutes: number,
|
||||||
|
seconds: number,
|
||||||
|
): number => hours * 3600 + minutes * 60 + seconds
|
||||||
|
Loading…
Reference in New Issue
Block a user