From 9603789f4e1dd79f5fbe8c08c8e8f9489c67e69f Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sat, 15 Feb 2025 17:38:49 +0100 Subject: [PATCH] wip --- modules/hls-downloader/example/.gitignore | 36 - modules/hls-downloader/example/App.tsx | 270 --- .../hls-downloader/example/android/.gitignore | 16 - .../example/android/app/build.gradle | 176 -- .../example/android/app/debug.keystore | Bin 2257 -> 0 bytes .../example/android/app/proguard-rules.pro | 14 - .../android/app/src/debug/AndroidManifest.xml | 7 - .../android/app/src/main/AndroidManifest.xml | 32 - .../hlsdownloader/example/MainActivity.kt | 61 - .../hlsdownloader/example/MainApplication.kt | 57 - .../res/drawable-hdpi/splashscreen_logo.png | Bin 20754 -> 0 bytes .../res/drawable-mdpi/splashscreen_logo.png | Bin 12863 -> 0 bytes .../res/drawable-xhdpi/splashscreen_logo.png | Bin 29081 -> 0 bytes .../res/drawable-xxhdpi/splashscreen_logo.png | Bin 47123 -> 0 bytes .../drawable-xxxhdpi/splashscreen_logo.png | Bin 66529 -> 0 bytes .../res/drawable/ic_launcher_background.xml | 6 - .../res/drawable/rn_edit_text_material.xml | 37 - .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 3300 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.webp | Bin 8031 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 4103 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 2048 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.webp | Bin 5079 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 2613 -> 0 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 4535 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.webp | Bin 11145 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 5673 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 7345 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.webp | Bin 18064 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 9091 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 10108 -> 0 bytes .../ic_launcher_foreground.webp | Bin 25030 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 12469 -> 0 bytes .../app/src/main/res/values-night/colors.xml | 1 - .../app/src/main/res/values/colors.xml | 6 - .../app/src/main/res/values/strings.xml | 5 - .../app/src/main/res/values/styles.xml | 17 - .../example/android/build.gradle | 41 - .../example/android/gradle.properties | 56 - .../android/gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 - .../hls-downloader/example/android/gradlew | 252 --- .../example/android/gradlew.bat | 94 - .../example/android/settings.gradle | 38 - modules/hls-downloader/example/app.json | 30 - .../example/assets/adaptive-icon.png | Bin 17547 -> 0 bytes .../hls-downloader/example/assets/favicon.png | Bin 1466 -> 0 bytes .../hls-downloader/example/assets/icon.png | Bin 22380 -> 0 bytes .../example/assets/splash-icon.png | Bin 17547 -> 0 bytes .../hls-downloader/example/babel.config.js | 6 - modules/hls-downloader/example/index.ts | 8 - modules/hls-downloader/example/ios/.gitignore | 30 - modules/hls-downloader/example/ios/.xcode.env | 11 - modules/hls-downloader/example/ios/Podfile | 66 - .../hls-downloader/example/ios/Podfile.lock | 1921 ----------------- .../example/ios/Podfile.properties.json | 5 - .../project.pbxproj | 538 ----- .../expohlsdownloaderexample.xcscheme | 88 - .../contents.xcworkspacedata | 10 - .../expohlsdownloaderexample/AppDelegate.h | 7 - .../expohlsdownloaderexample/AppDelegate.mm | 62 - .../App-Icon-1024x1024@1x.png | Bin 59468 -> 0 bytes .../AppIcon.appiconset/Contents.json | 14 - .../Images.xcassets/Contents.json | 6 - .../Contents.json | 20 - .../SplashScreenLogo.imageset/Contents.json | 23 - .../SplashScreenLogo.imageset/image.png | Bin 60870 -> 0 bytes .../SplashScreenLogo.imageset/image@2x.png | Bin 60870 -> 0 bytes .../SplashScreenLogo.imageset/image@3x.png | Bin 60870 -> 0 bytes .../ios/expohlsdownloaderexample/Info.plist | 80 - .../PrivacyInfo.xcprivacy | 48 - .../SplashScreen.storyboard | 44 - .../Supporting/Expo.plist | 12 - ...expohlsdownloaderexample-Bridging-Header.h | 3 - .../expohlsdownloaderexample.entitlements | 5 - .../ios/expohlsdownloaderexample/main.m | 10 - .../expohlsdownloaderexample/noop-file.swift | 4 - .../hls-downloader/example/metro.config.js | 34 - modules/hls-downloader/example/package.json | 29 - modules/hls-downloader/example/tsconfig.json | 10 - .../hls-downloader/example/webpack.config.js | 20 - .../hls-downloader/ios/HlsDownloader.podspec | 26 +- .../src/.HlsDownloaderModule.ts.swp | Bin 0 -> 12288 bytes package.json | 3 - 85 files changed, 9 insertions(+), 4403 deletions(-) delete mode 100644 modules/hls-downloader/example/.gitignore delete mode 100644 modules/hls-downloader/example/App.tsx delete mode 100644 modules/hls-downloader/example/android/.gitignore delete mode 100644 modules/hls-downloader/example/android/app/build.gradle delete mode 100644 modules/hls-downloader/example/android/app/debug.keystore delete mode 100644 modules/hls-downloader/example/android/app/proguard-rules.pro delete mode 100644 modules/hls-downloader/example/android/app/src/debug/AndroidManifest.xml delete mode 100644 modules/hls-downloader/example/android/app/src/main/AndroidManifest.xml delete mode 100644 modules/hls-downloader/example/android/app/src/main/java/expo/modules/hlsdownloader/example/MainActivity.kt delete mode 100644 modules/hls-downloader/example/android/app/src/main/java/expo/modules/hlsdownloader/example/MainApplication.kt delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/drawable/rn_edit_text_material.xml delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/values-night/colors.xml delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/values/colors.xml delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/values/strings.xml delete mode 100644 modules/hls-downloader/example/android/app/src/main/res/values/styles.xml delete mode 100644 modules/hls-downloader/example/android/build.gradle delete mode 100644 modules/hls-downloader/example/android/gradle.properties delete mode 100644 modules/hls-downloader/example/android/gradle/wrapper/gradle-wrapper.jar delete mode 100644 modules/hls-downloader/example/android/gradle/wrapper/gradle-wrapper.properties delete mode 100755 modules/hls-downloader/example/android/gradlew delete mode 100644 modules/hls-downloader/example/android/gradlew.bat delete mode 100644 modules/hls-downloader/example/android/settings.gradle delete mode 100644 modules/hls-downloader/example/app.json delete mode 100644 modules/hls-downloader/example/assets/adaptive-icon.png delete mode 100644 modules/hls-downloader/example/assets/favicon.png delete mode 100644 modules/hls-downloader/example/assets/icon.png delete mode 100644 modules/hls-downloader/example/assets/splash-icon.png delete mode 100644 modules/hls-downloader/example/babel.config.js delete mode 100644 modules/hls-downloader/example/index.ts delete mode 100644 modules/hls-downloader/example/ios/.gitignore delete mode 100644 modules/hls-downloader/example/ios/.xcode.env delete mode 100644 modules/hls-downloader/example/ios/Podfile delete mode 100644 modules/hls-downloader/example/ios/Podfile.lock delete mode 100644 modules/hls-downloader/example/ios/Podfile.properties.json delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample.xcodeproj/project.pbxproj delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample.xcodeproj/xcshareddata/xcschemes/expohlsdownloaderexample.xcscheme delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample.xcworkspace/contents.xcworkspacedata delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/AppDelegate.h delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/AppDelegate.mm delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Images.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Images.xcassets/Contents.json delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Images.xcassets/SplashScreenBackground.colorset/Contents.json delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Images.xcassets/SplashScreenLogo.imageset/Contents.json delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Images.xcassets/SplashScreenLogo.imageset/image.png delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Images.xcassets/SplashScreenLogo.imageset/image@2x.png delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Images.xcassets/SplashScreenLogo.imageset/image@3x.png delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Info.plist delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/PrivacyInfo.xcprivacy delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/SplashScreen.storyboard delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/Supporting/Expo.plist delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/expohlsdownloaderexample-Bridging-Header.h delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/expohlsdownloaderexample.entitlements delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/main.m delete mode 100644 modules/hls-downloader/example/ios/expohlsdownloaderexample/noop-file.swift delete mode 100644 modules/hls-downloader/example/metro.config.js delete mode 100644 modules/hls-downloader/example/package.json delete mode 100644 modules/hls-downloader/example/tsconfig.json delete mode 100644 modules/hls-downloader/example/webpack.config.js create mode 100644 modules/hls-downloader/src/.HlsDownloaderModule.ts.swp diff --git a/modules/hls-downloader/example/.gitignore b/modules/hls-downloader/example/.gitignore deleted file mode 100644 index d16e1efb..00000000 --- a/modules/hls-downloader/example/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files - -# dependencies -node_modules/ - -# Expo -.expo/ -dist/ -web-build/ -expo-env.d.ts - -# Native -*.orig.* -*.jks -*.p8 -*.p12 -*.key -*.mobileprovision - -# Metro -.metro-health-check* - -# debug -npm-debug.* -yarn-debug.* -yarn-error.* - -# macOS -.DS_Store -*.pem - -# local env files -.env*.local - -# typescript -*.tsbuildinfo diff --git a/modules/hls-downloader/example/App.tsx b/modules/hls-downloader/example/App.tsx deleted file mode 100644 index 3835eb3f..00000000 --- a/modules/hls-downloader/example/App.tsx +++ /dev/null @@ -1,270 +0,0 @@ -import * as FileSystem from "expo-file-system"; -import React, { useEffect, useMemo, useRef, useState } from "react"; -import { Button, Dimensions, StyleSheet, Text, View } from "react-native"; - -import { - PipStartedPayload, - PlaybackStatePayload, - ProgressUpdatePayload, - VlcPlayerViewRef, -} from "../../vlc-player/src/VlcPlayer.types"; -import VlcPlayerView from "../../vlc-player/src/VlcPlayerView"; -import { - downloadHLSAsset, - useDownloadComplete, - useDownloadError, - useDownloadProgress, -} from ".."; - -/** - * Parses boot.xml in the root download directory to extract the stream folder name. - */ -async function getStreamFolderFromBootXml( - downloadDir: string -): Promise { - try { - const bootPath = `${downloadDir}/boot.xml`; - const bootInfo = await FileSystem.getInfoAsync(bootPath); - if (!bootInfo.exists) { - console.error("boot.xml not found in", downloadDir); - return null; - } - const xmlContent = await FileSystem.readAsStringAsync(bootPath); - const match = xmlContent.match(/]*Path="([^"]+)"/); - return match ? match[1].trim() : null; - } catch (error) { - console.error("Error reading boot.xml:", error); - return null; - } -} - -/** - * Parses the StreamInfoBoot.xml file in the stream folder and returns a mapping: - * TS filename -> local fragment filename. - */ -async function parseStreamInfoMapping( - downloadDir: string, - streamFolder: string -): Promise> { - const mapping: Record = {}; - try { - const streamInfoPath = `${downloadDir}/${streamFolder}/StreamInfoBoot.xml`; - const info = await FileSystem.getInfoAsync(streamInfoPath); - if (!info.exists) { - console.error("StreamInfoBoot.xml not found in", streamFolder); - return mapping; - } - const xmlContent = await FileSystem.readAsStringAsync(streamInfoPath); - // Use a regex to match all elements and capture PATH and URL attributes. - const segRegex = /]*PATH="([^"]+)"\s+[^>]*URL="([^"]+)"[^>]*>/g; - let match: RegExpExecArray | null; - while ((match = segRegex.exec(xmlContent)) !== null) { - const fragName = match[1].trim(); - const urlAttr = match[2].trim(); - // Extract the TS filename from the URL (assumes it's the last path component) - const tsFilename = urlAttr.substring(urlAttr.lastIndexOf("/") + 1); - mapping[tsFilename] = fragName; - } - } catch (error) { - console.error("Error parsing StreamInfoBoot.xml:", error); - } - return mapping; -} - -/** - * Reads the master playlist, replaces TS filenames with local fragment paths from the mapping, - * and writes the modified playlist to a new file. - * @param masterUri Absolute path to the original master playlist. - * @param baseDir The stream folder directory. - * @param mapping Mapping from TS filename to fragment filename. - * @returns The new file URI for the modified playlist. - */ -async function rewriteMasterPlaylist( - masterUri: string, - baseDir: string, - mapping: Record -): Promise { - try { - const content = await FileSystem.readAsStringAsync(masterUri); - const lines = content.split("\n"); - const modifiedLines = lines.map((line) => { - // Only modify lines that look like segment references (not comments) - if (!line.startsWith("#") && line.trim().endsWith(".ts")) { - const tsFilename = line.trim(); - if (mapping[tsFilename]) { - // Replace with absolute path to the frag file. - return baseDir + "/" + mapping[tsFilename]; - } - // Fallback: use the TS filename with baseDir prefix. - return baseDir + "/" + tsFilename; - } - return line; - }); - const newContent = modifiedLines.join("\n"); - const newUri = masterUri.replace(/\.m3u8$/, "_modified.m3u8"); - await FileSystem.writeAsStringAsync(newUri, newContent); - console.log("Rewritten master playlist saved to:", newUri); - return newUri; - } catch (error) { - console.error("Error rewriting master playlist:", error); - throw error; - } -} - -/** - * Reads the root boot.xml to get the stream folder, then reads StreamInfoBoot.xml to extract - * the master playlist filename from master.m3u8 or from the MediaPlaylist. - * For simplicity, here we assume that the master playlist file is in the stream folder. - */ -async function getMasterPlaylistUri( - downloadDir: string, - streamFolder: string -): Promise { - try { - // Look in the stream folder for a file ending with ".m3u8" - const folderPath = `${downloadDir}/${streamFolder}`; - const items = await FileSystem.readDirectoryAsync(folderPath); - const masterFile = items.find((f) => f.toLowerCase().endsWith(".m3u8")); - if (!masterFile) { - console.error("No master playlist found in", folderPath); - return null; - } - return folderPath + "/" + masterFile; - } catch (error) { - console.error("Error reading master playlist:", error); - return null; - } -} - -export default function App() { - const progress = useDownloadProgress(); - const error = useDownloadError(); - const downloadLocation = useDownloadComplete("video-dir"); - const [playbackUrl, setPlaybackUrl] = useState(null); - const { width } = Dimensions.get("window"); - - useEffect(() => { - async function preparePlayback() { - if (!downloadLocation) return; - console.log("Download folder:", downloadLocation); - - // 1. Read boot.xml to get the stream folder. - const streamFolder = await getStreamFolderFromBootXml(downloadLocation); - if (!streamFolder) { - console.error("Stream folder not found in boot.xml"); - return; - } - console.log("Stream folder:", streamFolder); - - // 2. Parse StreamInfoBoot.xml to build mapping TS -> frag. - const mapping = await parseStreamInfoMapping( - downloadLocation, - streamFolder - ); - console.log("Mapping:", mapping); - - // 3. Get the master playlist file URI from the stream folder. - const masterUri = await getMasterPlaylistUri( - downloadLocation, - streamFolder - ); - if (!masterUri) { - console.error("Master playlist not found."); - return; - } - console.log("Master playlist found at:", masterUri); - - // 4. Rewrite the master playlist using the mapping. - const baseDir = `${downloadLocation}/${streamFolder}`; - const modifiedMasterUri = await rewriteMasterPlaylist( - masterUri, - baseDir, - mapping - ); - - console.log("setPlaybackUrl: ", modifiedMasterUri); - setPlaybackUrl(modifiedMasterUri); - } - preparePlayback(); - }, [downloadLocation]); - - const handleDownload = () => { - // Start the HLS download with a sample URL and asset title. - downloadHLSAsset( - "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8", - // "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8", - // "https://fredflix.se/videos/48129b3e-eb6c-5b35-a3f0-5aa4519f20e9/master.m3u8?DeviceId=TW96aWxsYS81LjAgKE1hY2ludG9zaDsgSW50ZWwgTWFjIE9TIFggMTBfMTVfNykgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzEyNS4wLjAuMCBTYWZhcmkvNTM3LjM2fDE3MTc2MjEwNjI2MDE1&MediaSourceId=48129b3eeb6c5b35a3f05aa4519f20e9&VideoCodec=av1,hevc,h264,vp9&AudioCodec=aac,opus,flac&AudioStreamIndex=1&VideoBitrate=226308506&AudioBitrate=191999&MaxFramerate=23.976025&PlaySessionId=110e05ff016a4778864a5e736b234ef9&api_key=9a506def548b4684a74a5ab410604de2&SubtitleMethod=Encode&TranscodingMaxAudioChannels=2&RequireAvc=false&EnableAudioVbrEncoding=true&Tag=40a3a2b4cb21057a5e1ea5f018b0c189&SegmentContainer=mp4&MinSegments=2&BreakOnNonKeyFrames=True&hevc-level=120&hevc-videobitdepth=10&hevc-profile=main10&av1-profile=main&av1-rangetype=SDR,HDR10,HLG&av1-level=19&vp9-rangetype=SDR,HDR10,HLG&hevc-rangetype=SDR,HDR10,HLG&hevc-deinterlace=true&h264-profile=high,main,baseline,constrainedbaseline,high10&h264-rangetype=SDR&h264-level=52&h264-deinterlace=true&TranscodeReasons=ContainerNotSupported,%20AudioChannelsNotSupported", - "MyHLSAsset" - ); - }; - - const downloading = useMemo(() => progress > 0 && progress < 1, [progress]); - const videoRef = useRef(null); - - return ( - - HLS Downloader Example -