diff --git a/README.md b/README.md
index 9272f17..9b183c2 100644
--- a/README.md
+++ b/README.md
@@ -1,48 +1,104 @@
-# Nuvio Media Hub
+
+
-
-
-
+
+[![Contributors][contributors-shield]][contributors-url]
+[![Forks][forks-shield]][forks-url]
+[![Stargazers][stars-shield]][stars-url]
+[![Issues][issues-shield]][issues-url]
+[![License][license-shield]][license-url]
-
- A modern media hub built with React Native and Expo, featuring comprehensive addon integration and content synchronization.
-
+
+
+
----
+
+
+ Table of Contents
+
+ -
+ About The Project
+
+
+ - Screenshots
+ -
+ Getting Started
+
+
+ - Usage Notes
+ - Contributing
+ - Support
+ - License
+ - Contact
+ - Acknowledgments
+
+
-## Installation
+
+## About The Project
-### AltStore
-
[](https://tinyurl.com/NuvioAltstore)
+Nuvio Media Hub is a crossâplatform app for managing, discovering, and streaming your media via a flexible addon ecosystem. Built with React Native + Expo, it integrates providers and sync services while keeping a simple, fast UI.
-### SideStore
-
[](https://tinyurl.com/NuvioSidestore)
+### Key Features
-**Manual URL:** `https://raw.githubusercontent.com/tapframe/NuvioStreaming/main/nuvio-source.json`
+* **đ Addon Ecosystem** â Integrate multiple providers and services
+* **⥠Fast & Modern UI** â React Native + Expo with optimized navigation
+* **đą CrossâPlatform** â iOS, Android, and Web (Expo) support
+* **đ PrivacyâFirst** â No bundled content; you control sources via addons
+* **đď¸ Library Sync** â Keep metadata and progress in sync across devices
+* **đ§Š Extensible** â Community addons, customizations, and theme support
----
-
-## Screenshots
-
-| Home | Details |
-|:----:|:-------:|
-|  |  |
-
----
-
-## Tech Stack
+### Built With
-
+
+ React Native ⢠Expo ⢠TypeScript
+
----
+
+## Demo
+
-## Development
+| Home | Details |
+|:----:|:-------:|
+|  |  |
+
+(back to top)
+
+
+## Getting Started
+
+Follow the steps below to run the app locally.
+
+### Installation
-### Setup
```bash
git clone https://github.com/tapframe/NuvioStreaming.git
cd NuvioStreaming
@@ -51,36 +107,90 @@ npx expo start
```
### Build
+
```bash
npx expo run:android # Android
npx expo run:ios # iOS
```
----
+
+ Alternative iOS Installation
+
+ ### AltStore
+
[](https://tinyurl.com/NuvioAltstore)
+
+ ### SideStore
+
[](https://tinyurl.com/NuvioSidestore)
+
+ **Manual URL:** `https://raw.githubusercontent.com/tapframe/NuvioStreaming/main/nuvio-source.json`
+
+
+
+(back to top)
+
+## Usage Notes
+
+* Nuvio ships without builtâin content. Install addons to access sources.
+* Some providers may require accounts, API keys, or region access.
+* Performance depends on device and provider; enable caching where available.
+
+(back to top)
## Contributing
-1. Fork the repository
-2. Create a feature branch
-3. Make your changes
-4. Submit a pull request
+Contributions make the openâsource community amazing! Any contributions are greatly appreciated.
----
+1. Fork the project
+2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
-## Issues
+(back to top)
-Report bugs and request features via [GitHub Issues](https://github.com/tapframe/NuvioStreaming/issues)
+## Support
----
+If you find Nuvio helpful, consider supporting development:
+
+* **KoâFi** â `https://ko-fi.com/tapframe`
+* **GitHub Star** â Star the repo to show support
+* **Share** â Tell others about the project
+
+(back to top)
## License
-[](http://www.gnu.org/licenses/gpl-3.0.en.html)
+Distributed under the GNU GPLv3 License. See `LICENSE` for more information.
-This project is licensed under the GNU General Public License v3.0.
+(back to top)
----
+## Contact
-## Disclaimer
+**Project Links:**
+* GitHub: `https://github.com/tapframe`
+* Issues: `https://github.com/tapframe/NuvioStreaming/issues`
-This application functions as a media hub with addon/plugin support. It does not contain any built-in content or host media content. Content access is only available through user-installed plugins and addons. Any legal concerns should be directed to the specific websites providing the content.
\ No newline at end of file
+(back to top)
+
+## Acknowledgments
+
+* [React Native](https://reactnative.dev/)
+* [Expo](https://expo.dev/)
+* [TypeScript](https://www.typescriptlang.org/)
+* Community contributors and testers
+
+**Disclaimer:** This application functions as a media hub with addon/plugin support. It does not contain any builtâin content or host media content. Content access is only available through userâinstalled plugins and addons. Any legal concerns should be directed to the specific websites providing the content.
+
+(back to top)
+
+
+[contributors-shield]: https://img.shields.io/github/contributors/tapframe/NuvioStreaming.svg?style=for-the-badge
+[contributors-url]: https://github.com/tapframe/NuvioStreaming/graphs/contributors
+[forks-shield]: https://img.shields.io/github/forks/tapframe/NuvioStreaming.svg?style=for-the-badge
+[forks-url]: https://github.com/tapframe/NuvioStreaming/network/members
+[stars-shield]: https://img.shields.io/github/stars/tapframe/NuvioStreaming.svg?style=for-the-badge
+[stars-url]: https://github.com/tapframe/NuvioStreaming/stargazers
+[issues-shield]: https://img.shields.io/github/issues/tapframe/NuvioStreaming.svg?style=for-the-badge
+[issues-url]: https://github.com/tapframe/NuvioStreaming/issues
+[license-shield]: https://img.shields.io/github/license/tapframe/NuvioStreaming.svg?style=for-the-badge
+[license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html
\ No newline at end of file
diff --git a/src/contexts/DownloadsContext.tsx b/src/contexts/DownloadsContext.tsx
index 28e3612..c7218a6 100644
--- a/src/contexts/DownloadsContext.tsx
+++ b/src/contexts/DownloadsContext.tsx
@@ -48,8 +48,8 @@ type StartDownloadInput = {
type DownloadsContextValue = {
downloads: DownloadItem[];
startDownload: (input: StartDownloadInput) => Promise;
- // pauseDownload: (id: string) => Promise;
- // resumeDownload: (id: string) => Promise;
+ pauseDownload: (id: string) => Promise;
+ resumeDownload: (id: string) => Promise;
cancelDownload: (id: string) => Promise;
removeDownload: (id: string) => Promise;
};
@@ -64,15 +64,56 @@ function sanitizeFilename(name: string): string {
function getExtensionFromUrl(url: string): string {
const lower = url.toLowerCase();
- if (/(\.|ext=)(m3u8)(\b|$)/i.test(lower)) return 'm3u8';
+
+ // Return appropriate extensions for various formats
if (/(\.|ext=)(mp4)(\b|$)/i.test(lower)) return 'mp4';
if (/(\.|ext=)(mkv)(\b|$)/i.test(lower)) return 'mkv';
- if (/(\.|ext=)(mpd)(\b|$)/i.test(lower)) return 'mpd';
+ if (/(\.|ext=)(avi)(\b|$)/i.test(lower)) return 'avi';
+ if (/(\.|ext=)(mov)(\b|$)/i.test(lower)) return 'mov';
+ if (/(\.|ext=)(wmv)(\b|$)/i.test(lower)) return 'wmv';
+ if (/(\.|ext=)(flv)(\b|$)/i.test(lower)) return 'flv';
+ if (/(\.|ext=)(webm)(\b|$)/i.test(lower)) return 'webm';
+ if (/(\.|ext=)(m4v)(\b|$)/i.test(lower)) return 'm4v';
+ if (/(\.|ext=)(3gp)(\b|$)/i.test(lower)) return '3gp';
+ if (/(\.|ext=)(ts)(\b|$)/i.test(lower)) return 'ts';
+ if (/(\.|ext=)(mpg)(\b|$)/i.test(lower)) return 'mpg';
+ if (/(\.|ext=)(mpeg)(\b|$)/i.test(lower)) return 'mpeg';
+
+ // Default to mp4 for unknown formats
return 'mp4';
}
+function isDownloadableUrl(url: string): boolean {
+ if (!url) return false;
+
+ const lower = url.toLowerCase();
+
+ // Check for streaming formats that should NOT be downloadable (only m3u8 and DASH)
+ const streamingFormats = [
+ '.m3u8', // HLS streaming
+ '.mpd', // DASH streaming
+ 'm3u8', // HLS without extension
+ 'mpd', // DASH without extension
+ ];
+
+ // Check if URL contains streaming format indicators
+ const isStreamingFormat = streamingFormats.some(format =>
+ lower.includes(format) ||
+ lower.includes(`ext=${format}`) ||
+ lower.includes(`format=${format}`) ||
+ lower.includes(`container=${format}`)
+ );
+
+ // Return true if it's NOT a streaming format (m3u8 or DASH)
+ return !isStreamingFormat;
+}
+
export const DownloadsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [downloads, setDownloads] = useState([]);
+ const downloadsRef = useRef(downloads);
+ useEffect(() => {
+ downloadsRef.current = downloads;
+ }, [downloads]);
// Keep active resumables in memory (not persisted)
const resumablesRef = useRef