npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@libs-ui/components-audio

v0.2.357-10

Published

> Component audio player đầy đủ tính năng cho Angular — hỗ trợ play/pause, seek, volume, mute và download có kiểm soát quyền.

Readme

@libs-ui/components-audio

Component audio player đầy đủ tính năng cho Angular — hỗ trợ play/pause, seek, volume, mute và download có kiểm soát quyền.

Giới thiệu

LibsUiComponentsAudioComponent là một standalone Angular component cung cấp giao diện phát audio hoàn chỉnh. Component tích hợp thanh tiến độ, điều chỉnh âm lượng dạng hover-reveal, hiển thị thời gian HH:MM:SS và cơ chế download có kiểm soát quyền thông qua callback. Ngoài việc phản ứng trực tiếp qua Output events, component còn cung cấp IAudioFunctionControlEvent — một API điều khiển chương trình từ component cha.

Tính năng

  • ✅ Phát/tạm dừng audio với giao diện icon trực quan
  • ✅ Thanh tiến độ tua nhanh/tua lại (seek 0–100%)
  • ✅ Điều chỉnh âm lượng với slider ẩn/hiện theo hover
  • ✅ Bật/tắt tiếng (mute/unmute) đồng bộ với volume slider
  • ✅ Hiển thị thời gian định dạng HH:MM:SS (currentTime / duration)
  • ✅ Download file audio có kiểm soát quyền qua async callback
  • ✅ Function Control API — điều khiển audio từ component cha qua outFunctionsControl
  • ✅ Hỗ trợ bàn phím (Enter, Space) cho mọi control
  • ✅ Angular Signals + OnPush Change Detection

Khi nào sử dụng

  • Cần nhúng audio player có giao diện chuẩn vào trang chi tiết, comment, hoặc tin nhắn
  • Cần điều khiển audio từ bên ngoài component (play, pause, seek, volume) thông qua API
  • Cần tracking trạng thái audio theo thời gian thực (đang phát, mute, vị trí hiện tại)
  • Cần giới hạn quyền download file audio theo logic nghiệp vụ (kiểm tra permission trước khi cho tải)
  • Phát file MP3, WAV, OGG trong ứng dụng web Angular

Cài đặt

npm install @libs-ui/components-audio

Import

import { LibsUiComponentsAudioComponent, IAudioFunctionControlEvent } from '@libs-ui/components-audio';

Ví dụ sử dụng

Ví dụ 1 — Cơ bản (Minimal)

import { Component } from '@angular/core';
import { LibsUiComponentsAudioComponent } from '@libs-ui/components-audio';

@Component({
  selector: 'app-audio-basic',
  standalone: true,
  imports: [LibsUiComponentsAudioComponent],
  template: `
    <libs_ui-components-audio
      [fileAudio]="audioUrl"
      [checkPermissionDownloadAudio]="checkDownloadPermission"
    />
  `,
})
export class AudioBasicComponent {
  audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  checkDownloadPermission = (): Promise<boolean> => Promise.resolve(true);
}

Ví dụ 2 — Lắng nghe trạng thái qua Output

import { Component, signal } from '@angular/core';
import { LibsUiComponentsAudioComponent, IAudioFunctionControlEvent } from '@libs-ui/components-audio';

@Component({
  selector: 'app-audio-status',
  standalone: true,
  imports: [LibsUiComponentsAudioComponent],
  template: `
    <libs_ui-components-audio
      [fileAudio]="audioUrl"
      [checkPermissionDownloadAudio]="checkDownloadPermission"
      (outPlay)="handlerPlay($event)"
      (outMute)="handlerMute($event)"
      (outVolumeControl)="handlerVolume($event)"
      (outTimeUpdate)="handlerTimeUpdate($event)"
      (outEnded)="handlerEnded()"
    />

    <div class="mt-4 text-sm text-gray-600">
      <p>Đang phát: {{ isPlaying() }}</p>
      <p>Tắt tiếng: {{ isMuted() }}</p>
      <p>Âm lượng: {{ volume() }}%</p>
      <p>Thời gian: {{ currentTime() }} / {{ duration() }}</p>
    </div>
  `,
})
export class AudioStatusComponent {
  audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  isPlaying = signal(false);
  isMuted = signal(false);
  volume = signal(100);
  currentTime = signal('00:00:00');
  duration = signal('00:00:00');

  checkDownloadPermission = (): Promise<boolean> => Promise.resolve(true);

  handlerPlay(isPlaying: boolean): void {
    this.isPlaying.set(isPlaying);
  }

  handlerMute(isMuted: boolean): void {
    this.isMuted.set(isMuted);
  }

  handlerVolume(vol: number): void {
    this.volume.set(vol);
  }

  handlerTimeUpdate(time: { currentTime: string; duration: string }): void {
    this.currentTime.set(time.currentTime);
    this.duration.set(time.duration);
  }

  handlerEnded(): void {
    this.isPlaying.set(false);
  }
}

Ví dụ 3 — Điều khiển audio từ bên ngoài (External Control API)

import { Component, signal } from '@angular/core';
import { LibsUiComponentsAudioComponent, IAudioFunctionControlEvent } from '@libs-ui/components-audio';

@Component({
  selector: 'app-audio-external',
  standalone: true,
  imports: [LibsUiComponentsAudioComponent],
  template: `
    <libs_ui-components-audio
      [fileAudio]="audioUrl"
      [checkPermissionDownloadAudio]="checkDownloadPermission"
      (outFunctionsControl)="handlerFunctionsControl($event)"
      (outPlay)="isPlaying.set($event)"
      (outMute)="isMuted.set($event)"
    />

    <div class="flex gap-2 mt-4">
      <button class="px-4 py-2 bg-blue-500 text-white rounded" (click)="handlerTogglePlay()">
        {{ isPlaying() ? 'Pause' : 'Play' }}
      </button>
      <button class="px-4 py-2 bg-gray-500 text-white rounded" (click)="handlerToggleMute()">
        {{ isMuted() ? 'Unmute' : 'Mute' }}
      </button>
      <button class="px-4 py-2 bg-green-500 text-white rounded" (click)="handlerSetVolume50()">
        Volume 50%
      </button>
      <button class="px-4 py-2 bg-orange-500 text-white rounded" (click)="handlerSeekMidpoint()">
        Seek 50%
      </button>
    </div>
  `,
})
export class AudioExternalControlComponent {
  audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  audioControls = signal<IAudioFunctionControlEvent | null>(null);
  isPlaying = signal(false);
  isMuted = signal(false);

  checkDownloadPermission = (): Promise<boolean> => Promise.resolve(true);

  handlerFunctionsControl(controls: IAudioFunctionControlEvent): void {
    this.audioControls.set(controls);
  }

  handlerTogglePlay(): void {
    this.audioControls()?.playPause();
  }

  handlerToggleMute(): void {
    this.audioControls()?.toggleMute();
  }

  handlerSetVolume50(): void {
    this.audioControls()?.setVolume(50);
  }

  handlerSeekMidpoint(): void {
    this.audioControls()?.seekTo(50);
  }
}

Ví dụ 4 — Kiểm soát quyền download theo nghiệp vụ

import { Component, inject } from '@angular/core';
import { LibsUiComponentsAudioComponent } from '@libs-ui/components-audio';

@Component({
  selector: 'app-audio-permission',
  standalone: true,
  imports: [LibsUiComponentsAudioComponent],
  template: `
    <libs_ui-components-audio
      [fileAudio]="audioUrl"
      [checkPermissionDownloadAudio]="checkDownloadPermission"
    />
  `,
})
export class AudioPermissionComponent {
  audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  /**
   * checkPermissionDownloadAudio nhận một factory function (() => Promise<boolean>),
   * không phải Promise<boolean> trực tiếp. Component sẽ gọi hàm này mỗi lần user nhấn download.
   */
  checkDownloadPermission = (): Promise<boolean> => {
    // Ví dụ: kiểm tra role của user trước khi cho phép tải
    const userRole = 'admin'; // lấy từ auth service thực tế
    return Promise.resolve(userRole === 'admin');
  };
}

@Input()

| Input | Type | Default | Mô tả | Ví dụ | |---|---|---|---|---| | fileAudio | string | required | URL của file audio cần phát. Hỗ trợ MP3, WAV, OGG. Khi giá trị thay đổi, audio sẽ tự động reload. | [fileAudio]="'https://example.com/audio.mp3'" | | checkPermissionDownloadAudio | () => Promise<boolean> | required | Factory function kiểm tra quyền download. Được gọi mỗi lần user nhấn nút tải. Trả về true để cho phép, false để chặn. | [checkPermissionDownloadAudio]="checkPermission" |

@Output()

| Output | Type | Mô tả | Handler TS | Binding HTML | |---|---|---|---|---| | (outFunctionsControl) | IAudioFunctionControlEvent | Emit ngay sau ngAfterViewInit — cung cấp API điều khiển audio từ bên ngoài. | handlerFunctionsControl(e: IAudioFunctionControlEvent): void { e; /* không stopPropagation vì đây là output signal */ this.controls.set(e); } | (outFunctionsControl)="handlerFunctionsControl($event)" | | (outPlay) | boolean | Emit mỗi khi trạng thái play/pause thay đổi. true = đang phát, false = đang dừng. | handlerPlay(e: boolean): void { this.isPlaying.set(e); } | (outPlay)="handlerPlay($event)" | | (outMute) | boolean | Emit mỗi khi trạng thái mute thay đổi. true = đang tắt tiếng. | handlerMute(e: boolean): void { this.isMuted.set(e); } | (outMute)="handlerMute($event)" | | (outVolumeControl) | number | Emit giá trị âm lượng hiện tại từ 0 đến 100, mỗi khi volume slider thay đổi. | handlerVolume(e: number): void { this.volume.set(e); } | (outVolumeControl)="handlerVolume($event)" | | (outTimeUpdate) | { currentTime: string; duration: string } | Emit theo từng tick cập nhật thời gian. Định dạng: 'HH:MM:SS'. | handlerTimeUpdate(e: { currentTime: string; duration: string }): void { this.currentTime.set(e.currentTime); this.duration.set(e.duration); } | (outTimeUpdate)="handlerTimeUpdate($event)" | | (outEnded) | void | Emit khi audio phát hết đến cuối file. | handlerEnded(): void { this.isPlaying.set(false); } | (outEnded)="handlerEnded()" |

Types & Interfaces

import { IAudioFunctionControlEvent } from '@libs-ui/components-audio';
interface IAudioFunctionControlEvent {
  /** Bắt đầu hoặc tạm dừng phát audio */
  playPause: (event?: Event) => void;

  /** Bật hoặc tắt âm thanh */
  toggleMute: (event?: Event) => void;

  /**
   * Điều chỉnh âm lượng
   * @param value Giá trị từ 0 đến 100
   */
  setVolume: (value: number) => void;

  /**
   * Di chuyển đến vị trí cụ thể trong audio
   * @param value Giá trị phần trăm từ 0 đến 100
   */
  seekTo: (value: number) => void;

  /** Tải xuống file audio (chỉ thực hiện nếu checkPermissionDownloadAudio trả về true) */
  download: (event?: Event) => void;

  /** Trả về true nếu audio đang phát */
  isPlaying: () => boolean;

  /** Trả về true nếu audio đang tắt tiếng */
  isMuted: () => boolean;
}

Cách sử dụng IAudioFunctionControlEvent

// Nhận controls qua outFunctionsControl
private audioControls = signal<IAudioFunctionControlEvent | null>(null);

handlerFunctionsControl(controls: IAudioFunctionControlEvent): void {
  this.audioControls.set(controls);
}

// Sau đó gọi từ bất kỳ đâu trong component cha
handlerPlayPause(): void {
  this.audioControls()?.playPause();
}

handlerSetVolumeTo30(): void {
  this.audioControls()?.setVolume(30);
}

handlerSeekToBeginning(): void {
  this.audioControls()?.seekTo(0);
}

handlerCheckState(): void {
  const playing = this.audioControls()?.isPlaying();   // boolean
  const muted = this.audioControls()?.isMuted();       // boolean
}

Lưu ý quan trọng

⚠️ checkPermissionDownloadAudio là factory function, không phải Promise: Input này nhận () => Promise<boolean>, không phải Promise<boolean> trực tiếp. Component gọi hàm này khi user nhấn download, không gọi trước. Đảm bảo truyền vào một hàm (arrow function hoặc method reference), không truyền kết quả gọi hàm.

// ❌ SAI — truyền Promise trực tiếp
[checkPermissionDownloadAudio]="checkPermission()"

// ✅ ĐÚNG — truyền function reference
[checkPermissionDownloadAudio]="checkPermission"

// ✅ ĐÚNG — truyền inline arrow function
[checkPermissionDownloadAudio]="() => permissionService.canDownloadAudio()"

⚠️ outFunctionsControl emit một lần sau ngAfterViewInit: Output này chỉ emit một lần khi component khởi tạo xong. Lưu lại giá trị vào signal hoặc biến class để dùng về sau. Không cần subscribe lại khi fileAudio thay đổi — cùng một controls object vẫn hoạt động với file mới.

⚠️ Định dạng thời gian outTimeUpdate luôn là HH:MM:SS: Ngay cả khi duration dưới 1 giờ, output vẫn là '00:03:45' (không phải '3:45'). Lưu ý điều này khi so sánh hoặc hiển thị thời gian.

⚠️ Audio không tự phát khi fileAudio thay đổi: Khi fileAudio input thay đổi, component reload audio nhưng không tự động phát. Gọi controls.playPause() sau khi cần thiết.

Unit Tests

# Chạy tests
npx nx test components-audio

# Chạy với coverage
npx nx test components-audio --coverage

# Chạy một file spec cụ thể
npx nx test components-audio --testFile=libs-ui/components/audio/src/audio.component.spec.ts