mirror of
https://github.com/myronblair/kino-app
synced 2026-07-30 06:23:40 -05:00
c174ac60b1
New read-only scan (GET /api/library/cleanup/scan) across movies, episodes, tracks, subtitles, HLS output, transcode queue, and watchlist/progress rows: - Missing files: DB record exists but the file is gone from disk - Orphaned episodes: show_id no longer exists - Orphaned HLS output: transcoded folders left behind by deleted content - Stuck transcode jobs: "running" 2+ hours with no completion (backend-restart artifact) - Duplicate imports: same file imported into the library more than once - Orphaned subtitles / dangling watchlist+progress rows Nothing is deleted until an admin clicks Fix (per-item or Fix All per category) via POST /api/library/cleanup/fix.
123 lines
5.8 KiB
JavaScript
123 lines
5.8 KiB
JavaScript
import { BrowserRouter, Routes, Route, Navigate, useLocation } from "react-router-dom";
|
|
import { Toaster } from "sonner";
|
|
import { AuthProvider, useAuth } from "./lib/auth";
|
|
import { ProfileProvider, useProfile } from "./lib/profile";
|
|
import { MusicPlayerProvider } from "./lib/musicPlayer";
|
|
import Navbar from "./components/Navbar";
|
|
import GrainOverlay from "./components/GrainOverlay";
|
|
import ServiceStatus from "./components/ServiceStatus";
|
|
import MusicPlayerBar from "./components/MusicPlayerBar";
|
|
import Login from "./pages/Login";
|
|
import Register from "./pages/Register";
|
|
import Browse from "./pages/Browse";
|
|
import MyList from "./pages/MyList";
|
|
import Search from "./pages/Search";
|
|
import Player from "./pages/Player";
|
|
import Requests from "./pages/Requests";
|
|
import Admin from "./pages/Admin";
|
|
import AdminUpload from "./pages/AdminUpload";
|
|
import Settings from "./pages/Settings";
|
|
import RadarrImport from "./pages/RadarrImport";
|
|
import TranscodeQueue from "./pages/TranscodeQueue";
|
|
import Shows from "./pages/Shows";
|
|
import ShowDetail from "./pages/ShowDetail";
|
|
import EpisodePlayer from "./pages/EpisodePlayer";
|
|
import SonarrImport from "./pages/SonarrImport";
|
|
import AdminShowEpisodes from "./pages/AdminShowEpisodes";
|
|
import Users from "./pages/Users";
|
|
import Music from "./pages/Music";
|
|
import AlbumDetail from "./pages/AlbumDetail";
|
|
import LibraryScan from "./pages/LibraryScan";
|
|
import LibraryCleanup from "./pages/LibraryCleanup";
|
|
|
|
const ProfileGate = ({ children }) => {
|
|
const { user, loading: authLoading } = useAuth();
|
|
const { active, loading: profLoading } = useProfile();
|
|
const loc = useLocation();
|
|
if (authLoading || profLoading || (user && !active)) {
|
|
return <div className="min-h-screen flex items-center justify-center bg-[#050505] text-[#8A8A8A]">Loading…</div>;
|
|
}
|
|
if (!user) return <Navigate to="/login" state={{ from: loc.pathname }} replace />;
|
|
return children;
|
|
};
|
|
|
|
const AdminGate = ({ children }) => {
|
|
const { user, loading } = useAuth();
|
|
if (loading) return <div className="min-h-screen flex items-center justify-center bg-[#050505] text-[#8A8A8A]">Loading…</div>;
|
|
if (!user) return <Navigate to="/login" replace />;
|
|
if (!user.is_admin) return <Navigate to="/browse" replace />;
|
|
return children;
|
|
};
|
|
|
|
const Shell = ({ children }) => {
|
|
const { user } = useAuth();
|
|
const { active } = useProfile();
|
|
const loc = useLocation();
|
|
const hideChrome = loc.pathname.startsWith("/watch") || loc.pathname === "/login" || loc.pathname === "/register";
|
|
return (
|
|
<>
|
|
{!hideChrome && user && active && <Navbar />}
|
|
{!hideChrome && user?.is_admin && active && <ServiceStatus />}
|
|
{children}
|
|
{!hideChrome && user && active && <MusicPlayerBar />}
|
|
<GrainOverlay />
|
|
</>
|
|
);
|
|
};
|
|
|
|
const RootRedirect = () => {
|
|
const { user, loading } = useAuth();
|
|
const { loading: pl } = useProfile();
|
|
if (loading || pl) return <div className="min-h-screen flex items-center justify-center bg-[#050505] text-[#8A8A8A]">Loading…</div>;
|
|
if (!user) return <Navigate to="/login" replace />;
|
|
return <Navigate to="/browse" replace />;
|
|
};
|
|
|
|
function App() {
|
|
return (
|
|
<div className="App min-h-screen bg-[#050505]">
|
|
<BrowserRouter>
|
|
<AuthProvider>
|
|
<ProfileProvider>
|
|
<MusicPlayerProvider>
|
|
<Shell>
|
|
<Routes>
|
|
<Route path="/" element={<RootRedirect />} />
|
|
<Route path="/login" element={<Login />} />
|
|
<Route path="/register" element={<Register />} />
|
|
<Route path="/browse" element={<ProfileGate><Browse /></ProfileGate>} />
|
|
<Route path="/shows" element={<ProfileGate><Shows /></ProfileGate>} />
|
|
<Route path="/show/:id" element={<ProfileGate><ShowDetail /></ProfileGate>} />
|
|
<Route path="/watch/episode/:id" element={<ProfileGate><EpisodePlayer /></ProfileGate>} />
|
|
<Route path="/my-list" element={<ProfileGate><MyList /></ProfileGate>} />
|
|
<Route path="/search" element={<ProfileGate><Search /></ProfileGate>} />
|
|
<Route path="/watch/:id" element={<ProfileGate><Player /></ProfileGate>} />
|
|
<Route path="/requests" element={<ProfileGate><Requests /></ProfileGate>} />
|
|
<Route path="/settings" element={<ProfileGate><Settings /></ProfileGate>} />
|
|
<Route path="/music" element={<ProfileGate><Music /></ProfileGate>} />
|
|
<Route path="/music/album/:artist/:album" element={<ProfileGate><AlbumDetail /></ProfileGate>} />
|
|
<Route path="/admin" element={<AdminGate><Admin /></AdminGate>} />
|
|
<Route path="/admin/users" element={<AdminGate><Users /></AdminGate>} />
|
|
<Route path="/admin/upload" element={<AdminGate><AdminUpload /></AdminGate>} />
|
|
<Route path="/admin/radarr" element={<AdminGate><RadarrImport /></AdminGate>} />
|
|
<Route path="/admin/sonarr" element={<AdminGate><SonarrImport /></AdminGate>} />
|
|
<Route path="/admin/scan" element={<AdminGate><LibraryScan /></AdminGate>} />
|
|
<Route path="/admin/cleanup" element={<AdminGate><LibraryCleanup /></AdminGate>} />
|
|
<Route path="/admin/shows/:id/episodes" element={<AdminGate><AdminShowEpisodes /></AdminGate>} />
|
|
<Route path="/admin/queue" element={<AdminGate><TranscodeQueue /></AdminGate>} />
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
</Shell>
|
|
<Toaster position="bottom-right" theme="dark" toastOptions={{
|
|
style: { background: "#0F0F0F", border: "1px solid #222", color: "#F2F2F2" },
|
|
}} />
|
|
</MusicPlayerProvider>
|
|
</ProfileProvider>
|
|
</AuthProvider>
|
|
</BrowserRouter>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|