Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ Once you have uploaded your first video you can show your videos in different fo

You can either change the settings through our interface or provide it as attributes. To learn which attributes you can use to change the appearance of your player, go to [our docs](https://docs.mave.io).

For a space with JWT playback enabled, set the customer JWT as a JavaScript
property. The component exchanges it once for an embed-scoped media session;
the customer JWT is never added to media URLs or reflected to an HTML attribute.

```js
const player = document.querySelector('mave-player');
player.token = customerJwt;
```

The same non-reflecting `token` property is available on `mave-clip`,
`mave-img`, `mave-text`, `mave-files`, `mave-list`, and `mave-pop`.

### Clip

```html
Expand All @@ -108,12 +120,16 @@ We often find ourselves using simple `.mp4` files, because we just want to show
### List

```html
<mave-list token="<token>">
<mave-list id="videos">
<template>
<div slot="item-title"></div>
<mave-img></mave-img>
</template>
</mave-list>

<script type="module">
document.querySelector('#videos').token = customerJwt;
</script>
```

<img width="894" alt="Screenshot 2023-05-22 at 15 37 55" src="https://github.com/maveio/components/assets/238946/aa7b04e0-01f1-4ac2-976d-3dfe4157a809">
Expand Down
2 changes: 1 addition & 1 deletion src/components/clip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class Clip extends LitElement {
}

private _token: string;
@property()
@property({ attribute: false })
get token(): string {
return this._token;
}
Expand Down
16 changes: 15 additions & 1 deletion src/components/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ export class Files extends MaveElement {
}
}

private _token: string;
@property({ attribute: false })
get token(): string {
return this._token;
}

set token(value: string) {
if (this._token !== value) {
this._token = value;
this.requestUpdate('token');
this.embedController.token = value;
}
}

get _slottedChildren() {
const slot = this.shadowRoot?.querySelector('slot');
return slot?.assignedElements({ flatten: true }) || [];
Expand Down Expand Up @@ -319,7 +333,7 @@ export class Files extends MaveElement {
this._data?.video?.version && this._data.video.version > 0
? `v${this._data.video.version}/`
: '';
return `${this.cdn_root}/${this.embedId}/${versionSegment}${filename}`;
return this.embedController.embedFile(`${versionSegment}${filename}`);
}

#buildVideoDownloadUrl(rendition?: Rendition): string {
Expand Down
53 changes: 50 additions & 3 deletions src/components/img.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,45 @@
import { Task } from '@lit/task';
import { css, html } from 'lit';
import { property } from 'lit/decorators.js';
import { PlaybackSession, playbackSession } from '../embed/playback';
import { MaveElement } from '../utils/mave_element';

export class Image extends MaveElement {
private _token: string;
private sessionTask = new Task(this, {
task: async ([embed, token]) => {
if (!embed || !token) return;
const session = await playbackSession(token, embed);
if (this.embed !== embed || this.token !== token) return;
return session;
},
args: () => [this.embed, this.token] as const,
});

@property({ type: String })
get embed(): string {
return this._embed;
}

set embed(value: string) {
if (this._embed !== value) {
this._embed = value;
this.requestUpdate('embed');
}
}

@property({ attribute: false })
get token(): string {
return this._token;
}

set token(value: string) {
if (this._token !== value) {
this._token = value;
this.requestUpdate('token');
}
}

static styles = css`
:host {
display: block;
Expand All @@ -13,12 +51,21 @@ export class Image extends MaveElement {
}
`;

get poster(): string {
return `${this.cdn_root}/${this.embedId}/poster.webp`;
poster(session?: PlaybackSession): string {
if (!session) return `${this.cdn_root}/${this.embedId}/poster.webp`;
const url = new URL(`${session.media_base_url.replace(/\/$/, '')}/poster.webp`);
url.searchParams.set('token', session.token);
return url.toString();
}

render() {
return html`<img src=${this.poster} />`;
if (!this.token) return html`<img src=${this.poster()} />`;

return html`${this.sessionTask.render({
pending: () => html``,
error: () => html``,
complete: (session) => html`<img src=${this.poster(session)} />`,
})}`;
}
}

Expand Down
34 changes: 26 additions & 8 deletions src/components/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,19 @@ import { MaveElement } from '../utils/mave_element';
import { checkPop } from './pop.js';

export class List extends MaveElement {
@property() token: string;
private _token: string;
@property({ attribute: false })
get token(): string {
return this._token;
}

set token(value: string) {
if (this._token !== value) {
this._token = value;
this.embedController.token = value;
this.requestUpdate('token');
}
}
@property() order?: 'oldest' | 'newest' | 'az' | 'za' = 'newest';

static styles = css`
Expand All @@ -24,13 +36,6 @@ export class List extends MaveElement {
this.embedController.token = this.token;
}

requestUpdate(name?: PropertyKey, oldValue?: unknown) {
super.requestUpdate(name, oldValue);
if (name === 'embed') {
this.embedController.token = this.token;
}
}

get _slottedChildren() {
const slot = this.shadowRoot?.querySelector('slot');
return slot?.assignedElements({ flatten: true }) || [];
Expand Down Expand Up @@ -166,6 +171,11 @@ export class List extends MaveElement {
this.#setEmbedAttribute(template, 'mave-clip', video.id);
this.#setEmbedAttribute(template, 'mave-player', video.id);
this.#setEmbedAttribute(template, 'mave-img', video.id);
this.#setTokenProperty(template, 'mave-clip');
this.#setTokenProperty(template, 'mave-player');
this.#setTokenProperty(template, 'mave-img');
this.#setTokenProperty(template, 'mave-text');
this.#setTokenProperty(template, 'mave-files');

const clip = template.querySelector('mave-clip');
const title = this.#querySlotElement(template, 'item-title');
Expand Down Expand Up @@ -229,6 +239,14 @@ export class List extends MaveElement {
this.#clearSlotAttributes(element);
}

#setTokenProperty(template: DocumentFragment, selector: string) {
const element = template.querySelector(selector) as
| (HTMLElement & { token?: string })
| null;
if (!element || !this.token) return;
element.token = this.token;
}

#setTextContent(template: DocumentFragment, slotName: string, text: string) {
const element = this.#querySlotElement(template, slotName);
if (!element) return;
Expand Down
12 changes: 3 additions & 9 deletions src/components/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export class Player extends MaveElement {
}

private _token: string;
@property()
@property({ attribute: false })
get token(): string {
return this._token;
}
Expand Down Expand Up @@ -821,13 +821,7 @@ export class Player extends MaveElement {
}

#xhrHLSSetup(xhr: XMLHttpRequest, url: string) {
const newUrl = new URL(url);
if (this.token && !newUrl.searchParams.get('token')) {
const params = new URLSearchParams();
params.append('token', this.token);
newUrl.search = params.toString();
}
xhr.open('GET', newUrl.toString());
xhr.open('GET', url);
}

connectedCallback(): void {
Expand Down Expand Up @@ -1297,7 +1291,7 @@ export class Player extends MaveElement {
return this.#manifestPoster();
}

return `https://image.mave.io/${this.embedController.spaceId}${this.embedController.embedId}.jpg?time=${time}`;
return this.embedController.dynamicImage(time);
}

#hasPosterValue(value: string | number | null | undefined): value is string | number {
Expand Down
15 changes: 15 additions & 0 deletions src/components/pop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ export class Pop extends LitElement {
@query('.backdrop') _backdrop: HTMLElement;

private _player?: Player;
private _token: string;

@property({ attribute: false })
get token(): string {
return this._token;
}

set token(value: string) {
if (this._token !== value) {
this._token = value;
if (this._player) this._player.token = value;
this.requestUpdate('token');
}
}

static styles = css`
:host {
Expand Down Expand Up @@ -173,6 +187,7 @@ export class Pop extends LitElement {

open(player: Player) {
this._player = player;
if (this.token !== undefined) player.token = this.token;
if (player.aspect_ratio) {
const [w, h] = player.aspect_ratio.split('/');
this.style.setProperty('--frame-ratio-w', w);
Expand Down
15 changes: 15 additions & 0 deletions src/components/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,26 @@ export class Text extends LitElement {
if (this._embedId != value) {
this._embedId = value;
this.captionController = new CaptionController(this, this.embed);
this.captionController.token = this.token;
this.reset();
this.requestUpdate('embed');
}
}

private _token: string;
@property({ attribute: false })
get token(): string {
return this._token;
}

set token(value: string) {
if (this._token !== value) {
this._token = value;
if (this.captionController) this.captionController.token = value;
this.requestUpdate('token');
}
}

@property() highlight: string;
@property({ attribute: 'highlight-mode' }) highlightMode: HighlightMode = 'sentence';
@property({ attribute: 'transcript-label' }) transcriptLabel: string = 'Transcript';
Expand Down
19 changes: 16 additions & 3 deletions src/embed/caption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ReactiveControllerHost } from 'lit';

import { Config } from '../config';
import * as API from './api';
import { PlaybackSession, playbackSession } from './playback';

// More compatible version of:
// const regex = /(?<!\bwww\.\S+)(?<!@\S+)(?<!\.\d)(?<=[.!?])\s+/g;
Expand Down Expand Up @@ -40,6 +41,7 @@ export class CaptionController {
private task: Task;
private _embed: string;
private _token: string;
private _playbackSession?: PlaybackSession;
private _version: number;

constructor(host: ReactiveControllerHost, embed: string) {
Expand All @@ -50,6 +52,14 @@ export class CaptionController {
this.host,
async () => {
try {
const requestEmbed = this.embed;
const requestToken = this.token;
const session = requestToken
? await playbackSession(requestToken, requestEmbed)
: undefined;
if (this.embed !== requestEmbed || this.token !== requestToken) return;
this._playbackSession = session;

const url = this.embedFile('subtitle.json');

const response = await fetch(url);
Expand Down Expand Up @@ -162,13 +172,14 @@ export class CaptionController {
throw new Error();
}
},
() => [this.embed],
() => [this.embed, this.token],
);
}

set embed(value: string) {
if (this._embed != value) {
this._embed = value;
this._playbackSession = undefined;
this.host.requestUpdate();
}
}
Expand All @@ -180,6 +191,7 @@ export class CaptionController {
set token(value: string) {
if (this._token != value) {
this._token = value;
this._playbackSession = undefined;
this.host.requestUpdate();
}
}
Expand Down Expand Up @@ -215,12 +227,13 @@ export class CaptionController {
}

embedFile(file: string, params = new URLSearchParams()): string {
const mediaBaseUrl = this._playbackSession?.media_base_url.replace(/\/$/, '');
const url = new URL(
`${this.cdnRoot}/${this.embedId}${
`${mediaBaseUrl || `${this.cdnRoot}/${this.embedId}`}${
file == 'manifest.json' ? '/' : this.version
}${file}`,
);
if (this.token) params.append('token', this.token);
if (this._playbackSession) params.append('token', this._playbackSession.token);
url.search = params.toString();
return url.toString();
}
Expand Down
Loading