Examples
info
Important: Don't forget to add the dependency in your ./android/app/build.gradle
:
dependencies {
implementation fileTree(dir: "../../node_modules/@TheWidlarzGroup/react-native-video-stream-downloader/native-libs", include: ["*.aar"])
//...
}
- Basic Example
- DRM Example
- Metadata Example
- Error Handling
Simple example of downloading and managing video content:
import React, { useEffect, useState } from "react";
import { Button, SafeAreaView, StyleSheet, Text, View } from "react-native";
import {
downloadStream,
useEvent,
pauseDownload,
resumeDownload,
getDownloadedAssets,
registerPlugin,
disablePlugin,
deleteDownloadedAsset,
type DownloadedAsset,
} from "@TheWidlarzGroup/react-native-video-stream-downloader";
// Define enum for download status
enum DownloadStatus {
READY = "Ready to Download",
DOWNLOADING = "Downloading...",
PAUSED = "Download Paused",
RESUMING = "Resuming Download...",
COMPLETED = "Download Completed",
}
const API_KEY = ""; // Use your actual API key
const VIDEO = {
title: "Video Title", // Replace with actual title
url: "https://example.com/video.m3u8", // Replace with actual video URL
};
export default function App() {
const [downloadId, setDownloadId] = useState<string | null>(null);
const [downloadProgress, setDownloadProgress] = useState(0);
const [downloadStatus, setDownloadStatus] = useState<DownloadStatus>(
DownloadStatus.READY
);
const [isDownloading, setIsDownloading] = useState(false);
const [isSDKRegistered, setIsSDKRegistered] = useState(false);
const [downloadedAsset, setDownloadedAsset] =
useState<DownloadedAsset | null>(null);
// Load downloaded assets and update progress if needed
const loadAssets = async () => {
const result = await getDownloadedAssets();
console.log(result);
setDownloadedAsset(result[0] ? result[0] : null);
setIsDownloading(result.length > 0);
setDownloadProgress(result.length > 0 ? 100 : 0);
};
// Event listeners for download progress and errors
useEvent("onDownloadProgress", (statuses) => {
if (statuses && statuses[0]) {
setDownloadProgress(Math.round(statuses[0].progress * 100));
}
});
useEvent("onDownloadEnd", async () => {
console.log("Download completed");
setDownloadProgress(100);
setDownloadStatus(DownloadStatus.COMPLETED);
loadAssets();
});
// Register and disable plugin
useEffect(() => {
const registerSDK = async () => {
try {
await registerPlugin(API_KEY);
setIsSDKRegistered(true);
console.log("Plugin registered successfully");
loadAssets();
} catch (error) {
setIsSDKRegistered(false);
console.log("Error registering plugin", error);
}
};
registerSDK();
return () => {
disablePlugin();
};
}, []);
// Handle video download
const handleDownload = async () => {
setDownloadStatus(DownloadStatus.DOWNLOADING);
const result = await downloadStream(VIDEO.url, {
checkStorageBeforeDownload: true,
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
metadata: {
title: VIDEO.title,
type: "video",
tags: ["example", "video"],
},
});
setDownloadId(result.id);
setIsDownloading(true);
};
// Pause and resume download actions
const handlePause = async () => {
if (downloadId) {
await pauseDownload(downloadId);
setDownloadStatus(DownloadStatus.PAUSED);
}
};
const handleResume = async () => {
if (downloadId) {
await resumeDownload(downloadId);
setDownloadStatus(DownloadStatus.RESUMING);
}
};
const handlePlay = () => {
console.log(
`Playing video... The correct source.uri is ${downloadedAsset?.pathToFile}`
);
};
// Delete the downloaded video
const handleDelete = async () => {
if (downloadedAsset) {
await deleteDownloadedAsset(downloadedAsset.id);
setDownloadStatus(DownloadStatus.READY);
setDownloadedAsset(null);
loadAssets();
console.log("Deleted asset with ID:", downloadedAsset.id);
}
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.card}>
<Text style={styles.title}>{VIDEO.title}</Text>
{!isDownloading ? (
<Button title="Download" onPress={handleDownload} color="#4caf50" />
) : (
<View style={styles.progressContainer}>
<Text style={styles.text}>Progress: {downloadProgress}%</Text>
<Text style={styles.text}>{downloadStatus}</Text>
<View style={styles.buttonContainer}>
<Button title="Pause" onPress={handlePause} color="#ff5722" />
<Button title="Resume" onPress={handleResume} color="#4caf50" />
</View>
</View>
)}
{downloadProgress === 100 && (
<Button title="Play Video" onPress={handlePlay} color="#3f51b5" />
)}
<Text style={styles.text}>
SDK Registration: {isSDKRegistered ? "✅ Active" : "❌ Inactive"}
</Text>
{downloadedAsset && (
<View style={styles.deleteContainer}>
<Button
title="Delete Video"
onPress={handleDelete}
color="#d32f2f"
/>
</View>
)}
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: "#121212",
justifyContent: "center",
},
card: {
backgroundColor: "#1e1e1e",
padding: 20,
marginVertical: 30,
borderRadius: 8,
shadowColor: "#000",
shadowOpacity: 0.1,
shadowRadius: 8,
shadowOffset: { width: 0, height: 2 },
},
title: {
fontSize: 20,
color: "#fff",
marginBottom: 15,
textAlign: "center",
},
text: {
color: "#bbb",
marginBottom: 10,
textAlign: "center",
},
progressContainer: {
marginBottom: 20,
},
buttonContainer: {
flexDirection: "row",
justifyContent: "space-between",
},
deleteContainer: {
marginTop: 20,
alignItems: "center",
},
});
Complete example of downloading DRM-protected content with platform-specific configuration:
import React, { useState } from "react";
import { Platform } from "react-native";
import {
downloadStream,
getDownloadedAssets,
} from "@TheWidlarzGroup/react-native-video-stream-downloader";
const DRMDownloadExample = () => {
const [isDownloading, setIsDownloading] = useState(false);
const generateDRMConfig = async () => {
const platformKey = Platform.OS as "android" | "ios";
const licenseServerMap = {
android: "https://your-widevine-license-server.com/license",
ios: "https://your-fairplay-license-server.com/license",
};
const fairplayCertificate =
"https://your-fairplay-certificate.com/certificate";
const licenseServer = licenseServerMap[platformKey];
const token = await getDrmToken(); // Your DRM token function
const drmConfig = {
licenseServer,
certificateUrl: platformKey === "ios" ? fairplayCertificate : undefined,
headers: {
"x-drm-usertoken": token,
},
};
return drmConfig;
};
const downloadDRMContent = async () => {
try {
setIsDownloading(true);
const drmConfig = await generateDRMConfig();
const downloadStatus = await downloadStream(
"https://example.com/drm-video.m3u8",
{
drm: drmConfig,
}
);
console.log("Download started:", downloadStatus);
} catch (error) {
console.error("Download failed:", error);
} finally {
setIsDownloading(false);
}
};
const getDownloadedContent = () => {
const assets = getDownloadedAssets();
console.log("Downloaded assets:", assets);
};
return (
<View>
<Button
title={isDownloading ? "Downloading..." : "Download DRM Content"}
onPress={downloadDRMContent}
disabled={isDownloading}
/>
<Button title="Get Downloaded Assets" onPress={getDownloadedContent} />
</View>
);
};
Example of using custom metadata to organize and categorize downloads:
import React, { useState, useEffect } from "react";
import { View, Text, Button, FlatList } from "react-native";
import {
downloadStream,
getDownloadedAssets,
useEvent,
} from "@TheWidlarzGroup/react-native-video-stream-downloader";
const MetadataExample = () => {
const [assets, setAssets] = useState([]);
const [filteredAssets, setFilteredAssets] = useState([]);
const [selectedCategory, setSelectedCategory] = useState("all");
useEffect(() => {
loadAssets();
}, []);
const loadAssets = async () => {
const downloadedAssets = await getDownloadedAssets();
setAssets(downloadedAssets);
filterAssets(downloadedAssets, selectedCategory);
};
const filterAssets = (assetList, category) => {
if (category === "all") {
setFilteredAssets(assetList);
} else {
const filtered = assetList.filter(
(asset) => asset.metadata?.category === category
);
setFilteredAssets(filtered);
}
};
const downloadWithMetadata = async (url, title, category) => {
const downloadStatus = await downloadStream(url, {
metadata: {
title: title,
category: category,
tags: [category, "downloaded"],
userRating: 0,
isWatched: false,
},
});
console.log("Download started with metadata:", downloadStatus);
};
const updateAssetMetadata = (assetId, updates) => {
// Note: This would require a function to update metadata
// For now, we'll just reload assets
loadAssets();
};
const renderAsset = ({ item }) => (
<View
style={{ padding: 16, borderBottomWidth: 1, borderBottomColor: "#ccc" }}
>
<Text style={{ fontSize: 16, fontWeight: "bold" }}>
{item.metadata?.title || "Untitled"}
</Text>
<Text>Category: {item.metadata?.category || "Uncategorized"}</Text>
<Text>
Downloaded: {new Date(item.metadata?.downloadDate).toLocaleDateString()}
</Text>
<Text>Tags: {item.metadata?.tags?.join(", ") || "No tags"}</Text>
<Text>Watched: {item.metadata?.isWatched ? "Yes" : "No"}</Text>
</View>
);
return (
<View style={{ flex: 1, padding: 16 }}>
<Text style={{ fontSize: 20, fontWeight: "bold", marginBottom: 16 }}>
Metadata Example
</Text>
<View style={{ flexDirection: "row", marginBottom: 16 }}>
<Button
title="Download Movie"
onPress={() =>
downloadWithMetadata(
"https://example.com/movie.m3u8",
"Action Movie 2024",
"movies"
)
}
/>
<Button
title="Download Series"
onPress={() =>
downloadWithMetadata(
"https://example.com/series.m3u8",
"Drama Series S01E01",
"series"
)
}
/>
</View>
<View style={{ flexDirection: "row", marginBottom: 16 }}>
<Button
title="All"
onPress={() => {
setSelectedCategory("all");
filterAssets(assets, "all");
}}
/>
<Button
title="Movies"
onPress={() => {
setSelectedCategory("movies");
filterAssets(assets, "movies");
}}
/>
<Button
title="Series"
onPress={() => {
setSelectedCategory("series");
filterAssets(assets, "series");
}}
/>
</View>
<FlatList
data={filteredAssets}
renderItem={renderAsset}
keyExtractor={(item) => item.id}
/>
</View>
);
};
Example of proper error handling for downloads:
import React, { useState } from "react";
import { Alert } from "react-native";
import {
downloadStream,
useEvent,
} from "@TheWidlarzGroup/react-native-video-stream-downloader";
const DownloadWithErrorHandling = () => {
const [downloads, setDownloads] = useState([]);
useEvent("onError", (error) => {
Alert.alert("Download Error", error);
});
useEvent("onDownloadEnd", (download) => {
if (download.status === "failed") {
Alert.alert(
"Download Failed",
`Failed to download: ${download.error || "Unknown error"}`
);
} else if (download.status === "completed") {
Alert.alert("Download Complete", "Video downloaded successfully!");
}
});
const startDownload = async (url) => {
try {
const downloadStatus = await downloadStream(url, {
includeAllTracks: true,
});
setDownloads((prev) => [...prev, downloadStatus]);
} catch (error) {
Alert.alert("Error", "Failed to start download");
}
};
return (
<View>
<Button
title="Download Video"
onPress={() => startDownload("https://example.com/video.m3u8")}
/>
</View>
);
};