diff --git a/src/app/elements/replay/asciicast/asciicast.component.html b/src/app/elements/replay/asciicast/asciicast.component.html index e2b49aa96..a6e6347ea 100644 --- a/src/app/elements/replay/asciicast/asciicast.component.html +++ b/src/app/elements/replay/asciicast/asciicast.component.html @@ -48,24 +48,26 @@ -
-
- {{ "Commands" | translate }} ({{ commands.length }}) -
-
-
-
{{ item.input }}
-
{{ item.atime }}
+ @if (commands && commands.length > 0) { +
+
+ {{ "Commands" | translate }} ({{ commands.length }}) +
+
+
+
{{ item.input }}
+
{{ item.atime }}
+
-
+ }
diff --git a/src/app/elements/replay/asciicast/asciicast.component.ts b/src/app/elements/replay/asciicast/asciicast.component.ts index fd3ace477..2505b3fa1 100644 --- a/src/app/elements/replay/asciicast/asciicast.component.ts +++ b/src/app/elements/replay/asciicast/asciicast.component.ts @@ -1,9 +1,9 @@ -import { AfterViewInit, Component, HostListener, Input, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { filter } from 'rxjs'; +import { HttpService } from '@app/services'; import { Command, Replay } from '@app/model'; import { formatTime } from '@app/utils/common'; -import { HttpService } from '@app/services'; -import { filter } from 'rxjs'; +import { ActivatedRoute } from '@angular/router'; +import { AfterViewInit, Component, HostListener, Input, OnInit } from '@angular/core'; @Component({ standalone: false, @@ -22,12 +22,13 @@ export class ElementReplayAsciicastComponent implements OnInit, AfterViewInit { rows: number; duration: string; position: string; - timer: any; // 多长时间播放下一个 + timer: any; startTime = null; startTimeStamp = null; commands: Command[]; page = 0; private AsciinemaPlayer: any; + private playerCreated = false; constructor( private route: ActivatedRoute, @@ -37,10 +38,10 @@ export class ElementReplayAsciicastComponent implements OnInit, AfterViewInit { } async ngOnInit() { - // 动态加载 asciinema-player await this.loadAsciinemaPlayer(); this.commands = new Array(); + const start = new Date(this.replay.date_start); const end = new Date(this.replay.date_end); const duration = end.getTime() - start.getTime(); @@ -56,32 +57,49 @@ export class ElementReplayAsciicastComponent implements OnInit, AfterViewInit { if (this.startAt >= duration) { this.startAt = duration; } - /* */ }); this.rows = Math.min(25, Math.floor((window.innerHeight - 120) / 16)); // 限制最大25行,调整计算方式 this.position = '00:00'; this.duration = formatTime(duration); this.getCommands(this.page); + + this.createPlayerWhenReady(); } private async loadAsciinemaPlayer() { try { - // 动态导入 asciinema-player const module = await import('asciinema-player'); + this.AsciinemaPlayer = module.default || module; } catch (error) { console.error('Failed to load asciinema-player:', error); - // 如果动态导入失败,尝试从全局变量获取(兼容性) + this.AsciinemaPlayer = (window as any).AsciinemaPlayer; } } ngAfterViewInit() { + this.createPlayerWhenReady(); + } + + private createPlayerWhenReady() { + if (this.playerCreated) { + return; + } + if (this.AsciinemaPlayer) { this.player = this.createPlayer(); - this.player.play(); - this.isPlaying = true; - this.createTimer(); + + if (this.player) { + this.player.play(); + this.isPlaying = true; + this.createTimer(); + this.playerCreated = true; + } else { + console.error('Player creation failed'); + } + } else { + setTimeout(() => this.createPlayerWhenReady(), 100); } } @@ -176,9 +194,28 @@ export class ElementReplayAsciicastComponent implements OnInit, AfterViewInit { console.error('AsciinemaPlayer not loaded'); return null; } + const el = document.getElementById('screen'); + + if (!el) { + console.error('Screen element not found'); + return null; + } + + if (!this.replay?.src) { + console.error('Replay src not available'); + return null; + } + const opt = this.getPlayerOptions(); - return this.AsciinemaPlayer.create(this.replay.src, el, opt); + + try { + const player = this.AsciinemaPlayer.create(this.replay.src, el, opt); + return player; + } catch (error) { + console.error('Error creating player:', error); + return null; + } } getUserLang() { @@ -199,6 +236,7 @@ export class ElementReplayAsciicastComponent implements OnInit, AfterViewInit { if (!this.startTimeStamp) { return; } + this._http.getCommandsData(this.replay.id, page).subscribe( data => { const results = data.results; @@ -209,6 +247,7 @@ export class ElementReplayAsciicastComponent implements OnInit, AfterViewInit { this.commands = this.commands.concat(results); }, err => { + console.error('getCommandsData error:', err); alert('没找到命令记录'); } ); diff --git a/src/app/pages/replay/replay.component.ts b/src/app/pages/replay/replay.component.ts index 4c45857e4..708154c12 100644 --- a/src/app/pages/replay/replay.component.ts +++ b/src/app/pages/replay/replay.component.ts @@ -1,8 +1,8 @@ -import {Component, OnInit} from '@angular/core'; -import {ActivatedRoute} from '@angular/router'; -import {HttpService, I18nService, LogService, SettingService} from '@app/services'; -import {Asset, Replay, Session, User} from '@app/model'; -import {getWaterMarkContent} from '@app/utils/common'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { HttpService, I18nService, LogService, SettingService } from '@app/services'; +import { Asset, Replay, Session, User } from '@app/model'; +import { getWaterMarkContent } from '@app/utils/common'; @Component({ standalone: false, @@ -18,11 +18,13 @@ export class PagesReplayComponent implements OnInit { sessionUser: User; loading: boolean = true; - constructor(private route: ActivatedRoute, - private _http: HttpService, - private _settingSvc: SettingService, - private _i18n: I18nService, - private _logger: LogService) { + constructor( + private route: ActivatedRoute, + private _http: HttpService, + private _settingSvc: SettingService, + private _i18n: I18nService, + private _logger: LogService + ) { this.getCurrentUser(); } @@ -34,12 +36,21 @@ export class PagesReplayComponent implements OnInit { async ngOnInit() { let sid = ''; + this.route.params.subscribe(params => { sid = params['sid']; }); + this.session = await this._http.getSessionDetail(sid); - this.asset = await this._http.getAssetDetail(this.session.asset_id).toPromise(); + + try { + this.asset = await this._http.getAssetDetail(this.session.asset_id).toPromise(); + } catch (error) { + this.asset = new Asset(); + } + this.sessionUser = await this._http.getUserDetail(this.session.user_id); + const interval = setInterval(() => { this._http.getReplay(sid).subscribe( data => { @@ -55,11 +66,13 @@ export class PagesReplayComponent implements OnInit { clearInterval(interval); this.loading = false; const auditorUser = `${this._i18n.instant('Viewer')}: ${this.user.name}(${this.user.username})`; - const sessionContent = getWaterMarkContent(this.sessionUser, this.asset, this._settingSvc); - const content = `${auditorUser}\n${sessionContent}`; - this._settingSvc.createWaterMarkIfNeed( - document.body, `${content}` + const sessionContent = getWaterMarkContent( + this.sessionUser, + this.asset, + this._settingSvc ); + const content = `${auditorUser}\n${sessionContent}`; + this._settingSvc.createWaterMarkIfNeed(document.body, `${content}`); } }, err => { @@ -78,11 +91,11 @@ export class PagesReplayComponent implements OnInit { const tp = this.replay.type; const supportedType = { - 'json': true, - 'guacamole': true, - 'asciicast': true, - 'mp4': true, - 'parts': true + json: true, + guacamole: true, + asciicast: true, + mp4: true, + parts: true }; return !supportedType[tp]; }