mirror of
https://github.com/p-stream/p-stream.git
synced 2026-03-28 16:48:49 +00:00
add debug info copy button to playback error
This commit is contained in:
parent
a7c5821dd3
commit
bd9ff1698f
3 changed files with 210 additions and 3 deletions
|
|
@ -749,6 +749,8 @@
|
|||
"errorNetwork": "Some kind of network error occurred which prevented the media from being successfully fetched, despite having previously been available.",
|
||||
"errorNotSupported": "The media or media provider object is not supported."
|
||||
},
|
||||
"copyDebugInfo": "Copy debug info",
|
||||
"debugInfo": "Check console for more details.",
|
||||
"homeButton": "Go home",
|
||||
"text": "There was an error trying to play the media 😖. Please try again or try a different source!",
|
||||
"title": "Failed to play video!"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import { Button } from "@/components/buttons/Button";
|
|||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { Modal } from "@/components/overlays/Modal";
|
||||
import { DisplayError } from "@/components/player/display/displayInterface";
|
||||
import {
|
||||
formatErrorDebugInfo,
|
||||
gatherErrorDebugInfo,
|
||||
} from "@/utils/errorDebugInfo";
|
||||
|
||||
export function ErrorCard(props: {
|
||||
error: DisplayError | string;
|
||||
|
|
@ -25,7 +29,13 @@ export function ErrorCard(props: {
|
|||
|
||||
function copyError() {
|
||||
if (!props.error || !navigator.clipboard) return;
|
||||
navigator.clipboard.writeText(`\`\`\`${errorMessage}\`\`\``);
|
||||
|
||||
const debugInfo = gatherErrorDebugInfo(props.error);
|
||||
const formattedDebugInfo = formatErrorDebugInfo(debugInfo);
|
||||
|
||||
const fullErrorReport = `\`\`\`\n${errorMessage}\n\n${formattedDebugInfo}\n\`\`\``;
|
||||
|
||||
navigator.clipboard.writeText(fullErrorReport);
|
||||
|
||||
setHasCopied(true);
|
||||
|
||||
|
|
@ -57,7 +67,7 @@ export function ErrorCard(props: {
|
|||
<>
|
||||
<Icon icon={Icons.COPY} className="text-2xl" />
|
||||
<span className="hidden min-[400px]:inline-block ml-3">
|
||||
{t("actions.copy")}
|
||||
{t("player.playbackError.copyDebugInfo")}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
|
@ -74,7 +84,7 @@ export function ErrorCard(props: {
|
|||
<div className="pointer-events-auto mt-4 h-60 select-text overflow-y-auto whitespace-pre text-left">
|
||||
{errorMessage}
|
||||
</div>
|
||||
<p className="mt-4 text-sm">Check console for more details</p>
|
||||
<p className="mt-4 text-sm">{t("player.playbackError.debugInfo")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
195
src/utils/errorDebugInfo.ts
Normal file
195
src/utils/errorDebugInfo.ts
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
import { detect } from "detect-browser";
|
||||
|
||||
import { usePlayerStore } from "@/stores/player/store";
|
||||
|
||||
export interface ErrorDebugInfo {
|
||||
timestamp: string;
|
||||
error: {
|
||||
message: string;
|
||||
type: string;
|
||||
stackTrace?: string;
|
||||
};
|
||||
device: {
|
||||
userAgent: string;
|
||||
browser: string;
|
||||
os: string;
|
||||
isMobile: boolean;
|
||||
isTV: boolean;
|
||||
screenResolution: string;
|
||||
viewportSize: string;
|
||||
};
|
||||
player: {
|
||||
status: string;
|
||||
sourceId: string | null;
|
||||
currentQuality: string | null;
|
||||
meta: {
|
||||
title: string;
|
||||
type: string;
|
||||
tmdbId: string;
|
||||
imdbId?: string;
|
||||
releaseYear: number;
|
||||
season?: number;
|
||||
episode?: number;
|
||||
} | null;
|
||||
};
|
||||
network: {
|
||||
online: boolean;
|
||||
connectionType?: string;
|
||||
effectiveType?: string;
|
||||
downlink?: number;
|
||||
rtt?: number;
|
||||
};
|
||||
|
||||
performance: {
|
||||
memory?: {
|
||||
usedJSHeapSize: number;
|
||||
totalJSHeapSize: number;
|
||||
jsHeapSizeLimit: number;
|
||||
};
|
||||
timing: {
|
||||
navigationStart: number;
|
||||
loadEventEnd: number;
|
||||
domContentLoadedEventEnd: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function gatherErrorDebugInfo(error: any): ErrorDebugInfo {
|
||||
const browserInfo = detect();
|
||||
const isMobile = window.innerWidth <= 768;
|
||||
const isTV =
|
||||
/SmartTV|Tizen|WebOS|SamsungBrowser|HbbTV|Viera|NetCast|AppleTV|Android TV|GoogleTV|Roku|PlayStation|Xbox|Opera TV|AquosBrowser|Hisense|SonyBrowser|SharpBrowser|AFT|Chromecast/i.test(
|
||||
navigator.userAgent,
|
||||
);
|
||||
|
||||
const playerStore = usePlayerStore.getState();
|
||||
|
||||
// Get network information
|
||||
const connection =
|
||||
(navigator as any).connection ||
|
||||
(navigator as any).mozConnection ||
|
||||
(navigator as any).webkitConnection;
|
||||
|
||||
// Get performance information
|
||||
const performanceInfo = performance.getEntriesByType(
|
||||
"navigation",
|
||||
)[0] as PerformanceNavigationTiming;
|
||||
const memory = (performance as any).memory;
|
||||
|
||||
return {
|
||||
timestamp: new Date().toISOString(),
|
||||
error: {
|
||||
message: error?.message || error?.key || String(error),
|
||||
type: error?.type || "unknown",
|
||||
stackTrace: error?.stackTrace || error?.stack,
|
||||
},
|
||||
device: {
|
||||
userAgent: navigator.userAgent,
|
||||
browser: browserInfo?.name || "unknown",
|
||||
os: browserInfo?.os || "unknown",
|
||||
isMobile,
|
||||
isTV,
|
||||
screenResolution: `${window.screen.width}x${window.screen.height}`,
|
||||
viewportSize: `${window.innerWidth}x${window.innerHeight}`,
|
||||
},
|
||||
player: {
|
||||
status: playerStore.status,
|
||||
sourceId: playerStore.sourceId,
|
||||
currentQuality: playerStore.currentQuality,
|
||||
meta: playerStore.meta
|
||||
? {
|
||||
title: playerStore.meta.title,
|
||||
type: playerStore.meta.type,
|
||||
tmdbId: playerStore.meta.tmdbId,
|
||||
imdbId: playerStore.meta.imdbId,
|
||||
releaseYear: playerStore.meta.releaseYear,
|
||||
season: playerStore.meta.season?.number,
|
||||
episode: playerStore.meta.episode?.number,
|
||||
}
|
||||
: null,
|
||||
},
|
||||
network: {
|
||||
online: navigator.onLine,
|
||||
connectionType: connection?.type,
|
||||
effectiveType: connection?.effectiveType,
|
||||
downlink: connection?.downlink,
|
||||
rtt: connection?.rtt,
|
||||
},
|
||||
performance: {
|
||||
memory: memory
|
||||
? {
|
||||
usedJSHeapSize: memory.usedJSHeapSize,
|
||||
totalJSHeapSize: memory.totalJSHeapSize,
|
||||
jsHeapSizeLimit: memory.jsHeapSizeLimit,
|
||||
}
|
||||
: undefined,
|
||||
timing: {
|
||||
navigationStart: performanceInfo?.fetchStart || 0,
|
||||
loadEventEnd: performanceInfo?.loadEventEnd || 0,
|
||||
domContentLoadedEventEnd:
|
||||
performanceInfo?.domContentLoadedEventEnd || 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function formatErrorDebugInfo(info: ErrorDebugInfo): string {
|
||||
const sections = [
|
||||
`=== ERROR DEBUG INFO ===`,
|
||||
`Timestamp: ${info.timestamp}`,
|
||||
``,
|
||||
`=== ERROR DETAILS ===`,
|
||||
`Type: ${info.error.type}`,
|
||||
`Message: ${info.error.message}`,
|
||||
info.error.stackTrace ? `Stack Trace:\n${info.error.stackTrace}` : "",
|
||||
``,
|
||||
`=== DEVICE INFO ===`,
|
||||
`Browser: ${info.device.browser} (${info.device.os})`,
|
||||
`User Agent: ${info.device.userAgent}`,
|
||||
`Screen: ${info.device.screenResolution}`,
|
||||
`Viewport: ${info.device.viewportSize}`,
|
||||
`Mobile: ${info.device.isMobile}`,
|
||||
`TV: ${info.device.isTV}`,
|
||||
``,
|
||||
`=== PLAYER STATE ===`,
|
||||
`Status: ${info.player.status}`,
|
||||
`Source ID: ${info.player.sourceId || "null"}`,
|
||||
`Quality: ${info.player.currentQuality || "null"}`,
|
||||
info.player.meta
|
||||
? [
|
||||
`Media: ${info.player.meta.title} (${info.player.meta.type})`,
|
||||
`TMDB ID: ${info.player.meta.tmdbId}`,
|
||||
info.player.meta.imdbId ? `IMDB ID: ${info.player.meta.imdbId}` : "",
|
||||
`Year: ${info.player.meta.releaseYear}`,
|
||||
info.player.meta.season ? `Season: ${info.player.meta.season}` : "",
|
||||
info.player.meta.episode
|
||||
? `Episode: ${info.player.meta.episode}`
|
||||
: "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
: "No media loaded",
|
||||
``,
|
||||
`=== NETWORK INFO ===`,
|
||||
`Online: ${info.network.online}`,
|
||||
info.network.connectionType
|
||||
? `Connection Type: ${info.network.connectionType}`
|
||||
: "",
|
||||
info.network.effectiveType
|
||||
? `Effective Type: ${info.network.effectiveType}`
|
||||
: "",
|
||||
info.network.downlink ? `Downlink: ${info.network.downlink} Mbps` : "",
|
||||
info.network.rtt ? `RTT: ${info.network.rtt} ms` : "",
|
||||
``,
|
||||
`=== PERFORMANCE ===`,
|
||||
info.performance.memory
|
||||
? [
|
||||
`Memory Used: ${Math.round(info.performance.memory.usedJSHeapSize / 1024 / 1024)} MB`,
|
||||
`Memory Total: ${Math.round(info.performance.memory.totalJSHeapSize / 1024 / 1024)} MB`,
|
||||
`Memory Limit: ${Math.round(info.performance.memory.jsHeapSizeLimit / 1024 / 1024)} MB`,
|
||||
].join("\n")
|
||||
: "Memory info not available",
|
||||
];
|
||||
|
||||
return sections.filter(Boolean).join("\n");
|
||||
}
|
||||
Loading…
Reference in a new issue