Skip to content

Commit 6be9211

Browse files
committed
Release 0.6.0: video preview + popup UI refresh
- Video preview: search results now show a play overlay and duration badge on video assets. Click plays the transcoded stream inline; a new "expand" button pops out a dedicated 960x600 player window via chrome.windows.create. The Copy action is dropped for videos (Share + Download remain). Behind a featureVideoPreview flag (default on). - Inline preview is capped at 150 MB; the dedicated player window is capped at 500 MB, with a graceful "Open in Immich" fallback above the cap or on fetch failure. - Popup tab bar redesigned as a segmented pill control with icons. "Recent" tab renamed to "Saved" for clarity. - About: new "Found a bug or have an idea?" card linking directly to the GitHub issue picker. - "Show Immich matches on Google" now defaults to off for new installs (existing users keep their setting).
1 parent c03d8d8 commit 6be9211

8 files changed

Lines changed: 579 additions & 24 deletions

File tree

lib/immich.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ const DEFAULTS = {
2020
featureContextMenus: true,
2121
featureShareToolbar: true,
2222
featureNotifications: true,
23-
featureGoogleInline: true,
23+
featureGoogleInline: false,
24+
featureVideoPreview: true, // local-only flag, no options UI yet
25+
2426
// Clipboard
2527
clipboardCopyOriginal: false,
2628
// Search & share
@@ -169,6 +171,9 @@ export function viewUrl(serverUrl, assetId) {
169171
export function shareUrl(serverUrl, key) {
170172
return `${serverUrl}/share/${key}`;
171173
}
174+
export function videoPlaybackUrl(serverUrl, assetId) {
175+
return `${apiBase(serverUrl)}/assets/${assetId}/video/playback`;
176+
}
172177

173178
export async function uploadAsset({ blob, filename, deviceAssetId, fileCreatedAt, fileModifiedAt }) {
174179
const cfg = await getConfig();

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "Immich Companion",
4-
"version": "0.5.1",
4+
"version": "0.6.0",
55
"description": "Save images and videos from any page to your self-hosted Immich, search via the omnibox, and turn the new tab into a memory feed.",
66
"minimum_chrome_version": "114",
77
"browser_specific_settings": {

pages/options.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,26 @@ <h1>About</h1>
252252
<li><strong>New tab</strong> — random photo + "On this day".</li>
253253
<li><strong>Share toolbar</strong> — Slideshow + Download-all on share pages.</li>
254254
</ul>
255+
256+
<a href="https://github.com/bjoernch/immich-companion/issues/new/choose"
257+
target="_blank" rel="noopener" class="issue-card">
258+
<div class="issue-card-icon">
259+
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
260+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
261+
<line x1="12" y1="7" x2="12" y2="13"/>
262+
<line x1="12" y1="17" x2="12.01" y2="17"/>
263+
</svg>
264+
</div>
265+
<div class="issue-card-body">
266+
<div class="issue-card-title">Found a bug or have an idea?</div>
267+
<div class="issue-card-sub">Report it on GitHub — bug reports and feature requests welcome.</div>
268+
</div>
269+
<svg class="issue-card-arrow" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
270+
<line x1="7" y1="17" x2="17" y2="7"/>
271+
<polyline points="7 7 17 7 17 17"/>
272+
</svg>
273+
</a>
274+
255275
<div class="row">
256276
<a href="https://github.com/bjoernch/immich-companion" target="_blank" rel="noopener" class="github-btn">
257277
<svg viewBox="0 0 16 16" width="18" height="18" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>

pages/player.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>Immich preview</title>
6+
<link rel="stylesheet" href="shared.css" />
7+
</head>
8+
<body class="player">
9+
<video id="video" controls autoplay playsinline></video>
10+
<div id="status" class="player-status" hidden></div>
11+
<script type="module" src="player.js"></script>
12+
</body>
13+
</html>

pages/player.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { getConfig, isConfigured, videoPlaybackUrl, viewUrl } from "../lib/immich.js";
2+
3+
// Higher cap than the popup's inline 150MB — this window has its own memory
4+
// budget and the user explicitly asked for the bigger view. Above this we
5+
// punt to Immich's own viewer rather than freezing the tab.
6+
const PLAYER_MAX_BYTES = 500 * 1024 * 1024;
7+
8+
const $ = (id) => document.getElementById(id);
9+
10+
function showStatus(text, action) {
11+
const el = $("status");
12+
el.replaceChildren();
13+
const t = document.createElement("div");
14+
t.textContent = text;
15+
el.appendChild(t);
16+
if (action) {
17+
const a = document.createElement("a");
18+
a.href = "#";
19+
a.textContent = action.label;
20+
a.addEventListener("click", (e) => { e.preventDefault(); action.onClick(); });
21+
el.appendChild(a);
22+
}
23+
el.hidden = false;
24+
$("video").hidden = true;
25+
}
26+
27+
async function init() {
28+
const params = new URLSearchParams(location.search);
29+
const id = params.get("id");
30+
if (!id) return showStatus("No asset id provided.");
31+
32+
const cfg = await getConfig();
33+
if (!isConfigured(cfg)) {
34+
return showStatus("Immich is not configured.", {
35+
label: "Open settings",
36+
onClick: () => chrome.runtime.openOptionsPage(),
37+
});
38+
}
39+
40+
if (cfg.theme === "dark" || cfg.theme === "light") {
41+
document.documentElement.setAttribute("data-theme", cfg.theme);
42+
}
43+
44+
// Best-effort: set the window title to the asset's filename.
45+
try {
46+
const meta = await fetch(`${cfg.serverUrl}/api/assets/${id}`, {
47+
headers: { "x-api-key": cfg.apiKey },
48+
}).then((r) => (r.ok ? r.json() : null));
49+
if (meta?.originalFileName) document.title = meta.originalFileName;
50+
} catch {}
51+
52+
let blobUrl;
53+
try {
54+
const res = await fetch(videoPlaybackUrl(cfg.serverUrl, id), {
55+
headers: { "x-api-key": cfg.apiKey },
56+
});
57+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
58+
59+
const len = parseInt(res.headers.get("Content-Length") || "0", 10);
60+
if (len && len > PLAYER_MAX_BYTES) {
61+
return showStatus("Video is too large for inline preview.", {
62+
label: "Open in Immich",
63+
onClick: () => (location.href = viewUrl(cfg.serverUrl, id)),
64+
});
65+
}
66+
67+
const blob = await res.blob();
68+
if (blob.size > PLAYER_MAX_BYTES) {
69+
return showStatus("Video is too large for inline preview.", {
70+
label: "Open in Immich",
71+
onClick: () => (location.href = viewUrl(cfg.serverUrl, id)),
72+
});
73+
}
74+
blobUrl = URL.createObjectURL(blob);
75+
} catch (e) {
76+
return showStatus(`Couldn't load video: ${e.message || e}`, {
77+
label: "Open in Immich",
78+
onClick: () => (location.href = viewUrl(cfg.serverUrl, id)),
79+
});
80+
}
81+
82+
const video = $("video");
83+
video.src = blobUrl;
84+
85+
window.addEventListener("beforeunload", () => {
86+
try { URL.revokeObjectURL(blobUrl); } catch {}
87+
});
88+
}
89+
90+
init().catch((e) => showStatus(`Player error: ${e.message || e}`));

pages/popup.html

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,28 @@
66
</head>
77
<body class="popup">
88
<div class="popup-tabs">
9-
<button class="popup-tab active" data-tab="search">Search</button>
10-
<button class="popup-tab" data-tab="recent">Recent <span id="recentCount" class="badge"></span></button>
11-
<button class="popup-tab" data-tab="upload">Upload</button>
9+
<button class="popup-tab active" data-tab="search">
10+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
11+
<circle cx="11" cy="11" r="7"/>
12+
<path d="m21 21-4.3-4.3"/>
13+
</svg>
14+
<span>Search</span>
15+
</button>
16+
<button class="popup-tab" data-tab="recent">
17+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
18+
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/>
19+
</svg>
20+
<span>Saved</span>
21+
<span id="recentCount" class="badge"></span>
22+
</button>
23+
<button class="popup-tab" data-tab="upload">
24+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
25+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
26+
<polyline points="17 8 12 3 7 8"/>
27+
<line x1="12" y1="3" x2="12" y2="15"/>
28+
</svg>
29+
<span>Upload</span>
30+
</button>
1231
</div>
1332

1433
<!-- Search panel -->
@@ -37,7 +56,7 @@
3756
<!-- Recent uploads panel -->
3857
<div class="popup-panel" data-panel="recent">
3958
<div id="recentEmpty" class="results-state show">
40-
<div>No uploads yet.</div>
59+
<div>Nothing saved yet.</div>
4160
<div class="hint">Right-click any image on the web → "Save to Immich".</div>
4261
</div>
4362
<ul id="recentList" class="recent-list" hidden></ul>

0 commit comments

Comments
 (0)