Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(internal): CF livestream viewer with stats #33

Merged
merged 4 commits into from
Nov 14, 2024
Merged
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
13 changes: 0 additions & 13 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3598,8 +3598,6 @@ export namespace Components {
*/
"t": DyteI18n;
}
interface TestComponent {
}
}
export interface DyteAiCustomEvent<T> extends CustomEvent<T> {
detail: T;
Expand Down Expand Up @@ -4764,12 +4762,6 @@ declare global {
prototype: HTMLDyteWaitingScreenElement;
new (): HTMLDyteWaitingScreenElement;
};
interface HTMLTestComponentElement extends Components.TestComponent, HTMLStencilElement {
}
var HTMLTestComponentElement: {
prototype: HTMLTestComponentElement;
new (): HTMLTestComponentElement;
};
interface HTMLElementTagNameMap {
"dyte-ai": HTMLDyteAiElement;
"dyte-ai-chat": HTMLDyteAiChatElement;
Expand Down Expand Up @@ -4912,7 +4904,6 @@ declare global {
"dyte-viewer-count": HTMLDyteViewerCountElement;
"dyte-virtualized-participant-list": HTMLDyteVirtualizedParticipantListElement;
"dyte-waiting-screen": HTMLDyteWaitingScreenElement;
"test-component": HTMLTestComponentElement;
}
}
declare namespace LocalJSX {
Expand Down Expand Up @@ -8928,8 +8919,6 @@ declare namespace LocalJSX {
*/
"t"?: DyteI18n;
}
interface TestComponent {
}
interface IntrinsicElements {
"dyte-ai": DyteAi;
"dyte-ai-chat": DyteAiChat;
Expand Down Expand Up @@ -9072,7 +9061,6 @@ declare namespace LocalJSX {
"dyte-viewer-count": DyteViewerCount;
"dyte-virtualized-participant-list": DyteVirtualizedParticipantList;
"dyte-waiting-screen": DyteWaitingScreen;
"test-component": TestComponent;
}
}
export { LocalJSX as JSX };
Expand Down Expand Up @@ -9220,7 +9208,6 @@ declare module "@stencil/core" {
"dyte-viewer-count": LocalJSX.DyteViewerCount & JSXBase.HTMLAttributes<HTMLDyteViewerCountElement>;
"dyte-virtualized-participant-list": LocalJSX.DyteVirtualizedParticipantList & JSXBase.HTMLAttributes<HTMLDyteVirtualizedParticipantListElement>;
"dyte-waiting-screen": LocalJSX.DyteWaitingScreen & JSXBase.HTMLAttributes<HTMLDyteWaitingScreenElement>;
"test-component": LocalJSX.TestComponent & JSXBase.HTMLAttributes<HTMLTestComponentElement>;
}
}
}
5 changes: 1 addition & 4 deletions packages/core/src/components/dyte-chat/dyte-chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,7 @@ export class DyteChat {
}

private isPrivateChatSupported = () => {
return (
this.canPrivateMessage &&
!this.disablePrivateChat
);
return this.canPrivateMessage && !this.disablePrivateChat;
};

private updateRecipients = (event: CustomEvent<ChannelItem>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export class DyteLivestreamPlayer {

@State() latency: number = 0;

@State() livestreamId: string = null;

@State() audioPlaybackError: boolean = false;

/**
Expand All @@ -49,8 +51,27 @@ export class DyteLivestreamPlayer {

private livestreamUpdateListener = (state: LivestreamState) => {
this.livestreamState = state;
this.playbackUrl = this.meeting.livestream.playbackUrl;
};

@Watch('livestreamState')
// @ts-ignore
private updateLivestreamId() {
const url = this.meeting.livestream.playbackUrl;
if (!url || this.livestreamState !== 'LIVESTREAMING') {
this.livestreamId = null;
this.player = null;
// @ts-ignore
window.dyteLivestreamPlayerElement = null;
return;
}

const parts = url.split('/');
const manifestIndex = parts.findIndex((part) => part === 'manifest');
const streamId = parts[manifestIndex - 1];
this.livestreamId = streamId;
}

private getLoadingState = () => {
let loadingMessage = '';
let isLoading = false;
Expand Down Expand Up @@ -110,19 +131,100 @@ export class DyteLivestreamPlayer {
return { isError, errorMessage };
};

connectedCallback() {
private isScriptWithSrcPresent(srcUrl) {
const scripts = document.querySelectorAll('script');
for (let script of scripts) {
if (script.src === srcUrl) {
return true;
}
}
return false;
}

/**
* Make sure to call loadLivestreamPlayer before startLivestreamPlayer.
*/
private startLivestreamPlayer = async () => {
try {
this.meeting.__internals__.logger.info(
'dyte-livestream-player:: Initialising player element.'
);
// @ts-ignore
await window.__stream.initElement(this.player);
this.meeting.__internals__.logger.info('dyte-livestream-player:: About to start player.');
// @ts-ignore
await window.dyte_hls.play();
this.playerState = PlayerState.PLAYING;
this.audioPlaybackError = false;
this.meeting.__internals__.logger.info(
'dyte-livestream-player:: Player has started playing.'
);
} catch (error) {
this.meeting.__internals__.logger.error(`dyte-livestream-player:: Player couldn't start.`, {
error,
});
// Retry with user gesture
this.audioPlaybackError = true;
}
};

private loadLivestreamPlayer = async () => {
const playerSrc = `https://cdn.dyte.in/streams/script.js`;
if (!(window as any).__stream && this.isScriptWithSrcPresent(playerSrc)) {
// Script loading is ongoing; Do Nothing
return false;
}

if ((window as any).__stream) {
return true;
}

// Since script is not there, let's add script first
return new Promise((resolve) => {
const script = document.createElement('script');
script.src = playerSrc;
script.onload = () => {
setTimeout(() => {
if ((window as any).__stream) {
this.meeting.__internals__.logger.info(
`dyte-livestream-player:: Finished script load. Added window._stream.`
);
resolve(true);
return;
}
this.meeting.__internals__.logger.error(
`dyte-livestream-player:: onLoad didn't add window._stream in time.`
);
resolve(false);
}, 1000);
};
script.onerror = (error: any) => {
this.meeting.__internals__.logger.error(
`dyte-livestream-player:: CDN script didn't load.`,
{ error }
);
resolve(false);
};
document.head.appendChild(script);
});
};

async connectedCallback() {
this.meetingChanged(this.meeting);
}

disconnectedCallback() {
this.meeting.livestream.removeListener('livestreamUpdate', this.livestreamUpdateListener);
this.player = undefined;
this.player = null;
// @ts-ignore
window.dyteLivestreamPlayerElement = null;
}

@Watch('meeting')
meetingChanged(meeting) {
if (meeting == null) return;
this.livestreamState = this.meeting.livestream.state;
this.playbackUrl = this.meeting.livestream.playbackUrl;
this.meeting.livestream.on('livestreamUpdate', this.livestreamUpdateListener);
}

Expand All @@ -134,26 +236,40 @@ export class DyteLivestreamPlayer {
return (
<Host>
<div class="player-container">
{this.livestreamState === 'LIVESTREAMING' ? (
<iframe
src={this.meeting.livestream.playbackUrl.replace(
'/manifest/video.m3u8',
'/iframe?autoplay=true'
)}
allowFullScreen
allowTransparency
allow="accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture;"
class={'player z-10'}
></iframe>
) : null}
{this.livestreamState === 'LIVESTREAMING' && this.livestreamId && (
<div class="flex h-full w-full items-start justify-center pb-20">
<stream
width="100%"
height="80vh"
className="overflow-hidden rounded-lg"
src={this.livestreamId}
ref={async (self) => {
this.player = self;
// Add player instance on window to satisfy cdn script
// @ts-ignore
window.dyteLivestreamPlayerElement = self;
const isPlayerLoaded = await this.loadLivestreamPlayer();
if (isPlayerLoaded) {
await this.startLivestreamPlayer();
}
}}
cmcd
autoplay
force-flavor="llhls"
customer-domain-prefix="customer-s8oj0c1n5ek8ah1e"
></stream>
</div>
)}
{this.audioPlaybackError && (
<div class="unmute-popup">
<h3>{this.t('audio_playback.title')}</h3>
<p>{this.t('audio_playback.description')}</p>
<dyte-button
kind="wide"
onClick={() => {
this.player.muted = false;
if (this.player) {
this.player.muted = false;
}
this.audioPlaybackError = false;
}}
title={this.t('audio_playback')}
Expand Down
Loading