mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
refactor: Feature/offline mode rework (#859)
Co-authored-by: lostb1t <coding-mosses0z@icloud.com> Co-authored-by: Fredrik Burmester <fredrik.burmester@gmail.com> Co-authored-by: Gauvain <68083474+Gauvino@users.noreply.github.com> Co-authored-by: Gauvino <uruknarb20@gmail.com> Co-authored-by: storm1er <le.storm1er@gmail.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Chris <182387676+whoopsi-daisy@users.noreply.github.com> Co-authored-by: arch-fan <55891793+arch-fan@users.noreply.github.com> Co-authored-by: Alex Kim <alexkim@Alexs-MacBook-Pro.local>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
132
providers/Downloads/types.ts
Normal file
132
providers/Downloads/types.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import type {
|
||||
BaseItemDto,
|
||||
MediaSourceInfo,
|
||||
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { Bitrate } from "@/components/BitrateSelector";
|
||||
|
||||
/**
|
||||
* Represents the data for downloaded trickplay files.
|
||||
*/
|
||||
export interface TrickPlayData {
|
||||
/** The local directory path where trickplay image sheets are stored. */
|
||||
path: string;
|
||||
/** The total size of all trickplay images in bytes. */
|
||||
size: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the user data for a downloaded item.
|
||||
*/
|
||||
interface UserData {
|
||||
subtitleStreamIndex: number;
|
||||
/** The last known audio stream index. */
|
||||
audioStreamIndex: number;
|
||||
}
|
||||
|
||||
/** Represents a segment of time in a media item, used for intro/credit skipping. */
|
||||
export interface MediaTimeSegment {
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface Segment {
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
/** Represents a single downloaded media item with all necessary metadata for offline playback. */
|
||||
export interface DownloadedItem {
|
||||
/** The Jellyfin item DTO. */
|
||||
item: BaseItemDto;
|
||||
/** The media source information. */
|
||||
mediaSource: MediaSourceInfo;
|
||||
/** The local file path of the downloaded video. */
|
||||
videoFilePath: string;
|
||||
/** The size of the video file in bytes. */
|
||||
videoFileSize: number;
|
||||
/** The local file path of the downloaded trickplay images. */
|
||||
trickPlayData?: TrickPlayData;
|
||||
/** The intro segments for the item. */
|
||||
introSegments?: MediaTimeSegment[];
|
||||
/** The credit segments for the item. */
|
||||
creditSegments?: MediaTimeSegment[];
|
||||
/** The user data for the item. */
|
||||
userData: UserData;
|
||||
}
|
||||
/**
|
||||
* Represents a downloaded Season, containing a map of its episodes.
|
||||
*/
|
||||
export interface DownloadedSeason {
|
||||
/** A map of episode numbers to their downloaded item data. */
|
||||
episodes: Record<number, DownloadedItem>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a downloaded series, containing seasons and their episodes.
|
||||
*/
|
||||
export interface DownloadedSeries {
|
||||
/** The Jellyfin item DTO for the series. */
|
||||
seriesInfo: BaseItemDto;
|
||||
/** A map of season numbers to their downloaded season data. */
|
||||
seasons: Record<
|
||||
number,
|
||||
{
|
||||
/** A map of episode numbers to their downloaded episode data. */
|
||||
episodes: Record<number, DownloadedItem>;
|
||||
}
|
||||
>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main structure for all downloaded content stored locally.
|
||||
* This object is what will be saved to your local storage.
|
||||
*/
|
||||
export interface DownloadsDatabase {
|
||||
/** A map of movie IDs to their downloaded movie data. */
|
||||
movies: Record<string, DownloadedItem>;
|
||||
/** A map of series IDs to their downloaded series data. */
|
||||
series: Record<string, DownloadedSeries>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the status of a download job.
|
||||
*/
|
||||
export type JobStatus = {
|
||||
/** Unique identifier for the download job (also the {@link itemId}) */
|
||||
id: string;
|
||||
/** The input URL for the media to be downloaded (passed in when first downloading) */
|
||||
inputUrl: string;
|
||||
/** The Jellyfin {@link BaseItemDto} associated with this job */
|
||||
item: BaseItemDto;
|
||||
/** The ID of the item being downloaded */
|
||||
itemId: string;
|
||||
/** The device ID where the download is occurring */
|
||||
deviceId: string;
|
||||
/** Download progress as a percentage (0-100) */
|
||||
progress: number;
|
||||
/** Current status of the download job */
|
||||
status:
|
||||
| "downloading" // The job is actively downloading
|
||||
| "paused" // The job is paused
|
||||
| "error" // The job encountered an error
|
||||
| "pending" // The job is waiting to start
|
||||
| "completed" // The job has finished downloading
|
||||
| "queued"; // The job is queued to start
|
||||
/** Timestamp of when the job was created or last updated */
|
||||
timestamp: Date;
|
||||
/** The {@link MediaSourceInfo} for the download */
|
||||
mediaSource: MediaSourceInfo;
|
||||
/** The bit rate we are downloading the media file atq */
|
||||
maxBitrate: Bitrate;
|
||||
/** The number of bytes downloaded so far (optional) */
|
||||
bytesDownloaded?: number;
|
||||
/** The last time the download progress was updated (optional) */
|
||||
lastProgressUpdateTime?: Date;
|
||||
/** Current download speed in bytes per second (optional) */
|
||||
speed?: number;
|
||||
/** Estimated total size of the download in bytes (optional) this is used when we
|
||||
* download transcoded content because we don't know the size of the file until it's downloaded */
|
||||
estimatedTotalSizeBytes?: number;
|
||||
};
|
||||
@@ -380,8 +380,6 @@ function useProtectedRoute(user: UserDto | null, loaded = false) {
|
||||
useEffect(() => {
|
||||
if (loaded === false) return;
|
||||
|
||||
console.log("Loaded", user);
|
||||
|
||||
const inAuthGroup = segments[0] === "(auth)";
|
||||
|
||||
if (!user?.Id && inAuthGroup) {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import type React from "react";
|
||||
import { createContext } from "react";
|
||||
import { useJobProcessor } from "@/utils/atoms/queue";
|
||||
|
||||
const JobQueueContext = createContext(null);
|
||||
|
||||
export const JobQueueProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
useJobProcessor();
|
||||
|
||||
return (
|
||||
<JobQueueContext.Provider value={null}>{children}</JobQueueContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -8,7 +8,7 @@ import { createContext, useCallback, useContext, useState } from "react";
|
||||
import type { Bitrate } from "@/components/BitrateSelector";
|
||||
import { settingsAtom } from "@/utils/atoms/settings";
|
||||
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
|
||||
import generateDeviceProfile from "@/utils/profiles/native";
|
||||
import { generateDeviceProfile } from "@/utils/profiles/native";
|
||||
import { apiAtom, userAtom } from "./JellyfinProvider";
|
||||
|
||||
export type PlaybackType = {
|
||||
@@ -77,7 +77,7 @@ export const PlaySettingsProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
}
|
||||
|
||||
try {
|
||||
const native = await generateDeviceProfile();
|
||||
const native = generateDeviceProfile();
|
||||
const data = await getStreamUrl({
|
||||
api,
|
||||
deviceProfile: native,
|
||||
|
||||
Reference in New Issue
Block a user