feat(player): add video playback

This commit is contained in:
MrTimscampi 2020-09-12 01:20:43 +02:00
parent 371ff6e4a3
commit 8874a31b70
18 changed files with 509 additions and 260 deletions

134
components/VideoPlayer.vue Normal file
View File

@ -0,0 +1,134 @@
<template>
<v-container fill-height fluid class="pa-0">
<div ref="videoContainer">
<video ref="videoPlayer" :poster="poster" autoplay></video>
</div>
</v-container>
</template>
<script lang="ts">
import Vue from 'vue';
import { stringify } from 'qs';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import shaka from 'shaka-player/dist/shaka-player.ui';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import muxjs from 'mux.js';
import 'shaka-player/dist/controls.css';
declare global {
interface Window {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
muxjs: any;
}
}
export default Vue.extend({
props: {
item: {
type: Object,
required: true
},
poster: {
type: String,
default: ''
}
},
data() {
return {
source: '',
player: null as any,
ui: null as any
};
},
watch: {
async source(newSource) {
if (this.player) {
try {
await this.player.load(newSource);
} catch (e) {
console.error('Error code', e.code, 'object', e);
}
}
}
},
async mounted() {
try {
const response = await this.$api.mediaInfo.getPostedPlaybackInfo({
itemId: this.$route.params.itemId,
userId: this.$auth.user.Id,
deviceProfileDto: {
DeviceProfile: this.$playbackProfile
}
});
let mediaSource;
if (response?.data?.MediaSources) {
mediaSource = response.data.MediaSources[0];
} else {
throw new Error("This item can't be played.");
}
if (mediaSource.SupportsDirectStream) {
const directOptions: Record<string, any> = {
Static: true,
mediaSourceId: mediaSource.Id,
deviceId: this.$store.state.deviceProfile.deviceId,
api_key: this.$store.state.user.accessToken
};
if (mediaSource.ETag) {
directOptions.Tag = mediaSource.ETag;
}
if (mediaSource.LiveStreamId) {
directOptions.LiveStreamId = mediaSource.LiveStreamId;
}
const params = stringify(directOptions);
this.source = `${this.$axios.defaults.baseURL}/Videos/${mediaSource.Id}/stream.${mediaSource.Container}?${params}`;
} else if (
mediaSource.SupportsTranscoding &&
mediaSource.TranscodingUrl
) {
this.source = this.$axios.defaults.baseURL + mediaSource.TranscodingUrl;
}
window.muxjs = muxjs;
shaka.polyfill.installAll();
if (shaka.Player.isBrowserSupported()) {
// Everything looks good!
this.player = new shaka.Player(this.$refs.videoPlayer);
this.ui = new shaka.ui.Overlay(
this.player,
this.$refs.videoContainer,
this.$refs.videoPlayer
);
} else {
this.$nuxt.error({
message: this.$t('browserNotSupported') as string
});
}
} catch (error) {
this.$nuxt.error({
statusCode: 404,
message: error
});
}
},
beforeDestroy() {
if (this.player) {
window.muxjs = undefined;
this.player.unload();
this.player.destroy();
}
}
});
</script>
<style scoped>
video {
width: 100vw;
height: 100vh;
}
</style>

View File

@ -1,34 +1,35 @@
{
"alphabetically": "Alphabetically",
"releaseDate": "Release date",
"endDate": "End date",
"rating": "Rating",
"badRequest": "Bad request. Try again",
"browserNotSupported": "Your browser is not supported for playing this file.",
"continueListening": "Continue listening",
"continueWatching": "Continue watching",
"episodeNumber": "Episode {episodeNumber}",
"endDate": "End date",
"endsAt": "Ends at {time}",
"episodeNumber": "Episode {episodeNumber}",
"home": "Home",
"incorrectUsernameOrPassword": "Incorrect username or password",
"itemNotFound": "Item not found",
"libraryEmpty": "This library is empty",
"libraryNotFound": "Library not found",
"login": "Login",
"logout": "Logout",
"more": "More",
"moreLikeThis": "More like this",
"password": "Password",
"play": "Play",
"playType": "Play {mediaType}",
"present": "Present",
"rating": "Rating",
"releaseDate": "Release date",
"serverAddress": "Server address",
"serverAddressMustBeUrl": "Server address must be a valid URL",
"serverAddressRequired": "Server address is required",
"serverNotFound": "Server not found",
"settings": "Settings",
"unexpectedError": "Unexpected error",
"usernameRequired": "Username is required",
"serverAddress": "Server address",
"username": "Username",
"password": "Password",
"playType": "Play {mediaType}",
"signIn": "Sign in",
"unexpectedError": "Unexpected error",
"upNext": "Up next",
"libraryNotFound": "Library not found",
"itemNotFound": "Item not found",
"libraryEmpty": "This library is empty"
"username": "Username",
"usernameRequired": "Username is required"
}

View File

@ -1,30 +1,34 @@
{
"alphabetically": "",
"badRequest": "Virheellinen pyyntö. Yritä uudelleen",
"continueListening": "Jatka kuuntelua",
"continueWatching": "Jatka katselua",
"episodeNumber": "Jakso {episodeNumber}",
"endDate": "",
"endsAt": "Päättyy {time}",
"episodeNumber": "Jakso {episodeNumber}",
"home": "Koti",
"incorrectUsernameOrPassword": "Väärä käyttäjätunnus tai salasana",
"itemNotFound": "Kohdetta ei löydy",
"libraryEmpty": "Kirjasto on tyhjä",
"libraryNotFound": "Kirjastoa ei löydy",
"login": "Kirjaudu",
"logout": "Kirjaudu ulos",
"more": "Lisää",
"moreLikeThis": "Lisää samanlaisia",
"password": "Salasana",
"play": "Toista",
"playType": "Toista {mediaType}",
"present": "Nykyinen",
"rating": "",
"releaseDate": "",
"serverAddress": "Palvelinosoite",
"serverAddressMustBeUrl": "Palvelinosoitteen on oltava kelvollinen URL-osoite",
"serverAddressRequired": "Palvelinosoite vaaditaan",
"serverNotFound": "Palvelinta ei löydy",
"settings": "Asetukset",
"unexpectedError": "Odottamaton virhe",
"usernameRequired": "Käyttäjänimi vaaditaan",
"serverAddress": "Palvelinosoite",
"username": "Käyttäjätunnus",
"password": "Salasana",
"playType": "Toista {mediaType}",
"signIn": "Kirjaudu sisään",
"unexpectedError": "Odottamaton virhe",
"upNext": "Seuraava",
"libraryNotFound": "Kirjastoa ei löydy",
"itemNotFound": "Kohdetta ei löydy",
"libraryEmpty": "Kirjasto on tyhjä"
"username": "Käyttäjätunnus",
"usernameRequired": "Käyttäjänimi vaaditaan"
}

View File

@ -1,29 +1,34 @@
{
"alphabetically": "",
"badRequest": "Onjuist verzoek. Probeer het opnieuw",
"continueListening": "Verder luisteren",
"continueWatching": "Verder kijken",
"endDate": "",
"endsAt": "Eindigt om {time}",
"episodeNumber": "Aflevering {episodeNumber}",
"home": "Home",
"incorrectUsernameOrPassword": "Onjuiste gebruikersnaam en/of wachtwoord",
"itemNotFound": "Item niet gevonden",
"libraryEmpty": "",
"libraryNotFound": "Bibliotheek niet gevonden",
"login": "Inloggen",
"logout": "Uitloggen",
"more": "Meer",
"moreLikeThis": "Meer zoals dit",
"password": "Wachtwoord",
"play": "Afspelen",
"playType": "Speel {mediaType}",
"present": "Huidig",
"rating": "",
"releaseDate": "",
"serverAddress": "Server adres",
"serverAddressMustBeUrl": "Server adres moet een geldige URL zijn",
"serverAddressRequired": "Server adres is verplicht",
"serverNotFound": "Server niet gevonden",
"settings": "Instellingen",
"signIn": "Inloggen",
"unexpectedError": "Onverwachte fout",
"upNext": "Volgende",
"usernameRequired": "Gebruikersnaam is verplicht",
"itemNotFound": "Item niet gevonden",
"libraryNotFound": "Bibliotheek niet gevonden",
"signIn": "Inloggen",
"password": "Wachtwoord",
"username": "Gebruikersnaam",
"serverAddress": "Server adres",
"episodeNumber": "Aflevering {episodeNumber}",
"playType": "Speel {mediaType}",
"present": "Huidig",
"moreLikeThis": "Meer zoals dit",
"endsAt": "Eindigt om {time}"
"usernameRequired": "Gebruikersnaam is verplicht"
}

View File

@ -1,27 +1,34 @@
{
"alphabetically": "",
"badRequest": "Błędne żądanie. Spróbuj ponownie",
"continueListening": "Kontynuuj słuchanie",
"continueWatching": "Kontynuuj odtwarzanie",
"endDate": "",
"endsAt": "",
"episodeNumber": "Odcinek {episodeNumber}",
"home": "Start",
"incorrectUsernameOrPassword": "Błędna nazwa użytkownika lub hasło",
"itemNotFound": "Nie znaleziono",
"libraryEmpty": "",
"libraryNotFound": "Biblioteka nie została znaleziona",
"login": "Logowanie",
"logout": "Wyloguj",
"more": "Więcej",
"moreLikeThis": "",
"password": "Hasło",
"play": "Odtwarzaj",
"playType": "Odtwarzaj {mediaType}",
"present": "Teraz",
"rating": "",
"releaseDate": "",
"serverAddress": "Adres serwera",
"serverAddressMustBeUrl": "Adres serwera musi być poprawnym adresem URL",
"serverAddressRequired": "Adres serwera jest wymagany",
"serverNotFound": "Serwer nie został znaleziony",
"settings": "Ustawienia",
"unexpectedError": "Niespodziewany błąd",
"usernameRequired": "Nazwa użytkownika jest wymagana",
"serverAddress": "Adres serwera",
"username": "Nazwa użytkownika",
"password": "Hasło",
"signIn": "Zaloguj",
"unexpectedError": "Niespodziewany błąd",
"upNext": "Następnie",
"libraryNotFound": "Biblioteka nie została znaleziona",
"itemNotFound": "Nie znaleziono",
"episodeNumber": "Odcinek {episodeNumber}",
"playType": "Odtwarzaj {mediaType}",
"present": "Teraz"
"username": "Nazwa użytkownika",
"usernameRequired": "Nazwa użytkownika jest wymagana"
}

View File

@ -1,30 +1,34 @@
{
"alphabetically": "",
"badRequest": "Cerere greșită. Încearcă din nou",
"continueListening": "Continuați să ascultați",
"continueWatching": "Continuă vizionarea",
"endDate": "",
"endsAt": "Se termină la {time}",
"episodeNumber": "Episod {episodeNumber}",
"home": "Acasă",
"incorrectUsernameOrPassword": "Numele de utilizator sau parola incorectă",
"itemNotFound": "Articolul nu a fost găsit",
"libraryEmpty": "Această bibliotecă este goală",
"libraryNotFound": "Biblioteca nu a fost găsită",
"login": "Autentificare",
"logout": "Deconectare",
"more": "Mai mult",
"moreLikeThis": "Similare",
"password": "Parolă",
"play": "Rulează",
"playType": "Redă {mediaType}",
"present": "Acum",
"rating": "",
"releaseDate": "",
"serverAddress": "Adresa serverului",
"serverAddressMustBeUrl": "Adresa serverului trebuie să fie o adresă URL validă",
"serverAddressRequired": "Adresa serverului este obligatorie",
"serverNotFound": "Serverul nu a fost gasit",
"settings": "Setări",
"unexpectedError": "Eroare neașteptată",
"usernameRequired": "Numele de utilizator este necesar",
"serverAddress": "Adresa serverului",
"username": "Utilizator",
"password": "Parolă",
"signIn": "Autentificare",
"unexpectedError": "Eroare neașteptată",
"upNext": "Urmează",
"libraryNotFound": "Biblioteca nu a fost găsită",
"itemNotFound": "Articolul nu a fost găsit",
"episodeNumber": "Episod {episodeNumber}",
"libraryEmpty": "Această bibliotecă este goală",
"playType": "Redă {mediaType}",
"present": "Acum",
"moreLikeThis": "Similare",
"endsAt": "Se termină la {time}"
"username": "Utilizator",
"usernameRequired": "Numele de utilizator este necesar"
}

View File

@ -1,30 +1,34 @@
{
"alphabetically": "",
"badRequest": "Zlá požiadavka. Skúste to znova",
"continueListening": "Pokračovať v počúvaní",
"continueWatching": "Pokračovať v pozeraní",
"endDate": "",
"endsAt": "Končí o {time}",
"episodeNumber": "Epizóda {episodeNumber}",
"home": "Domov",
"incorrectUsernameOrPassword": "Nesprávne používateľské meno alebo heslo",
"itemNotFound": "Položka nebola nájdená",
"libraryEmpty": "Knižnica je prázdna",
"libraryNotFound": "Knižnica nebola nájdená",
"login": "Prhlásanie",
"logout": "Odhlásenie",
"more": "Viac",
"moreLikeThis": "Podobné",
"password": "Heslo",
"play": "Prehrať",
"playType": "Prehrať {mediaType}",
"present": "Prezentovať",
"rating": "",
"releaseDate": "",
"serverAddress": "Adresa servera",
"serverAddressMustBeUrl": "Adresa servera musí byť platná URL",
"serverAddressRequired": "Adresa servera je požadovaná",
"serverNotFound": "Server nebol nájdený",
"settings": "Nastavenia",
"unexpectedError": "Neočakávaná chyba",
"usernameRequired": "Používateľské meno je požadované",
"serverAddress": "Adresa servera",
"username": "Používateľské meno",
"password": "Heslo",
"playType": "Prehrať {mediaType}",
"signIn": "Prihlásiť sa",
"unexpectedError": "Neočakávaná chyba",
"upNext": "Nasleduje",
"libraryNotFound": "Knižnica nebola nájdená",
"itemNotFound": "Položka nebola nájdená",
"present": "Prezentovať",
"moreLikeThis": "Podobné",
"endsAt": "Končí o {time}",
"libraryEmpty": "Knižnica je prázdna"
"username": "Používateľské meno",
"usernameRequired": "Používateľské meno je požadované"
}

View File

@ -1,23 +1,34 @@
{
"alphabetically": "",
"badRequest": "Slaba zahteva. Poskusite znova",
"continueListening": "Nadaljuj s poslušanjem",
"continueWatching": "Nadaljuj z gledanjem",
"endDate": "",
"endsAt": "",
"episodeNumber": "",
"home": "Domov",
"incorrectUsernameOrPassword": "Napačno uporabniško ime ali geslo",
"itemNotFound": "",
"libraryEmpty": "",
"libraryNotFound": "Knjižnice ni najdena",
"login": "Prijava",
"logout": "Odjava",
"more": "Več",
"moreLikeThis": "",
"password": "Geslo",
"play": "Predvajaj",
"playType": "",
"present": "",
"rating": "",
"releaseDate": "",
"serverAddress": "Naslov strežnika",
"serverAddressMustBeUrl": "Naslov strežnika mora biti veljaven URL",
"serverAddressRequired": "Naslov strežnika je zahtevan",
"serverNotFound": "Strežnika ni mogoče najti",
"settings": "Nastavitve",
"unexpectedError": "Nepričakovana napaka",
"usernameRequired": "Uporabniško ime je zahtevano",
"serverAddress": "Naslov strežnika",
"username": "Uporabniško ime",
"password": "Geslo",
"signIn": "Prijavite se",
"unexpectedError": "Nepričakovana napaka",
"upNext": "Naslednje",
"libraryNotFound": "Knjižnice ni najdena"
"username": "Uporabniško ime",
"usernameRequired": "Uporabniško ime je zahtevano"
}

View File

@ -1,32 +1,34 @@
{
"alphabetically": "Alfabatiskt",
"badRequest": "",
"continueListening": "Fortsätt lyssna",
"continueWatching": "Fortsätt titta",
"endDate": "Slut datum",
"endsAt": "Slutar {time}",
"episodeNumber": "Episod {episodeNumber}",
"home": "Hem",
"incorrectUsernameOrPassword": "Fel användarnamn eller lösenord",
"itemNotFound": "",
"libraryEmpty": "Biblioteket är tomt",
"libraryNotFound": "Hittade inte biblioteket",
"login": "Logga in",
"logout": "Logga ut",
"more": "Mer",
"moreLikeThis": "Mer som detta",
"password": "Lösenord",
"play": "Spela",
"playType": "Spela {mediaType}",
"present": "",
"rating": "Betyg",
"releaseDate": "Utgivningsdatum",
"serverAddress": "Server adress",
"serverAddressMustBeUrl": "",
"serverAddressRequired": "",
"serverNotFound": "Kunde inte hitta servern",
"settings": "Inställningar",
"unexpectedError": "Oförutsett fel",
"usernameRequired": "Användarnamn krävs",
"serverAddress": "Server adress",
"username": "Användarnamn",
"password": "Lösenord",
"signIn": "Logga in",
"unexpectedError": "Oförutsett fel",
"upNext": "Kommer näst",
"libraryEmpty": "Biblioteket är tomt",
"libraryNotFound": "Hittade inte biblioteket",
"playType": "Spela {mediaType}",
"moreLikeThis": "Mer som detta",
"endsAt": "Slutar {time}",
"episodeNumber": "Episod {episodeNumber}",
"rating": "Betyg",
"endDate": "Slut datum",
"releaseDate": "Utgivningsdatum",
"alphabetically": "Alfabatiskt"
"username": "Användarnamn",
"usernameRequired": "Användarnamn krävs"
}

View File

@ -1,27 +1,34 @@
{
"alphabetically": "",
"badRequest": "Hatalı istek. Tekrar deneyin",
"continueListening": "Dinlemeye Devam Et",
"continueWatching": "İzlemeye devam et",
"endDate": "",
"endsAt": "",
"episodeNumber": "Bölüm {episodeNumber}",
"home": "Anasayfa",
"incorrectUsernameOrPassword": "Hatalı kullanıcı adı veya şifre",
"itemNotFound": "Öğe bulunamadı",
"libraryEmpty": "Bu kütüphane boş",
"libraryNotFound": "Kütüphane bulunamadı",
"login": "Oturum aç",
"logout": ıkış",
"more": "Dahası",
"moreLikeThis": "Buna Benzer Daha Fazla",
"password": "Şifre",
"play": "Oynat",
"playType": "",
"present": "",
"rating": "",
"releaseDate": "",
"serverAddress": "Sunucu adresi",
"serverAddressMustBeUrl": "Sunucu adresi geçerli bir URL olmalıdır",
"serverAddressRequired": "Sunucu adresi gerekli",
"serverNotFound": "Sunucu bulunamadı",
"settings": "Ayarlar",
"unexpectedError": "Beklenmedik hata",
"usernameRequired": "Kullanıcı adı gerekli",
"serverAddress": "Sunucu adresi",
"username": "Kullanıcı Adı",
"password": "Şifre",
"signIn": "Giriş yap",
"unexpectedError": "Beklenmedik hata",
"upNext": "Sonraki",
"libraryNotFound": "Kütüphane bulunamadı",
"itemNotFound": "Öğe bulunamadı",
"libraryEmpty": "Bu kütüphane boş",
"moreLikeThis": "Buna Benzer Daha Fazla"
"username": "Kullanıcı Adı",
"usernameRequired": "Kullanıcı adı gerekli"
}

View File

@ -73,7 +73,7 @@ const config: NuxtConfig = {
[
'nuxt-vuex-localstorage',
{
localStorage: ['user']
localStorage: ['user', 'deviceProfile']
}
],
// Doc: https://axios.nuxtjs.org/usage

View File

@ -32,10 +32,12 @@
"@types/uuid": "^8.3.0",
"blurhash": "^1.1.3",
"lodash": "^4.17.20",
"mux.js": "^5.6.4",
"nuxt": "^2.14.7",
"nuxt-i18n": "^6.15.4",
"nuxt-vuex-localstorage": "^1.2.7",
"qs": "^6.9.4",
"shaka-player": "^3.0.4",
"uuid": "^8.3.1",
"vue-virtual-scroller": "^1.0.10",
"vueperslides": "^2.10.8"

View File

@ -0,0 +1,49 @@
<template>
<v-container fill-height fluid class="pa-0">
<video-player
v-if="item.MediaType === 'Video'"
:item="item"
:poster="poster"
/>
</v-container>
</template>
<script lang="ts">
import Vue from 'vue';
import { BaseItemDto } from '~/api/api';
import imageHelper from '~/mixins/imageHelper';
export default Vue.extend({
layout: 'fullpage',
mixins: [imageHelper],
data() {
return {
poster: '',
item: [] as BaseItemDto
};
},
async beforeMount() {
try {
const response = await this.$api.items.getItems({
uId: this.$auth.user.Id,
userId: this.$auth.user.Id,
ids: this.$route.params.itemId,
fields: 'Overview,Genres'
});
if (response?.data?.Items && response.data.Items.length > 0) {
this.item = response.data.Items[0];
} else {
throw new Error('Item not found');
}
this.poster = this.getImageUrl(this.$route.params.itemId, 'backdrop');
} catch (error) {
this.$nuxt.error({
statusCode: 404,
message: error
});
}
}
});
</script>

33
plugins/mediaInfoApi.ts Normal file
View File

@ -0,0 +1,33 @@
import { Plugin } from '@nuxt/types';
import { AxiosInstance } from 'axios';
import { MediaInfoApi } from '~/api/api';
import { Configuration } from '~/api/configuration';
declare module '@nuxt/types' {
interface Context {
$mediaInfoApi: MediaInfoApi;
}
interface NuxtAppOptions {
$mediaInfoApi: MediaInfoApi;
}
}
declare module 'vue/types/vue' {
interface Vue {
$mediaInfoApi: MediaInfoApi;
}
}
const mediaInfoApiPlugin: Plugin = (context, inject) => {
const config = new Configuration();
const mediaInfoApi = new MediaInfoApi(
config,
'',
context.$axios as AxiosInstance
);
inject('mediaInfoApi', mediaInfoApi);
};
export default mediaInfoApiPlugin;

View File

@ -8,7 +8,8 @@ const userInitPlugin: Plugin = async (context) => {
) {
context.$axios.setBaseURL(context.store.state.user.serverUrl);
context.$auth.setUserToken(context.store.state.user.accessToken);
const accessToken = `MediaBrowser Client="${context.store.state.deviceProfile.clientName}", Device="${context.store.state.deviceProfile.deviceName}", DeviceId="${context.store.state.deviceProfile.deviceId}", Version="${context.store.state.deviceProfile.clientVersion}", Token="${context.store.state.user.accessToken}"`;
context.$auth.setUserToken(accessToken);
const response = await context.$api.user.getUserById({
userId: context.store.state.user.id

View File

@ -1,9 +1,5 @@
import { hasH264Support, hasH265Support } from './mp4VideoFormats';
import {
hasEac3Support,
hasMp3Support,
hasAacSupport
} from './mp4AudioFormats';
import { hasEac3Support, hasAacSupport } from './mp4AudioFormats';
import { getSupportedAudioCodecs } from './audioFormats';
import { browserDetector } from '~/plugins/browserDetection';
@ -78,10 +74,6 @@ export function getHlsAudioCodecs(
}
}
if (hasMp3Support(videoTestElement)) {
hlsVideoAudioCodecs.push('mp3');
}
if (hasAacSupport(videoTestElement)) {
hlsVideoAudioCodecs.push('aac');
}

View File

@ -34,26 +34,6 @@ export function hasEac3Support(videoTestElement: HTMLVideoElement): boolean {
.replace(/no/, '');
}
/**
*
*
* @param {HTMLVideoElement} videoTestElement
* @returns
*/
export function hasMp3Support(videoTestElement: HTMLVideoElement): boolean {
return !!(
videoTestElement
.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"')
.replace(/no/, '') ||
videoTestElement
.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"')
.replace(/no/, '') ||
videoTestElement
.canPlayType('video/mp4; codecs="avc1.640029, mp3"')
.replace(/no/, '')
);
}
/**
*
*
@ -119,10 +99,6 @@ export function getSupportedMP4AudioCodecs(
codecs.push('eac3');
}
if (hasMp3Support(videoTestElement)) {
codecs.push('mp3');
}
if (hasAacSupport(videoTestElement)) {
codecs.push('aac');
}

View File

@ -5023,6 +5023,11 @@ elliptic@^6.5.3:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.0"
eme-encryption-scheme-polyfill@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.0.1.tgz#b080b01bffd74c75c9cf8044c1cabedf3b83954f"
integrity sha512-Wz+Ro1c0/2Wsx2RLFvTOO0m4LvYn+7cSnq3XOvRvLLBq8jbvUACH/zpU9s0/5+mQa5oaelkU69x+q0z/iWYrFA==
emittery@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.1.tgz#c02375a927a40948c0345cc903072597f5270451"
@ -8683,6 +8688,11 @@ mute-stream@0.0.8:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
mux.js@^5.6.4:
version "5.6.4"
resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-5.6.4.tgz#0975fb4f2641884d127d912f08eb629171a4b423"
integrity sha512-k7UUafOn1axLqcnx0oF3xbTrVMXHd54ytwFHW30v+SRbZED63QjK7al+q9KMlF+NQOkydd+46xPqHk+ELZxL+g==
nan@^2.12.1:
version "2.14.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
@ -11202,6 +11212,13 @@ sha.js@^2.4.0, sha.js@^2.4.8:
inherits "^2.0.1"
safe-buffer "^5.0.1"
shaka-player@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-3.0.4.tgz#241245f4019b3550ef58d6f83b8fc75c66ab8816"
integrity sha512-sjmArz8ukKNx5SU2O99kdJr1Z3TyDRn/p11ivUm/67jptCgYuIGI8swfvkJO5KD7MBJSaBP6z32D38dBx5AAxA==
dependencies:
eme-encryption-scheme-polyfill "^2.0.1"
shallow-clone@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"