diff --git a/android/app/build.gradle b/android/app/build.gradle index 76a6412..1fca73a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -86,8 +86,8 @@ android { applicationId "com.vega" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 45 - versionName "2.2.2" + versionCode 46 + versionName "2.2.3" } signingConfigs { release { diff --git a/package.json b/package.json index d5d8cea..f9c4bd6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vega", - "version": "2.2.2", + "version": "2.2.3", "private": true, "scripts": { "android": "react-native run-android", diff --git a/src/App.tsx b/src/App.tsx index 32f1e9c..ebda54e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,6 +21,7 @@ import About, {checkForUpdate} from './screens/settings/About'; import {MMKV} from './lib/Mmkv'; import BootSplash from 'react-native-bootsplash'; import {enableFreeze, enableScreens} from 'react-native-screens'; +import Preferences from './screens/settings/Preference'; enableScreens(true); enableFreeze(true); @@ -60,6 +61,7 @@ export type SettingsStackParamList = { Settings: undefined; DisableProviders: undefined; About: undefined; + Preferences: undefined; }; const Tab = createBottomTabNavigator(); const App = () => { @@ -135,6 +137,7 @@ const App = () => { component={DisableProviders} /> + ); } @@ -208,7 +211,7 @@ const App = () => { useEffect(() => { if (MMKV.getBool('autoCheckUpdate') !== false) { - checkForUpdate(() => {}, MMKV.getBool('autoDownload') || false); + checkForUpdate(() => {}, MMKV.getBool('autoDownload') || false, false); } }, []); diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index c5393a3..c673ff8 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -76,17 +76,24 @@ function Hero() { setSearchActive(false)} autoFocus={true} onSubmitEditing={e => { - searchNavigation.navigate('ScrollList', { - providerValue: provider.value, - filter: 'query' + e.nativeEvent.text, - title: `${provider.name}`, - }); + if (e.nativeEvent.text.includes('https://')) { + navigation.navigate('Info', { + link: e.nativeEvent.text, + }); + } else { + searchNavigation.navigate('ScrollList', { + providerValue: provider.value, + filter: 'query' + e.nativeEvent.text, + title: `${provider.name}`, + }); + } }} placeholder={`Search in ${provider.name}`} className="w-[95%] px-4 h-10 rounded-full border-white border" diff --git a/src/components/SeasonList.tsx b/src/components/SeasonList.tsx index 23881a1..181ec33 100644 --- a/src/components/SeasonList.tsx +++ b/src/components/SeasonList.tsx @@ -25,6 +25,7 @@ import {manifest} from '../lib/Manifest'; import SharedGroupPreferences from 'react-native-shared-group-preferences'; import RNReactNativeHapticFeedback from 'react-native-haptic-feedback'; import Feather from '@expo/vector-icons/Feather'; +import useWatchHistoryStore from '../lib/zustand/watchHistrory'; const SeasonList = ({ LinkList, @@ -32,12 +33,18 @@ const SeasonList = ({ metaTitle, providerValue, refreshing, + routeParams, }: { LinkList: Link[]; poster: string; metaTitle: string; providerValue: string; refreshing?: boolean; + routeParams: Readonly<{ + link: string; + provider?: string; + poster?: string; + }>; }) => { const navigation = useNavigation>(); @@ -57,6 +64,8 @@ const SeasonList = ({ LinkList[0], ); + const {addItem} = useWatchHistoryStore(state => state); + useEffect(() => { const fetchList = async () => { if (!ActiveSeason?.episodesLink) { @@ -158,7 +167,14 @@ const SeasonList = ({ return; } }; + const playHandler = async ({link, type, title, file}: playHandlerProps) => { + addItem({ + link: routeParams.link, + title: metaTitle, + image: routeParams.poster!, + provider: providerValue, + }); const externalPlayer = MMKV.getString('externalPlayer'); const downloaded = await ifExists(file); if (externalPlayer && !downloaded) { diff --git a/src/components/Slider.tsx b/src/components/Slider.tsx index e3497b8..c122ed1 100644 --- a/src/components/Slider.tsx +++ b/src/components/Slider.tsx @@ -1,13 +1,15 @@ -import {Image, Text, TouchableOpacity, View} from 'react-native'; +import {Image, Pressable, Text, TouchableOpacity, View} from 'react-native'; import React from 'react'; import type {Post} from '../lib/providers/types'; import {NativeStackNavigationProp} from '@react-navigation/native-stack'; import {useNavigation} from '@react-navigation/native'; import {HomeStackParamList} from '../App'; -import {Skeleton} from 'moti/skeleton'; import useContentStore from '../lib/zustand/contentStore'; import {FlashList} from '@shopify/flash-list'; import SkeletonLoader from './Skeleton'; +import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; +import AntDesign from '@expo/vector-icons/AntDesign'; +import useWatchHistoryStore from '../lib/zustand/watchHistrory'; export default function Slider({ isLoading, @@ -25,21 +27,25 @@ export default function Slider({ const {provider} = useContentStore(state => state); const navigation = useNavigation>(); + const [isSelected, setSelected] = React.useState(''); + const {removeItem} = useWatchHistoryStore(state => state); return ( - + setSelected('')} className="gap-3 mt-7"> {title} - - navigation.navigate('ScrollList', { - title: title, - filter: filter, - providerValue: providerValue, - }) - }> - more - + {filter && ( + + navigation.navigate('ScrollList', { + title: title, + filter: filter, + providerValue: providerValue, + }) + }> + more + + )} {isLoading ? ( @@ -57,18 +63,29 @@ export default function Slider({ estimatedItemSize={30} showsHorizontalScrollIndicator={false} data={posts} + extraData={isSelected} horizontal contentContainerStyle={{paddingHorizontal: 3, paddingTop: 7}} renderItem={({item}) => ( + onLongPress={e => { + e.stopPropagation(); + ReactNativeHapticFeedback.trigger('effectClick', { + enableVibrateFallback: true, + ignoreAndroidSystemSettings: false, + }); + setSelected(item.link); + }} + onPress={e => { + e.stopPropagation(); + setSelected(''); navigation.navigate('Info', { link: item.link, - provider: providerValue || provider?.value, + provider: item.provider || providerValue || provider?.value, poster: item?.image, - }) - }> + }); + }}> + {isSelected === item.link && ( + + { + console.log('remove', item); + setSelected(''); + removeItem(item); + }} + /> + + )} {item.title.length > 24 @@ -98,6 +129,6 @@ export default function Slider({ keyExtractor={item => item.link} /> )} - + ); } diff --git a/src/lib/Manifest.ts b/src/lib/Manifest.ts index 7e078de..7fb2b5d 100644 --- a/src/lib/Manifest.ts +++ b/src/lib/Manifest.ts @@ -16,7 +16,7 @@ import {modGetEpisodeLinks} from './providers/mod/modGetEpisodesList'; import {modGetStream} from './providers/mod/modGetStream'; /// uhd -import {uhdCatalogList} from './providers/uhd/uhCtatalog'; +import {uhdCatalogList, uhdGenresList} from './providers/uhd/uhCtatalog'; import {uhdGetPosts} from './providers/uhd/uhdGetPosts'; import getUhdInfo from './providers/uhd/getUhdInfo'; import {uhdGetStream} from './providers/uhd/uhdGetStream'; @@ -150,7 +150,7 @@ export const manifest: Manifest = { }, uhd: { catalog: uhdCatalogList, - genres: [], + genres: uhdGenresList, blurImage: true, nonStreamableServer: ['Gdrive-Instant'], getStream: uhdGetStream, diff --git a/src/lib/getHomepagedata.ts b/src/lib/getHomepagedata.ts index eb6b5f4..d9c9b56 100644 --- a/src/lib/getHomepagedata.ts +++ b/src/lib/getHomepagedata.ts @@ -1,11 +1,6 @@ import {Content} from './zustand/contentStore'; import {manifest} from './Manifest'; - -export interface Post { - title: string; - link: string; - image: string; -} +import {Post} from './providers/types'; export interface HomePageData { title: string; diff --git a/src/lib/providers/dramacool/dcGetPosts.ts b/src/lib/providers/dramacool/dcGetPosts.ts index 0fea4db..696fdf5 100644 --- a/src/lib/providers/dramacool/dcGetPosts.ts +++ b/src/lib/providers/dramacool/dcGetPosts.ts @@ -12,10 +12,11 @@ export const dcGetPosts = async function ( ): Promise { try { const urlRes = await axios.get( - 'https://consumet8.vercel.app/movies/dramacool/info?id=drama-detail/shogun', + 'https://himanshu8443.github.io/providers/modflix.json', ); - const resData = urlRes.data.episodes[0].url; - const baseUrl = resData.split('/').slice(0, 3).join('/'); + const dataRes = urlRes.data; + // console.log(dataRes.hdhub.url); + const baseUrl = dataRes?.dc?.url; console.log('dcBaseUrl', baseUrl); const url = filter.includes('query') ? `${baseUrl}/search?type=movies&keyword=${filter.replace( diff --git a/src/lib/providers/mod/catalog.ts b/src/lib/providers/mod/catalog.ts index 53a07bd..ba79f18 100644 --- a/src/lib/providers/mod/catalog.ts +++ b/src/lib/providers/mod/catalog.ts @@ -18,6 +18,22 @@ export const catalogList = [ ]; export const modGenresList = [ + { + title: 'Apple TV+', + filter: '/ott/apple-tv', + }, + { + title: 'Disney+', + filter: '/ott/disney-plus', + }, + { + title: 'Hulu', + filter: '/ott/hulu', + }, + { + title: 'Crunchyroll', + filter: '/ott/crunchyroll', + }, { title: 'Action', filter: '/movies-by-genre/action/', diff --git a/src/lib/providers/mod/header.ts b/src/lib/providers/mod/header.ts index fe0c37d..f6a6dcf 100644 --- a/src/lib/providers/mod/header.ts +++ b/src/lib/providers/mod/header.ts @@ -10,7 +10,8 @@ export const headers = { 'sec-ch-ua-platform': '"Windows"', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', - Referer: 'https://moviesmod.live/', + Referer: 'https://moviesmod.band/', + Cookie: 'popads_user_id=6ba8fe60a481387a3249f05aa058822d', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', 'Upgrade-Insecure-Requests': '1', diff --git a/src/lib/providers/mod/modGetStream.ts b/src/lib/providers/mod/modGetStream.ts index bb68161..d925faf 100644 --- a/src/lib/providers/mod/modGetStream.ts +++ b/src/lib/providers/mod/modGetStream.ts @@ -74,42 +74,46 @@ export const modGetStream = async ( const driveRes = await axios.get(driveLink, {headers}); const driveHtml = driveRes.data; const $drive = cheerio.load(driveHtml); - const resumeBot = $drive('.btn.btn-light').attr('href') || ''; - const resumeBotRes = await axios.get(resumeBot, {headers}); - const resumeBotToken = resumeBotRes.data.match( - /formData\.append\('token', '([a-f0-9]+)'\)/, - )[1]; - const resumeBotBody = new FormData(); - resumeBotBody.append('token', resumeBotToken); - const resumeBotPath = resumeBotRes.data.match( - /fetch\('\/download\?id=([a-zA-Z0-9\/+]+)'/, - )[1]; - const resumeBotBaseUrl = resumeBot.split('/download')[0]; - // console.log( - // 'resumeBotPath', - // resumeBotBaseUrl + '/download?id=' + resumeBotPath, - // ); - // console.log('resumeBotBody', resumeBotToken); - const resumeBotDownload = await fetch( - resumeBotBaseUrl + '/download?id=' + resumeBotPath, - { - method: 'POST', - body: resumeBotBody, - headers: { - Referer: resumeBot, - Cookie: 'PHPSESSID=7e9658ce7c805dab5bbcea9046f7f308', - }, - }, - ); - const resumeBotDownloadData = await resumeBotDownload.json(); - console.log('resumeBotDownloadData', resumeBotDownloadData.url); - servers.push({ - server: 'ResumeBot', - link: resumeBotDownloadData.url, - type: 'mkv', - }); + try { + const resumeBot = $drive('.btn.btn-light').attr('href') || ''; + const resumeBotRes = await axios.get(resumeBot, {headers}); + const resumeBotToken = resumeBotRes.data.match( + /formData\.append\('token', '([a-f0-9]+)'\)/, + )[1]; + const resumeBotBody = new FormData(); + resumeBotBody.append('token', resumeBotToken); + const resumeBotPath = resumeBotRes.data.match( + /fetch\('\/download\?id=([a-zA-Z0-9\/+]+)'/, + )[1]; + const resumeBotBaseUrl = resumeBot.split('/download')[0]; + // console.log( + // 'resumeBotPath', + // resumeBotBaseUrl + '/download?id=' + resumeBotPath, + // ); + // console.log('resumeBotBody', resumeBotToken); + const resumeBotDownload = await fetch( + resumeBotBaseUrl + '/download?id=' + resumeBotPath, + { + method: 'POST', + body: resumeBotBody, + headers: { + Referer: resumeBot, + Cookie: 'PHPSESSID=7e9658ce7c805dab5bbcea9046f7f308', + }, + }, + ); + const resumeBotDownloadData = await resumeBotDownload.json(); + console.log('resumeBotDownloadData', resumeBotDownloadData.url); + servers.push({ + server: 'ResumeBot', + link: resumeBotDownloadData.url, + type: 'mkv', + }); + } catch (err) { + console.log('ResumeBot link not found', err); + } // CF workers type 1 try { const cfWorkersLink = driveLink.replace('/file', '/wfile') + '?type=1'; @@ -199,7 +203,7 @@ const isDriveLink = async (ddl: string) => { /window\.location\.replace\("([^"]+)"\)/, )[1]; const mainUrl = ddl.split('/')[2]; - console.log(`https://${mainUrl}${path}`); + console.log(`driveUrl = https://${mainUrl}${path}`); return `https://${mainUrl}${path}`; } else { return ddl; diff --git a/src/lib/providers/multi/multiGetStream.ts b/src/lib/providers/multi/multiGetStream.ts index ed33c1e..4eb3a69 100644 --- a/src/lib/providers/multi/multiGetStream.ts +++ b/src/lib/providers/multi/multiGetStream.ts @@ -34,30 +34,69 @@ export const multiGetStream = async ( }); const playerData = await playerRes.json(); console.log('playerData', playerData); - const ifameUrl = + let ifameUrl = playerData?.embed_url?.match(/]+src="([^"]+)"[^>]*>/i)?.[1] || playerData?.embed_url; console.log('ifameUrl', ifameUrl); + if (!ifameUrl.includes('multimovies')) { + const iframeRes = await axios.get(ifameUrl, {headers}); + const $$ = cheerio.load(iframeRes.data); + let newIframeUrl = $$('li[data-sourceKey="smwh"]').attr('data-link'); + if (newIframeUrl) { + ifameUrl = newIframeUrl; + } + } const iframeRes = await axios.get(ifameUrl, {headers}); const iframeData = iframeRes.data; - const streamUrl = iframeData?.match(/file:\s*"([^"]+\.m3u8[^"]*)"/)?.[1]; + + // Step 1: Extract the function parameters and the encoded string + var functionRegex = + /eval\(function\((.*?)\)\{.*?return p\}.*?\('(.*?)'\.split/; + var match = functionRegex.exec(iframeData); + let p = ''; + if (match) { + var params = match[1].split(',').map(param => param.trim()); + var encodedString = match[2]; + + console.log('Parameters:', params); + // console.log('Encoded String:', encodedString.split("',36,")[0], '🔥🔥'); + + p = encodedString.split("',36,")?.[0].trim(); + let a = 36; + let c = encodedString.split("',36,")[1].slice(2).split('|').length; + let k = encodedString.split("',36,")[1].slice(2).split('|'); + + while (c--) { + if (k[c]) { + var regex = new RegExp('\\b' + c.toString(a) + '\\b', 'g'); + p = p.replace(regex, k[c]); + } + } + + // console.log('Decoded String:', p); + } else { + console.log('No match found'); + } + + const streamUrl = p?.match(/file:\s*"([^"]+\.m3u8[^"]*)"/)?.[1]; const subtitles: { lang: string; url: string; }[] = []; - const subtitleMatch = iframeData?.match(/https:\/\/[^\s"]+\.vtt/g); + const subtitleMatch = p?.match(/https:\/\/[^\s"]+\.vtt/g); // console.log('subtitleMatch', subtitleMatch); - if (subtitleMatch?.length > 0) { + if (subtitleMatch?.length) { subtitleMatch.forEach((sub: any) => { const lang = sub.match(/_([a-zA-Z]{3})\.vtt$/)[1]; subtitles.push({lang: lang, url: sub}); }); } - // console.log('streamUrl', subtitles); + console.log('streamUrl', streamUrl); + console.log('newUrl', streamUrl?.replace(/&i=\d+,'\.4&/, '&i=0.4&')); if (streamUrl) { streamLinks.push({ server: 'Multi', - link: streamUrl, + link: streamUrl.replace(/&i=\d+,'\.4&/, '&i=0.4&'), type: 'm3u8', subtitles: subtitles, }); diff --git a/src/lib/providers/types.ts b/src/lib/providers/types.ts index 8c5ae23..f83f6fc 100644 --- a/src/lib/providers/types.ts +++ b/src/lib/providers/types.ts @@ -3,6 +3,7 @@ export interface Post { title: string; link: string; image: string; + provider?: string; } // getStream diff --git a/src/lib/providers/uhd/uhCtatalog.ts b/src/lib/providers/uhd/uhCtatalog.ts index fa2b124..50fa4f5 100644 --- a/src/lib/providers/uhd/uhCtatalog.ts +++ b/src/lib/providers/uhd/uhCtatalog.ts @@ -17,4 +17,21 @@ export const uhdCatalogList = [ }, ]; -export const uhdGenresList = []; +export const uhdGenresList = [ + { + title: '4K HEVC', + filter: '/2160p-hevc', + }, + { + title: 'HD 10bit', + filter: '/1080p-10bit', + }, + { + title: 'English Movies', + filter: '/movies/english-movies', + }, + { + title: 'Dual Audio', + filter: '/movies/dual-audio-movies', + }, +]; diff --git a/src/lib/providers/vega/header.ts b/src/lib/providers/vega/header.ts index e2daf7a..157e55b 100644 --- a/src/lib/providers/vega/header.ts +++ b/src/lib/providers/vega/header.ts @@ -12,6 +12,8 @@ export const headers = { 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', + Cookie: + '_lscache_vary=ee6da9b0889c5478e05dcac864810b28; cf_clearance=MykCU0GeV7b1PSR0hFBwPHQX3zT0gBoq8MpsAExRTAg-1722511539-1.0.1.1-GROEaFc9_8OD8sUDs6eNIYmBc6pyhs4jiGUu6bpHlsUTvxYYWS.aOV3TftZTi05_YNwEmLyuUjd._tk0siURdw', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0', diff --git a/src/lib/zustand/herostore.ts b/src/lib/zustand/herostore.ts index d75fe7f..7e66091 100644 --- a/src/lib/zustand/herostore.ts +++ b/src/lib/zustand/herostore.ts @@ -1,5 +1,5 @@ import {create} from 'zustand'; -import {Post} from '../providers/vega/getPosts'; +import {Post} from '../providers/types'; export interface Hero { hero: Post; diff --git a/src/lib/zustand/watchHistrory.ts b/src/lib/zustand/watchHistrory.ts new file mode 100644 index 0000000..7c2b106 --- /dev/null +++ b/src/lib/zustand/watchHistrory.ts @@ -0,0 +1,35 @@ +import {create} from 'zustand'; +import {Post} from '../providers/types'; +import {MMKV} from '../Mmkv'; +export interface History { + history: Post[]; + removeItem: (item: Post) => void; + addItem: (item: Post) => void; + clearHistory: () => void; +} +const showWatchHistory = MMKV.getBool('showRecentlyWatched'); + +const useWatchHistoryStore = create(set => ({ + history: JSON.parse(MMKV.getString('recentlyWatched') || '[]'), + removeItem: item => { + const history = JSON.parse(MMKV.getString('recentlyWatched') || '[]'); + const newHistory = history.filter((i: Post) => i.link !== item.link); + MMKV.setString('recentlyWatched', JSON.stringify(newHistory)); + set({history: newHistory}); + }, + addItem: item => { + if (showWatchHistory) { + const history = JSON.parse(MMKV.getString('recentlyWatched') || '[]'); + const newHistory = history.filter((i: Post) => i.link !== item.link); + newHistory.unshift(item); + MMKV.setString('recentlyWatched', JSON.stringify(newHistory)); + set({history: newHistory}); + } + }, + clearHistory: () => { + MMKV.setString('recentlyWatched', '[]'); + set({history: []}); + }, +})); + +export default useWatchHistoryStore; diff --git a/src/screens/home/Home.tsx b/src/screens/home/Home.tsx index 15fb762..348891b 100644 --- a/src/screens/home/Home.tsx +++ b/src/screens/home/Home.tsx @@ -9,7 +9,7 @@ import React, {useEffect, useState} from 'react'; import Hero from '../../components/Hero'; import {View} from 'moti'; import {getHomePageData, HomePageData} from '../../lib/getHomepagedata'; -import {MmmkvCache} from '../../lib/Mmkv'; +import {MMKV, MmmkvCache} from '../../lib/Mmkv'; import useContentStore from '../../lib/zustand/contentStore'; import useHeroStore from '../../lib/zustand/herostore'; import {manifest} from '../../lib/Manifest'; @@ -17,6 +17,7 @@ import notifee, {EventDetail, EventType} from '@notifee/react-native'; import RNFS from 'react-native-fs'; import useDownloadsStore from '../../lib/zustand/downloadsStore'; import {FFmpegKit} from 'ffmpeg-kit-react-native'; +import useWatchHistoryStore from '../../lib/zustand/watchHistrory'; const Home = () => { const [refreshing, setRefreshing] = useState(false); @@ -24,6 +25,8 @@ const Home = () => { const [loading, setLoading] = useState(true); const [backgroundColor, setBackgroundColor] = useState('transparent'); const downloadStore = useDownloadsStore(state => state); + const recentlyWatched = useWatchHistoryStore(state => state).history; + const ShowRecentlyWatched = MMKV.getBool('showRecentlyWatched'); const {provider} = useContentStore(state => state); const {setHero} = useHeroStore(state => state); @@ -142,6 +145,14 @@ const Home = () => { }> + {!loading && recentlyWatched?.length > 0 && ShowRecentlyWatched && ( + + )} {loading ? manifest[provider.value].catalog.map((item, index) => ( ( + className="text-white text-xs bg-tertiary p-1 rounded-sm"> {actor} ))} {info?.cast?.slice(0, 3).map((actor: string) => ( + className="text-white text-xs bg-tertiary p-1 rounded-sm"> {actor} ))} @@ -274,7 +274,7 @@ export default function Info({route, navigation}: Props): React.JSX.Element { Synopsis - + {route.params.provider || provider.value} @@ -308,7 +308,7 @@ export default function Info({route, navigation}: Props): React.JSX.Element { - + {meta?.description ? meta?.description.length > 180 ? meta?.description.slice(0, 180) + '...' @@ -365,6 +365,7 @@ export default function Info({route, navigation}: Props): React.JSX.Element { } poster={meta?.logo || ''} metaTitle={meta?.name || info?.title} + routeParams={route.params} /> )} diff --git a/src/screens/settings/About.tsx b/src/screens/settings/About.tsx index ff932af..01318ee 100644 --- a/src/screens/settings/About.tsx +++ b/src/screens/settings/About.tsx @@ -92,6 +92,7 @@ const downloadUpdate = async (url: string, name: string) => { export const checkForUpdate = async ( setUpdateLoading: React.Dispatch>, autoDownload: boolean, + showToast: boolean = true, ) => { setUpdateLoading(true); try { @@ -116,7 +117,7 @@ export const checkForUpdate = async ( ]); console.log('version', data.tag_name.replace('v', ''), pkg.version); } else { - ToastAndroid.show('App is up to date', ToastAndroid.SHORT); + showToast && ToastAndroid.show('App is up to date', ToastAndroid.SHORT); console.log('version', data.tag_name.replace('v', ''), pkg.version); } } catch (error) { diff --git a/src/screens/settings/Preference.tsx b/src/screens/settings/Preference.tsx new file mode 100644 index 0000000..cbcc780 --- /dev/null +++ b/src/screens/settings/Preference.tsx @@ -0,0 +1,104 @@ +import {View, Text, Switch, ScrollView, TouchableOpacity} from 'react-native'; +import React, {useState} from 'react'; +import {MMKV} from '../../lib/Mmkv'; +import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'; +import RNReactNativeHapticFeedback from 'react-native-haptic-feedback'; +import useWatchHistoryStore from '../../lib/zustand/watchHistrory'; + +const Preferences = () => { + const [showRecentlyWatched, setShowRecentlyWatched] = useState( + MMKV.getBool('showRecentlyWatched') || false, + ); + const {clearHistory} = useWatchHistoryStore(state => state); + + const [ExcludedQualities, setExcludedQualities] = useState( + MMKV.getArray('ExcludedQualities') || [], + ); + return ( + + + Preference + + + + {/* show recentlyWatched */} + + + Show Recently Watched + + + { + MMKV.setBool('showRecentlyWatched', !showRecentlyWatched); + setShowRecentlyWatched(!showRecentlyWatched); + }} + /> + + + {/* clear watch history */} + + Clear Watch History + { + RNReactNativeHapticFeedback.trigger('virtualKey', { + enableVibrateFallback: true, + ignoreAndroidSystemSettings: false, + }); + clearHistory(); + }}> + + + + + {/* Excluded qualities */} + + Excluded qualities + + {['480p', '720p', '1080p'].map((quality, index) => ( + { + RNReactNativeHapticFeedback.trigger('effectTick', { + enableVibrateFallback: true, + ignoreAndroidSystemSettings: false, + }); + if (ExcludedQualities.includes(quality)) { + setExcludedQualities(prev => + prev.filter(q => q !== quality), + ); + MMKV.setArray( + 'ExcludedQualities', + ExcludedQualities.filter(q => q !== quality), + ); + } else { + setExcludedQualities(prev => [...prev, quality]); + MMKV.setArray('ExcludedQualities', [ + ...ExcludedQualities, + quality, + ]); + } + console.log(ExcludedQualities); + }}> + + {quality} + + + ))} + + + + + ); +}; + +export default Preferences; diff --git a/src/screens/settings/Settings.tsx b/src/screens/settings/Settings.tsx index a9b07de..6c14818 100644 --- a/src/screens/settings/Settings.tsx +++ b/src/screens/settings/Settings.tsx @@ -17,7 +17,12 @@ import {providersList} from '../../lib/constants'; import {startActivityAsync, ActivityAction} from 'expo-intent-launcher'; import {NativeStackScreenProps} from '@react-navigation/native-stack'; import {SettingsStackParamList} from '../../App'; -import {MaterialCommunityIcons, AntDesign, Feather} from '@expo/vector-icons'; +import { + MaterialCommunityIcons, + AntDesign, + Feather, + MaterialIcons, +} from '@expo/vector-icons'; const players = [ { @@ -42,10 +47,6 @@ const Settings = ({navigation}: Props) => { players[0], ); - const [ExcludedQualities, setExcludedQualities] = useState( - MMKV.getArray('ExcludedQualities') || [], - ); - const {provider, setProvider} = useContentStore(state => state); return ( @@ -256,43 +257,20 @@ const Settings = ({navigation}: Props) => { - {/* Excluded qualities */} - - Excluded qualities - - {['480p', '720p', '1080p'].map((quality, index) => ( - { - ReactNativeHapticFeedback.trigger('effectTick', { - enableVibrateFallback: true, - ignoreAndroidSystemSettings: false, - }); - if (ExcludedQualities.includes(quality)) { - setExcludedQualities(prev => prev.filter(q => q !== quality)); - MMKV.setArray( - 'ExcludedQualities', - ExcludedQualities.filter(q => q !== quality), - ); - } else { - setExcludedQualities(prev => [...prev, quality]); - MMKV.setArray('ExcludedQualities', [ - ...ExcludedQualities, - quality, - ]); - } - console.log(ExcludedQualities); - }}> - - {quality} - - - ))} + {/* Preferences */} + { + navigation.navigate('Preferences'); + }} + background={TouchableNativeFeedback.Ripple('gray', false)}> + + + + Preference + + - + {/* clear cache */}