diff --git a/src/utils/__tests__/address-candidates.test.ts b/src/utils/__tests__/address-candidates.test.ts index 829a1123..171233db 100644 --- a/src/utils/__tests__/address-candidates.test.ts +++ b/src/utils/__tests__/address-candidates.test.ts @@ -33,12 +33,26 @@ describe('Address Candidates', () => { }); it('should use the specified port when provided', () => { - const candidates = getAddressCandidates('http://example.com:8888'); + const candidates = getAddressCandidates('example.com:8888'); expect(candidates).toHaveLength(2); expect(candidates[0]).toBe('https://example.com:8888/'); expect(candidates[1]).toBe('http://example.com:8888/'); }); + it('should return a single candidate when protocol and port are specified', () => { + let candidates = getAddressCandidates('https://example.com:443'); + expect(candidates).toHaveLength(1); + expect(candidates[0]).toBe('https://example.com/'); + + candidates = getAddressCandidates('http://example.com:80'); + expect(candidates).toHaveLength(1); + expect(candidates[0]).toBe('http://example.com/'); + + candidates = getAddressCandidates('http://example.com:8096'); + expect(candidates).toHaveLength(1); + expect(candidates[0]).toBe('http://example.com:8096/'); + }); + it('should return an empty list for urls with non http(s) protocols', () => { const candidates = getAddressCandidates('ftp://example.com'); expect(candidates).toHaveLength(0); diff --git a/src/utils/address-candidates.ts b/src/utils/address-candidates.ts index 3a5eca5b..8ffafb33 100644 --- a/src/utils/address-candidates.ts +++ b/src/utils/address-candidates.ts @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { HTTPS_PORT, HTTP_PORT, HTTP_PROTOCOL, HTTPS_PROTOCOL, parseUrl, copyUrl, getDefaultPort } from './url'; +import { HTTPS_PORT, HTTP_PORT, HTTP_PROTOCOL, HTTPS_PROTOCOL, parseUrl, copyUrl, getDefaultPort, hasProtocolAndPort } from './url'; /** The default http port for Jellyfin servers. */ export const JF_HTTP_PORT = 8096; @@ -54,14 +54,23 @@ export function getAddressCandidates(input: string): Array { try { const url = parseUrl(input); + + // If the input specifies the protocol and port return it directly + if (hasProtocolAndPort(input, url)) { + return [ url.toString() ]; + } + + // Add the parsed url as a candidate candidates.push(url); + // Always try https if http is specified if (url.protocol === HTTP_PROTOCOL) { const copy = copyUrl(url); copy.protocol = HTTPS_PROTOCOL; candidates.push(copy); } + // Add candidates with JF default ports for candidates using the protocol default port candidates .filter(val => !val.port || val.port === getDefaultPort(val.protocol).toString()) .forEach(val => { @@ -79,9 +88,11 @@ export function getAddressCandidates(input: string): Array { } }); - return candidates - .sort((a, b) => getScore(b) - getScore(a)) - .map(candidate => candidate.toString()); + // Sort by score + candidates.sort((a, b) => getScore(b) - getScore(a)); + + // Return the list of candidate urls as strings + return candidates.map(candidate => candidate.toString()); } catch (err) { console.warn(err); return []; diff --git a/src/utils/url/__tests__/url.test.ts b/src/utils/url/__tests__/url.test.ts index 693a75db..8f5065b1 100644 --- a/src/utils/url/__tests__/url.test.ts +++ b/src/utils/url/__tests__/url.test.ts @@ -6,7 +6,7 @@ import { describe, it, expect } from 'vitest'; -import { getDefaultPort, HTTP_PORT, HTTPS_PORT, HTTPS_PROTOCOL, HTTP_PROTOCOL, copyUrl, parseUrl } from '..'; +import { getDefaultPort, HTTP_PORT, HTTPS_PORT, HTTPS_PROTOCOL, HTTP_PROTOCOL, copyUrl, parseUrl, hasProtocolAndPort } from '..'; /** * Url tests. @@ -40,6 +40,26 @@ describe('Url', () => { }); }); + describe('hasProtocolAndPort()', () => { + it('should return true if the protocol and port are specified', () => { + const urlString = 'https://example.com:443'; + const url = parseUrl(urlString); + expect(hasProtocolAndPort(urlString, url)).toBe(true); + }); + + it('should return false if the port is not specified', () => { + const urlString = 'https://example.com'; + const url = parseUrl(urlString); + expect(hasProtocolAndPort(urlString, url)).toBe(false); + }); + + it('should return false if the protocol is not specified', () => { + const urlString = 'example.com:443'; + const url = parseUrl(urlString); + expect(hasProtocolAndPort(urlString, url)).toBe(false); + }); + }); + describe('parseUrl()', () => { it('should parse a url string', () => { const url = parseUrl('https://example.com'); diff --git a/src/utils/url/index.ts b/src/utils/url/index.ts index 77c421b7..122ddbe5 100644 --- a/src/utils/url/index.ts +++ b/src/utils/url/index.ts @@ -30,6 +30,20 @@ export function getDefaultPort(protocol: string): number { throw new Error('Unsupported protocol'); } +/** + * Checks if the url string specifies the protocol and port. + * @param urlString The url string to test. + * @param url The parsed url object. + * @returns True if the the url string includes the protocol and port. + */ +export function hasProtocolAndPort(urlString: string, url: URL): boolean { + // The parsed URL object drops the default port + const port = url.port || getDefaultPort(url.protocol); + return urlString + .toLowerCase() + .startsWith(`${url.protocol}//${url.hostname.toLowerCase()}:${port}`); +} + /** * Parses a string to a Url object. * @param input A string representing a url.