fix(plex): match legacy agent GUIDs

closing #1566
This commit is contained in:
edde746
2026-07-14 18:11:04 +02:00
parent 8c88385977
commit 9d812fa3cc
5 changed files with 350 additions and 22 deletions
@@ -0,0 +1,205 @@
import 'dart:convert';
import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:plezy/database/app_database.dart';
import 'package:plezy/media/media_kind.dart';
import 'package:plezy/services/plex_api_cache.dart';
import 'package:plezy/utils/external_ids.dart';
import '../test_helpers/backend_client_fixtures.dart';
http.Response _json(Object body) => http.Response(jsonEncode(body), 200, headers: {'content-type': 'application/json'});
void main() {
late AppDatabase db;
setUp(() {
db = AppDatabase.forTesting(NativeDatabase.memory());
PlexApiCache.initialize(db);
});
tearDown(() async {
await db.close();
});
test('falls back to an official legacy scalar guid without changing the request contract', () async {
late Uri requestUri;
final client = testPlexClient(
handler: (request) async {
requestUri = request.url;
return _json({
'MediaContainer': {
'Metadata': [
{
'ratingKey': 'legacy-movie',
'type': 'movie',
'title': 'Legacy Movie',
'librarySectionID': 5,
'librarySectionTitle': 'Legacy Movies',
'guid': 'com.plexapp.agents.imdb://tt29768334?lang=en',
'Guid': [
{'id': 'tmdb://999'},
],
},
],
},
});
},
);
addTearDown(client.close);
final match = await client.findByExternalIds(
const ExternalIds(imdb: 'tt29768334'),
kind: MediaKind.movie,
title: 'Legacy Movie',
);
expect(match?.id, 'legacy-movie');
expect(match?.libraryId, '5');
expect(match?.libraryTitle, 'Legacy Movies');
expect(match?.serverId, 'server-1');
expect(match?.serverName, 'Server');
expect(requestUri.path, '/library/all');
expect(requestUri.queryParameters['title'], 'Legacy Movie');
expect(requestUri.queryParameters['type'], '1');
expect(requestUri.queryParameters['includeGuids'], '1');
expect(requestUri.queryParameters['X-Plex-Container-Size'], '20');
expect(requestUri.queryParameters.containsKey('guid'), isFalse);
});
test('matches a HAMA show guid', () async {
final client = testPlexClient(
handler: (request) async => _json({
'MediaContainer': {
'Metadata': [
{
'ratingKey': 'legacy-show',
'type': 'show',
'title': 'Legacy Show',
'guid': 'com.plexapp.agents.hama://tvdb4-315500?lang=en',
},
],
},
}),
);
addTearDown(client.close);
final match = await client.findByExternalIds(
const ExternalIds(tvdb: 315500),
kind: MediaKind.show,
title: 'Legacy Show',
);
expect(match?.id, 'legacy-show');
});
test('prefers a modern Guid match over an earlier legacy candidate', () async {
final client = testPlexClient(
handler: (request) async => _json({
'MediaContainer': {
'Metadata': [
{
'ratingKey': 'legacy-match',
'type': 'movie',
'title': 'Duplicate',
'guid': 'com.plexapp.agents.imdb://tt12345',
},
{
'ratingKey': 'modern-match',
'type': 'movie',
'title': 'Duplicate',
'guid': 'plex://movie/modern',
'Guid': [
{'id': 'imdb://tt12345'},
],
},
],
},
}),
);
addTearDown(client.close);
final match = await client.findByExternalIds(
const ExternalIds(imdb: 'tt12345'),
kind: MediaKind.movie,
title: 'Duplicate',
);
expect(match?.id, 'modern-match');
});
test('prefers an unfiltered modern match over a year-filtered legacy candidate', () async {
final requests = <Uri>[];
final client = testPlexClient(
handler: (request) async {
requests.add(request.url);
final isFiltered = request.url.queryParameters.containsKey('year');
return _json({
'MediaContainer': {
'Metadata': [
if (isFiltered)
{
'ratingKey': 'filtered-legacy',
'type': 'movie',
'title': 'Missing Year',
'guid': 'com.plexapp.agents.themoviedb://42',
}
else
{
'ratingKey': 'unfiltered-modern',
'type': 'movie',
'title': 'Missing Year',
'guid': 'plex://movie/modern',
'Guid': [
{'id': 'tmdb://42'},
],
},
],
},
});
},
);
addTearDown(client.close);
final match = await client.findByExternalIds(
const ExternalIds(tmdb: 42),
kind: MediaKind.movie,
title: 'Missing Year',
year: 2024,
);
expect(match?.id, 'unfiltered-modern');
expect(requests, hasLength(2));
expect(requests.first.queryParameters['year'], '2023,2024,2025');
expect(requests.last.queryParameters.containsKey('year'), isFalse);
});
test('does not match unsupported or malformed scalar GUIDs', () async {
final client = testPlexClient(
handler: (request) async => _json({
'MediaContainer': {
'Metadata': [
{
'ratingKey': 'anidb',
'type': 'show',
'title': 'Unsupported',
'guid': 'com.plexapp.agents.hama://anidb-11905',
},
{'ratingKey': 'wrong-shape', 'type': 'show', 'title': 'Unsupported', 'guid': 315500},
],
},
}),
);
addTearDown(client.close);
final match = await client.findByExternalIds(
const ExternalIds(tvdb: 315500),
kind: MediaKind.show,
title: 'Unsupported',
);
expect(match, isNull);
});
}