mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-20 18:18:04 +00:00
Compare commits
2 Commits
lostb1t-pa
...
codeql-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c0ed076d5 | ||
|
|
118c24ee05 |
6
.github/workflows/ci-codeql.yml
vendored
6
.github/workflows/ci-codeql.yml
vendored
@@ -31,13 +31,13 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: 🏁 Initialize CodeQL
|
- name: 🏁 Initialize CodeQL
|
||||||
uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
|
uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
queries: +security-extended,security-and-quality
|
queries: +security-extended,security-and-quality
|
||||||
|
|
||||||
- name: 🛠️ Autobuild
|
- name: 🛠️ Autobuild
|
||||||
uses: github/codeql-action/autobuild@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
|
uses: github/codeql-action/autobuild@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||||
|
|
||||||
- name: 🧪 Perform CodeQL Analysis
|
- name: 🧪 Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
|
uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||||
|
|||||||
1
.github/workflows/notification.yml
vendored
1
.github/workflows/notification.yml
vendored
@@ -1,4 +1,5 @@
|
|||||||
name: 🛎️ Discord Notification
|
name: 🛎️ Discord Notification
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|||||||
4
app.json
4
app.json
@@ -2,7 +2,7 @@
|
|||||||
"expo": {
|
"expo": {
|
||||||
"name": "Streamyfin",
|
"name": "Streamyfin",
|
||||||
"slug": "streamyfin",
|
"slug": "streamyfin",
|
||||||
"version": "0.40.0",
|
"version": "0.39.0",
|
||||||
"orientation": "default",
|
"orientation": "default",
|
||||||
"icon": "./assets/images/icon.png",
|
"icon": "./assets/images/icon.png",
|
||||||
"scheme": "streamyfin",
|
"scheme": "streamyfin",
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"jsEngine": "hermes",
|
"jsEngine": "hermes",
|
||||||
"versionCode": 72,
|
"versionCode": 71,
|
||||||
"adaptiveIcon": {
|
"adaptiveIcon": {
|
||||||
"foregroundImage": "./assets/images/icon-android-plain.png",
|
"foregroundImage": "./assets/images/icon-android-plain.png",
|
||||||
"monochromeImage": "./assets/images/icon-android-themed.png",
|
"monochromeImage": "./assets/images/icon-android-themed.png",
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ const Page: React.FC = () => {
|
|||||||
const { offline } = useLocalSearchParams() as { offline?: string };
|
const { offline } = useLocalSearchParams() as { offline?: string };
|
||||||
const isOffline = offline === "true";
|
const isOffline = offline === "true";
|
||||||
|
|
||||||
const { data: item, isError } = useItemQuery(itemId, false, undefined, [ItemFields.MediaSources]);
|
const { data: item, isError } = useItemQuery(id, isOffline);
|
||||||
const { data: mediaSourcesitem, isError } = useItemQuery(id, isOffline);
|
|
||||||
|
|
||||||
const opacity = useSharedValue(1);
|
const opacity = useSharedValue(1);
|
||||||
const animatedStyle = useAnimatedStyle(() => {
|
const animatedStyle = useAnimatedStyle(() => {
|
||||||
@@ -91,7 +90,7 @@ const Page: React.FC = () => {
|
|||||||
<View className='h-12 bg-neutral-900 rounded-lg w-full mb-2' />
|
<View className='h-12 bg-neutral-900 rounded-lg w-full mb-2' />
|
||||||
<View className='h-24 bg-neutral-900 rounded-lg mb-1 w-full' />
|
<View className='h-24 bg-neutral-900 rounded-lg mb-1 w-full' />
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
{item && <ItemContent item={item} isOffline={isOffline} mediaSourcesItem={mediaSourcesItem} />}
|
{item && <ItemContent item={item} isOffline={isOffline} />}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -216,9 +216,7 @@ export const HomeIndex = () => {
|
|||||||
|
|
||||||
const latestMediaViews = collections.map((c) => {
|
const latestMediaViews = collections.map((c) => {
|
||||||
const includeItemTypes: BaseItemKind[] =
|
const includeItemTypes: BaseItemKind[] =
|
||||||
c.CollectionType === "tvshows" || c.CollectionType === "movies"
|
c.CollectionType === "tvshows" ? ["Series"] : ["Movie"];
|
||||||
? []
|
|
||||||
: ["Movie"];
|
|
||||||
const title = t("home.recently_added_in", { libraryName: c.Name });
|
const title = t("home.recently_added_in", { libraryName: c.Name });
|
||||||
const queryKey = [
|
const queryKey = [
|
||||||
"home",
|
"home",
|
||||||
|
|||||||
6
eas.json
6
eas.json
@@ -45,14 +45,14 @@
|
|||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
"channel": "0.40.0",
|
"channel": "0.39.0",
|
||||||
"android": {
|
"android": {
|
||||||
"image": "latest"
|
"image": "latest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"production-apk": {
|
"production-apk": {
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
"channel": "0.40.0",
|
"channel": "0.39.0",
|
||||||
"android": {
|
"android": {
|
||||||
"buildType": "apk",
|
"buildType": "apk",
|
||||||
"image": "latest"
|
"image": "latest"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
},
|
},
|
||||||
"production-apk-tv": {
|
"production-apk-tv": {
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
"channel": "0.40.0",
|
"channel": "0.39.0",
|
||||||
"android": {
|
"android": {
|
||||||
"buildType": "apk",
|
"buildType": "apk",
|
||||||
"image": "latest"
|
"image": "latest"
|
||||||
|
|||||||
@@ -1,56 +1,31 @@
|
|||||||
import { getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import { ItemFields } from "@jellyfin/sdk/lib/generated-client/models";
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { useDownload } from "@/providers/DownloadProvider";
|
import { useDownload } from "@/providers/DownloadProvider";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
|
|
||||||
// Helper to exclude specific fields
|
export const useItemQuery = (itemId: string, isOffline: boolean) => {
|
||||||
export const excludeFields = (fieldsToExclude: ItemFields[]) => {
|
|
||||||
return Object.values(ItemFields).filter(
|
|
||||||
(field) => !fieldsToExclude.includes(field)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useItemQuery = (
|
|
||||||
itemId: string | undefined,
|
|
||||||
isOffline?: boolean,
|
|
||||||
fields?: ItemFields[],
|
|
||||||
excludeFields?: ItemFields[]
|
|
||||||
) => {
|
|
||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
const [user] = useAtom(userAtom);
|
const [user] = useAtom(userAtom);
|
||||||
const { getDownloadedItemById } = useDownload();
|
const { getDownloadedItemById } = useDownload();
|
||||||
|
|
||||||
// Calculate final fields: use excludeFields if provided, otherwise use fields
|
|
||||||
const finalFields = excludeFields
|
|
||||||
? Object.values(ItemFields).filter(field => !excludeFields.includes(field))
|
|
||||||
: fields;
|
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["item", itemId, finalFields],
|
queryKey: ["item", itemId],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (!itemId) throw new Error('Item ID is required');
|
|
||||||
|
|
||||||
if (isOffline) {
|
if (isOffline) {
|
||||||
return getDownloadedItemById(itemId)?.item;
|
return getDownloadedItemById(itemId)?.item;
|
||||||
}
|
}
|
||||||
|
if (!api || !user || !itemId) return null;
|
||||||
if (!api || !user) return null;
|
const res = await getUserLibraryApi(api).getItem({
|
||||||
|
itemId: itemId,
|
||||||
const response = await getUserLibraryApi(api).getItem({
|
userId: user?.Id,
|
||||||
itemId,
|
|
||||||
userId: user.Id,
|
|
||||||
...(finalFields && { fields: finalFields }),
|
|
||||||
});
|
});
|
||||||
|
return res.data;
|
||||||
return response.data;
|
|
||||||
},
|
},
|
||||||
enabled: !!itemId,
|
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
refetchOnReconnect: true,
|
refetchOnReconnect: true,
|
||||||
networkMode: "always",
|
networkMode: "always",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -96,7 +96,9 @@ export const useWebSocket = ({
|
|||||||
| Record<string, string>
|
| Record<string, string>
|
||||||
| undefined; // Arguments are Dictionary<string, string>
|
| undefined; // Arguments are Dictionary<string, string>
|
||||||
|
|
||||||
console.log("[WS] ~ ", lastMessage);
|
// Sanitize output to avoid log injection
|
||||||
|
const msgStr = JSON.stringify(lastMessage).replaceAll(/[\n\r]/g, " ");
|
||||||
|
console.log("[WS] ~ %s", msgStr);
|
||||||
|
|
||||||
if (command === "PlayPause") {
|
if (command === "PlayPause") {
|
||||||
console.log("Command ~ PlayPause");
|
console.log("Command ~ PlayPause");
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
setJellyfin(
|
setJellyfin(
|
||||||
() =>
|
() =>
|
||||||
new Jellyfin({
|
new Jellyfin({
|
||||||
clientInfo: { name: "Streamyfin", version: "0.40.0" },
|
clientInfo: { name: "Streamyfin", version: "0.39.0" },
|
||||||
deviceInfo: {
|
deviceInfo: {
|
||||||
name: deviceName,
|
name: deviceName,
|
||||||
id,
|
id,
|
||||||
@@ -87,7 +87,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
return {
|
return {
|
||||||
authorization: `MediaBrowser Client="Streamyfin", Device=${
|
authorization: `MediaBrowser Client="Streamyfin", Device=${
|
||||||
Platform.OS === "android" ? "Android" : "iOS"
|
Platform.OS === "android" ? "Android" : "iOS"
|
||||||
}, DeviceId="${deviceId}", Version="0.40.0"`,
|
}, DeviceId="${deviceId}", Version="0.39.0"`,
|
||||||
};
|
};
|
||||||
}, [deviceId]);
|
}, [deviceId]);
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,9 @@ export const WebSocketProvider = ({ children }: WebSocketProviderProps) => {
|
|||||||
newWebSocket.onmessage = (e) => {
|
newWebSocket.onmessage = (e) => {
|
||||||
try {
|
try {
|
||||||
const message = JSON.parse(e.data);
|
const message = JSON.parse(e.data);
|
||||||
console.log("[WS] Received message:", message);
|
// Sanitize output to avoid log injection
|
||||||
|
const msgStr = JSON.stringify(message).replaceAll(/[\n\r]/g, " ");
|
||||||
|
console.log("[WS] Received message: %s", msgStr);
|
||||||
setLastMessage(message); // Store the last message in context
|
setLastMessage(message); // Store the last message in context
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error parsing WebSocket message:", error);
|
console.error("Error parsing WebSocket message:", error);
|
||||||
|
|||||||
Reference in New Issue
Block a user