Skip to content

Commit b11261a

Browse files
authored
Merge pull request #2 from sheeshcake/fix/downloads-feature
Fix/downloads feature
2 parents 73f97ae + e11425b commit b11261a

27 files changed

+1445
-445
lines changed

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{
2+
}

App.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
import React from 'react';
2-
import {StatusBar} from 'react-native';
2+
import {StatusBar, View} from 'react-native';
33
import {GestureHandlerRootView} from 'react-native-gesture-handler';
4-
import {AppProvider} from './src/context';
4+
import {AppProvider, VideoPlayerProvider, useVideoPlayer} from './src/context';
55
import {AppNavigator} from './src/navigation';
6+
import {VideoPlayerSheet} from './src/components/VideoPlayerSheet';
7+
8+
const AppContent: React.FC = () => {
9+
const {playerState} = useVideoPlayer();
10+
11+
return (
12+
<View style={{flex: 1}}>
13+
<AppNavigator />
14+
{playerState.content && <VideoPlayerSheet />}
15+
</View>
16+
);
17+
};
618

719
function App(): React.JSX.Element {
820
return (
921
<GestureHandlerRootView style={{flex: 1}}>
1022
<AppProvider>
11-
<StatusBar barStyle="light-content" backgroundColor="#000000" />
12-
<AppNavigator />
23+
<VideoPlayerProvider>
24+
<StatusBar barStyle="light-content" backgroundColor="#000000" />
25+
<AppContent />
26+
</VideoPlayerProvider>
1327
</AppProvider>
1428
</GestureHandlerRootView>
1529
);

YOUTUBE_STYLE_IMPLEMENTATION.md

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
# YouTube-Style Video Player Implementation
2+
3+
## Overview
4+
Successfully implemented a YouTube-style draggable video player with mini player functionality, exactly matching the gesture behavior from the reference repository (https://github.com/guneyural/youtube-clone).
5+
6+
## Key Features
7+
8+
### 1. Pan Gesture Video Player
9+
- **Drag Down**: Minimizes video to mini player at bottom-right
10+
- **Drag Up**: Expands mini player back to full screen
11+
- **Swipe Down Further**: Closes player completely
12+
- **Tap Mini Player**: Expands to full screen
13+
14+
### 2. Smooth Animations
15+
All animations use spring physics for natural feel:
16+
- Video height: 33% screen → 150px → 60px (mini)
17+
- Video width: 100% → 30%
18+
- Mini player slides in from right
19+
- Content opacity fades during drag
20+
- Position interpolates smoothly
21+
22+
### 3. Mini Player Features
23+
- Muted video thumbnail continues playing
24+
- Shows content title (truncated if long)
25+
- Displays episode info for TV shows (S1 E1)
26+
- Play/pause button
27+
- Close button
28+
- Tap anywhere to expand
29+
30+
### 4. Gesture Thresholds
31+
- Drag > 100px: Minimizes to mini player
32+
- Drag < 100px: Snaps back to full screen
33+
- Drag > screen height - 80px: Closes completely
34+
35+
### 5. Detail Screen Content
36+
- Full video player with controls
37+
- Content title and like button
38+
- Overview/description
39+
- Episodes list (for TV shows)
40+
- Similar content recommendations
41+
- Video scraping integration
42+
43+
### 6. Back Button Handling
44+
- When expanded: Minimizes to mini player
45+
- When minimized: Handled by navigation
46+
47+
## Architecture
48+
49+
### New Files Created
50+
51+
#### 1. `src/context/VideoPlayerContext.tsx`
52+
Global state management for video player:
53+
```typescript
54+
interface VideoPlayerState {
55+
content: Movie | TVShow | null;
56+
videoUrl: string;
57+
isPlaying: boolean;
58+
currentTime: number;
59+
duration: number;
60+
selectedSeason?: number;
61+
selectedEpisode?: number;
62+
}
63+
```
64+
65+
**Methods:**
66+
- `openDetailSheet(content)` - Opens player with content
67+
- `closeDetailSheet()` - Closes player and clears state
68+
- `setVideoUrl(url)` - Updates video URL
69+
- `setPlaybackState(isPlaying, currentTime, duration)` - Syncs playback
70+
- `setEpisodeInfo(season, episode)` - Sets episode info
71+
72+
#### 2. `src/components/VideoPlayerSheet/VideoPlayerSheet.tsx`
73+
Main video player component with gestures:
74+
- Pan gesture handler for drag interactions
75+
- Animated interpolations for smooth transitions
76+
- Video player integration
77+
- Content details display
78+
- Episode selection
79+
- Similar content recommendations
80+
81+
#### 3. `src/components/VideoPlayerSheet/index.tsx`
82+
Export file for the component
83+
84+
### Modified Files
85+
86+
#### 1. `App.tsx`
87+
- Added `VideoPlayerProvider` wrapper
88+
- Added `AppContent` component to access video player context
89+
- Conditionally renders `VideoPlayerSheet` when content is selected
90+
91+
#### 2. `src/context/index.ts`
92+
- Exported `VideoPlayerProvider` and `useVideoPlayer`
93+
94+
#### 3. `src/screens/HomeScreen.tsx`
95+
- Uses `openDetailSheet()` instead of `navigation.navigate('Detail')`
96+
- Removed Detail screen navigation
97+
98+
#### 4. `src/screens/SearchScreen.tsx`
99+
- Uses `openDetailSheet()` instead of `navigation.navigate('Detail')`
100+
- Removed Detail screen navigation
101+
102+
#### 5. `src/navigation/AppNavigator.tsx`
103+
- Removed Detail screen from navigation stack
104+
- Detail content now opens via overlay instead of navigation
105+
106+
#### 6. `src/types/navigation.ts`
107+
- Removed `Detail` route from `RootStackParamList`
108+
109+
## Technical Implementation
110+
111+
### Gesture Handling
112+
Uses `react-native-gesture-handler` Gesture API:
113+
```typescript
114+
const panGesture = Gesture.Pan()
115+
.onStart(() => {
116+
context.value = {y: translationY.value};
117+
})
118+
.onUpdate(event => {
119+
translationY.value = event.translationY + context.value.y;
120+
translationY.value = Math.max(0, translationY.value);
121+
})
122+
.onEnd(() => {
123+
// Snap to positions based on threshold
124+
});
125+
```
126+
127+
### Animation Interpolations
128+
Uses `react-native-reanimated` for 60fps animations:
129+
130+
**Video Height:**
131+
```typescript
132+
interpolate(
133+
translationY,
134+
[0, SCREEN_HEIGHT - 300, SCREEN_HEIGHT - 80],
135+
[VIDEO_HEIGHT, 150, MINI_PLAYER_HEIGHT],
136+
Extrapolation.CLAMP
137+
)
138+
```
139+
140+
**Video Width:**
141+
```typescript
142+
interpolate(
143+
translationY,
144+
[0, 500, SCREEN_HEIGHT - 80],
145+
[100, 100, 30], // percentages
146+
Extrapolation.CLAMP
147+
)
148+
```
149+
150+
**Mini Player Position:**
151+
```typescript
152+
interpolate(
153+
translationY,
154+
[0, 500, SCREEN_HEIGHT - 80],
155+
[500, 500, 125], // translateX values
156+
Extrapolation.CLAMP
157+
)
158+
```
159+
160+
**Opacity:**
161+
```typescript
162+
interpolate(
163+
translationY,
164+
[0, 350, 600],
165+
[1, 0.5, 0],
166+
Extrapolation.CLAMP
167+
)
168+
```
169+
170+
### State Synchronization
171+
- Video playback state syncs between full player and mini player
172+
- Episode info persists during minimize/maximize
173+
- Video URL maintained across transitions
174+
- Playback continues seamlessly
175+
176+
## Usage
177+
178+
### Opening Content
179+
```typescript
180+
const {openDetailSheet} = useVideoPlayer();
181+
182+
// On content card press
183+
openDetailSheet(movieOrTVShow);
184+
```
185+
186+
### User Interactions
187+
1. **View Content**: Tap any movie/TV show card
188+
2. **Minimize**: Drag video player down
189+
3. **Expand**: Drag mini player up or tap it
190+
4. **Close**: Swipe mini player down or press close button
191+
5. **Browse**: Continue browsing with mini player visible
192+
6. **Select Episode**: Tap episode to play (TV shows)
193+
194+
## Benefits
195+
196+
1. **Better UX**: Browse while video plays
197+
2. **Familiar Pattern**: YouTube-like interaction
198+
3. **Smooth Performance**: 60fps animations
199+
4. **Gesture-Based**: Intuitive swipe controls
200+
5. **Seamless Playback**: Video continues during transitions
201+
6. **Episode Support**: Easy episode switching for TV shows
202+
7. **Content Discovery**: Similar content recommendations
203+
204+
## Dependencies Used
205+
206+
- `react-native-gesture-handler`: Gesture recognition
207+
- `react-native-reanimated`: High-performance animations
208+
- `react-native-video`: Video playback
209+
- `@react-native-vector-icons/material-icons`: Icons
210+
211+
## Testing
212+
213+
To test the implementation:
214+
1. Run the app
215+
2. Tap any content card from home or search
216+
3. Video player sheet slides up
217+
4. Drag down to minimize
218+
5. Drag up or tap to expand
219+
6. Swipe down far to close
220+
7. Try with TV shows to test episode selection
221+
222+
## Notes
223+
224+
- Mini player shows muted video thumbnail
225+
- Full player has audio and controls
226+
- Back button minimizes when expanded
227+
- Video scraping happens automatically
228+
- Similar content loads in background
229+
- Episode list shows first 5 episodes
230+
231+
## Future Enhancements
232+
233+
Possible improvements:
234+
- Full episode list with seasons selector
235+
- Picture-in-picture mode
236+
- Playlist/queue functionality
237+
- Swipe between episodes
238+
- Download progress in mini player
239+
- Cast button integration
240+
- Subtitle selector in mini player

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ android {
8383
minSdkVersion rootProject.ext.minSdkVersion
8484
targetSdkVersion rootProject.ext.targetSdkVersion
8585
versionCode 1
86-
versionName "1.0.1"
86+
versionName "1.0.2"
8787
}
8888
signingConfigs {
8989
debug {

android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
22

33
<uses-permission android:name="android.permission.INTERNET" />
4+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
45

56
<application
67
android:name=".MainApplication"

android/app/src/main/java/com/flickv4/MainApplication.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.facebook.react.ReactApplication
66
import com.facebook.react.ReactHost
77
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
88
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
9+
// import com.eko.RNBackgroundDownloaderTurboPackage;
910

1011
class MainApplication : Application(), ReactApplication {
1112

@@ -16,6 +17,7 @@ class MainApplication : Application(), ReactApplication {
1617
PackageList(this).packages.apply {
1718
// Packages that cannot be autolinked yet can be added manually here, for example:
1819
// add(MyReactNativePackage())
20+
// add(RNBackgroundDownloaderTurboPackage())
1921
},
2022
)
2123
}

package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"react-native-linear-gradient": "^2.8.3",
3535
"react-native-orientation-locker": "^1.7.0",
3636
"react-native-reanimated": "^4.1.5",
37+
"react-native-reanimated-carousel": "^4.0.3",
3738
"react-native-safe-area-context": "^5.5.2",
3839
"react-native-screens": "^4.18.0",
3940
"react-native-svg": "^15.14.0",

src/components/ContinueWatchingCard.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ const ContinueWatchingCard: React.FC<ContinueWatchingCardProps> = ({
4646

4747
const cardDimensions = getCardDimensions();
4848

49-
// Fetch content details
5049
useEffect(() => {
5150
const fetchContentDetails = async () => {
5251
try {

src/components/DownloadComponents.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export const DownloadButton: React.FC<DownloadButtonProps> = ({
165165

166166
const contentType = 'title' in content ? 'movie' : 'tv';
167167
const isDownloaded = downloadService.isContentDownloaded(
168-
content.id,
168+
content?.id,
169169
contentType,
170170
season,
171171
episode
@@ -691,19 +691,18 @@ export const DownloadsList: React.FC<DownloadsListProps> = ({
691691
Season {download.season}, Episode {download.episode}
692692
</Text>
693693
)}
694-
695694
<View style={styles.downloadMeta}>
696695
<View style={[styles.statusBadge, { backgroundColor: getStatusColor(download.status) }]}>
697696
<Text style={styles.statusText}>{getStatusText(download.status)}</Text>
698697
</View>
699698

700699
<Text style={styles.quality}>{download.quality}</Text>
701700

702-
{download.totalSize && (
701+
{/* {download.totalSize && (
703702
<Text style={styles.fileSize}>
704703
{formatFileSize(download.totalSize)}
705704
</Text>
706-
)}
705+
)} */}
707706
</View>
708707

709708
{download.status === DownloadStatus.DOWNLOADING && (

0 commit comments

Comments
 (0)