Skip to content

Commit

Permalink
Merge pull request #33 from dyte-io/feat/WEB-4088-cf-viewer-with-stats
Browse files Browse the repository at this point in the history
feat(internal): CF livestream viewer with stats
  • Loading branch information
ravindra-dyte authored Nov 14, 2024
2 parents e27506e + 33d5082 commit 768ef7b
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 32 deletions.
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

0 comments on commit 768ef7b

Please sign in to comment.