Skip to content

Commit

Permalink
create videopack and editor
Browse files Browse the repository at this point in the history
  • Loading branch information
nknapp committed Aug 18, 2024
1 parent 9dba7af commit 8e1b623
Show file tree
Hide file tree
Showing 25 changed files with 3,111 additions and 47 deletions.
31 changes: 31 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@astrojs/tailwind": "5.1.0",
"@material-design-icons/svg": "0.14.13",
"@nanostores/persistent": "0.10.2",
"@tanstack/solid-table": "8.20.1",
"@testing-library/jest-dom": "6.4.8",
"@testing-library/react": "16.0.0",
"astro-i18next": "1.0.0-beta.21",
Expand Down
37 changes: 37 additions & 0 deletions scripts/buildVideoPack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Just a one time script to generate a video pack. This may be removed in the future.
*/

import darmstadt from "../src/data/dojos/aikido-dojo-darmstadt/details.ts";
import { resolveExamTables } from "$core/resolveExamTables";
import { buildTechniqueTree } from "$core/buildExamTable/buildExamTable.ts";
import type { VideoPack } from "$core/model/VideoPack.ts";
import { coerceToArray } from "$core/utils/coerceToArray.ts";

import prettier from "prettier";

const allTechniques = resolveExamTables(darmstadt.exams);

let counter = 0;
for (const technique of allTechniques) {
for (const youtube of coerceToArray(technique.metadata.youtube)) {
youtube.id = String(counter++);
}
}

const pack: VideoPack = {
name: "Aikido Kompendium",
source: "https://www.aikido-kompendium.de",
videos: buildTechniqueTree(allTechniques, (technique) => coerceToArray(technique.metadata.youtube)),
};

// eslint-disable-next-line no-console
console.log(
await prettier.format(
`import type {VideoPack} from "$core/model/VideoPack";
export default ${JSON.stringify(pack, null, 2)} satisfies VideoPack
`,
{ parser: "typescript" },
),
);
6 changes: 5 additions & 1 deletion scripts/sort-translations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import fs from "node:fs/promises";
import prettier from "prettier";
import { fileURLToPath } from "url";

for (const file of process.argv.slice(2)) {
const urls = import.meta.glob<string>("../src/i18n/**/*.json", { query: "url", eager: true, import: "default" });
const files = Object.keys(urls).map((file) => fileURLToPath(new URL(file, import.meta.url)));

for (const file of files) {
const translations = JSON.parse(await fs.readFile(file, "utf-8"));
const sortedTranslations = Object.fromEntries(
Object.entries(translations).toSorted((a, b) => {
Expand Down
48 changes: 48 additions & 0 deletions src/YoutubePlayer/adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { youtubeEnabled } from "$core/store/youtube.ts";

export interface YoutubeAdapter {
loadVideoById(videoId: string, startSeconds?: number, endSeconds?: number): Promise<void>;
playVideo(): Promise<void>;
stopVideo(): Promise<void>;
setSize(width: number, height: number): Promise<object>;
waitForStop(): Promise<void>;
getCurrentTime(): Promise<number>;
destroy(): Promise<void>;
}

export async function loadYoutubeAdapter(container: HTMLDivElement): Promise<YoutubeAdapter> {
if (!youtubeEnabled.get()) {
throw new Error("No youtube consent was given.");
}
const { default: Player } = await import("youtube-player");
const player = Player(container, {
host: "https://www.youtube-nocookie.com",
playerVars: {
rel: 0,
autoplay: 0,
modestbranding: 1,
controls: 1,
},
});

return {
playVideo: player.playVideo,
loadVideoById(videoId, startSeconds, endSeconds) {
return player.loadVideoById({ videoId, startSeconds, endSeconds });
},
stopVideo: player.stopVideo,
setSize: player.setSize,
waitForStop() {
return new Promise<void>((resolve) => {
player.on("stateChange", (event) => {
if (event.data === 0) {
// Video has ended
resolve();
}
});
});
},
getCurrentTime: player.getCurrentTime,
destroy: player.destroy,
};
}
22 changes: 4 additions & 18 deletions src/YoutubePlayer/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { YoutubeLink } from "$core/model";
import { renderPlayerContainer } from "@/YoutubePlayer/PlayerContainer.tsx";
import { youtubeEnabled } from "$core/store/youtube.ts";
import { loadYoutubeAdapter } from "@/YoutubePlayer/adapter.ts";

export interface YoutubePlayer {
loadVideo(videoId: string): Promise<void>;
Expand Down Expand Up @@ -32,15 +33,7 @@ youtubeEnabled.subscribe((value) => {

async function createPlayer(): Promise<YoutubePlayer> {
const container = await renderPlayerContainer();
const { default: Player } = await import("youtube-player");
const player = Player(container.htmlElement, {
host: "https://www.youtube-nocookie.com",
playerVars: {
rel: 0,
autoplay: 0,
modestbranding: 1,
},
});
const player = await loadYoutubeAdapter(container.htmlElement);

function updatePlayerSize() {
player?.setSize(window.innerWidth, window.innerHeight);
Expand All @@ -51,7 +44,7 @@ async function createPlayer(): Promise<YoutubePlayer> {

const result = {
loadVideo(videoId: string) {
return player.loadVideoById({ videoId });
return player.loadVideoById(videoId);
},
async play() {
await player.playVideo();
Expand All @@ -62,14 +55,7 @@ async function createPlayer(): Promise<YoutubePlayer> {
container.setVisible(false);
},
async waitForStop() {
return new Promise<void>((resolve) => {
player.on("stateChange", (event) => {
if (event.data === 0) {
// Video has ended
resolve();
}
});
});
await player.waitForStop();
},
};
container.addEventListener("stop", () => {
Expand Down
8 changes: 2 additions & 6 deletions src/components/solid/organisms/Reader/Reader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { type DelayControl, DelayIndicator } from "@/components/solid/atoms/Dela
import { youtubeEnabled } from "$core/store/youtube.ts";
import { usePersistentStore } from "@/components/solid/hooks/usePersistentStore.ts";
import { YoutubePlayButton } from "@/components/solid/atoms/YoutubePlayButton.tsx";
import { coerceToArray } from "$core/utils/coerceToArray.ts";

export const Reader: Component<{ dojoInfo: DojoInfo; speechPack: SpeechPack }> = (props) => {
const techniqueStore = createTechniqueStore(props.dojoInfo.id);
Expand Down Expand Up @@ -56,7 +57,7 @@ export const Reader: Component<{ dojoInfo: DojoInfo; speechPack: SpeechPack }> =
ready={playerLoaded()}
onClickAutoPlay={() => setAutoPlay(!autoPlay())}
autoPlayEnabled={autoPlay()}
youtube={youtubeLinks(lastTechnique()?.metadata?.youtube)}
youtube={coerceToArray(lastTechnique()?.metadata?.youtube)}
/>
<DelayIndicator setDelayControl={setDelayControl} disabled={!autoPlay()} />
<ExamScroll
Expand Down Expand Up @@ -142,8 +143,3 @@ const Player: Component<{
</>
);
};

function youtubeLinks(links: YoutubeLink[] | YoutubeLink | undefined): YoutubeLink[] {
if (Array.isArray(links)) return links;
return links ? [links] : [];
}
Loading

0 comments on commit 8e1b623

Please sign in to comment.