vtt subtitles

This commit is contained in:
2026-04-15 19:08:31 -07:00
parent 707ba4cdf2
commit f7636f61a7
7 changed files with 57 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ const spacetimedb = schema({
{
id: t.u64().primaryKey(),
url: t.string(),
subtitleUrl: t.string(),
timePosition: t.f64(),
isPlaying: t.bool(),
lastUpdatedAt: t.timestamp(),
@@ -19,6 +20,7 @@ export const init = spacetimedb.init((ctx) => {
ctx.db.videoState.insert({
id: 1n,
url: "https://cdn.cazzzer.com/LycoReco08.mkv",
subtitleUrl: "",
timePosition: 0.0,
isPlaying: false,
lastUpdatedAt: ctx.timestamp,
@@ -34,6 +36,7 @@ export const set_url = spacetimedb.reducer({ url: t.string() }, (ctx, { url }) =
ctx.db.videoState.id.update({
...row,
url,
subtitleUrl: "", // Clear subtitle on new video
timePosition: 0.0,
isPlaying: false,
lastUpdatedAt: ctx.timestamp,
@@ -71,3 +74,13 @@ export const seek = spacetimedb.reducer({ time_position: t.f64() }, (ctx, { time
lastUpdatedAt: ctx.timestamp,
});
});
export const set_subtitle_url = spacetimedb.reducer({ url: t.string() }, (ctx, { url }) => {
const row = ctx.db.videoState.id.find(1n);
if (!row) return;
ctx.db.videoState.id.update({
...row,
subtitleUrl: url,
lastUpdatedAt: ctx.timestamp,
});
});

View File

@@ -37,6 +37,7 @@ import {
import PauseReducer from "./pause_reducer";
import PlayReducer from "./play_reducer";
import SeekReducer from "./seek_reducer";
import SetSubtitleUrlReducer from "./set_subtitle_url_reducer";
import SetUrlReducer from "./set_url_reducer";
// Import all procedure arg schemas
@@ -66,6 +67,7 @@ const reducersSchema = __reducers(
__reducerSchema("pause", PauseReducer),
__reducerSchema("play", PlayReducer),
__reducerSchema("seek", SeekReducer),
__reducerSchema("set_subtitle_url", SetSubtitleUrlReducer),
__reducerSchema("set_url", SetUrlReducer),
);

View File

@@ -0,0 +1,15 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
/* eslint-disable */
/* tslint:disable */
import {
TypeBuilder as __TypeBuilder,
t as __t,
type AlgebraicTypeType as __AlgebraicTypeType,
type Infer as __Infer,
} from "spacetimedb";
export default {
url: __t.string(),
};

View File

@@ -13,6 +13,7 @@ import {
export const VideoState = __t.object("VideoState", {
id: __t.u64(),
url: __t.string(),
subtitleUrl: __t.string(),
timePosition: __t.f64(),
isPlaying: __t.bool(),
lastUpdatedAt: __t.timestamp(),

View File

@@ -9,10 +9,12 @@ import { type Infer as __Infer } from "spacetimedb";
import PauseReducer from "../pause_reducer";
import PlayReducer from "../play_reducer";
import SeekReducer from "../seek_reducer";
import SetSubtitleUrlReducer from "../set_subtitle_url_reducer";
import SetUrlReducer from "../set_url_reducer";
export type PauseParams = __Infer<typeof PauseReducer>;
export type PlayParams = __Infer<typeof PlayReducer>;
export type SeekParams = __Infer<typeof SeekReducer>;
export type SetSubtitleUrlParams = __Infer<typeof SetSubtitleUrlReducer>;
export type SetUrlParams = __Infer<typeof SetUrlReducer>;

View File

@@ -13,6 +13,7 @@ import {
export default __t.row({
id: __t.u64().primaryKey(),
url: __t.string(),
subtitleUrl: __t.string().name("subtitle_url"),
timePosition: __t.f64().name("time_position"),
isPlaying: __t.bool().name("is_playing"),
lastUpdatedAt: __t.timestamp().name("last_updated_at"),

View File

@@ -8,12 +8,14 @@
const videoState = $derived($videoStates.find((state) => state.id === 1n));
const setUrlReducer = useReducer(reducers.setUrl);
const setSubtitleUrlReducer = useReducer(reducers.setSubtitleUrl);
const playReducer = useReducer(reducers.play);
const pauseReducer = useReducer(reducers.pause);
const seekReducer = useReducer(reducers.seek);
let videoElement: HTMLVideoElement | undefined = $state();
let newUrl = $state("");
let newSubtitleUrl = $state("");
let isSyncing = false;
let syncTimeout: ReturnType<typeof setTimeout> | undefined;
@@ -111,6 +113,13 @@
setUrlReducer({ url: newUrl });
newUrl = "";
}
function handleSetSubtitle(e: SubmitEvent) {
e.preventDefault();
if (!newSubtitleUrl.trim() || !$conn.isActive) return;
setSubtitleUrlReducer({ url: newSubtitleUrl });
newSubtitleUrl = "";
}
</script>
<div class="p-4">
@@ -134,18 +143,32 @@
<button type="submit" class="p-2" disabled={!$conn.isActive}>Set URL</button>
</form>
<form onsubmit={handleSetSubtitle} class="mb-2">
<input
type="url"
placeholder="Enter subtitle track URL (.vtt)"
bind:value={newSubtitleUrl}
class="mr-2 w-96 p-2"
/>
<button type="submit" class="p-2">Set Subtitles</button>
</form>
<div>
<!-- svelte-ignore a11y_media_has_caption -->
<video
bind:this={videoElement}
muted
controls
crossorigin="anonymous"
onplay={handlePlay}
onpause={handlePause}
onseeked={handleSeeked}
ontimeupdate={handleTimeUpdate}
class="w-full max-w-2xl bg-black"
>
{#if videoState?.subtitleUrl}
<track src={videoState.subtitleUrl} kind="subtitles" srclang="en" label="English" default />
{/if}
Your browser does not support the video tag.
</video>
</div>