diff --git a/src/app/apps/files.tsx b/src/app/apps/files.tsx index dbaec41..db19095 100644 --- a/src/app/apps/files.tsx +++ b/src/app/apps/files.tsx @@ -87,11 +87,11 @@ const FilesPage = () => { } disabled={parentPath == null} - onPress={() => setParams({ ...params, path: parentPath })} + onPress={() => setParams({ path: parentPath })} /> } - onPress={() => setParams({ ...params, path: "" })} + onPress={() => setParams({ path: "" })} /> { files={data} onSelect={(file) => { if (file.isDirectory) { - setParams({ ...params, path: file.path }); + setParams({ path: file.path }); } else { setViewFile(file); } diff --git a/src/components/containers/AudioPlayer.tsx b/src/components/containers/AudioPlayer.tsx index 9d4688c..a05f971 100644 --- a/src/components/containers/AudioPlayer.tsx +++ b/src/components/containers/AudioPlayer.tsx @@ -1,5 +1,5 @@ import { AVPlaybackStatusSuccess, Audio } from "expo-av"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { forwardRef, useEffect, useMemo, useRef, useState } from "react"; import jsmediatags from "jsmediatags/build2/jsmediatags"; import { MediaTags } from "@/types/mediaTags"; import Box from "../ui/Box"; @@ -20,6 +20,7 @@ import { Slider } from "@miblanchard/react-native-slider"; import bgImage from "@/assets/images/audioplayer-bg.jpeg"; import { FileItem } from "@/types/files"; import Input from "@ui/Input"; +import { useAsyncStorage } from "@/hooks/useAsyncStorage"; type Props = { path: string; @@ -32,11 +33,21 @@ const AudioPlayer = ({ path, uri }: Props) => { const [curFileIdx, setFileIdx] = useState(-1); const [status, setStatus] = useState(null); const [mediaTags, setMediaTags] = useState(null); + const [playback, setPlayback] = useAsyncStorage("playback", { + repeat: false, + shuffle: false, + }); const filename = getFilename(decodeURIComponent(uri)); const playlist = useMemo(() => { - return files.filter((f) => getFileType(f.name) === "audio"); - }, [files]); + let items = files.filter((f) => getFileType(f.name) === "audio"); + + if (playback.shuffle) { + items = items.sort(() => Math.random() - 0.5); + } + + return items; + }, [files, playback.shuffle]); const playIdx = (idx: number) => { if (!playlist.length || idx < 0) { @@ -49,12 +60,12 @@ const AudioPlayer = ({ path, uri }: Props) => { } }; - const playNext = (increment = 1) => { - if (!playlist.length || curFileIdx < 0) { + const playNext = (increment = 1, startIdx?: number) => { + const curIdx = startIdx ?? curFileIdx; + if (!playlist.length || curIdx < 0) { return; } - - playIdx(curFileIdx + increment); + playIdx(curIdx + increment); }; useEffect(() => { @@ -65,12 +76,14 @@ const AudioPlayer = ({ path, uri }: Props) => { const fileIdx = playlist.findIndex((file) => path === file.path); setFileIdx(fileIdx); - const onNext = () => playIdx(fileIdx + 1); + const onNext = () => playNext(1, fileIdx); async function play() { try { const { sound } = await Audio.Sound.createAsync({ uri }); soundRef.current = sound; + + sound.setIsLoopingAsync(playback.repeat); sound.setOnPlaybackStatusUpdate((st: AVPlaybackStatusSuccess) => { setStatus(st as any); @@ -93,31 +106,32 @@ const AudioPlayer = ({ path, uri }: Props) => { } } - // function loadMediaTags() { - // const tagsReader = new jsmediatags.Reader(uri + "&dl=true"); - // setMediaTags(null); + function loadMediaTags() { + const tagsReader = new jsmediatags.Reader(uri + "&dl=true"); + setMediaTags(null); - // tagsReader.read({ - // onSuccess: (result: any) => { - // const mediaTagsResult = { ...result }; + tagsReader.read({ + onSuccess: (result: any) => { + const mediaTagsResult = { ...result }; - // if (result?.tags?.picture) { - // const { data, format } = result.tags.picture; - // let base64String = ""; - // for (let i = 0; i < data.length; i++) { - // base64String += String.fromCharCode(data[i]); - // } - // mediaTagsResult.picture = `data:${format};base64,${base64encode( - // base64String - // )}`; - // delete data?.tags?.picture; - // } + if (result?.tags?.picture) { + const { data, format } = result.tags.picture; + let base64String = ""; + for (let i = 0; i < data.length; i++) { + base64String += String.fromCharCode(data[i]); + } + mediaTagsResult.picture = `data:${format};base64,${base64encode( + base64String + )}`; + delete data?.tags?.picture; + } - // setMediaTags(mediaTagsResult); - // }, - // }); - // } + setMediaTags(mediaTagsResult); + }, + }); + } + loadMediaTags(); play(); return () => { @@ -126,6 +140,12 @@ const AudioPlayer = ({ path, uri }: Props) => { }; }, [uri, path, playlist]); + useEffect(() => { + if (status?.isLoaded) { + soundRef.current?.setIsLoopingAsync(playback.repeat); + } + }, [playback.repeat, status?.isLoaded]); + return ( @@ -160,42 +180,62 @@ const AudioPlayer = ({ path, uri }: Props) => { ) : null} - { - if (!soundRef.current) { - return; + + { + if (!soundRef.current) { + return; + } - if (!status?.isPlaying) { - await soundRef.current.playAsync(); - } + if (!status?.isPlaying) { + await soundRef.current.playAsync(); + } - const [progress] = value; - const pos = (progress / 100.0) * (status?.durationMillis || 0); - soundRef.current.setPositionAsync(pos); - }} - /> + const [progress] = value; + const pos = (progress / 100.0) * (status?.durationMillis || 0); + soundRef.current.setPositionAsync(pos); + }} + /> + + + {formatTime(status?.positionMillis || 0)} + + + {formatTime(status?.durationMillis || 0)} + + +