fix(artwork): show square background art on portrait heroes
Cycling backdrops reach a fallback path only once every rotating path has failed to load, but every hero passed the rotation-agnostic backdrop list as the rotation set and the aspect-ordered candidates as the fallback. One servable wide backdrop was therefore enough to hide the square background for good, so phone detail and Discover heroes cover-fitted a 16:9 backdrop into a portrait box instead of showing the square image Plex supplies. Give the rotation set the same aspect-aware preference the candidate list already has: near-square containers rotate the square background alone and keep the backdrops behind it as fallbacks. close #1700
This commit is contained in:
@@ -228,6 +228,67 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('MediaItem.heroRotationPaths', () {
|
||||
/// The order `CyclingMediaBackdrop` attempts paths as each one fails:
|
||||
/// every rotating path, then the fallbacks it is not already rotating.
|
||||
/// Only the head of this list is ever displayed by a healthy server.
|
||||
List<String> displayOrder(MediaItem item, double aspect) {
|
||||
final rotation = item.heroRotationPaths(containerAspectRatio: aspect);
|
||||
final candidates = item.heroArtCandidates(containerAspectRatio: aspect);
|
||||
return [...rotation, ...candidates.where((path) => !rotation.contains(path))];
|
||||
}
|
||||
|
||||
test('near-square containers hold on square art instead of rotating backdrops', () {
|
||||
final movie = _movie(
|
||||
backend: MediaBackend.jellyfin,
|
||||
artPath: '/art-0',
|
||||
backdropPaths: ['/art-0', '/art-1'],
|
||||
backgroundSquarePath: '/square',
|
||||
);
|
||||
|
||||
expect(movie.heroRotationPaths(containerAspectRatio: 1.0), ['/square']);
|
||||
});
|
||||
|
||||
test('near-square containers rotate backdrops when there is no square art', () {
|
||||
final movie = _movie(backend: MediaBackend.jellyfin, artPath: '/art-0', backdropPaths: ['/art-0', '/art-1']);
|
||||
|
||||
expect(movie.heroRotationPaths(containerAspectRatio: 1.0), ['/art-0', '/art-1']);
|
||||
});
|
||||
|
||||
test('wide containers rotate backdrops and leave square art behind them', () {
|
||||
final movie = _movie(
|
||||
backend: MediaBackend.jellyfin,
|
||||
artPath: '/art-0',
|
||||
backdropPaths: ['/art-0', '/art-1'],
|
||||
backgroundSquarePath: '/square',
|
||||
);
|
||||
|
||||
expect(movie.heroRotationPaths(containerAspectRatio: 16 / 9), ['/art-0', '/art-1']);
|
||||
});
|
||||
|
||||
test('rotation before fallback reproduces the candidate order at every aspect', () {
|
||||
final episode = testMediaItem(
|
||||
id: 'e-order',
|
||||
backend: MediaBackend.jellyfin,
|
||||
kind: MediaKind.episode,
|
||||
artPath: '/episode-0',
|
||||
backdropPaths: ['/episode-0', '/episode-1'],
|
||||
grandparentArtPath: '/show-0',
|
||||
grandparentBackdropPaths: ['/show-0', '/show-1'],
|
||||
backgroundSquarePath: '/square',
|
||||
serverId: 's1',
|
||||
);
|
||||
|
||||
for (final aspect in [0.75, 1.0, 1.38, 1.39, 16 / 9, 2.4]) {
|
||||
expect(
|
||||
displayOrder(episode, aspect),
|
||||
episode.heroArtCandidates(containerAspectRatio: aspect),
|
||||
reason: 'aspect $aspect',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
group('MediaItem.isPartiallyWatched', () {
|
||||
test('show with some leaves watched is partially watched', () {
|
||||
final show = testMediaItem(
|
||||
|
||||
Reference in New Issue
Block a user