Guest User

Untitled

a guest
Mar 24th, 2024
432
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TypeScript 5.00 KB | Source Code | 0 0
  1. import { AVPlaybackSource, Video } from "expo-av";
  2. import { cacheDirectory, downloadAsync } from "expo-file-system";
  3. import { useEffect, useReducer, useState } from "react";
  4. import { Button, View } from "react-native";
  5.  
  6. const publicURL =
  7.   "https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
  8.  
  9. type FileState = {
  10.   fileSize: number | null;
  11.   currentRange: number | null;
  12.   targetRange: number | null;
  13.   threshold: number | null;
  14. };
  15.  
  16. type FileAction =
  17.   | { type: "SET_FILE_SIZE"; payload: number }
  18.   | {
  19.       type: "UPDATE_RANGES";
  20.       payload: { currentRange: number; targetRange: number };
  21.     };
  22.  
  23. const THRESHOLD_VALUE = 0.1;
  24.  
  25. const contentRangeReducer = (
  26.   state: FileState,
  27.   action: FileAction
  28. ): FileState => {
  29.   switch (action.type) {
  30.     case "SET_FILE_SIZE":
  31.       return {
  32.         ...state,
  33.         fileSize: action.payload,
  34.         threshold: Math.floor(action.payload * THRESHOLD_VALUE),
  35.       };
  36.     case "UPDATE_RANGES": {
  37.       return { ...state, ...action.payload };
  38.     }
  39.     default:
  40.       return state;
  41.   }
  42. };
  43.  
  44. export default function VideoFetchTest() {
  45.   const [fetchInfo, dispatch] = useReducer(contentRangeReducer, {
  46.     fileSize: null,
  47.     currentRange: null,
  48.     targetRange: null,
  49.     threshold: null,
  50.   });
  51.   const [videoSource, setVideoSource] = useState<AVPlaybackSource | null>(null);
  52.   const [isFetching, setIsFetching] = useState<boolean>(false);
  53.  
  54.   useEffect(() => {
  55.     if (videoSource) {
  56.       console.log("observer: video updated!");
  57.     }
  58.   }, [videoSource]);
  59.  
  60.   const getFetchedFileSize = async (url: string) => {
  61.     try {
  62.       const response = await fetch(url, {
  63.         method: "HEAD",
  64.       });
  65.  
  66.       const contentLength = Number(response.headers.get("content-length"));
  67.  
  68.       if (isNaN(contentLength)) throw new Error("contentLength is NaN!");
  69.  
  70.       dispatch({ type: "SET_FILE_SIZE", payload: contentLength });
  71.  
  72.       return contentLength;
  73.     } catch (error) {
  74.       console.error("Error fetching file size:", error);
  75.     }
  76.   };
  77.  
  78.   const calculateRanges = () => {
  79.     if (!fetchInfo.threshold) throw new Error("Threshold not specified!");
  80.     const newCurrentRange =
  81.       fetchInfo.currentRange !== null
  82.         ? fetchInfo.currentRange + fetchInfo.threshold
  83.         : 0;
  84.  
  85.     const newTargetRange = fetchInfo.targetRange
  86.       ? fetchInfo.targetRange + fetchInfo.threshold
  87.       : fetchInfo.threshold;
  88.  
  89.     console.log("newCurrent", newCurrentRange);
  90.     console.log("newTarget", newTargetRange);
  91.  
  92.     dispatch({
  93.       type: "UPDATE_RANGES",
  94.       payload: { currentRange: newCurrentRange, targetRange: newTargetRange },
  95.     });
  96.  
  97.     return {
  98.       targetRange: newTargetRange,
  99.       currentRange: newCurrentRange,
  100.     };
  101.   };
  102.  
  103.   const downloadVideo = async (downloadLink: string) => {
  104.     try {
  105.       const fileSize =
  106.         fetchInfo.fileSize || (await getFetchedFileSize(downloadLink));
  107.       if (!fileSize) throw new Error(`fileSize is undefined (${fileSize})`);
  108.  
  109.       const fileName = "video";
  110.       const extension = "mp4";
  111.  
  112.       const { currentRange, targetRange } = calculateRanges();
  113.  
  114.       console.log({
  115.         Range: `bytes=${currentRange}-${targetRange}`,
  116.       });
  117.  
  118.       const downloadResult = await downloadAsync(
  119.         downloadLink,
  120.         cacheDirectory + [fileName, extension].join("."),
  121.         {
  122.           headers: {
  123.             Range: `bytes=${currentRange}-${targetRange}`,
  124.           },
  125.         }
  126.       );
  127.  
  128.       setVideoSource(downloadResult);
  129.       setIsFetching(false);
  130.       return console.log("isFetching false");
  131.     } catch (err) {
  132.       console.error(err);
  133.     }
  134.   };
  135.  
  136.   return (
  137.     <View>
  138.       <Button
  139.         title="Get File Size"
  140.         onPress={() => {
  141.           getFetchedFileSize(publicURL);
  142.         }}
  143.       />
  144.  
  145.       {fetchInfo.fileSize && (
  146.         <Button
  147.           title="Download Video"
  148.           onPress={() => {
  149.             downloadVideo(publicURL);
  150.           }}
  151.         />
  152.       )}
  153.  
  154.       {videoSource && (
  155.         <Video
  156.           shouldPlay
  157.           useNativeControls
  158.           style={{ width: 400, height: 300 }}
  159.           source={videoSource}
  160.           rate={4}
  161.           progressUpdateIntervalMillis={1000}
  162.           onPlaybackStatusUpdate={async (status) => {
  163.             // TODO: change to Type-guard.
  164.             console.log(status.isLoaded);
  165.  
  166.             if (!("positionMillis" in status)) {
  167.               console.log("no positionMillis");
  168.               return;
  169.             }
  170.             const { positionMillis, playableDurationMillis } = status;
  171.  
  172.             console.log({ playableDurationMillis, positionMillis });
  173.  
  174.             if (playableDurationMillis && !isFetching)
  175.               if (positionMillis >= playableDurationMillis * 0.3) {
  176.                 console.log("dispatch");
  177.  
  178.                 setIsFetching(true);
  179.                 console.log("isFetching true");
  180.  
  181.                 await downloadVideo(publicURL);
  182.               }
  183.           }}
  184.         />
  185.       )}
  186.     </View>
  187.   );
  188. }
  189.  
Advertisement
Add Comment
Please, Sign In to add comment