Nix configurations for my homelab
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add patch to jellyfin-web for frame-by-frame scrubbing

yemou 418bf7aa 9b952433

+383
+1
modules/services/jellyfin.nix
··· 12 12 ../../patches/jellyfin-web/0003-refactor-getListViewHtml-to-receive-new-settings-in-.patch 13 13 ../../patches/jellyfin-web/0004-blur-title-and-description-in-episode-details-and-Mo.patch 14 14 ../../patches/jellyfin-web/0005-fix-lint.patch 15 + ../../patches/jellyfin-web/pr7315-per-frame-playback.patch 15 16 ]; 16 17 } 17 18 );
+382
patches/jellyfin-web/pr7315-per-frame-playback.patch
··· 1 + From 3c4cef0f2fedd7417333ba0584c86637a66bd7ab Mon Sep 17 00:00:00 2001 2 + From: "Philipp A." <flying-sheep@web.de> 3 + Date: Sat, 8 Nov 2025 19:05:43 +0100 4 + Subject: [PATCH 01/11] add scrub controls 5 + 6 + --- 7 + src/controllers/playback/video/index.js | 12 ++++++++++++ 8 + 1 file changed, 12 insertions(+) 9 + 10 + diff --git a/src/controllers/playback/video/index.js b/src/controllers/playback/video/index.js 11 + index e3f3d3733aa3..cb81b84d9c20 100644 12 + --- a/src/controllers/playback/video/index.js 13 + +++ b/src/controllers/playback/video/index.js 14 + @@ -1310,6 +1310,18 @@ export default function (view) { 15 + showOsd(btnFastForward); 16 + } 17 + break; 18 + + case ',': 19 + + if (!e.shiftKey) { 20 + + e.preventDefault(); 21 + + playbackManager.seekRelative(currentPlayer, 1); 22 + + } 23 + + break; 24 + + case '.': 25 + + if (!e.shiftKey) { 26 + + e.preventDefault(); 27 + + playbackManager.seekRelative(currentPlayer, -1); 28 + + } 29 + + break; 30 + case 'j': 31 + case 'J': 32 + case 'ArrowLeft': 33 + 34 + From b65f95036745ef61707a2726ef1f03df265fed46 Mon Sep 17 00:00:00 2001 35 + From: "Philipp A." <flying-sheep@web.de> 36 + Date: Sat, 8 Nov 2025 19:08:02 +0100 37 + Subject: [PATCH 02/11] fix direction 38 + 39 + --- 40 + src/controllers/playback/video/index.js | 4 ++-- 41 + 1 file changed, 2 insertions(+), 2 deletions(-) 42 + 43 + diff --git a/src/controllers/playback/video/index.js b/src/controllers/playback/video/index.js 44 + index cb81b84d9c20..85d43cb3526e 100644 45 + --- a/src/controllers/playback/video/index.js 46 + +++ b/src/controllers/playback/video/index.js 47 + @@ -1313,13 +1313,13 @@ export default function (view) { 48 + case ',': 49 + if (!e.shiftKey) { 50 + e.preventDefault(); 51 + - playbackManager.seekRelative(currentPlayer, 1); 52 + + playbackManager.seekRelative(currentPlayer, -1); 53 + } 54 + break; 55 + case '.': 56 + if (!e.shiftKey) { 57 + e.preventDefault(); 58 + - playbackManager.seekRelative(currentPlayer, -1); 59 + + playbackManager.seekRelative(currentPlayer, 1); 60 + } 61 + break; 62 + case 'j': 63 + 64 + From bedceb362c09e4a3677f2abcd75ff221b4511507 Mon Sep 17 00:00:00 2001 65 + From: "Philipp A." <flying-sheep@web.de> 66 + Date: Sat, 8 Nov 2025 19:18:24 +0100 67 + Subject: [PATCH 03/11] fix order 68 + 69 + --- 70 + src/controllers/playback/video/index.js | 4 ++-- 71 + 1 file changed, 2 insertions(+), 2 deletions(-) 72 + 73 + diff --git a/src/controllers/playback/video/index.js b/src/controllers/playback/video/index.js 74 + index 85d43cb3526e..3afb4d8f1c7f 100644 75 + --- a/src/controllers/playback/video/index.js 76 + +++ b/src/controllers/playback/video/index.js 77 + @@ -1313,13 +1313,13 @@ export default function (view) { 78 + case ',': 79 + if (!e.shiftKey) { 80 + e.preventDefault(); 81 + - playbackManager.seekRelative(currentPlayer, -1); 82 + + playbackManager.seekRelative(-1, currentPlayer); 83 + } 84 + break; 85 + case '.': 86 + if (!e.shiftKey) { 87 + e.preventDefault(); 88 + - playbackManager.seekRelative(currentPlayer, 1); 89 + + playbackManager.seekRelative(1, currentPlayer); 90 + } 91 + break; 92 + case 'j': 93 + 94 + From 6023a198d0317e066a0339c985cf480b8ab5e071 Mon Sep 17 00:00:00 2001 95 + From: "Philipp A." <flying-sheep@web.de> 96 + Date: Sat, 8 Nov 2025 20:09:02 +0100 97 + Subject: [PATCH 04/11] hardcode 25fps for now 98 + 99 + --- 100 + src/controllers/playback/video/index.js | 4 ++-- 101 + 1 file changed, 2 insertions(+), 2 deletions(-) 102 + 103 + diff --git a/src/controllers/playback/video/index.js b/src/controllers/playback/video/index.js 104 + index 3afb4d8f1c7f..4a841d10c1d5 100644 105 + --- a/src/controllers/playback/video/index.js 106 + +++ b/src/controllers/playback/video/index.js 107 + @@ -1313,13 +1313,13 @@ export default function (view) { 108 + case ',': 109 + if (!e.shiftKey) { 110 + e.preventDefault(); 111 + - playbackManager.seekRelative(-1, currentPlayer); 112 + + playbackManager.seekRelative(-4e4, currentPlayer); 113 + } 114 + break; 115 + case '.': 116 + if (!e.shiftKey) { 117 + e.preventDefault(); 118 + - playbackManager.seekRelative(1, currentPlayer); 119 + + playbackManager.seekRelative(4e4, currentPlayer); 120 + } 121 + break; 122 + case 'j': 123 + 124 + From 373f30d79d9e708802f1e4df7ccb00334965ccba Mon Sep 17 00:00:00 2001 125 + From: "Philipp A." <flying-sheep@web.de> 126 + Date: Sun, 9 Nov 2025 14:56:52 +0100 127 + Subject: [PATCH 05/11] get fps 128 + 129 + --- 130 + src/components/playback/playbackmanager.js | 18 ++++++++++++++++++ 131 + src/controllers/playback/video/index.js | 4 ++-- 132 + 2 files changed, 20 insertions(+), 2 deletions(-) 133 + 134 + diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js 135 + index f9c1f2512f69..9bd4ef1f594e 100644 136 + --- a/src/components/playback/playbackmanager.js 137 + +++ b/src/components/playback/playbackmanager.js 138 + @@ -1345,6 +1345,10 @@ export class PlaybackManager { 139 + } 140 + }; 141 + 142 + + self.getFps = function (player) { 143 + + self.currentMediaSource(player).MediaStreams.find(s => s.Type === "Video")?.RealFrameRate 144 + + } 145 + + 146 + function getSavedMaxStreamingBitrate(apiClient, mediaType) { 147 + if (!apiClient) { 148 + // This should hopefully never happen 149 + @@ -3885,6 +3889,20 @@ export class PlaybackManager { 150 + this.seekRelative(offsetTicks, player); 151 + } 152 + 153 + + nextFrame(player = this._currentPlayer) { 154 + + const fps = this.getFps(player); 155 + + if (fps != null) { 156 + + this.seekRelative(1e6 / fps, player); 157 + + } 158 + + } 159 + + 160 + + previousFrame(player = this._currentPlayer) { 161 + + const fps = this.getFps(player); 162 + + if (fps != null) { 163 + + this.seekRelative(-1e6 / fps, player); 164 + + } 165 + + } 166 + + 167 + seekPercent(percent, player = this._currentPlayer) { 168 + let ticks = this.duration(player) || 0; 169 + 170 + diff --git a/src/controllers/playback/video/index.js b/src/controllers/playback/video/index.js 171 + index 4a841d10c1d5..2f52aed1c326 100644 172 + --- a/src/controllers/playback/video/index.js 173 + +++ b/src/controllers/playback/video/index.js 174 + @@ -1313,13 +1313,13 @@ export default function (view) { 175 + case ',': 176 + if (!e.shiftKey) { 177 + e.preventDefault(); 178 + - playbackManager.seekRelative(-4e4, currentPlayer); 179 + + playbackManager.previousFrame(currentPlayer); 180 + } 181 + break; 182 + case '.': 183 + if (!e.shiftKey) { 184 + e.preventDefault(); 185 + - playbackManager.seekRelative(4e4, currentPlayer); 186 + + playbackManager.nextFrame(currentPlayer); 187 + } 188 + break; 189 + case 'j': 190 + 191 + From 60469d524d9bef458ec7f97b38c5f684a0b9a97b Mon Sep 17 00:00:00 2001 192 + From: "Philipp A." <flying-sheep@web.de> 193 + Date: Sun, 9 Nov 2025 15:03:11 +0100 194 + Subject: [PATCH 06/11] style 195 + 196 + --- 197 + src/components/playback/playbackmanager.js | 4 ++-- 198 + 1 file changed, 2 insertions(+), 2 deletions(-) 199 + 200 + diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js 201 + index 9bd4ef1f594e..de425de02079 100644 202 + --- a/src/components/playback/playbackmanager.js 203 + +++ b/src/components/playback/playbackmanager.js 204 + @@ -1346,8 +1346,8 @@ export class PlaybackManager { 205 + }; 206 + 207 + self.getFps = function (player) { 208 + - self.currentMediaSource(player).MediaStreams.find(s => s.Type === "Video")?.RealFrameRate 209 + - } 210 + + return self.currentMediaSource(player).MediaStreams.find(s => s.Type === 'Video')?.RealFrameRate; 211 + + }; 212 + 213 + function getSavedMaxStreamingBitrate(apiClient, mediaType) { 214 + if (!apiClient) { 215 + 216 + From a4d1bac7a38e19fc4b63e45737b53ff2ab1ac11e Mon Sep 17 00:00:00 2001 217 + From: "Philipp A." <flying-sheep@web.de> 218 + Date: Sun, 9 Nov 2025 15:17:22 +0100 219 + Subject: [PATCH 07/11] fix exponent 220 + 221 + --- 222 + src/components/playback/playbackmanager.js | 4 ++-- 223 + 1 file changed, 2 insertions(+), 2 deletions(-) 224 + 225 + diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js 226 + index de425de02079..0dbbf0c2e950 100644 227 + --- a/src/components/playback/playbackmanager.js 228 + +++ b/src/components/playback/playbackmanager.js 229 + @@ -3892,14 +3892,14 @@ export class PlaybackManager { 230 + nextFrame(player = this._currentPlayer) { 231 + const fps = this.getFps(player); 232 + if (fps != null) { 233 + - this.seekRelative(1e6 / fps, player); 234 + + this.seekRelative(1e5 / fps, player); 235 + } 236 + } 237 + 238 + previousFrame(player = this._currentPlayer) { 239 + const fps = this.getFps(player); 240 + if (fps != null) { 241 + - this.seekRelative(-1e6 / fps, player); 242 + + this.seekRelative(-1e5 / fps, player); 243 + } 244 + } 245 + 246 + 247 + From 62e82fc4ecdd13d9af3c798bf0c2a59ebc549636 Mon Sep 17 00:00:00 2001 248 + From: "Philipp A." <flying-sheep@web.de> 249 + Date: Sun, 9 Nov 2025 15:29:57 +0100 250 + Subject: [PATCH 08/11] oops 251 + 252 + --- 253 + src/components/playback/playbackmanager.js | 5 +++-- 254 + 1 file changed, 3 insertions(+), 2 deletions(-) 255 + 256 + diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js 257 + index 0dbbf0c2e950..46f49be09fe6 100644 258 + --- a/src/components/playback/playbackmanager.js 259 + +++ b/src/components/playback/playbackmanager.js 260 + @@ -3889,17 +3889,18 @@ export class PlaybackManager { 261 + this.seekRelative(offsetTicks, player); 262 + } 263 + 264 + + // tick = ⅒ns, so ticks/frame = 1e10 ns / 1e5 * 265 + nextFrame(player = this._currentPlayer) { 266 + const fps = this.getFps(player); 267 + if (fps != null) { 268 + - this.seekRelative(1e5 / fps, player); 269 + + this.seekRelative(1e7 / fps, player); 270 + } 271 + } 272 + 273 + previousFrame(player = this._currentPlayer) { 274 + const fps = this.getFps(player); 275 + if (fps != null) { 276 + - this.seekRelative(-1e5 / fps, player); 277 + + this.seekRelative(-1e7 / fps, player); 278 + } 279 + } 280 + 281 + 282 + From 2198a66007fadb83d6e48410b4f1d65a332e9131 Mon Sep 17 00:00:00 2001 283 + From: "Philipp A." <flying-sheep@web.de> 284 + Date: Sun, 9 Nov 2025 15:33:56 +0100 285 + Subject: [PATCH 09/11] remove comment 286 + 287 + --- 288 + src/components/playback/playbackmanager.js | 1 - 289 + 1 file changed, 1 deletion(-) 290 + 291 + diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js 292 + index 46f49be09fe6..96356d946a67 100644 293 + --- a/src/components/playback/playbackmanager.js 294 + +++ b/src/components/playback/playbackmanager.js 295 + @@ -3889,7 +3889,6 @@ export class PlaybackManager { 296 + this.seekRelative(offsetTicks, player); 297 + } 298 + 299 + - // tick = ⅒ns, so ticks/frame = 1e10 ns / 1e5 * 300 + nextFrame(player = this._currentPlayer) { 301 + const fps = this.getFps(player); 302 + if (fps != null) { 303 + 304 + From 1b70b814626ac4c1f497f25c4c6c389e7c7bcd0a Mon Sep 17 00:00:00 2001 305 + From: "Philipp A." <flying-sheep@web.de> 306 + Date: Sun, 9 Nov 2025 15:58:03 +0100 307 + Subject: [PATCH 10/11] use constant 308 + 309 + --- 310 + src/components/playback/playbackmanager.js | 5 +++-- 311 + 1 file changed, 3 insertions(+), 2 deletions(-) 312 + 313 + diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js 314 + index 96356d946a67..4f5f6b3450a8 100644 315 + --- a/src/components/playback/playbackmanager.js 316 + +++ b/src/components/playback/playbackmanager.js 317 + @@ -27,6 +27,7 @@ import { PlayerEvent } from 'apps/stable/features/playback/constants/playerEvent 318 + import { bindMediaSegmentManager } from 'apps/stable/features/playback/utils/mediaSegmentManager'; 319 + import { bindMediaSessionSubscriber } from 'apps/stable/features/playback/utils/mediaSessionSubscriber'; 320 + import { AppFeature } from 'constants/appFeature'; 321 + +import { TICKS_PER_SECOND } from 'constants/time'; 322 + import { ServerConnections } from 'lib/jellyfin-apiclient'; 323 + import { MediaError } from 'types/mediaError'; 324 + import { getMediaError } from 'utils/mediaError'; 325 + @@ -3892,14 +3893,14 @@ export class PlaybackManager { 326 + nextFrame(player = this._currentPlayer) { 327 + const fps = this.getFps(player); 328 + if (fps != null) { 329 + - this.seekRelative(1e7 / fps, player); 330 + + this.seekRelative(TICKS_PER_SECOND / fps, player); 331 + } 332 + } 333 + 334 + previousFrame(player = this._currentPlayer) { 335 + const fps = this.getFps(player); 336 + if (fps != null) { 337 + - this.seekRelative(-1e7 / fps, player); 338 + + this.seekRelative(-TICKS_PER_SECOND / fps, player); 339 + } 340 + } 341 + 342 + 343 + From 148bcd7138fb23f15038e7ac11e53b26c626300b Mon Sep 17 00:00:00 2001 344 + From: "Philipp A." <flying-sheep@web.de> 345 + Date: Sat, 6 Dec 2025 14:10:35 +0100 346 + Subject: [PATCH 11/11] Apply suggestions from code review 347 + 348 + Co-authored-by: Bill Thornton <thornbill@users.noreply.github.com> 349 + --- 350 + src/components/playback/playbackmanager.js | 6 +++--- 351 + 1 file changed, 3 insertions(+), 3 deletions(-) 352 + 353 + diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js 354 + index 0ac75f2057bf..1cbdf1d3a345 100644 355 + --- a/src/components/playback/playbackmanager.js 356 + +++ b/src/components/playback/playbackmanager.js 357 + @@ -1347,7 +1347,7 @@ export class PlaybackManager { 358 + }; 359 + 360 + self.getFps = function (player) { 361 + - return self.currentMediaSource(player).MediaStreams.find(s => s.Type === 'Video')?.RealFrameRate; 362 + + return self.currentMediaSource(player).MediaStreams.find(s => s.Type === 'Video')?.ReferenceFrameRate; 363 + }; 364 + 365 + function getSavedMaxStreamingBitrate(apiClient, mediaType) { 366 + @@ -3891,14 +3891,14 @@ export class PlaybackManager { 367 + 368 + nextFrame(player = this._currentPlayer) { 369 + const fps = this.getFps(player); 370 + - if (fps != null) { 371 + + if (fps) { 372 + this.seekRelative(TICKS_PER_SECOND / fps, player); 373 + } 374 + } 375 + 376 + previousFrame(player = this._currentPlayer) { 377 + const fps = this.getFps(player); 378 + - if (fps != null) { 379 + + if (fps) { 380 + this.seekRelative(-TICKS_PER_SECOND / fps, player); 381 + } 382 + }