Compare commits

..

12 Commits

Author SHA1 Message Date
Fredrik Burmester
a99e7b950e fix: pip 2024-08-13 16:21:56 +02:00
Fredrik Burmester
51fc2a0edb chore: versions 2024-08-13 16:02:47 +02:00
Fredrik Burmester
3a13503d1d fix: enable background playback 2024-08-13 16:01:52 +02:00
Fredrik Burmester
2fdf90ab4b fix: enable background play 2024-08-13 16:00:50 +02:00
Fredrik Burmester
6fed0c1c77 fix: change colors 2024-08-13 16:00:43 +02:00
Fredrik Burmester
ee7ff3444e chore: todo 2024-08-13 16:00:39 +02:00
Fredrik Burmester
dec175a300 fix: route to download page 2024-08-13 16:00:26 +02:00
Fredrik Burmester
27099d3184 fix: improve pause/play logic 2024-08-13 16:00:07 +02:00
Fredrik Burmester
bfad77dd7a fix: change color to purple 2024-08-13 15:59:34 +02:00
Fredrik Burmester
74a33f8f82 fix: update download list when a download is finished 2024-08-13 15:59:27 +02:00
Fredrik Burmester
75de878618 fix: show loader for videos but not music 2024-08-13 14:41:37 +02:00
Fredrik Burmester
9628285701 fix: remove parsing of the url 2024-08-13 14:00:18 +02:00
10 changed files with 84 additions and 56 deletions

View File

@@ -2,8 +2,8 @@
"expo": {
"name": "Streamyfin",
"slug": "streamyfin",
"version": "0.3.4",
"orientation": "portrait",
"version": "0.4.0",
"orientation": "default",
"icon": "./assets/images/icon.png",
"scheme": "streamyfin",
"userInterfaceStyle": "dark",
@@ -18,20 +18,15 @@
"requireFullScreen": true,
"infoPlist": {
"NSCameraUsageDescription": "The app needs access to your camera to scan barcodes.",
"NSMicrophoneUsageDescription": "The app needs access to your microphone."
"NSMicrophoneUsageDescription": "The app needs access to your microphone.",
"UIBackgroundModes": ["audio", "movie-playback", "picture-in-picture"]
},
"supportsTablet": true,
"bundleIdentifier": "com.fredrikburmester.streamyfin"
},
"android": {
"jsEngine": "hermes",
"versionCode": 12,
"orientation": "default",
"androidNavigationBar": {
"visible": true,
"barStyle": "dark-content",
"backgroundColor": "#000000"
},
"versionCode": 13,
"adaptiveIcon": {
"foregroundImage": "./assets/images/icon.png"
},

View File

@@ -16,14 +16,18 @@ import { runningProcesses } from "@/utils/atoms/downloads";
import { router } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
import { FFmpegKit } from "ffmpeg-kit-react-native";
import * as FileSystem from "expo-file-system";
const downloads: React.FC = () => {
const [process, setProcess] = useAtom(runningProcesses);
const { data: downloadedFiles, isLoading } = useQuery({
queryKey: ["downloaded_files"],
queryKey: ["downloaded_files", process?.item.Id],
queryFn: async () =>
JSON.parse(
(await AsyncStorage.getItem("downloaded_files")) || "[]",
) as BaseItemDto[],
staleTime: 0,
});
const movies = useMemo(
@@ -41,8 +45,6 @@ const downloads: React.FC = () => {
return Object.values(series);
}, [downloadedFiles]);
const [process, setProcess] = useAtom(runningProcesses);
const eta = useMemo(() => {
const length = process?.item?.RunTimeTicks || 0;
@@ -77,7 +79,7 @@ const downloads: React.FC = () => {
<View>
<Text className="font-semibold">{process.item.Name}</Text>
<Text className="text-xs opacity-50">{process.item.Type}</Text>
<View className="flex flex-row items-center space-x-2 mt-1 text-red-600">
<View className="flex flex-row items-center space-x-2 mt-1 text-purple-600">
<Text className="text-xs">
{process.progress.toFixed(0)}%
</Text>
@@ -97,7 +99,7 @@ const downloads: React.FC = () => {
</TouchableOpacity>
<View
className={`
absolute bottom-0 left-0 h-1 bg-red-600
absolute bottom-0 left-0 h-1 bg-purple-600
`}
style={{
width: process.progress

View File

@@ -55,25 +55,8 @@ const Login: React.FC = () => {
}
};
const parsedServerURL = useMemo(() => {
let parsedServerURL = serverURL.trim();
if (parsedServerURL) {
parsedServerURL = parsedServerURL.endsWith("/")
? parsedServerURL.replace("/", "")
: parsedServerURL;
parsedServerURL = parsedServerURL.startsWith("http")
? parsedServerURL
: "http://" + parsedServerURL;
return parsedServerURL;
}
return "";
}, [serverURL]);
const handleConnect = (url: string) => {
setServer({ address: url });
setServer({ address: url.trim() });
};
if (api?.basePath) {
@@ -165,9 +148,7 @@ const Login: React.FC = () => {
textContentType="URL"
maxLength={500}
/>
<Button onPress={() => handleConnect(parsedServerURL)}>
Connect
</Button>
<Button onPress={() => handleConnect(serverURL)}>Connect</Button>
</View>
</View>
</KeyboardAvoidingView>

View File

@@ -61,7 +61,7 @@ const ContinueWatchingPoster: React.FC<ContinueWatchingPosterProps> = ({
style={{
width: `${progress}%`,
}}
className={`absolute bottom-0 left-0 h-1 bg-red-600 w-full`}
className={`absolute bottom-0 left-0 h-1 bg-purple-600 w-full`}
></View>
</>
)}

View File

@@ -57,6 +57,8 @@ export const CurrentlyPlayingBar: React.FC = () => {
const [paused, setPaused] = useState(true);
const [progress, setProgress] = useState(0);
const [pip, setPip] = useState(false);
const aBottom = useSharedValue(0);
const aPadding = useSharedValue(0);
const aHeight = useSharedValue(100);
@@ -229,14 +231,22 @@ export const CurrentlyPlayingBar: React.FC = () => {
{cp.playbackUrl && (
<Video
ref={videoRef}
allowsExternalPlayback
style={{ width: "100%", height: "100%" }}
allowsExternalPlayback={true}
playInBackground={true}
playWhenInactive={true}
playInBackground={true}
showNotificationControls={true}
ignoreSilentSwitch="ignore"
controls={false}
poster={backdropUrl ? backdropUrl : undefined}
pictureInPicture={true}
onPictureInPictureStatusChanged={(e) => {
setPip(e.isActive);
}}
poster={
backdropUrl && item?.Type === "Audio"
? backdropUrl
: undefined
}
paused={paused}
onProgress={(e) => onProgress(e)}
subtitleStyle={{
@@ -250,8 +260,14 @@ export const CurrentlyPlayingBar: React.FC = () => {
onBuffer={(e) =>
e.isBuffering ? console.log("Buffering...") : null
}
onFullscreenPlayerDidDismiss={() => {
play();
onPlaybackStateChanged={(e) => {
if (e.isPlaying) {
setPaused(false);
} else if (e.isSeeking) {
return;
} else {
setPaused(true);
}
}}
onError={(e) => {
console.log(e);
@@ -261,15 +277,12 @@ export const CurrentlyPlayingBar: React.FC = () => {
);
}}
renderLoader={
item?.Type === "Video" && (
item?.Type !== "Audio" && (
<View className="flex flex-col items-center justify-center h-full">
<ActivityIndicator size={"small"} color={"white"} />
</View>
)
}
subtitleStyle={{
fontSize: 20,
}}
/>
)}
</TouchableOpacity>

View File

@@ -88,7 +88,7 @@ export const DownloadItem: React.FC<DownloadProps> = ({
{process ? (
<TouchableOpacity
onPress={() => {
cancelRemuxing();
router.push("/downloads");
}}
className="flex flex-row items-center"
>
@@ -122,17 +122,14 @@ export const DownloadItem: React.FC<DownloadProps> = ({
) : downloaded ? (
<TouchableOpacity
onPress={() => {
router.push(
`/(auth)/player/offline/page?url=${item.Id}.mp4&itemId=${item.Id}`,
);
router.push("/downloads");
}}
>
<Ionicons name="cloud-download" size={26} color="#16a34a" />
<Ionicons name="cloud-download" size={26} color="#9333ea" />
</TouchableOpacity>
) : (
<TouchableOpacity
onPress={() => {
// downloadFile();
startRemuxing();
}}
>

View File

@@ -13,6 +13,22 @@ export const EpisodeCard: React.FC<{ item: BaseItemDto }> = ({ item }) => {
const { deleteFile } = useFiles();
const [_, setCp] = useAtom(currentlyPlayingItemAtom);
// const fetchFileSize = async () => {
// try {
// const filePath = `${FileSystem.documentDirectory}/${item.Id}.mp4`;
// const info = await FileSystem.getInfoAsync(filePath);
// return info.exists ? info.size : null;
// } catch (e) {
// console.log(e);
// return null;
// }
// };
// const { data: fileSize } = useQuery({
// queryKey: ["fileSize", item?.Id],
// queryFn: fetchFileSize,
// });
const openFile = useCallback(() => {
setCp({
item,
@@ -43,6 +59,12 @@ export const EpisodeCard: React.FC<{ item: BaseItemDto }> = ({ item }) => {
<Text className=" text-xs opacity-50">
Episode {item.IndexNumber}
</Text>
{/* <Text className=" text-xs opacity-50">
Size:{" "}
{fileSize
? `${(fileSize / 1000000).toFixed(0)} MB`
: "Calculating..."}{" "}
</Text> */}
</TouchableOpacity>
</ContextMenu.Trigger>
<ContextMenu.Content

View File

@@ -9,11 +9,23 @@ import { useCallback } from "react";
import * as Haptics from "expo-haptics";
import { useAtom } from "jotai";
import { currentlyPlayingItemAtom } from "../CurrentlyPlayingBar";
import { useQuery } from "@tanstack/react-query";
export const MovieCard: React.FC<{ item: BaseItemDto }> = ({ item }) => {
const { deleteFile } = useFiles();
const [_, setCp] = useAtom(currentlyPlayingItemAtom);
// const fetchFileSize = async () => {
// const filePath = `${FileSystem.documentDirectory}/${item.Id}.mp4`;
// const info = await FileSystem.getInfoAsync(filePath);
// return info.exists ? info.size : null;
// };
// const { data: fileSize } = useQuery({
// queryKey: ["fileSize", item?.Id],
// queryFn: fetchFileSize,
// });
const openFile = useCallback(() => {
setCp({
item,
@@ -41,11 +53,17 @@ export const MovieCard: React.FC<{ item: BaseItemDto }> = ({ item }) => {
className="bg-neutral-900 border border-neutral-800 rounded-2xl p-4"
>
<Text className=" font-bold">{item.Name}</Text>
<View className="flex flex-row items-center justify-between">
<View className="flex flex-col">
<Text className=" text-xs opacity-50">{item.ProductionYear}</Text>
<Text className=" text-xs opacity-50">
{runtimeTicksToMinutes(item.RunTimeTicks)}
</Text>
{/* <Text className=" text-xs opacity-50">
Size:{" "}
{fileSize
? `${(fileSize / 1000000).toFixed(0)} MB`
: "Calculating..."}{" "}
</Text>*/}
</View>
</TouchableOpacity>
</ContextMenu.Trigger>

View File

@@ -12,5 +12,5 @@ export const Colors = {
tint: tintColorDark,
icon: "#9BA1A6",
tabIconDefault: "#9BA1A6",
tabIconSelected: "#EE4B2B",
tabIconSelected: "#9333ea",
};

View File

@@ -21,13 +21,13 @@
}
},
"production": {
"channel": "0.3.4",
"channel": "0.4.0",
"android": {
"image": "latest"
}
},
"production-apk": {
"channel": "0.3.4",
"channel": "0.4.0",
"android": {
"buildType": "apk",
"image": "latest"