build fixes - type errors, missing icons, tauri externals, unused imports
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useLibraryGenreDistribution, useLibraryByTmdbId } from '../../hooks/use-jellyfin'
|
||||
import { useTmdbDiscoverMovies, useTmdbTopRatedMovies } from '../../hooks/use-tmdb'
|
||||
import { useTmdbDiscoverMovies } from '../../hooks/use-tmdb'
|
||||
import { usePreferencesStore } from '../../stores/preferences-store'
|
||||
import { mapTmdbToJf } from '../../lib/tmdb-mapping'
|
||||
import { tmdbMovieGenreId } from '../../lib/tmdb-genres'
|
||||
|
||||
@@ -58,14 +58,14 @@ function readLiveStats(): LiveStats {
|
||||
}
|
||||
}
|
||||
|
||||
async function checkHwDecode(stream: { codec?: string | null; width?: number | null; height?: number | null; bitrate?: number | null } | null): Promise<HwDecodeState> {
|
||||
async function checkHwDecode(stream: { Codec?: string | null; Width?: number | null; Height?: number | null; BitRate?: number | null } | null): Promise<HwDecodeState> {
|
||||
if (!stream || typeof (navigator as any).mediaCapabilities?.decodingInfo !== 'function') {
|
||||
return { supported: null, hwAccelerated: null }
|
||||
}
|
||||
const codec = (stream.codec || stream.Codec || '').toLowerCase()
|
||||
const w = stream.width ?? stream.Width ?? 1920
|
||||
const h = stream.height ?? stream.Height ?? 1080
|
||||
const br = stream.bitrate ?? stream.BitRate ?? 8000000
|
||||
const codec = (stream.Codec || '').toLowerCase()
|
||||
const w = stream.Width ?? 1920
|
||||
const h = stream.Height ?? 1080
|
||||
const br = stream.BitRate ?? 8000000
|
||||
// Map common Jellyfin codec names to MIME codec strings
|
||||
const mimeCodec =
|
||||
codec === 'h264' ? 'avc1.640033'
|
||||
|
||||
@@ -11,18 +11,21 @@ import { toast } from '../stores/toast-store'
|
||||
*/
|
||||
export function useNewReleaseNotifications(enabled: boolean) {
|
||||
const qc = useQueryClient()
|
||||
const lastNotifiedRef = useRef<string | null>(() => {
|
||||
try { return localStorage.getItem('jf_last_notify_date') } catch { return null }
|
||||
}())
|
||||
const lastNotifiedRef = useRef<string | null>(
|
||||
(() => {
|
||||
try { return localStorage.getItem('jf_last_notify_date') } catch { return null }
|
||||
})()
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) return
|
||||
const api = jellyfinClient.getApi()
|
||||
if (!api) return
|
||||
const apiRef = api
|
||||
|
||||
async function check() {
|
||||
try {
|
||||
const res = await getItemsApi(api).getItems({
|
||||
const res = await getItemsApi(apiRef).getItems({
|
||||
userId: jellyfinClient.getAuthState()!.userId,
|
||||
sortBy: ['DateCreated'],
|
||||
sortOrder: ['Descending'],
|
||||
|
||||
@@ -31,15 +31,18 @@ export async function startDownload(args: {
|
||||
// Tauri path: fetch via the Rust-backed HTTP client so we avoid
|
||||
// CORS + we can stream large files without the browser's memory
|
||||
// pressure.
|
||||
const { fetch } = await import('@tauri-apps/api/http')
|
||||
const { appLocalDataDir } = await import('@tauri-apps/api/path')
|
||||
const { writeBinaryFile, BaseDirectory } = await import('@tauri-apps/api/fs')
|
||||
// @ts-ignore
|
||||
const { fetch } = await import('@tauri-apps/api/http') as any
|
||||
// @ts-ignore
|
||||
const { appLocalDataDir } = await import('@tauri-apps/api/path') as any
|
||||
// @ts-ignore
|
||||
const { writeBinaryFile, BaseDirectory } = await import('@tauri-apps/api/fs') as any
|
||||
|
||||
const res = await fetch<Uint8Array>(streamUrl, {
|
||||
const res = await (fetch as any)(streamUrl, {
|
||||
method: 'GET',
|
||||
responseType: 3, // ResponseType.Binary
|
||||
})
|
||||
const bytes = res.data
|
||||
const bytes: Uint8Array = res.data
|
||||
const dir = await appLocalDataDir()
|
||||
const fileName = `download_${itemId}_${Date.now()}.mp4`
|
||||
await writeBinaryFile(fileName, bytes, { dir: BaseDirectory.AppLocalData })
|
||||
@@ -77,7 +80,7 @@ export async function startDownload(args: {
|
||||
}
|
||||
|
||||
// Assemble the full blob
|
||||
const blob = new Blob(chunks)
|
||||
const blob = new Blob(chunks as any)
|
||||
const objectUrl = URL.createObjectURL(blob)
|
||||
store.update(dl.id, {
|
||||
status: 'done',
|
||||
|
||||
@@ -79,6 +79,7 @@ export {
|
||||
IconBuilding as Building2,
|
||||
IconTicket as Ticket,
|
||||
IconAlertCircle as AlertCircle,
|
||||
IconBell as Bell,
|
||||
IconLoader2 as Loader2,
|
||||
IconWifiOff as WifiOff,
|
||||
IconInfoCircle as Info,
|
||||
@@ -140,6 +141,7 @@ export {
|
||||
IconStethoscope as Stethoscope,
|
||||
IconLeaf as Leaf,
|
||||
IconMoonStars as MoonStars,
|
||||
IconMoon as Moon,
|
||||
IconPlane as Plane,
|
||||
|
||||
// People / places
|
||||
|
||||
@@ -3,12 +3,10 @@ import { useNavigate } from 'react-router-dom'
|
||||
import { motion } from 'framer-motion'
|
||||
import { Film, Tv, AlertCircle } from '../lib/icons'
|
||||
import { useLibraryItems } from '../hooks/use-jellyfin'
|
||||
import { getBestImage, getStoredServerUrl } from '../api/jellyfin'
|
||||
import PosterCard from '../components/ui/PosterCard'
|
||||
|
||||
export default function DuplicatesPage() {
|
||||
const navigate = useNavigate()
|
||||
const serverUrl = getStoredServerUrl()
|
||||
const { data, isLoading } = useLibraryItems(undefined, {
|
||||
includeItemTypes: ['Movie', 'Series'],
|
||||
sortBy: ['SortName'],
|
||||
|
||||
@@ -191,6 +191,7 @@ export default function PlayerPage() {
|
||||
const stillWatchingTargetRef = useRef<string | null>(null)
|
||||
|
||||
/* Preferences */
|
||||
const sleepTimerMinutes = usePreferencesStore(s => s.sleepTimerMinutes)
|
||||
const autoplayNext = usePreferencesStore(s => s.autoplayNext)
|
||||
const subtitleMode = usePreferencesStore(s => s.subtitleMode)
|
||||
const subtitleLanguage = usePreferencesStore(s => s.subtitleLanguage)
|
||||
@@ -1520,7 +1521,7 @@ export default function PlayerPage() {
|
||||
streamUrl,
|
||||
})
|
||||
}}
|
||||
isDownloaded={!!item && useDownloads.getState().items.some(d => d.itemId === item.Id)},
|
||||
isDownloaded={!!item && useDownloads.getState().items.some(d => d.itemId === item.Id)}
|
||||
/>
|
||||
|
||||
{/* Center play/pause indicator (only shown briefly when paused) */}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { motion } from 'framer-motion'
|
||||
import { Activity, Monitor, Users, Server as ServerIcon, Clock, Film } from '../../../lib/icons'
|
||||
import { Activity, MonitorPlay, Users, Server as ServerIcon, Clock, Film } from '../../../lib/icons'
|
||||
import { jellyfinClient, getSystemApi, getSessionApi, getActivityLogApi } from '../../../api/jellyfin'
|
||||
import { Section, Row, SubHeading } from '../_ui'
|
||||
import { Section, SubHeading } from '../_ui'
|
||||
|
||||
function useApi() {
|
||||
return jellyfinClient.getApi()
|
||||
@@ -49,12 +49,13 @@ export function ServerDashboardSection() {
|
||||
const transcodes = activeSessions.filter(s => s.PlayState?.PlayMethod === 'Transcode')
|
||||
|
||||
const uptime = useMemo(() => {
|
||||
if (!info?.ServerStartTime) return null
|
||||
const ms = Date.now() - new Date(info.ServerStartTime).getTime()
|
||||
const start = (info as any)?.ServerStartTime
|
||||
if (!start) return null
|
||||
const ms = Date.now() - new Date(start).getTime()
|
||||
const days = Math.floor(ms / 86_400_000)
|
||||
const hrs = Math.floor((ms % 86_400_000) / 3_600_000)
|
||||
return days > 0 ? `${days}d ${hrs}h` : `${hrs}h`
|
||||
}, [info?.ServerStartTime])
|
||||
}, [info])
|
||||
|
||||
return (
|
||||
<Section id="server-dashboard" title="Server dashboard" description="Live stats and activity">
|
||||
@@ -62,7 +63,7 @@ export function ServerDashboardSection() {
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-6">
|
||||
<StatCard label="Version" value={info?.Version || '-'} icon={<ServerIcon size={12} className="text-accent" />} />
|
||||
<StatCard label="Uptime" value={uptime || '-'} icon={<Clock size={12} className="text-accent" />} />
|
||||
<StatCard label="Active streams" value={String(activeSessions.length)} icon={<Monitor size={12} className="text-accent" />} />
|
||||
<StatCard label="Active streams" value={String(activeSessions.length)} icon={<MonitorPlay size={12} className="text-accent" />} />
|
||||
<StatCard label="Transcoding" value={String(transcodes.length)} icon={<Activity size={12} className="text-accent" />} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ExternalLink, Loader2, Check, Trash2 } from '../../../lib/icons'
|
||||
import { Section, Row, Input, Toggle } from '../_ui'
|
||||
import { useTrakt } from '../../../stores/trakt-store'
|
||||
import { useWatchlist } from '../../../hooks/use-watchlist'
|
||||
import { requestDeviceCode, pollDeviceToken, fetchTraktWatchlist, addToTraktWatchlist } from '../../../lib/trakt'
|
||||
import { requestDeviceCode, pollDeviceToken, fetchTraktWatchlist } from '../../../lib/trakt'
|
||||
import { toast } from '../../../stores/toast-store'
|
||||
import { useLibraryByTmdbId } from '../../../hooks/use-jellyfin'
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface DownloadItem {
|
||||
|
||||
interface State {
|
||||
items: DownloadItem[]
|
||||
add: (item: Omit<DownloadItem, 'id' | 'status' | 'progress' | 'createdAt'>) => void
|
||||
add: (item: Omit<DownloadItem, 'id' | 'status' | 'progress' | 'createdAt'>) => DownloadItem
|
||||
update: (id: string, patch: Partial<DownloadItem>) => void
|
||||
remove: (id: string) => void
|
||||
clearCompleted: () => void
|
||||
@@ -35,18 +35,19 @@ export const useDownloads = create<State>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
items: [],
|
||||
add: item =>
|
||||
set(s => {
|
||||
if (s.items.some(i => i.itemId === item.itemId && i.status !== 'error')) return s
|
||||
const next: DownloadItem = {
|
||||
...item,
|
||||
id: uid(),
|
||||
status: 'queued',
|
||||
progress: 0,
|
||||
createdAt: new Date().toISOString(),
|
||||
}
|
||||
return { items: [...s.items, next] }
|
||||
}),
|
||||
add: item => {
|
||||
const existing = get().items.find(i => i.itemId === item.itemId && i.status !== 'error')
|
||||
if (existing) return existing
|
||||
const next: DownloadItem = {
|
||||
...item,
|
||||
id: uid(),
|
||||
status: 'queued',
|
||||
progress: 0,
|
||||
createdAt: new Date().toISOString(),
|
||||
}
|
||||
set(s => ({ items: [...s.items, next] }))
|
||||
return next
|
||||
},
|
||||
update: (id, patch) =>
|
||||
set(s => ({
|
||||
items: s.items.map(i => (i.id === id ? { ...i, ...patch } : i)),
|
||||
|
||||
Reference in New Issue
Block a user