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

@sgrsoft/vpe-reactnative-tv-sdk

v0.1.2

Published

VPE React Native TV SDK for Android TV / Apple TV (tvOS)

Readme

@sgrsoft/vpe-reactnative-tv-sdk

Android TV / Apple TV(tvOS) / Fire TV 비디오 플레이어 SDK

Platform - Android TV Platform - tvOS Platform - Fire TV

React Native(react-native-tvos) 기반의 TV 전용 비디오 플레이어 SDK. 웹 SDK(vpe-react-sdk)와 동일한 API를 제공하여 플랫폼 간 일관된 개발 경험을 지원한다.

주요 기능

  • HLS / DASH / MP4 네이티브 재생 (ExoPlayer / AVPlayer)
  • DRM 지원 (Widevine + FairPlay)
  • IMA 광고 (Google Interactive Media Ads)
  • 플레이리스트 (자동 다음 재생)
  • 자막 (VTT / SMI, EUC-KR 자동 디코딩)
  • 선언적 레이아웃 커스터마이징
  • 라이브 스트림 + LL-HLS 저지연 모드
  • 다국어 (ko / en / ja)
  • 이어보기 (onExit + initialPosition)
  • D-pad / 리모컨 네비게이션
  • HLS ABR 화질 선택
  • 캡처 방지
  • vpe-core-sdk 연동 (라이선스, MA 분석)

설치

# npm
npm install @sgrsoft/vpe-reactnative-tv-sdk

# yarn
yarn add @sgrsoft/vpe-reactnative-tv-sdk

peerDependencies 설치

yarn add react react-native @sgrsoft/react-native-video @sgrsoft/vpe-core-sdk \
  @sgrsoft/vpe-react-native-ui @react-native-async-storage/async-storage

필수: react-native는 반드시 react-native-tvos로 alias되어야 한다.

// package.json
"react-native": "npm:[email protected]"

선택적 의존성

# 캡처 방지 기능 사용 시
yarn add react-native-capture-protection

iOS (tvOS)

cd ios && pod install

Android TV

IMA 광고를 사용하는 경우 android/gradle.properties에 추가:

RNVideo_useExoplayerIMA=true

빠른 시작

import React, { useRef } from 'react';
import { View, StyleSheet } from 'react-native';
import { VpePlayer } from '@sgrsoft/vpe-reactnative-tv-sdk';
import type { PlayerHandle } from '@sgrsoft/vpe-reactnative-tv-sdk';

function App() {
  const playerRef = useRef<PlayerHandle>(null);

  return (
    <View style={styles.container}>
      <VpePlayer
        ref={playerRef}
        accessKey="YOUR_ACCESS_KEY"
        options={{
          playlist: [{
            file: 'https://example.com/video.m3u8',
            description: { title: '영상 제목' },
          }],
          autostart: true,
        }}
        onBack={() => {
          // 뒤로가기 처리
        }}
        onEvent={(event) => {
          if (event.type === 'ready') {
            console.log('Player ready');
          }
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#000' },
});

Props (PlayerProps)

| Prop | 타입 | 기본값 | 설명 | |------|------|--------|------| | accessKey | string | - | API 접근 키 (필수) | | devAppId | string | - | 로컬 테스트용 앱 식별자. 자동 감지 실패 시 fallback | | platform | 'pub' \| 'gov' | 'pub' | 플랫폼 | | stage | string | 'prod' | 스테이지 | | isDev | boolean | false | 개발 모드 | | options | PlayerOptions | - | 플레이어 옵션 | | layout | ControlBarLayout \| ControlBarLayoutVariant \| ControlBarLayoutResponsive | - | 레이아웃 커스터마이징 | | scopeId | string | - | 스코프 ID | | onEvent | (event: PlayerEvent) => void | - | 이벤트 핸들러 | | onBack | () => void | - | 뒤로가기 콜백 (TV 전용) | | onExit | (info: PlayerExitInfo) => void | - | 플레이어 종료 시 재생 정보 전달 (TV 전용) | | initialPosition | number | - | 시작 위치 (초 단위, 이어보기용, TV 전용) | | errorOverride | ReactNode \| ComponentType \| Function | - | 커스텀 에러 UI |

Options (PlayerOptions)

재생

| 옵션 | 타입 | 기본값 | 설명 | |------|------|--------|------| | playlist | PlaylistItem[] | - | 재생할 미디어 목록 | | autostart | boolean | true | 자동 재생 | | muted | boolean | false | 초기 음소거 | | repeat | boolean | false | 반복 재생 | | playIndex | number | 0 | 시작 재생 인덱스 | | token | string | - | 스트림 URL 토큰 | | lowLatencyMode | boolean | false | LL-HLS 저지연 모드 |

UI

| 옵션 | 타입 | 기본값 | 설명 | |------|------|--------|------| | controls | boolean | true | 컨트롤바 표시 | | controlActiveTime | number | 3000 | 컨트롤바 자동 숨김 (ms) | | progressBarColor | string | - | 진행바 색상 | | controlBtn | object | - | 버튼별 표시 on/off | | descriptionNotVisible | boolean | false | 메타 설명 숨김 | | lang | string | 'ko' | UI 언어 (ko / en / ja) | | playRateSetting | number[] | [0.5, 0.75, 1, 1.5, 2] | 재생 속도 목록 |

자막 / 워터마크

| 옵션 | 타입 | 기본값 | 설명 | |------|------|--------|------| | visibleWatermark | boolean | false | 워터마크 표시 | | watermarkText | string | - | 워터마크 텍스트 | | watermarkConfig | object | - | 워터마크 위치/투명도/랜덤 이동 설정 |

광고 / 보안

| 옵션 | 타입 | 기본값 | 설명 | |------|------|--------|------| | ads | { tagUrl: string; enabled?: boolean } | - | IMA 광고 설정 | | screenRecordingPrevention | boolean | false | 캡처/녹화 방지 (TV 전용) |

PlaylistItem

type PlaylistItem = {
  file?: string;          // 미디어 URL (.mp4, .m3u8, .mpd)
  poster?: string;        // 포스터 이미지 URL
  drm?: DrmConfig;        // DRM 설정
  description?: {
    title?: string;
    profile_name?: string;
    profile_image?: string;
    created_at?: string;
  };
  vtt?: Array<{           // 자막 트랙
    label?: string;       // 표시 이름 ("한국어", "English")
    src?: string;         // VTT 또는 SMI 파일 URL
    default?: boolean;    // 기본 자막 여부
  }>;
};

자막 예제

options={{
  playlist: [{
    file: 'https://example.com/video.m3u8',
    vtt: [
      { label: '한국어', src: 'https://example.com/ko.vtt', default: true },
      { label: 'English', src: 'https://example.com/en.vtt' },
      { label: '日本語', src: 'https://example.com/ja.smi' },  // SMI도 지원
    ],
  }],
}}

ref 메소드 (PlayerHandle)

const playerRef = useRef<PlayerHandle>(null);

// 재생 제어
playerRef.current?.play();
playerRef.current?.pause();
playerRef.current?.mute();

// 시킹
playerRef.current?.currentTime(30);  // 30초로 이동

// 플레이리스트
playerRef.current?.next();
playerRef.current?.prev();
playerRef.current?.addNextSource({ file: 'https://...' });
playerRef.current?.addPrevSource({ file: 'https://...' });

// 토큰 갱신
playerRef.current?.tokenChange('NEW_TOKEN');

// 레이아웃 변경
playerRef.current?.layout({ bottom: [{ items: ['PlayBtn'], wrapper: 'Group' }] }, true);

// 컨트롤바 제어
playerRef.current?.controlBarActive();
playerRef.current?.controlBarDeactive();
playerRef.current?.uiHidden();
playerRef.current?.uiVisible();

// 재생 모드 강제 전환
playerRef.current?.changePlayMode('live');

| 메소드 | 설명 | TV 참고사항 | |--------|------|------------| | play() | 재생 | - | | pause() | 일시정지 | - | | mute() | 음소거 토글 | - | | prev() / next() | 트랙 이동 | - | | currentTime(time) | 시킹 (초) | - | | volume(vol) | 볼륨 설정 | TV: no-op (시스템 볼륨 사용) | | layout(layout, merge) | 레이아웃 변경 | - | | tokenChange(token) | DRM 토큰 갱신 | - | | addNextSource(source) | 다음 소스 추가 | - | | addPrevSource(source) | 이전 소스 추가 | - | | uiHidden() / uiVisible() | 컨트롤바 숨김/표시 | - | | controlBarActive() / controlBarDeactive() | 컨트롤바 활성화/비활성화 | - | | changePlayMode(mode) | VOD/Live 모드 전환 | - | | isSeeking() | SeekBar 시킹 상태 조회 | - | | fullscreen() / pip() | 전체화면 / PiP | TV: no-op (항상 전체화면, PiP 미지원) |

이벤트 (PlayerEvent)

<VpePlayer
  onEvent={(event) => {
    console.log(event.type, event.state);
  }}
/>

이벤트 타입

| 카테고리 | 이벤트 | 설명 | |---------|--------|------| | 재생 | ready, canplay, start | 플레이어 준비/재생 시작 | | | play, playing, pause, ended | 재생 상태 변경 | | | timeupdate, durationchange | 시간/길이 변경 | | 버퍼링 | bufferingStart, bufferingEnd | 버퍼링 시작/종료 | | | loadingStart, loadingEnd | 로딩 시작/종료 | | 시킹 | seeking, seeked, waiting | 시킹 상태 | | UI | controlbarActive, controlbarDeactive | 컨트롤바 표시/숨김 | | | stateChange | 상태 변경 | | 트랙 | next, nextTrack, prev, prevTrack | 트랙 이동 | | | skipForward, skipBack | ±10초 시킹 | | | playlistChange | 플레이리스트 변경 | | | quality_change, volumechange | 화질/볼륨 변경 | | 광고 | adStart, adStarted, adComplete | 광고 시작/완료 | | | adSkip, adSkipped, adError | 광고 스킵/에러 | | | adLoaded, adBreakStart, adBreakEnd | 광고 로드/브레이크 | | 에러 | error | 에러 발생 |

DRM 설정

Widevine (Android TV)

options={{
  playlist: [{
    file: 'https://example.com/video.mpd',
    drm: {
      'com.widevine.alpha': {
        licenseUri: 'https://license-server.com/widevine',
        licenseRequestHeader: {
          'pallycon-customdata-v2': 'TOKEN',
        },
      },
    },
  }],
}}

FairPlay (tvOS)

options={{
  playlist: [{
    file: 'https://example.com/video.m3u8',
    drm: {
      'com.apple.fps': {
        licenseUri: 'https://license-server.com/fairplay',
        certificateUri: 'https://license-server.com/cert',
        certificateRequestHeader: {
          'pallycon-customdata-v2': 'TOKEN',
        },
      },
    },
  }],
}}

react-native-video 직접 DRM 형식

options={{
  playlist: [{
    file: 'https://example.com/video.m3u8',
    drm: {
      type: 'fairplay',
      licenseServer: 'https://license-server.com/fairplay',
      certificateUrl: 'https://license-server.com/cert',
      headers: { 'pallycon-customdata-v2': 'TOKEN' },
    },
  }],
}}

IMA 광고

<VpePlayer
  accessKey="YOUR_ACCESS_KEY"
  options={{
    playlist: [{ file: 'https://example.com/video.m3u8' }],
    ads: {
      tagUrl: 'https://pubads.g.doubleclick.net/gampad/ads?...',
      enabled: true,  // 기본: true
    },
  }}
/>

네이티브 빌드 설정 (필수)

tvOSios/Podfile:

$RNVideoUseGoogleIMA = true

이후 pod install 실행.

Android TVandroid/gradle.properties:

RNVideo_useExoplayerIMA=true

광고 재생 중에는 컨트롤바가 자동으로 숨겨지며, 네이티브 IMA SDK가 자체 UI를 렌더링한다.

레이아웃 커스터마이징

5개 섹션(top, upper, center, lower, bottom) + seekbar으로 구성된 선언적 레이아웃 시스템을 제공한다.

사용 가능한 아이템

| 아이템 | 설명 | |--------|------| | PlayBtn | 재생/일시정지/다시재생 | | BigPlayBtn | 중앙 대형 재생 + 이전/다음 | | BackBtn | 뒤로가기 | | SeekBar | 탐색바 | | SkipBackBtn / SkipForwardBtn | ±10초 시킹 | | PrevBtn / NextBtn / NextPrevBtn | 트랙 이동 | | TimeBtn / CurrentTimeBtn / DurationBtn | 시간 표시 | | MuteBtn / VolumeBtn | 음소거/볼륨 | | SubtitleBtn | 자막 메뉴 | | SettingBtn | 설정 메뉴 | | MetaDesc | 영상 메타정보 | | Blank / BlankBtn | 빈 공간 |

커스텀 레이아웃 예제

<VpePlayer
  accessKey="YOUR_ACCESS_KEY"
  options={{ playlist: [...] }}
  layout={{
    top: [
      { items: ['BackBtn'], wrapper: 'Group' },
      { items: ['MetaDesc'] },
      { wrapper: 'Blank', items: [] },
    ],
    center: [
      { items: ['BigPlayBtn'], align: 'center' },
    ],
    lower: [
      { wrapper: 'Blank', items: ['SeekBar'], align: 'center' },
    ],
    bottom: [
      { items: ['PlayBtn'], wrapper: 'Group' },
      { items: ['NextPrevBtn'], wrapper: 'Group' },
      { items: ['MuteBtn'], wrapper: 'Group' },
      { items: ['TimeBtn'], wrapper: 'Group' },
      { wrapper: 'Blank', items: [] },
      { items: ['SubtitleBtn', 'SettingBtn'], wrapper: 'Group' },
    ],
  }}
/>

VOD / Live 변형

layout={{
  vod: { /* VOD 전용 레이아웃 */ },
  live: { /* Live 전용 레이아웃 */ },
}}

런타임 레이아웃 변경

// 부분 업데이트 (merge=true, 기본값)
playerRef.current?.layout({ bottom: [{ items: ['PlayBtn'], wrapper: 'Group' }] }, true);

// 전체 교체 (merge=false)
playerRef.current?.layout(newLayout, false);

라이브 스트림

라이브 스트림은 자동으로 감지된다:

  1. HLS 매니페스트에서 #EXT-X-ENDLIST 부재 확인
  2. Duration이 0 또는 Infinity
  3. 재생 중 duration 변화 감지

라이브 감지 시:

  • TimeBtn에 빨간 dot + "LIVE" 표시
  • 사이드 메뉴에서 재생속도 메뉴 숨김
  • SeekBar에서 시킹 비활성화

LL-HLS 저지연 모드

options={{
  lowLatencyMode: true,
  playlist: [{ file: 'https://example.com/live.m3u8' }],
}}
  • Android TV: ExoPlayer targetOffsetMs: 2000
  • tvOS: AVPlayer preferredForwardBufferDuration: 2
  • 5초 이상 뒤처지면 라이브 엣지로 자동 복귀

이어보기 (Resume Playback)

SDK는 onExit 콜백으로 종료 시 재생 정보를 제공하고, initialPosition으로 시작 위치를 지정한다. 저장/복원 로직은 앱에서 구현한다.

function PlayerScreen() {
  const [startPos, setStartPos] = useState<number | undefined>();

  useEffect(() => {
    // 저장된 위치 불러오기
    AsyncStorage.getItem('lastPosition').then(val => {
      if (val) setStartPos(Number(val));
    });
  }, []);

  return (
    <VpePlayer
      accessKey="YOUR_ACCESS_KEY"
      options={{ playlist: [...] }}
      initialPosition={startPos}
      onExit={(info) => {
        // info: { currentTime, duration, playIndex, sourceUri }
        AsyncStorage.setItem('lastPosition', String(info.currentTime));
      }}
    />
  );
}

에러 처리 (errorOverride)

기본 에러 UI 대신 커스텀 에러 UI를 렌더링할 수 있다.

// 함수 패턴
<VpePlayer
  errorOverride={(info) => (
    <View>
      <Text>에러: {info.errorCode}</Text>
      <Text>{info.errorMessage}</Text>
    </View>
  )}
/>

// 컴포넌트 패턴
<VpePlayer errorOverride={MyErrorComponent} />

// ReactNode 패턴
<VpePlayer errorOverride={<Text>에러가 발생했습니다</Text>} />

플랫폼별 참고사항

해상도

| 플랫폼 | 보고 해상도 | 스케일 | 실제 해상도 | |--------|-----------|--------|-----------| | Apple TV | 1920 x 1080 | 1x | 1920 x 1080 | | Android TV | 960 x 540 | 2x | 1920 x 1080 |

Android TV는 960x540 dp로 보고되므로 SDK 내부에서 자동 스케일 보정을 적용한다.

리모컨 이벤트

| 이벤트 | Apple TV | Android TV | 설명 | |--------|---------|-----------|------| | select | O | O | 확인/선택 | | playPause | O | O | 재생/일시정지 | | up/down/left/right | O | O | 방향키 | | menu | O | X | 메뉴 (뒤로가기) | | play / pause | X | O | 재생/일시정지 전용 | | rewind / fastForward | X | O | 되감기/빨리감기 |

웹 SDK와의 차이점

| 기능 | 웹 | TV | |------|-----|-----| | 전체화면 | requestFullscreen() | 항상 전체화면 (no-op) | | PiP | requestPictureInPicture() | 미지원 (no-op) | | 볼륨 | 슬라이더 UI | 시스템 볼륨 (MuteBtn = 뮤트 토글) | | 입력 | 마우스/키보드 | 리모컨 D-pad | | 자막 | DOM TextTrack API | VTT/SMI 오버레이 렌더링 | | 광고 | IMA + NAM | IMA만 지원 |

버전 호환성

| 항목 | 최소 | 권장 | |------|------|------| | react-native-tvos | 0.83.1-1 | 0.84.0-0 | | React | 19.x | 19.2.x | | Node.js | 22.11+ | 22.x LTS | | tvOS | 15.1+ | 16.0+ | | Android API | 24 (7.0) | 28+ | | TypeScript | 5.0+ | 5.8.x |

라이선스

MIT