#66575 WebOS : Каналы стартуют медленнее чем в старом плеере, на фильмах,...
This commit is contained in:
+2
-2
@@ -25,7 +25,7 @@
|
||||
var player = spbtvplayer('my-video', {
|
||||
log: true,
|
||||
features: {
|
||||
api: true,
|
||||
api: false,
|
||||
drm: false,
|
||||
metrics: true
|
||||
},
|
||||
@@ -63,7 +63,7 @@
|
||||
player.afterInitialize(() => {
|
||||
console.log("afterInitialize")
|
||||
// player.setControlbarVisibility(true)
|
||||
// player.attachSource("https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd")
|
||||
player.attachSource("https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd", {autoplay: true})
|
||||
})
|
||||
|
||||
player.addEventListener('play', onPlay, window)
|
||||
|
||||
@@ -8,6 +8,7 @@ services:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
restart: always
|
||||
command: "serve.sh"
|
||||
volumes:
|
||||
- $PWD:/usr/voka
|
||||
- node_modules:/usr/voka/node_modules
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Собираем production-бандл
|
||||
echo "[VOKA] Building production bundle..."
|
||||
#npm run build-prod
|
||||
nodemon --watch src --watch vendors -e ts,js,scss --exec npm run build &
|
||||
|
||||
# Проверим, что установлен serve, если нет — установим
|
||||
if ! npx --no-install serve -v >/dev/null 2>&1; then
|
||||
echo "[VOKA] Installing 'serve'..."
|
||||
npm install -g serve
|
||||
fi
|
||||
|
||||
# Запускаем локальный сервер на 5000 порту (или любой другой)
|
||||
echo "[VOKA] Starting server at http://localhost:8080"
|
||||
serve -s demo -l 8080
|
||||
@@ -407,24 +407,26 @@ namespace VokaCorePlayer {
|
||||
// не подходит для хромкаст. Временное решение для сохранения исходника.
|
||||
this.player._originalSrc = playableContent
|
||||
this.bus.publish(VokaBusEvent.switchContent(content))
|
||||
this.bus.publish(
|
||||
VokaBusEvent.adsMarkersSet({
|
||||
items: [
|
||||
{
|
||||
index: 0,
|
||||
timestamp: 10
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
timestamp: 60
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
timestamp: 90
|
||||
}
|
||||
]
|
||||
})
|
||||
)
|
||||
|
||||
// MARK: Пример задания рекламных точек
|
||||
// this.bus.publish(
|
||||
// VokaBusEvent.adsMarkersSet({
|
||||
// items: [
|
||||
// {
|
||||
// index: 0,
|
||||
// timestamp: 10
|
||||
// },
|
||||
// {
|
||||
// index: 1,
|
||||
// timestamp: 60
|
||||
// },
|
||||
// {
|
||||
// index: 2,
|
||||
// timestamp: 90
|
||||
// }
|
||||
// ]
|
||||
// })
|
||||
// )
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
@@ -497,9 +499,6 @@ namespace VokaCorePlayer {
|
||||
private initOptions(options: CorePlayerOptions.IOptions): VideoJsPlayerOptions {
|
||||
// Required player's plugins.
|
||||
const plugins: Record<string, any> = { }
|
||||
//plugins.vokaStatisticsPlugin = {}
|
||||
//plugins.vokaAdvertisementPlugin = {}
|
||||
//plugins.vokaCaptionsPlugin = {}
|
||||
// plugins.vokaKeyboardPlugin = {
|
||||
// skip: {
|
||||
// forward: 5,
|
||||
@@ -579,7 +578,7 @@ namespace VokaCorePlayer {
|
||||
language: 'ru',
|
||||
children: childrenComponents,
|
||||
// MARK: Пример постера
|
||||
poster: 'https://i.ytimg.com/vi/u3q7GGLpiqk/maxresdefault.jpg',
|
||||
// poster: 'https://i.ytimg.com/vi/u3q7GGLpiqk/maxresdefault.jpg',
|
||||
responsive: true,
|
||||
breakpoints: {
|
||||
tiny: undefined,
|
||||
@@ -625,7 +624,7 @@ namespace VokaCorePlayer {
|
||||
previewPopup: {
|
||||
imageCallback: (percent: number) => {
|
||||
return ''
|
||||
// timeline from voka (for test)
|
||||
// MARK: пример ссылки на превью
|
||||
// return `https://streaming.voka.tv/vod_preview/velcom/4W17sRxFu8eCAps3kdTxJrFk7d46DNmum_320x180.jpeg?preview_pos=${percent}`;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import videojs from "video.js";
|
||||
import { VokaOptions } from './@types'
|
||||
import { IVokaPlayer } from './IVokaPlayer'
|
||||
import EncryptSystem from '@/internal/utils/EncryptSystem'
|
||||
@@ -10,6 +11,11 @@ import BrowserUtils from '@/internal/utils/BrowserUtils'
|
||||
import VokaGlobalFunctions from '@/public/VokaGlobalFunctions'
|
||||
import ObjectUtils from '@/internal/utils/ObjectUtils'
|
||||
|
||||
function log(...args) {
|
||||
const now = new Date().toISOString(); // формат ISO: 2025-08-25T10:15:30.123Z
|
||||
videojs.log(`[${now}][VokaPlayer]`, ...args);
|
||||
}
|
||||
|
||||
namespace VokaPlayer {
|
||||
|
||||
let playerFeatures: SupportedCodecs.ISelectProtocolResult | null = null
|
||||
@@ -29,8 +35,9 @@ namespace VokaPlayer {
|
||||
element: HTMLElement | string | null,
|
||||
creationOptions: VokaOptions.IOptions | null
|
||||
): IVokaPlayer | VokaGlobalFunctions {
|
||||
log("Player constructor started")
|
||||
let htmlElement: HTMLElement | null = null
|
||||
|
||||
let htmlElement: HTMLElement | null = null
|
||||
if (typeof document !== 'undefined') {
|
||||
if (typeof element === 'string') {
|
||||
htmlElement = document.getElementById(element) as HTMLElement
|
||||
@@ -57,13 +64,16 @@ namespace VokaPlayer {
|
||||
|
||||
const id = GUIDUtils.globalUnique
|
||||
const options = ObjectUtils.mergeDeep({}, defaultOptions, creationOptions, { loggerId: id } as VokaOptions.IOptions)
|
||||
return new VokaPlayerImpl(
|
||||
const player = new VokaPlayerImpl(
|
||||
id, // id
|
||||
GUIDUtils.generateGUID(), // SessionGUID
|
||||
options, // merge options default and passed from outside
|
||||
detectFeatures(options),
|
||||
VokaCorePlayer.createPlayer(htmlElement),
|
||||
)
|
||||
log("Player constructor finished")
|
||||
|
||||
return player
|
||||
}
|
||||
|
||||
export async function detectFeatures(options: VokaOptions.IOptions): Promise<SupportedCodecs.ISelectProtocolResult> {
|
||||
|
||||
+132
-58
@@ -1,3 +1,4 @@
|
||||
import { BusEvent } from "ts-bus/types";
|
||||
import videojs from 'video.js'
|
||||
import { VokaOptions } from './@types'
|
||||
import { IAudioTrack, IQuality, ISubtitle, ITimeRange, IVideoInfo, IVokaPlayer } from './IVokaPlayer'
|
||||
@@ -16,6 +17,11 @@ import { EventBus } from 'ts-bus'
|
||||
import { VokaError } from '@/public/models/VokaError'
|
||||
import VokaBusEvent, { adError, adFinished, adStarted } from '@/internal/events/VokaBusEvent'
|
||||
|
||||
function log(...args) {
|
||||
const now = new Date().toISOString()
|
||||
videojs.log(`[${ now }][VokaPlayerImpl]`, ...args)
|
||||
}
|
||||
|
||||
export class VokaPlayerImpl implements IVokaPlayer {
|
||||
|
||||
private static readonly version = '0.0.3'
|
||||
@@ -35,11 +41,11 @@ export class VokaPlayerImpl implements IVokaPlayer {
|
||||
public static playerVersion(): string { return `${this.version}.${this.build}` }
|
||||
|
||||
constructor(
|
||||
id: number,
|
||||
sessionGUID: string,
|
||||
options: VokaOptions.IOptions,
|
||||
features: Promise<SupportedCodecs.ISelectProtocolResult>,
|
||||
playerCreationClosure: VokaCorePlayer.PlayerCreationClosure
|
||||
id: number,
|
||||
sessionGUID: string,
|
||||
options: VokaOptions.IOptions,
|
||||
features: Promise<SupportedCodecs.ISelectProtocolResult>,
|
||||
playerCreationClosure: VokaCorePlayer.PlayerCreationClosure,
|
||||
) {
|
||||
if (!options.log) videojs.log.level("off")
|
||||
|
||||
@@ -53,47 +59,115 @@ export class VokaPlayerImpl implements IVokaPlayer {
|
||||
|
||||
const playerOptions = this.prepareCoreOptions(options)
|
||||
this.initializePromise = new Promise(
|
||||
(resolve, reject) => {
|
||||
Promise.all([features, playerCreationClosure(playerOptions)]).then(
|
||||
([features, player]) => {
|
||||
this._player = player
|
||||
this._features = features
|
||||
this.initialize(player, features).then((result) => { resolve() }, reject)
|
||||
if (!!options.log) {
|
||||
videojs.log("[OPTIONS PREPARE]:" + this.uniqID, playerOptions);
|
||||
(resolve, reject) => {
|
||||
Promise.all([features, playerCreationClosure(playerOptions)]).then(
|
||||
([features, player]) => {
|
||||
this._player = player
|
||||
this._features = features
|
||||
this.initialize(player, features).then((result) => {
|
||||
resolve()
|
||||
}, reject)
|
||||
|
||||
// Применяем декоратор ко всем public методам класса
|
||||
const allMethods = Object.getOwnPropertyNames(this.__proto__)
|
||||
const toRemove = ["constructor", "prepareCoreOptions", "load", "initialize", "safeStringify", "logMethodCall"]
|
||||
for (const propertyName of allMethods.filter(method => !toRemove.includes(method))) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(this.__proto__, propertyName);
|
||||
if (descriptor && typeof descriptor.value === 'function') {
|
||||
Object.defineProperty(this.__proto__, propertyName, this.logMethodCall(this.__proto__, propertyName, descriptor));
|
||||
const INSTANCE_DECORATED = Symbol("instanceDecorated")
|
||||
const DECORATED_METHODS = Symbol("decoratedMethods")
|
||||
|
||||
if (options.log && !(this as any)[INSTANCE_DECORATED]) {
|
||||
log("[OPTIONS PREPARE]:" + this.uniqID, playerOptions)
|
||||
|
||||
const proto = Object.getPrototypeOf(this)
|
||||
const toSkip = new Set([
|
||||
"constructor",
|
||||
"prepareCoreOptions",
|
||||
"load",
|
||||
"initialize",
|
||||
"safeStringify",
|
||||
"logMethodCall",
|
||||
])
|
||||
|
||||
const decorated: Set<string> = new Set()
|
||||
Object.defineProperty(this, DECORATED_METHODS, {
|
||||
value: decorated,
|
||||
configurable: true,
|
||||
})
|
||||
|
||||
for (const name of Object.getOwnPropertyNames(proto)) {
|
||||
if (toSkip.has(name)) continue
|
||||
|
||||
const d = Object.getOwnPropertyDescriptor(proto, name)
|
||||
if (!d || typeof d.value !== "function") continue
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this, name)) continue
|
||||
|
||||
const instanceDesc: PropertyDescriptor = {
|
||||
configurable: true,
|
||||
enumerable: d.enumerable ?? false,
|
||||
writable: true,
|
||||
value: d.value,
|
||||
}
|
||||
|
||||
const wrappedDesc = this.logMethodCall(proto, name, instanceDesc)
|
||||
wrappedDesc.value.__logged = true
|
||||
decorated.add(name)
|
||||
|
||||
Object.defineProperty(this, name, wrappedDesc)
|
||||
}
|
||||
|
||||
for (const name of Object.getOwnPropertyNames(this)) {
|
||||
if (toSkip.has(name)) continue
|
||||
if (decorated.has(name)) continue
|
||||
|
||||
const d = Object.getOwnPropertyDescriptor(this, name)
|
||||
if (!d || typeof d.value !== "function") continue
|
||||
|
||||
if (d.value.__logged) continue
|
||||
|
||||
const instanceDesc: PropertyDescriptor = {
|
||||
configurable: true,
|
||||
enumerable: d.enumerable ?? true,
|
||||
writable: true,
|
||||
value: d.value,
|
||||
}
|
||||
|
||||
const wrappedDesc = this.logMethodCall(this, name, instanceDesc)
|
||||
wrappedDesc.value.__logged = true
|
||||
decorated.add(name)
|
||||
|
||||
Object.defineProperty(this, name, wrappedDesc)
|
||||
}
|
||||
|
||||
Object.defineProperty(this, INSTANCE_DECORATED, {
|
||||
value: true,
|
||||
configurable: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
this.initializePromise.catch(e => {
|
||||
console.error(e)
|
||||
this.destroy()
|
||||
});
|
||||
})
|
||||
|
||||
this.bus.subscribe(
|
||||
VokaBusEvent.adStarted,
|
||||
event => { this._adIsPlaying = true },
|
||||
VokaBusEvent.adStarted,
|
||||
event => {
|
||||
this._adIsPlaying = true
|
||||
},
|
||||
)
|
||||
|
||||
this.bus.subscribe(
|
||||
VokaBusEvent.adFinished,
|
||||
event => { this._adIsPlaying = false },
|
||||
VokaBusEvent.adFinished,
|
||||
event => {
|
||||
this._adIsPlaying = false
|
||||
},
|
||||
)
|
||||
|
||||
this.bus.subscribe(
|
||||
VokaBusEvent.adError,
|
||||
event => { this._adIsPlaying = false },
|
||||
VokaBusEvent.adError,
|
||||
event => {
|
||||
this._adIsPlaying = false
|
||||
},
|
||||
)
|
||||
|
||||
}
|
||||
@@ -102,7 +176,7 @@ export class VokaPlayerImpl implements IVokaPlayer {
|
||||
|
||||
private prepareCoreOptions(options: VokaOptions.IOptions): CorePlayerOptions.IOptions {
|
||||
if (!!options.log) {
|
||||
videojs.log("[OPTIONS]:" + this.uniqID, options);
|
||||
log("[OPTIONS]:" + this.uniqID, options)
|
||||
}
|
||||
return {
|
||||
controls: {
|
||||
@@ -247,40 +321,40 @@ export class VokaPlayerImpl implements IVokaPlayer {
|
||||
}
|
||||
|
||||
private static safeStringify(obj: any, indent = 2): string {
|
||||
const cache = new Set();
|
||||
const cache = new Set()
|
||||
return JSON.stringify(obj, (key, value) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (cache.has(value)) {
|
||||
return '[Circular]';
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if (cache.has(value)) {
|
||||
return "[Circular]"
|
||||
}
|
||||
cache.add(value)
|
||||
}
|
||||
cache.add(value);
|
||||
}
|
||||
// Убираем ненужные большие объекты (например, window или DOM-элементы)
|
||||
if (value instanceof Window) return '[Window]';
|
||||
if (value instanceof Document) return '[Document]';
|
||||
if (value instanceof HTMLElement) return `[HTMLElement: ${value.tagName}]`;
|
||||
return value;
|
||||
}, indent);
|
||||
}
|
||||
// Убираем ненужные большие объекты (например, window или DOM-элементы)
|
||||
if (value instanceof Window) return "[Window]"
|
||||
if (value instanceof Document) return "[Document]"
|
||||
if (value instanceof HTMLElement) return `[HTMLElement: ${ value.tagName }]`
|
||||
return value
|
||||
}, indent)
|
||||
}
|
||||
|
||||
private logMethodCall(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
const originalMethod = descriptor.value;
|
||||
const originalMethod = descriptor.value
|
||||
const id = this.uniqID
|
||||
|
||||
descriptor.value = function(...args: any[]) {
|
||||
const res = originalMethod.apply(this, args);
|
||||
descriptor.value = function (...args: any[]) {
|
||||
const res = originalMethod.apply(this, args)
|
||||
if (this.uniqID === id) {
|
||||
const logKey ="[VokaPlayerImpl]:[ID=" + this.uniqID + "]:[" + propertyKey + "]:"
|
||||
videojs.log(logKey,
|
||||
" Arguments:" + VokaPlayerImpl.safeStringify(args),
|
||||
" Result:" + VokaPlayerImpl.safeStringify(res)
|
||||
);
|
||||
console.trace(logKey)
|
||||
const logKey = "[ID=" + this.uniqID + "]:[" + propertyKey + "]:"
|
||||
log(logKey,
|
||||
" Arguments:" + VokaPlayerImpl.safeStringify(args),
|
||||
" Result:" + VokaPlayerImpl.safeStringify(res),
|
||||
)
|
||||
console.trace(logKey, "stacktrace: ")
|
||||
}
|
||||
return res
|
||||
};
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
return descriptor
|
||||
}
|
||||
|
||||
// MARK: - IVokaPlayer implementation
|
||||
@@ -413,7 +487,7 @@ export class VokaPlayerImpl implements IVokaPlayer {
|
||||
|
||||
getAdIsPlaying(): boolean { return this._adIsPlaying }
|
||||
cancelAdPlayback() {
|
||||
this.bus.publish(VokaBusEvent.adCancelPlaybackEvent)
|
||||
this.bus.publish(VokaBusEvent.adCancelPlaybackEvent as BusEvent)
|
||||
}
|
||||
|
||||
getAbsoluteCurrentTime(): number | null {
|
||||
|
||||
Reference in New Issue
Block a user