diff options
| author | Joe Mou <dev@mou.fo> | 2026-08-27 14:08:06 -0400 |
|---|---|---|
| committer | Joe Mou <dev@mou.fo> | 2026-08-27 14:53:38 -0400 |
| commit | 0d60b8c8235c3c07f675aba7a56dd37b62519f9e (patch) | |
| tree | 8c94cef7b3530e72fc8542072b037081e5bb18ae /cgithub/src/scraper.ts | |
| parent | 16dfeb61f79e6cd44c667422b8dc50f226269d80 (diff) | |
Display PDF blobs
GitHub renders a PDF with a viewer of its own and so ships no content for
one, leaving the blob view with nothing to show. Its raw bytes come as
application/octet-stream, which browsers download rather than display, so
serve them from a new embed route that retypes them and point an <object>
at that.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BErPR92xppzGFXkPqXPYj2
Diffstat (limited to 'cgithub/src/scraper.ts')
| -rw-r--r-- | cgithub/src/scraper.ts | 28 |
1 files changed, 24 insertions, 4 deletions
diff --git a/cgithub/src/scraper.ts b/cgithub/src/scraper.ts index 4220dc4..1a67d05 100644 --- a/cgithub/src/scraper.ts +++ b/cgithub/src/scraper.ts @@ -141,6 +141,7 @@ interface CodeViewBlobLayoutRouteStyledBlob { interface CodeViewBlobRoute { richText: string | null; + renderedFileInfo?: { renderFileType: string | null } | null; } interface SidebarAbout { @@ -250,6 +251,7 @@ export interface GitHubBlob extends GitHubNav { language: string | null; size: string; image: boolean; + renderFileType: string | null; textLines: string[] | null; htmlLines: string[] | null; htmlContent: string | null; @@ -378,15 +380,19 @@ export interface GitHubOwner { repos: OwnerRepo[]; } -async function fetchGitHubResponse(path: string, accept: string): Promise<Response> { - // GitHub throttles/blocks requests without realistic browser headers. - // These headers make the request appear as a standard browser visit. - const headers = { +// GitHub throttles/blocks requests without realistic browser headers. +// These headers make the request appear as a standard browser visit. +function browserHeaders(accept: string): Record<string, string> { + return { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", Accept: accept, "Accept-Language": "en-US,en;q=0.5", }; +} + +async function fetchGitHubResponse(path: string, accept: string): Promise<Response> { + const headers = browserHeaders(accept); const response = await fetch(`https://github.com/${path}`, { headers, redirect: "manual" }); if (response.status === 301 || response.status === 302) { const location = response.headers.get("location"); @@ -546,12 +552,26 @@ export async function getGitHubBlob( language: blob.language || null, size, image: blob.image, + renderFileType: payload?.codeViewBlobRoute?.renderedFileInfo?.renderFileType ?? null, textLines: styledBlob?.rawLines ?? null, htmlLines, htmlContent: payload?.codeViewBlobRoute?.richText ?? null, }); } +// For proxying embeds. +export async function getGitHubRaw( + owner: string, + repo: string, + branch: string, + path: string, +): Promise<Response> { + const url = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path}`; + const response = await fetch(url, { headers: browserHeaders("*/*") }); + if (!response.ok) throw new GitHubHTTPError(response.status, response.statusText); + return response; +} + export async function getGitHubIssues( owner: string, repo: string, |
