{"id":51182078,"url":"https://github.com/duongdev/glkvm-web-enhancements","last_synced_at":"2026-06-27T07:30:49.048Z","repository":{"id":359321958,"uuid":"1245525174","full_name":"duongdev/glkvm-web-enhancements","owner":"duongdev","description":"Chromium extension that adds speaker/mic device pickers, Picture-in-Picture, live data totals and a tidier toolbar to the GL.iNet GLKVM web interface.","archived":false,"fork":false,"pushed_at":"2026-05-21T11:31:21.000Z","size":562,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T18:24:19.471Z","etag":null,"topics":["chrome-extension","gl-inet","glkvm","kvm","picture-in-picture","pikvm","webrtc"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/duongdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-21T09:53:38.000Z","updated_at":"2026-05-21T11:31:04.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/duongdev/glkvm-web-enhancements","commit_stats":null,"previous_names":["duongdev/glkvm-web-enhancements"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/duongdev/glkvm-web-enhancements","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duongdev%2Fglkvm-web-enhancements","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duongdev%2Fglkvm-web-enhancements/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duongdev%2Fglkvm-web-enhancements/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duongdev%2Fglkvm-web-enhancements/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/duongdev","download_url":"https://codeload.github.com/duongdev/glkvm-web-enhancements/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duongdev%2Fglkvm-web-enhancements/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34845749,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chrome-extension","gl-inet","glkvm","kvm","picture-in-picture","pikvm","webrtc"],"created_at":"2026-06-27T07:30:48.330Z","updated_at":"2026-06-27T07:30:49.043Z","avatar_url":"https://github.com/duongdev.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"assets/icon.png\" width=\"96\" alt=\"GLKVM Web Enhancements icon\" /\u003e\n\n# GLKVM Web Enhancements\n\nQuality-of-life additions for the [GL.iNet GLKVM](https://www.gl-inet.com/products/gl-rm1/) web interface — speaker/mic device selection, Picture-in-Picture, live data totals, and a tidier toolbar.\n\nA single, dependency-free MV3 content script for Chromium browsers (Chrome / Edge / Brave).\n\n\u003c/div\u003e\n\n---\n\n## Why\n\nThe GLKVM web UI is great, but it always uses your system's **default** speaker and microphone, has no Picture-in-Picture, and the toolbar takes up space. Its frontend is closed-source (shipped as a minified bundle), so these gaps can't be patched directly. This extension layers the missing controls on top of the live page without touching its code.\n\n## Features\n\n| | Feature | How it works |\n|---|---|---|\n| 🔊 | **Speaker picker** | Route KVM audio to any output device via `setSinkId`, re-applied whenever audio (re)starts. |\n| 🎤 | **Microphone picker** | Forces the chosen input by hooking `getUserMedia`; hot-swaps the live track if the mic is already streaming. |\n| 🖼️ | **Picture-in-Picture** | Pops the remote screen into a floating, always-on-top window — with a \"Bring video back\" overlay so you can exit PiP from the otherwise-black page. |\n| 📊 | **Data totals** | Shows total downloaded / uploaded for the session, read from WebRTC `getStats()`, next to the bitrate. |\n| 🧹 | **Auto-collapse toolbar** | Collapses the top toolbar on load and centers the expand handle. |\n| 🔈 | **Speaker / mic on by default** | Optionally enable the remote speaker and/or microphone automatically when the page loads. |\n| ⌨️ | **Capture keyboard shortcuts** | Uses the Keyboard Lock API in fullscreen to forward Cmd+R/W/T and other shortcuts to the remote instead of the local browser. |\n| 🖥️ | **Status bar in fullscreen** | Keeps the bottom bar (speaker, mic, stats) and toolbar visible in fullscreen by intercepting GLKVM's fullscreen button and suppressing its `fullscreenchange` handler. |\n| 🎨 | **Active-state icon** | The toolbar icon shows in color only on tabs where the extension is running, and stays monotone everywhere else. |\n| ⚙️ | **Options page** | Toggle each behavior on/off; preferences sync across your Chrome profile. |\n\nThe device pickers appear as small chevrons next to the existing speaker/mic icons in the bottom status bar, plus a Picture-in-Picture button:\n\n![Status bar controls](assets/toolbar.png)\n\nThe session data totals sit inline with the stream info:\n\n![Live data totals](assets/stats.png)\n\n## Install\n\nThis is an unpacked extension (not on the Web Store).\n\n1. Download or clone this repository.\n2. Open `chrome://extensions` (or `edge://extensions`).\n3. Enable **Developer mode**.\n4. Click **Load unpacked** and select the project folder.\n5. Open / refresh your GLKVM tab. On first use, click a device chevron and **allow microphone access once** so Chrome reveals device names.\n\n\u003e Running GLKVM as a window? **⋮ → Cast, Save, and Share → Create Shortcut… → \"Open as window\"** gives an app-like window. The content script injects there too.\n\n## Configuration\n\nBy default the script runs on `glkvm.local`, any Tailscale (`*.ts.net`) host, and the GL.iNet remote-access domain (`*.glkvm.com`). If you reach your KVM another way (LAN IP, custom hostname), add it to `content_scripts[0].matches` in `manifest.json`:\n\n```json\n\"matches\": [\n  \"https://glkvm.local/*\",\n  \"https://*.ts.net/*\",\n  \"https://*.glkvm.com/*\",\n  \"https://192.168.1.50/*\"\n]\n```\n\nThen reload the extension.\n\n## Options\n\n**Click the toolbar icon** to open the settings popup. Each feature can be toggled independently:\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"assets/popup.png\" width=\"320\" alt=\"Settings popup\" /\u003e\u003c/p\u003e\n\n**Display**\n- **Collapse toolbar by default**\n- **Show network consumption**\n- **Show status bar in fullscreen** — keeps the bottom bar and toolbar visible while in fullscreen\n\n**Audio**\n- **Enable speaker by default**\n- **Enable microphone by default** (off by default — turning this on prompts for mic access and starts capturing on every load)\n\n**Keyboard**\n- **Capture keyboard shortcuts** — in fullscreen, sends Cmd+R/W/T and similar shortcuts to the remote instead of the local browser (uses the Keyboard Lock API; Cmd+Q and Cmd+Tab remain OS-reserved)\n\nSettings are stored in `chrome.storage.sync` and apply on the next page load; the network-stats toggle applies live. The popup talks to `chrome.storage`, while the main script runs in the page's MAIN world (no `chrome.*` access) — a small isolated-world bridge mirrors the settings between them.\n\n## How it works\n\nThe script runs in the page's **MAIN world** at `document_start`, so it is in place before the SPA initializes WebRTC:\n\n- Wraps `navigator.mediaDevices.getUserMedia` to inject the selected microphone `deviceId`.\n- Wraps the `RTCPeerConnection` constructor to track connections (the KVM video stream is receive-only, so `addTrack` is never called) — used for live mic swapping and reading transfer stats.\n- Manipulates the existing `\u003cvideo\u003e` / `\u003caudio\u003e` elements directly for output routing and PiP.\n- Re-injects its UI via a `MutationObserver` if the Vue-rendered status bar re-mounts.\n\nSelections persist in `localStorage`. **No data leaves your browser** — there are no network requests, analytics, or external dependencies, and the extension requests no special permissions beyond the host match.\n\n## Compatibility\n\n- **Chromium browsers (Chrome 111+, Edge, Brave):** full support — MV3 `world: \"MAIN\"` content scripts are required.\n- **Safari:** the script logic is portable, but Safari needs an Xcode wrapper and its `setSinkId` support is limited; not packaged here.\n\n## Limitations\n\n- Output (speaker) selection requires the page to be actively playing audio; it's applied on the next `play` event.\n- Microphone selection applies live if a mic is already active, otherwise on the next time the mic is enabled.\n- Data totals reflect the current WebRTC connection and reset on reconnect.\n\n## License\n\n[MIT](LICENSE)\n\n---\n\n\u003csub\u003eNot affiliated with or endorsed by GL.iNet. \"GLKVM\" is a trademark of its respective owner.\u003c/sub\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fduongdev%2Fglkvm-web-enhancements","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fduongdev%2Fglkvm-web-enhancements","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fduongdev%2Fglkvm-web-enhancements/lists"}