diff --git a/README-ko_kr.md b/README-ko_kr.md
index aeb20b0..453aa47 100644
--- a/README-ko_kr.md
+++ b/README-ko_kr.md
@@ -212,10 +212,11 @@ function App() {
### 유용한 기능
#### 재생 진행률 추적
+- `progressInterval`이 설정된 경우, 해당 간격(ms)마다 `onProgress` 콜백이 호출됩니다.
+- `progressInterval`이 `undefined`이거나 `0` 또는 `null`인 경우, `onProgress` 콜백은 호출되지 않습니다.
```tsx
function App() {
- // 1초마다 호출되는 진행률 이벤트 콜백
const handleProgress = useCallback((progress: ProgressData) => {
setCurrentTime(progress.currentTime);
setDuration(progress.duration);
@@ -225,6 +226,7 @@ function App() {
return (
)
diff --git a/README.md b/README.md
index e55edfc..d84eb8e 100644
--- a/README.md
+++ b/README.md
@@ -212,10 +212,11 @@ function App() {
### Useful Features
#### Playback Progress Tracking
+- If `progressInterval` is provided, the `onProgress` callback will be invoked at the specified interval (in milliseconds).
+- If `progressInterval` is `undefined`, `0`, or `null`, progress tracking is disabled and `onProgress` will not be called.
```tsx
function App() {
- // Progress event callback called every second
const handleProgress = useCallback((progress: ProgressData) => {
setCurrentTime(progress.currentTime);
setDuration(progress.duration);
@@ -225,6 +226,7 @@ function App() {
return (
)
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 6298305..8010420 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -26,6 +26,7 @@ function App() {
const [volume, setVolume] = useState(100);
const [isMuted, setIsMuted] = useState(false);
const [videoId, setVideoId] = useState('AbZH7XWDW_k');
+ const [progressInterval, setProgressInterval] = useState(1000);
const handleReady = useCallback((playerInfo: PlayerInfo) => {
console.log('Player is ready!');
@@ -183,6 +184,7 @@ function App() {
rel: false,
muted: true,
}}
+ progressInterval={progressInterval}
onReady={handleReady}
onStateChange={handleStateChange}
onProgress={handleProgress}
@@ -216,6 +218,15 @@ function App() {
버퍼: {(loadedFraction * 100).toFixed(1)}%
+
+ setProgressInterval(progressInterval === 0 ? 1000 : 0)}
+ >
+ {progressInterval}ms interval
+
+
+
(
videoId,
width = screenWidth,
height = 200,
+ progressInterval,
onReady,
onStateChange,
onError,
@@ -121,8 +123,12 @@ const YoutubePlayer = forwardRef(
);
const sendCommand = useCallback(
- // biome-ignore lint/suspicious/noExplicitAny:
- (command: string, args: (string | number | boolean | undefined)[] = [], needsResult = false): Promise => {
+ (
+ command: CommandType,
+ args: (string | number | boolean | undefined)[] = [],
+ needsResult = false,
+ // biome-ignore lint/suspicious/noExplicitAny:
+ ): Promise => {
return new Promise((resolve) => {
if (!webViewRef.current || !isReady) {
resolve(null);
@@ -222,6 +228,14 @@ const YoutubePlayer = forwardRef(
};
}, [isReady, sendCommand]);
+ useEffect(() => {
+ if (isReady) {
+ const safeInterval = safeNumber(progressInterval);
+
+ sendCommand('updateProgressInterval', [safeInterval]);
+ }
+ }, [progressInterval, isReady, sendCommand]);
+
return (
(
style={[styles.webView, webViewStyle]}
onMessage={handleMessage}
{...webviewProps}
- javaScriptEnabled={true}
- originWhitelist={['*']}
- domStorageEnabled={true}
- mediaPlaybackRequiresUserAction={false}
- allowsInlineMediaPlayback={true}
- allowsFullscreenVideo={true}
- scrollEnabled={false}
+ javaScriptEnabled
+ domStorageEnabled
+ allowsFullscreenVideo
+ allowsInlineMediaPlayback
bounces={false}
+ scrollEnabled={false}
+ mediaPlaybackRequiresUserAction={false}
+ originWhitelist={['*']}
onError={(error) => {
console.error('WebView error:', error);
onError?.({ code: -1, message: 'WebView loading error' });
diff --git a/src/YoutubePlayer.web.tsx b/src/YoutubePlayer.web.tsx
index 0928cba..1c42290 100644
--- a/src/YoutubePlayer.web.tsx
+++ b/src/YoutubePlayer.web.tsx
@@ -10,6 +10,7 @@ const YoutubePlayer = forwardRef(
videoId,
width,
height = 200,
+ progressInterval: interval,
onReady,
onStateChange,
onError,
@@ -38,6 +39,7 @@ const YoutubePlayer = forwardRef(
const containerRef = useRef(null);
const createPlayerRef = useRef<() => void>(null);
const progressInterval = useRef(null);
+ const intervalRef = useRef(interval);
const stopProgressTracking = useCallback(() => {
if (progressInterval.current) {
@@ -47,6 +49,10 @@ const YoutubePlayer = forwardRef(
}, []);
const startProgressTracking = useCallback(() => {
+ if (!intervalRef.current) {
+ return;
+ }
+
if (progressInterval.current) {
clearInterval(progressInterval.current);
}
@@ -73,7 +79,7 @@ const YoutubePlayer = forwardRef(
console.error('Progress tracking error:', error);
stopProgressTracking();
}
- }, 1000);
+ }, intervalRef.current);
}, [onProgress, stopProgressTracking]);
const loadYouTubeAPI = useCallback(() => {
@@ -229,6 +235,17 @@ const YoutubePlayer = forwardRef(
}
}, [videoId, createPlayer]);
+ useEffect(() => {
+ intervalRef.current = interval;
+
+ if (interval) {
+ startProgressTracking();
+ return;
+ }
+
+ stopProgressTracking();
+ }, [interval, startProgressTracking, stopProgressTracking]);
+
const play = useCallback(() => {
playerRef.current?.playVideo();
}, []);
diff --git a/src/hooks/useCreateLocalPlayerHtml.ts b/src/hooks/useCreateLocalPlayerHtml.ts
index 9212e9d..56a8c41 100644
--- a/src/hooks/useCreateLocalPlayerHtml.ts
+++ b/src/hooks/useCreateLocalPlayerHtml.ts
@@ -171,8 +171,21 @@ const useCreateLocalPlayerHtml = ({
},
setSize: (width, height) => player && player.setSize(width, height),
-
- cleanup: cleanup
+ updateProgressInterval: (newInterval) => {
+ const interval = Number(newInterval) > 0 ? Number(newInterval) : null;
+
+ window.currentInterval = interval;
+
+ if (progressInterval) {
+ clearInterval(progressInterval);
+ progressInterval = null;
+ }
+
+ if (interval && player && player.getPlayerState() === YT.PlayerState.PLAYING) {
+ startProgressTracking();
+ }
+ },
+ cleanup: cleanup,
};
window.addEventListener('message', function(event) {
diff --git a/src/hooks/youtubeIframeScripts.ts b/src/hooks/youtubeIframeScripts.ts
index 724bac3..0becb1a 100644
--- a/src/hooks/youtubeIframeScripts.ts
+++ b/src/hooks/youtubeIframeScripts.ts
@@ -1,6 +1,6 @@
const startProgressTracking = /* js */ `
function startProgressTracking() {
- if (isDestroyed) {
+ if (isDestroyed || typeof window.currentInterval !== 'number' || window.currentInterval <= 0) {
return;
}
@@ -31,7 +31,7 @@ const startProgressTracking = /* js */ `
console.error('Progress tracking error:', error);
stopProgressTracking();
}
- }, 1000);
+ }, window.currentInterval);
}
`;
diff --git a/src/types/message.ts b/src/types/message.ts
index 913f98c..f4a9be7 100644
--- a/src/types/message.ts
+++ b/src/types/message.ts
@@ -10,6 +10,31 @@ export type MessageType =
| 'autoplayBlocked'
| 'commandResult';
+export type CommandType =
+ | 'play'
+ | 'pause'
+ | 'stop'
+ | 'seekTo'
+ | 'setVolume'
+ | 'getVolume'
+ | 'mute'
+ | 'unMute'
+ | 'isMuted'
+ | 'getCurrentTime'
+ | 'getDuration'
+ | 'getVideoUrl'
+ | 'getVideoEmbedCode'
+ | 'getPlaybackRate'
+ | 'setPlaybackRate'
+ | 'getAvailablePlaybackRates'
+ | 'getPlayerState'
+ | 'getVideoLoadedFraction'
+ | 'loadVideoById'
+ | 'cueVideoById'
+ | 'setSize'
+ | 'cleanup'
+ | 'updateProgressInterval';
+
interface ReadyMessageData {
type: 'ready';
playerInfo: PlayerInfo;
diff --git a/src/types/youtube.ts b/src/types/youtube.ts
index f3198cb..3ca971d 100644
--- a/src/types/youtube.ts
+++ b/src/types/youtube.ts
@@ -22,6 +22,12 @@ export type YoutubePlayerProps = {
videoId: string;
width?: DimensionValue;
height?: DimensionValue;
+ /**
+ * @description The interval (in milliseconds) at which `onProgress` callback is called.
+ * Must be a positive number to enable progress tracking.
+ * If not provided or set to 0/falsy value, progress tracking is disabled.
+ */
+ progressInterval?: number;
style?: StyleProp;
/**
* @platform ios, android
@@ -40,6 +46,10 @@ export type YoutubePlayerProps = {
onReady?: (playerInfo: PlayerInfo) => void;
onStateChange?: (state: PlayerState) => void;
onError?: (error: YouTubeError) => void;
+ /**
+ * @description Callback function called at the specified `progressInterval`.
+ * Only invoked when `progressInterval` is provided as a positive number.
+ */
onProgress?: (progress: ProgressData) => void;
onPlaybackRateChange?: (playbackRate: number) => void;
onPlaybackQualityChange?: (quality: string) => void;