mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 05:06:16 -08:00
fix(playlist): cover the generic-URL fallback in the multi-MAC portal ambiguity guard
The round-2 guard judged ambiguity over portal-shaped URLs only, while the portal picker falls back to a generic URL when no shaped one exists — two root-URL panels with several MACs still cross-paired through that fallback. The guard now inspects the exact pool the picker draws from. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JE1De2dfyRZMYh4H2iGXb4
This commit is contained in:
2 files changed
+37
-9
No files matched your search
@@ -699,6 +699,26 @@ describe('detectProviderImportCandidates', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('treats several MACs with generic URLs on different hosts as ambiguous too', () => {
|
||||
// Without portal-shaped URLs the picker falls back to a generic
|
||||
// URL — the ambiguity guard must cover that pool as well, or two
|
||||
// root-URL panels would still cross-pair through the fallback.
|
||||
const candidates = detectProviderImportCandidates(
|
||||
[
|
||||
'http://panel-a.example.com',
|
||||
'MAC: 00:1A:79:AA:AA:AA',
|
||||
'http://panel-b.example.com',
|
||||
'MAC: 00:1A:79:BB:BB:BB',
|
||||
].join('\n')
|
||||
);
|
||||
|
||||
const stalker = only(candidates, 'stalker');
|
||||
expect(stalker).toHaveLength(2);
|
||||
for (const candidate of stalker) {
|
||||
expect(candidate.portalUrl).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('surfaces every MAC of a long multi-account list', () => {
|
||||
const macs = Array.from(
|
||||
{ length: 6 },
|
||||
|
||||
@@ -115,18 +115,26 @@ export function detectProviderImportCandidates(
|
||||
// label belongs to — and a device ID submitted with the wrong MAC is
|
||||
// pinned by the portal permanently. Multi-MAC candidates therefore carry
|
||||
// portal + MAC only, and the user supplies identity per account.
|
||||
// Several MACs meeting portal-shaped URLs on DIFFERENT hosts is two
|
||||
// Several MACs meeting portal candidates on DIFFERENT hosts is two
|
||||
// accounts from two panels in one paste — there is no deterministic way
|
||||
// to say which MAC belongs to which portal, so those candidates carry the
|
||||
// MAC alone instead of prefilling half of them with the wrong panel.
|
||||
// Same-host URL pairs (the Real/Panel lines of scanner dumps) stay
|
||||
// unambiguous, as does one portal shared by a whole MAC list.
|
||||
const stalkerHosts = new Set(
|
||||
urls
|
||||
.filter((url) => url.role === 'stalker')
|
||||
.map((url) => url.parsed.hostname)
|
||||
);
|
||||
const portalAmbiguous = macs.length > 1 && stalkerHosts.size > 1;
|
||||
// The ambiguity is judged over the POOL `pickStalkerPortalUrl` would
|
||||
// actually draw from: portal-shaped URLs when any exist, otherwise the
|
||||
// generic-URL fallback — a guard on the shaped pool alone would let two
|
||||
// root-URL panels slip through the fallback door. A single labeled
|
||||
// "Portal:" line, same-host Real/Panel pairs, and one portal shared by a
|
||||
// whole MAC list stay unambiguous.
|
||||
const shapedPortals = urls.filter((url) => url.role === 'stalker');
|
||||
const portalPool =
|
||||
shapedPortals.length > 0
|
||||
? shapedPortals
|
||||
: labeledHostUrl(labeled) !== undefined
|
||||
? []
|
||||
: urls.filter((url) => url.role === 'generic');
|
||||
const portalAmbiguous =
|
||||
macs.length > 1 &&
|
||||
new Set(portalPool.map((url) => url.parsed.hostname)).size > 1;
|
||||
const portal = portalAmbiguous
|
||||
? null
|
||||
: pickStalkerPortalUrl(urls, labeled);
|
||||
|
||||
Reference in new issue
Block a user