diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2e1768a..cb46d02 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -12,7 +12,8 @@ We want this community to be friendly and respectful to each other. Please follo
To get started with the project, run `bun` in the root directory to install the required dependencies for each package:
```sh
-bun
+bun install
+cd example && bun install
```
The [example app](/example/) demonstrates usage of the library. You need to run it to test any changes you make.
@@ -87,9 +88,9 @@ Our pre-commit hooks verify that your commit message matches this format when co
### Linting and tests
-[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)
+[biomejs](https://biomejs.dev/), [TypeScript](https://www.typescriptlang.org/)
-We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
+We use [TypeScript](https://www.typescriptlang.org/) for type checking, [biomejs](https://biomejs.dev/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
Our pre-commit hooks verify that the linter and tests pass when committing.
@@ -97,12 +98,6 @@ Our pre-commit hooks verify that the linter and tests pass when committing.
We use [changesets](https://github.com/changesets/changesets) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc.
-To publish new versions, run the following:
-
-```sh
-bun run release
-```
-
### Scripts
The `package.json` file contains various scripts for common tasks:
diff --git a/README-ko_kr.md b/README-ko_kr.md
new file mode 100644
index 0000000..aeb20b0
--- /dev/null
+++ b/README-ko_kr.md
@@ -0,0 +1,240 @@
+# React Native Youtube Bridge
+
+> [English](./README.md) | 한국어
+
+## 개요
+React Native에서 YouTube 플레이어를 사용하려면 복잡한 설정이 필요합니다.
+하지만 현재 지속적으로 유지보수되고 있는 React Native용 YouTube 플레이어 라이브러리가 없는 상황입니다. (가장 인기 있는 react-native-youtube-iframe의 [최근 릴리즈는 2023년 07월 02일](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
+
+`react-native-youtube-bridge`는 [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference)를 React Native에서 쉽게 사용할 수 있도록 도와주는 라이브러리입니다.
+
+- ✅ TypeScript 지원
+- ✅ iOS, Android, Web 플랫폼 지원
+- ✅ New Architecture 지원
+- ✅ YouTube 네이티브 플레이어 모듈 없이도 사용 가능
+- ✅ [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) 전체 기능 지원
+- ✅ 개발자 친화적인 API 제공
+- ✅ Expo 지원
+
+## 예제
+> 빠른 시작을 원하신다면 [예제](/example/)를 확인해보세요.
+
+- [웹 데모](https://react-native-youtube-bridge.pages.dev/)
+
+## 설치
+
+```bash
+npm install react-native-youtube-bridge
+
+pnpm add react-native-youtube-bridge
+
+yarn add react-native-youtube-bridge
+
+bun add react-native-youtube-bridge
+```
+
+## 사용법
+
+```tsx
+import { YoutubePlayer } from 'react-native-youtube-bridge';
+
+function App() {
+ return (
+
+ )
+}
+```
+
+### 이벤트
+YouTube iframe API의 상태 변화를 애플리케이션에 전달하기 위해 [이벤트](https://developers.google.com/youtube/iframe_api_reference#Events)를 발생시킵니다. 콜백 함수를 통해 원하는 이벤트를 구독할 수 있습니다.
+
+> 주의 - 성능 최적화 및 비정상 동작 방지를 위해 콜백 함수는 `useCallback`으로 감싸주세요.
+
+```tsx
+function App() {
+ const playerRef = useRef(null);
+
+ const handleReady = useCallback(() => {
+ console.log('플레이어 준비 완료!');
+ }, []);
+
+ const handleStateChange = useCallback((state: PlayerState) => {
+ console.log('플레이어 상태 변경:', state);
+ }, []);
+
+ const handlePlaybackRateChange = useCallback((rate: number) => {
+ console.log('재생 속도 변경:', rate);
+ }, []);
+
+ const handlePlaybackQualityChange = useCallback((quality: string) => {
+ console.log('재생 품질 변경:', quality);
+ }, []);
+
+ const handleAutoplayBlocked = useCallback(() => {
+ console.log('자동 재생이 차단되었습니다');
+ }, []);
+
+ const handleError = useCallback((error: YouTubeError) => {
+ console.error('플레이어 오류:', error);
+ }, []);
+
+ return (
+
+ )
+}
+```
+
+### 기능
+YouTube iframe API의 [함수들](https://developers.google.com/youtube/iframe_api_reference#Functions)을 `ref`를 통해 호출하여 음소거, 재생, 볼륨 조절 등 다양한 플레이어 기능을 제어할 수 있습니다.
+
+```tsx
+function App() {
+ const playerRef = useRef(null);
+
+ const [isPlaying, setIsPlaying] = useState(false);
+ const [currentTime, setCurrentTime] = useState(0);
+
+ const onPlay = useCallback(() => {
+ if (isPlaying) {
+ playerRef.current?.pause();
+ return;
+ }
+
+ playerRef.current?.play();
+ }, [isPlaying]);
+
+ const seekTo = useCallback((time: number, allowSeekAhead: boolean) => {
+ playerRef.current?.seekTo(time, allowSeekAhead);
+ }, []);
+
+ const stop = () => playerRef.current?.stop();
+
+ return (
+
+
+
+
+ seekTo(currentTime > 10 ? currentTime - 10 : 0)}
+ >
+ ⏪ -10초
+
+
+
+ {isPlaying ? '⏸️ 일시정지' : '▶️ 재생'}
+
+
+
+ ⏹️ 정지
+
+
+ seekTo(currentTime + 10, true)}
+ >
+ ⏭️ +10초
+
+
+
+ )
+}
+```
+
+### 플레이어 매개변수
+YouTube 내장 플레이어의 [매개변수](https://developers.google.com/youtube/player_parameters#Parameters)를 설정하여 재생 환경을 맞춤화할 수 있습니다.
+
+```tsx
+function App() {
+ return (
+
+ )
+}
+```
+
+### 스타일
+YouTube 플레이어의 스타일을 원하는 대로 커스터마이징할 수 있습니다.
+
+```tsx
+function App() {
+ return (
+
+ )
+}
+```
+
+### 유용한 기능
+
+#### 재생 진행률 추적
+
+```tsx
+function App() {
+ // 1초마다 호출되는 진행률 이벤트 콜백
+ const handleProgress = useCallback((progress: ProgressData) => {
+ setCurrentTime(progress.currentTime);
+ setDuration(progress.duration);
+ setLoadedFraction(progress.loadedFraction);
+ }, []);
+
+ return (
+
+ )
+}
+```
+
+## 기여하기
+
+리포지토리 기여 방법과 개발 워크플로우를 알아보려면 [기여 가이드](CONTRIBUTING.md)를 참고하세요.
+
+## 라이선스
+
+[MIT](./LICENSE)
diff --git a/README.md b/README.md
index 23c3468..e55edfc 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,235 @@
-# [WIP] react-native-youtube-bridge
+# React Native Youtube Bridge
-react-native-youtube
+> English | [한국어](./README-ko_kr.md)
+
+## Overview
+Using a YouTube player in React Native requires complex setup and configuration.
+However, there are currently no actively maintained YouTube player libraries for React Native. (The most popular react-native-youtube-iframe's [latest release was July 2, 2023](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
+
+`react-native-youtube-bridge` is a library that makes it easy to use the [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) in React Native applications.
+
+- ✅ TypeScript support
+- ✅ iOS, Android, and Web platform support
+- ✅ New Architecture support
+- ✅ Works without YouTube native player modules
+- ✅ Full [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) feature support
+- ✅ Developer-friendly API
+- ✅ Expo support
+
+## Example
+> For a quick start, check out the [example](/example/).
+
+- [Web Demo](https://react-native-youtube-bridge.pages.dev/)
## Installation
-```sh
+```bash
npm install react-native-youtube-bridge
+
+pnpm add react-native-youtube-bridge
+
+yarn add react-native-youtube-bridge
+
+bun add react-native-youtube-bridge
```
## Usage
+```tsx
+import { YoutubePlayer } from 'react-native-youtube-bridge';
+
+function App() {
+ return (
+
+ )
+}
+```
+
+### Events
+The library fires [events](https://developers.google.com/youtube/iframe_api_reference#Events) to notify your application of YouTube iframe API state changes. You can subscribe to these events using callback functions.
+
+> Note - Wrap callback functions with `useCallback` for performance optimization and to prevent abnormal behavior.
+
+```tsx
+function App() {
+ const playerRef = useRef(null);
+
+ const handleReady = useCallback(() => {
+ console.log('Player is ready!');
+ }, []);
+
+ const handleStateChange = useCallback((state: PlayerState) => {
+ console.log('Player state changed:', state);
+ }, []);
+
+ const handlePlaybackRateChange = useCallback((rate: number) => {
+ console.log('Playback rate changed:', rate);
+ }, []);
+
+ const handlePlaybackQualityChange = useCallback((quality: string) => {
+ console.log('Playback quality changed:', quality);
+ }, []);
+
+ const handleAutoplayBlocked = useCallback(() => {
+ console.log('Autoplay was blocked');
+ }, []);
+
+ const handleError = useCallback((error: YouTubeError) => {
+ console.error('Player error:', error);
+ }, []);
+
+ return (
+
+ )
+}
+```
+
+### Functions
+You can control various player features like mute, play, volume, and more by calling YouTube iframe API [functions](https://developers.google.com/youtube/iframe_api_reference#Functions) through the `ref`.
+
+```tsx
+function App() {
+ const playerRef = useRef(null);
+
+ const [isPlaying, setIsPlaying] = useState(false);
+ const [currentTime, setCurrentTime] = useState(0);
+
+ const onPlay = useCallback(() => {
+ if (isPlaying) {
+ playerRef.current?.pause();
+ return;
+ }
+
+ playerRef.current?.play();
+ }, [isPlaying]);
+
+ const seekTo = useCallback((time: number, allowSeekAhead: boolean) => {
+ playerRef.current?.seekTo(time, allowSeekAhead);
+ }, []);
+
+ const stop = () => playerRef.current?.stop();
+
+ return (
+
+
+
+
+ seekTo(currentTime > 10 ? currentTime - 10 : 0)}
+ >
+ ⏪ -10s
+
+
+
+ {isPlaying ? '⏸️ Pause' : '▶️ Play'}
+
+
+
+ ⏹️ Stop
+
+
+ seekTo(currentTime + 10, true)}
+ >
+ ⏭️ +10s
+
+
+
+ )
+}
+```
+
+### Player Parameters
+You can customize the playback environment by configuring YouTube embedded player [parameters](https://developers.google.com/youtube/player_parameters#Parameters).
+
+```tsx
+function App() {
+ return (
+
+ )
+}
+```
+
+### Styles
+You can customize the YouTube player's styling to match your application's design.
+
+```tsx
+function App() {
+ return (
+
+ )
+}
+```
+
+### Useful Features
+
+#### Playback Progress Tracking
+
+```tsx
+function App() {
+ // Progress event callback called every second
+ const handleProgress = useCallback((progress: ProgressData) => {
+ setCurrentTime(progress.currentTime);
+ setDuration(progress.duration);
+ setLoadedFraction(progress.loadedFraction);
+ }, []);
+
+ return (
+
+ )
+}
+```
## Contributing
@@ -17,4 +237,4 @@ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the
## License
-MIT
+[MIT](./LICENSE)
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 4bc9676..6298305 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -27,31 +27,29 @@ function App() {
const [isMuted, setIsMuted] = useState(false);
const [videoId, setVideoId] = useState('AbZH7XWDW_k');
- const handleReady = useCallback(async (playerInfo: PlayerInfo) => {
+ const handleReady = useCallback((playerInfo: PlayerInfo) => {
console.log('Player is ready!');
Alert.alert('알림', 'YouTube 플레이어가 준비되었습니다!');
// 플레이어 준비 완료 후 정보 가져오기
- try {
- console.log('rates', playerInfo.availablePlaybackRates);
- console.log('vol', playerInfo.volume);
- console.log('muted', playerInfo.muted);
-
- if (playerInfo.availablePlaybackRates) {
- setAvailableRates(playerInfo.availablePlaybackRates);
- }
- if (playerInfo.volume !== undefined) {
- setVolume(playerInfo.volume);
- }
- if (playerInfo.muted !== undefined) {
- setIsMuted(playerInfo.muted);
- }
- } catch (error) {
- console.error('Error getting player info:', error);
+ console.log('rates', playerInfo.availablePlaybackRates);
+ console.log('vol', playerInfo.volume);
+ console.log('muted', playerInfo.muted);
+
+ if (playerInfo?.availablePlaybackRates) {
+ setAvailableRates(playerInfo.availablePlaybackRates);
+ }
+
+ if (playerInfo?.volume !== undefined) {
+ setVolume(playerInfo.volume);
+ }
+
+ if (playerInfo?.muted !== undefined) {
+ setIsMuted(playerInfo.muted);
}
}, []);
- const handleStateChange = (state: PlayerState) => {
+ const handleStateChange = useCallback((state: PlayerState) => {
console.log('Player state changed:', state);
setIsPlaying(state === PlayerState.PLAYING);
@@ -75,7 +73,7 @@ function App() {
console.log('비디오가 큐에 준비되었습니다');
break;
}
- };
+ }, []);
const handleProgress = useCallback((progress: ProgressData) => {
setCurrentTime(progress.currentTime);