mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-07-15 17:02:58 +01:00
Some checks are pending
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Waiting to run
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Waiting to run
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Waiting to run
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Waiting to run
🏗️ Build Apps / 🍎 Build tvOS IPA (push) Waiting to run
🏗️ Build Apps / 🍎 Build tvOS IPA (Unsigned) (push) Waiting to run
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Waiting to run
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Waiting to run
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Waiting to run
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Waiting to run
🌐 Translation Sync / sync-translations (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Waiting to run
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (i18n:check) (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Waiting to run
🛡️ Trivy Security Scan / 🔎 Filesystem scan (push) Waiting to run
120 lines
3.8 KiB
TypeScript
120 lines
3.8 KiB
TypeScript
import { Directory, File, Paths } from "expo-file-system";
|
|
import { getAllDownloadedItems, getDownloadedItemById } from "./database";
|
|
import type { DownloadedItem } from "./types";
|
|
import { filePathToUri } from "./utils";
|
|
|
|
/**
|
|
* Delete a video file and all associated files (subtitles, trickplay, etc.)
|
|
*/
|
|
export function deleteVideoFile(filePath: string): void {
|
|
try {
|
|
const videoFile = new File(filePathToUri(filePath));
|
|
if (videoFile.exists) {
|
|
videoFile.delete();
|
|
console.log(`[DELETE] Video file deleted: ${filePath}`);
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to delete video file:", error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete all associated files for a downloaded item
|
|
* Includes: video, subtitles, trickplay images
|
|
*/
|
|
export function deleteAllAssociatedFiles(item: DownloadedItem): void {
|
|
try {
|
|
// Delete video file
|
|
if (item.videoFilePath) {
|
|
deleteVideoFile(item.videoFilePath);
|
|
}
|
|
|
|
// Delete subtitle files
|
|
if (item.mediaSource?.MediaStreams) {
|
|
for (const stream of item.mediaSource.MediaStreams) {
|
|
if (
|
|
stream.Type === "Subtitle" &&
|
|
stream.DeliveryMethod === "External" &&
|
|
stream.DeliveryUrl
|
|
) {
|
|
try {
|
|
const subtitleFilename = stream.DeliveryUrl.split("/").pop();
|
|
if (subtitleFilename) {
|
|
const subtitleFile = new File(Paths.document, subtitleFilename);
|
|
if (subtitleFile.exists) {
|
|
subtitleFile.delete();
|
|
console.log(`[DELETE] Subtitle deleted: ${subtitleFilename}`);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("[DELETE] Failed to delete subtitle:", error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete trickplay directory
|
|
if (item.trickPlayData?.path) {
|
|
try {
|
|
const trickplayDirName = item.trickPlayData.path.split("/").pop();
|
|
if (trickplayDirName) {
|
|
const trickplayDir = new Directory(Paths.document, trickplayDirName);
|
|
if (trickplayDir.exists) {
|
|
trickplayDir.delete();
|
|
console.log(
|
|
`[DELETE] Trickplay directory deleted: ${trickplayDirName}`,
|
|
);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("[DELETE] Failed to delete trickplay directory:", error);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("[DELETE] Error deleting associated files:", error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the size of a downloaded item by ID
|
|
* Includes video file size and trickplay data size
|
|
*/
|
|
export function getDownloadedItemSize(id: string): number {
|
|
const item = getDownloadedItemById(id);
|
|
if (!item) return 0;
|
|
|
|
const videoSize = item.videoFileSize || 0;
|
|
const trickplaySize = item.trickPlayData?.size || 0;
|
|
|
|
return videoSize + trickplaySize;
|
|
}
|
|
|
|
/**
|
|
* Calculate total size of all downloaded items
|
|
*/
|
|
export function calculateTotalDownloadedSize(): number {
|
|
const items = getAllDownloadedItems();
|
|
return items.reduce((sum, item) => {
|
|
// Trickplay bytes count too — getDownloadedItemSize models per-item size
|
|
// as video + trickplay, the total must match.
|
|
const trickplaySize = item.trickPlayData?.size ?? 0;
|
|
// Read the live file size on disk so the total reflects actual usage and
|
|
// self-heals items whose stored videoFileSize is 0 (old schema, or
|
|
// `fileInfo.size` was undefined at download time). Fall back to the stored
|
|
// value if the file can't be stat'd.
|
|
if (item.videoFilePath) {
|
|
try {
|
|
const file = new File(filePathToUri(item.videoFilePath));
|
|
if (file.exists) {
|
|
return sum + (file.size ?? item.videoFileSize ?? 0) + trickplaySize;
|
|
}
|
|
} catch (error) {
|
|
console.warn("Failed to stat downloaded file for size:", error);
|
|
}
|
|
}
|
|
return sum + (item.videoFileSize ?? 0) + trickplaySize;
|
|
}, 0);
|
|
}
|