Goal:
To improve Emby/Jellyfin playback performance by redirecting media requests to high-speed direct links (via Openlist/115 Drive), bypassing the low-bandwidth Emby server
Core Workflow (The "Why"):
- Intercept: The HonoJS server acts as a middleware/proxy for the Emby Client/Emby Web
- Rewrite: When a specific API request (e.g.,
/Items/{Id}/PlaybackInfo) is detected:- Extract the media hash/id
- Resolve the direct download link using
/packages/openlist - Replace the original media source URL in the JSON response
- Proxy: All other non-media requests (images, metadata) are transparently proxied to the upstream Emby server
Key Concepts:
- Upstream: The original Emby Server
- Direct Link: The short-lived, high-speed URL from the cloud storage
- Rewriter: The logic that patches the JSON response body
- Runtime: Deno 2.x (Use
denofor all commands) - Package Management: Deno Workspaces (No
package.json, usedeno.jsonimports) - Build Tool (App): Rsbuild (Rspack based)
- Build Tool (Lib): Rslib (Rspack based)
- Framework: HonoJS (Deno adapter)
- Logic: Pure TypeScript
- Framework: react 19
- UI: BaseUI
/apps/web: react 19 Frontend application (built with Rsbuild)/apps/server: HonoJS Backend server/packages/shared: shared types/logic/packages/openlist: openlist logic for get direct url/packages/emby: emby login for rewrite emby response (built with Rslib)/deno.json: Root configuration
- Use Web Standard APIs (Request, Response, fetch)
Before finalizing any code change, ensure it passes strict Deno standards:
- Format: Code must match
deno fmtrules - Lint: No
deno linterrors - Types: Must pass
deno check. DO NOT use@ts-ignoreunless absolutely necessary and documented
"useTabs": false,
"lineWidth": 120,
"indentWidth": 2,
"semiColons": true,
"singleQuote": false,
"proseWrap": "preserve",# test
deno test
# lint
deno lint
# types check
deno check
# format code
deno fmt
# server dev watch mode
deno task dev
# compile server
deno task compileFollow BDD (Behavior Driven Development) style using Deno Standard Library. Do not use Jest or Vitest syntax
import { describe, it } from "@std/testing/bdd";
import { expect } from "@std/expect";
import { FakeTime } from "@std/testing/time";
import { calculateMaxAgeMs, getCommonDataFromRequest, isWebBrowser, playbackPositionTicksToSeconds } from "./utils.ts";
describe("playbackPositionTicksToSeconds", () => {
it("basic", () => {
expect(playbackPositionTicksToSeconds(10_000_000)).toBe("1");
expect(playbackPositionTicksToSeconds(0)).toBe("0");
expect(playbackPositionTicksToSeconds(1_359_000)).toBe("0.135");
expect(playbackPositionTicksToSeconds(1_354_000)).toBe("0.135");
});
it("custom fraction digits", () => {
// 0.0019 seconds = 19,000 ticks
const ticks = 19000;
expect(playbackPositionTicksToSeconds(ticks)).toBe("0.001");
expect(playbackPositionTicksToSeconds(ticks, { fractionDigits: 2 })).toBe("0");
expect(playbackPositionTicksToSeconds(ticks, { fractionDigits: 4 })).toBe("0.0019");
});
});