@@ -1,4 +1,3 @@
|
|||||||
import 'dart:math' as math;
|
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:cached_network_image_ce/cached_network_image.dart';
|
import 'package:cached_network_image_ce/cached_network_image.dart';
|
||||||
@@ -77,9 +76,8 @@ class JellyfinTrickplayService implements ScrubPreviewSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pure math helper exposed for unit tests: maps a timestamp to the
|
/// Pure math helper exposed for unit tests: maps a timestamp to the
|
||||||
/// sheet index, tile coordinates within the sheet, and the sheet's
|
/// sheet index, tile coordinates within the sheet, and the sheet's full
|
||||||
/// (possibly partial) row/column count. Returns `null` only when the
|
/// row/column count. Returns `null` only when the manifest is degenerate.
|
||||||
/// manifest is degenerate.
|
|
||||||
TrickplayTileLocation? tileLocationFor(Duration time) {
|
TrickplayTileLocation? tileLocationFor(Duration time) {
|
||||||
if (_disposed) return null;
|
if (_disposed) return null;
|
||||||
final info = _info;
|
final info = _info;
|
||||||
@@ -94,17 +92,12 @@ class JellyfinTrickplayService implements ScrubPreviewSource {
|
|||||||
final tileColumn = tileInSheet % info.tileWidth;
|
final tileColumn = tileInSheet % info.tileWidth;
|
||||||
final tileRow = tileInSheet ~/ info.tileWidth;
|
final tileRow = tileInSheet ~/ info.tileWidth;
|
||||||
|
|
||||||
final firstThumbInSheet = sheetIndex * tilesPerSheet;
|
|
||||||
final thumbsInSheet = math.min(tilesPerSheet, info.thumbnailCount - firstThumbInSheet);
|
|
||||||
final sheetColumns = thumbsInSheet >= info.tileWidth ? info.tileWidth : thumbsInSheet;
|
|
||||||
final sheetRows = (thumbsInSheet + info.tileWidth - 1) ~/ info.tileWidth;
|
|
||||||
|
|
||||||
return TrickplayTileLocation(
|
return TrickplayTileLocation(
|
||||||
sheetIndex: sheetIndex,
|
sheetIndex: sheetIndex,
|
||||||
tileColumn: tileColumn,
|
tileColumn: tileColumn,
|
||||||
tileRow: tileRow,
|
tileRow: tileRow,
|
||||||
sheetColumns: sheetColumns,
|
sheetColumns: info.tileWidth,
|
||||||
sheetRows: sheetRows,
|
sheetRows: info.tileHeight,
|
||||||
sourceTileSize: Size(info.width.toDouble(), info.height.toDouble()),
|
sourceTileSize: Size(info.width.toDouble(), info.height.toDouble()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -176,9 +169,9 @@ class JellyfinTrickplayService implements ScrubPreviewSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pure data: which sheet to fetch, which tile within it to display, the
|
/// Pure data: which sheet to fetch, which tile within it to display, the
|
||||||
/// sheet's (possibly partial) row/column count, and the source tile's
|
/// sheet's row/column count, and the source tile's pixel dimensions for
|
||||||
/// pixel dimensions for aspect-correct scaling at render time. Returned
|
/// aspect-correct scaling at render time. Returned by
|
||||||
/// by [JellyfinTrickplayService.tileLocationFor] for unit tests.
|
/// [JellyfinTrickplayService.tileLocationFor] for unit tests.
|
||||||
class TrickplayTileLocation {
|
class TrickplayTileLocation {
|
||||||
final int sheetIndex;
|
final int sheetIndex;
|
||||||
final int tileColumn;
|
final int tileColumn;
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ void main() {
|
|||||||
// 250 thumbnails, 1s apart, 10×10 tiles per sheet ⇒ 3 sheets.
|
// 250 thumbnails, 1s apart, 10×10 tiles per sheet ⇒ 3 sheets.
|
||||||
// sheet 0: indices 0..99
|
// sheet 0: indices 0..99
|
||||||
// sheet 1: indices 100..199 (full)
|
// sheet 1: indices 100..199 (full)
|
||||||
// sheet 2: indices 200..249 (last sheet, only 50 thumbs ⇒ 5 rows)
|
// sheet 2: indices 200..249 (last sheet, padded to a full 10x10 grid)
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
client = await JellyfinClient.create(_conn());
|
client = await JellyfinClient.create(_conn());
|
||||||
@@ -143,9 +143,10 @@ void main() {
|
|||||||
expect(loc.sheetIndex, 2);
|
expect(loc.sheetIndex, 2);
|
||||||
expect(loc.tileColumn, 9);
|
expect(loc.tileColumn, 9);
|
||||||
expect(loc.tileRow, 4);
|
expect(loc.tileRow, 4);
|
||||||
// Sheet 2 has 50 thumbs ⇒ 5 rows, full 10 cols
|
// Jellyfin pads the final tile image to the full configured grid, so
|
||||||
|
// the renderer must keep full sheet geometry even for partial sheets.
|
||||||
expect(loc.sheetColumns, 10);
|
expect(loc.sheetColumns, 10);
|
||||||
expect(loc.sheetRows, 5);
|
expect(loc.sheetRows, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('clamps past the end to the last available thumbnail', () {
|
test('clamps past the end to the last available thumbnail', () {
|
||||||
@@ -168,7 +169,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('JellyfinTrickplayService — partial last sheet sizing', () {
|
group('JellyfinTrickplayService — padded last sheet sizing', () {
|
||||||
late JellyfinClient client;
|
late JellyfinClient client;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
@@ -177,7 +178,7 @@ void main() {
|
|||||||
|
|
||||||
tearDown(() => client.close());
|
tearDown(() => client.close());
|
||||||
|
|
||||||
test('last sheet with 1 thumbnail reports 1 col × 1 row', () {
|
test('last sheet with 1 thumbnail still reports the full sheet grid', () {
|
||||||
// 17 thumbs, 4×4 sheet ⇒ sheet 0 full (16), sheet 1 has 1 thumb only.
|
// 17 thumbs, 4×4 sheet ⇒ sheet 0 full (16), sheet 1 has 1 thumb only.
|
||||||
final svc = JellyfinTrickplayService.create(
|
final svc = JellyfinTrickplayService.create(
|
||||||
client: client,
|
client: client,
|
||||||
@@ -192,8 +193,8 @@ void main() {
|
|||||||
expect(loc.sheetIndex, 1);
|
expect(loc.sheetIndex, 1);
|
||||||
expect(loc.tileColumn, 0);
|
expect(loc.tileColumn, 0);
|
||||||
expect(loc.tileRow, 0);
|
expect(loc.tileRow, 0);
|
||||||
expect(loc.sheetColumns, 1);
|
expect(loc.sheetColumns, 4);
|
||||||
expect(loc.sheetRows, 1);
|
expect(loc.sheetRows, 4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user