Skip to content

Commit

Permalink
fix(player): fix downloaded track not loaded to player
Browse files Browse the repository at this point in the history
  • Loading branch information
aidenlx committed May 8, 2024
1 parent 8576e87 commit 5ffa6de
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 16 deletions.
6 changes: 6 additions & 0 deletions apps/app/src/components/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface MediaViewState {
getPlayer(): Promise<MediaPlayerInstance>;
loadFile(file: TFile, ctx: { vault: Vault; subpath?: string }): Promise<void>;
setSource(url: MediaURL, other: SourceFacet): void;
setTextTracks(tracks: LoadedTextTrack[]): void;
transform: Partial<TransformConfig> | null;
setTransform: (transform: Partial<TransformConfig> | null) => void;
controls?: boolean;
Expand Down Expand Up @@ -85,6 +86,11 @@ export function createMediaViewStore(plugin: MxPlugin) {
tempFragment: null,
volume: undefined,
},
setTextTracks(tracks) {
set(({ textTracks }) => ({
textTracks: { ...textTracks, local: tracks },
}));
},
async getPlayer(timeout = 10e3) {
const { player } = get();
if (player) return player;
Expand Down
1 change: 0 additions & 1 deletion apps/app/src/components/player/caption-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export function Captions() {
.setChecked(selected)
.onClick(() => {
select();
console.log("cacheSelectedTrack", track?.id ?? null, track?.mode);
cacheSelectedTrack(track?.id ?? null);
});
});
Expand Down
35 changes: 26 additions & 9 deletions apps/app/src/components/use-tracks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,15 @@ function useDefaultTrack() {
(
...tracks: { language?: string }[]
): ((
track: { language?: string; id: string },
track: { language?: string; id: string; wid?: string },
index: number,
) => boolean) => {
// if user has selected subtitle for this media
if (lastSelectedTrack === false) return () => false;
if (lastSelectedTrack) return ({ id }) => id === lastSelectedTrack;
if (lastSelectedTrack) {
const lastWid = getWebpageIDFromTrackID(lastSelectedTrack);
return ({ id, wid }) => id === lastSelectedTrack || wid === lastWid;
}
// if user have enabled subtitle by default
if (enableDefaultSubtitle) {
const lang = getDefaultLang(tracks, defaultLang);
Expand Down Expand Up @@ -98,10 +101,9 @@ export function useTextTracks() {
if (!player) return;
const provider = providerRef.current!.get(remoteTracks);
const customFetch: typeof window.fetch = async (url, init) => {
if (!(typeof url === "string" && url.startsWith("webview://")))
return fetch(url, init);
const id = getWebpageIDFromURL(url);
if (!id) return fetch(url, init);
if (!provider) return new Response(null, { status: 500 });
const id = url.slice(`webview://${webpageTrackPrefix}`.length);
const vtt = await provider.media.methods.getTrack(id);
if (!vtt) return new Response(null, { status: 404 });
return Response.json({ cues: vtt.cues, regions: vtt.regions });
Expand All @@ -116,7 +118,7 @@ export function useTextTracks() {
const { wid, src, ...props } = track;
const id = getTrackInfoID(track).id;
const isDefault = defaultTrackPredicate(
{ id, language: props.language },
{ id, language: props.language, wid },
i,
);
const out = new TextTrack({
Expand All @@ -129,16 +131,16 @@ export function useTextTracks() {
});
const remote = dedupeWebsiteTrack(remoteTracks, localTracks).map(
({ wid, ...props }, i) => {
const id = webpageTrackPrefix + wid;
const id = toWebpageID(wid);
const isDefault = defaultTrackPredicate(
{ id, language: props.language },
{ id, language: props.language, wid },
i + localTracks.length,
);
const track = new TextTrack({
...props,
id,
type: "json",
src: `webview://${id}`,
src: toWebpageUrl(wid),
default: isDefault,
});
// track.setMode(isDefault ? "showing" : "disabled");
Expand All @@ -163,6 +165,21 @@ export function useTextTracks() {

const webpageTrackPrefix = "webpage:";

function toWebpageID(wid: string) {
return webpageTrackPrefix + wid;
}
function toWebpageUrl(wid: string) {
return `webview://${toWebpageID(wid)}}`;
}
function getWebpageIDFromTrackID(id: string) {
if (!id.startsWith(webpageTrackPrefix)) return null;
return id.slice(webpageTrackPrefix.length);
}
function getWebpageIDFromURL(url: unknown) {
if (typeof url !== "string" || !url.startsWith("webview://")) return null;
return url.slice(`webview://${webpageTrackPrefix}`.length);
}

export function dedupeWebsiteTrack(
website: WebsiteTextTrack[],
local: TextTrackInfo[],
Expand Down
64 changes: 60 additions & 4 deletions apps/app/src/media-note/note-index/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { MediaPlayerInstance } from "@vidstack/react";
import { Component } from "obsidian";
import { Component, debounce } from "obsidian";
import type { MetadataCache, Vault, TFile } from "obsidian";
import { getMediaInfoID, isFileMediaInfo } from "@/info/media-info";
import { type MediaInfo } from "@/info/media-info";
Expand All @@ -8,6 +8,7 @@ import { getTrackInfoID, type TextTrackInfo } from "@/info/track-info";
import { iterateFiles } from "@/lib/iterate-files";
import { waitUntilResolve } from "@/lib/meta-resolve";
import { normalizeFilename } from "@/lib/norm";
import type { PlayerComponent } from "@/media-view/base";
import type MxPlugin from "@/mx-main";
import { mediaTitle } from "../title";
import type { MediaSourceFieldType } from "./def";
Expand All @@ -26,10 +27,14 @@ declare module "obsidian" {
on(name: "initialized", callback: () => any, ctx?: any): EventRef;
on(
name: "mx:transcript-changed",
callback: (trackIDs: Set<string>) => any,
callback: (trackIDs: Set<string>, mediaID: string) => any,
ctx?: any,
): EventRef;
trigger(name: "mx:transcript-changed", trackIDs: Set<string>): void;
trigger(
name: "mx:transcript-changed",
trackIDs: Set<string>,
mediaID: string,
): void;
initialized: boolean;
}
}
Expand Down Expand Up @@ -194,6 +199,7 @@ export class MediaNoteIndex extends Component {
this.app.metadataCache.trigger(
"mx:transcript-changed",
new Set(affected),
mediaID,
);
}
}
Expand Down Expand Up @@ -224,7 +230,11 @@ export class MediaNoteIndex extends Component {
});
}
if (affected.size > 0) {
this.app.metadataCache.trigger("mx:transcript-changed", affected);
this.app.metadataCache.trigger(
"mx:transcript-changed",
affected,
mediaID,
);
}
}

Expand All @@ -247,3 +257,49 @@ function* iterateMediaNote(ctx: {
yield { meta, file };
}
}

export function handleTrackUpdate(this: PlayerComponent) {
const updateTracks = asyncDebounce(async (url: MediaInfo) => {
const textTracks = await this.plugin.transcript.getTracks(url);
this.store.getState().setTextTracks(textTracks);
});
this.registerEvent(
this.plugin.app.metadataCache.on(
"mx:transcript-changed",
async (_, mediaID) => {
const currMedia = this.getMediaInfo();
if (!currMedia) return;
const currMediaID = getMediaInfoID(currMedia);
if (currMediaID !== mediaID) return;
await updateTracks(currMedia);
},
),
);
}

function asyncDebounce<F extends (...args: any[]) => Promise<any>>(
func: F,
wait?: number,
) {
const resolveSet = new Set<(p: any) => void>();
const rejectSet = new Set<(p: any) => void>();

const debounced = debounce((args: Parameters<F>) => {
func(...args)
.then((...res) => {
resolveSet.forEach((resolve) => resolve(...res));
resolveSet.clear();
})
.catch((...res) => {
rejectSet.forEach((reject) => reject(...res));
rejectSet.clear();
});
}, wait);

return (...args: Parameters<F>): ReturnType<F> =>
new Promise((resolve, reject) => {
resolveSet.add(resolve);
rejectSet.add(reject);
debounced(args);
}) as ReturnType<F>;
}
2 changes: 2 additions & 0 deletions apps/app/src/media-view/file-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { FileMediaInfo } from "@/info/media-info";
import { checkMediaType } from "@/info/media-type";
import type { PaneMenuSource } from "@/lib/menu";
import { handleWindowMigration } from "@/lib/window-migration";
import { handleTrackUpdate } from "@/media-note/note-index";
import type MediaExtended from "@/mx-main";
import { type PlayerComponent, addAction, onPaneMenu } from "./base";
import type { MediaFileViewType } from "./view-type";
Expand Down Expand Up @@ -37,6 +38,7 @@ abstract class MediaFileView
}

onload(): void {
handleTrackUpdate.call(this);
handleWindowMigration(this, () => this.render());
}

Expand Down
9 changes: 7 additions & 2 deletions apps/app/src/media-view/remote-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import {
} from "@/components/context";
import { Player } from "@/components/player";
import { isFileMediaInfo } from "@/info/media-info";
import type { MediaURL } from "@/info/media-url";
import { MediaURL } from "@/info/media-url";
import type { PaneMenuSource } from "@/lib/menu";
import { updateTitle } from "@/lib/view-title";
import { handleWindowMigration } from "@/lib/window-migration";
import { handleTrackUpdate } from "@/media-note/note-index";
import { compare } from "@/media-note/note-index/def";
import type MediaExtended from "@/mx-main";
import type { PlayerComponent } from "./base";
Expand Down Expand Up @@ -42,7 +43,9 @@ export abstract class MediaRemoteView
return { viewType: this.getViewType(), textTracks };
}
getMediaInfo() {
return this.store.getState().source?.url ?? null;
const url = this.store.getState().source?.url;
if (!(url instanceof MediaURL)) return null;
return url ?? null;
}
get sourceType(): string {
return this.store.getState().player?.state.source.type ?? "";
Expand All @@ -63,6 +66,7 @@ export abstract class MediaRemoteView
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
super.onload();
handleTrackUpdate.call(this);
// make sure to unmount the player before the leaf detach it from DOM
this.register(
around(this.leaf, {
Expand All @@ -74,6 +78,7 @@ export abstract class MediaRemoteView
},
}),
);

handleWindowMigration(this, () => this.render());
}

Expand Down

0 comments on commit 5ffa6de

Please sign in to comment.