diff --git a/frontend/package.json b/frontend/package.json index 49fbacfb..c0a82d11 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,6 +40,7 @@ "radix-vue": "1.7.4", "sortablejs": "1.15.2", "swiper": "11.1.1", + "unique-names-generator": "4.7.1", "uuid": "9.0.1", "vue": "3.4.27", "vue-i18n": "9.13.1", diff --git a/frontend/src/pages/settings/index.vue b/frontend/src/pages/settings/index.vue index cdfffa6e..fde839b2 100644 --- a/frontend/src/pages/settings/index.vue +++ b/frontend/src/pages/settings/index.vue @@ -33,6 +33,10 @@ {{ clientVersion }} + + {{ $t('deviceName') }} + {{ remote.sdk.deviceInfo.name }} + diff --git a/frontend/src/plugins/remote/sdk/sdk-utils.ts b/frontend/src/plugins/remote/sdk/sdk-utils.ts index 01aeefd7..efe7a9cd 100644 --- a/frontend/src/plugins/remote/sdk/sdk-utils.ts +++ b/frontend/src/plugins/remote/sdk/sdk-utils.ts @@ -1,75 +1,55 @@ +/** + * We generate an unique name for this device instead of guessing the user's browser based on the user agent: + * - Browser user agent can be spoofed and is not reliable. + * - There's a major push to deprecate it: https://developers.google.com/privacy-sandbox/blog/user-agent-reduction-deprecation-trial?hl=es-419 + * - Browsers are so ubiquitous that there are multiple form factors and devices that can run them. We can't easily track + * which device the user is using, so the best thing is to not rely on any platform-specific support and provide generic support + * for everything, using browser APIs like MediaCapabilities for playback capability detection. + */ import { type Api, Jellyfin } from '@jellyfin/sdk'; import { v4 } from 'uuid'; -import { - isAndroid, - isApple, - isChrome, - isChromiumBased, - isEdge, - isFirefox, - isMobile, - isTizen, - isWebOS -} from '@/utils/browser-detection'; import { version } from '@/../package.json'; +import { adjectives, animals, colors, countries, languages, names, starWars, uniqueNamesGenerator } from 'unique-names-generator'; +import { destr } from 'destr'; + +interface DeviceInfo { + id: string; + name: string; +} /** - * Returns the device ID, creating it in case it does not exist + * Returns the device ID and name, creating it in case it does not exist */ -function ensureDeviceId(): string { - const storageKey = 'deviceId'; - const val = window.localStorage.getItem(storageKey); +function ensureDeviceInfo(): DeviceInfo { + const storageKey = 'device'; + const storeData = destr(window.localStorage.getItem(storageKey)); + + if (!storeData) { + const payload = { + id: v4(), + name: uniqueNamesGenerator({ + dictionaries: [adjectives, animals, colors, countries, names, languages, starWars], + separator: '-', + length: 3 + }) + }; - if (!val) { - const id = v4(); + window.localStorage.setItem(storageKey, JSON.stringify(payload)); - window.localStorage.setItem(storageKey, id); - return id; + return payload; } - return val; + return storeData; } const SDK = new Jellyfin({ clientInfo: { - name: 'Jellyfin Web (Vue)', + name: 'Jellyfin Vue', version: version }, - deviceInfo: { - name: getDeviceName(), - id: ensureDeviceId() - } + deviceInfo: ensureDeviceInfo() }); -/** - * Gets the device's name based on the browser's user agent. - */ -function getDeviceName(): string { - let deviceName = 'Unknown'; - - if (isChrome()) { - deviceName = 'Chrome'; - } else if (isEdge() && !isChromiumBased()) { - deviceName = 'Edge (EdgeHTML)'; - } else if (isEdge()) { - deviceName = 'Edge (Chromium)'; - } else if (isFirefox()) { - deviceName = 'Firefox'; - } else if (isApple() && !isMobile()) { - deviceName = 'Safari'; - } else if (isWebOS()) { - deviceName = 'LG Smart TV'; - } else if (isTizen()) { - deviceName = 'Samsung Smart TV'; - } else if (isApple() && isMobile()) { - deviceName = 'iPhone'; - } else if (isAndroid()) { - deviceName = 'Android'; - } - - return deviceName; -} - /** * Connects to the given server with the given credentials without * altering the app's API/SDK or axios instance. diff --git a/frontend/src/store/playback-manager.ts b/frontend/src/store/playback-manager.ts index 6b3ebd53..441bb55b 100644 --- a/frontend/src/store/playback-manager.ts +++ b/frontend/src/store/playback-manager.ts @@ -31,7 +31,6 @@ import { remote } from '@/plugins/remote'; import { apiStore } from '@/store/api'; import { getImageInfo } from '@/utils/images'; import { getItemRuntime } from '@/utils/items'; -import playbackProfile from '@/utils/playback-profiles'; import { msToTicks } from '@/utils/time'; import { mediaControls, mediaElementRef } from '@/store'; import { CommonStore } from '@/store/super/common-store'; diff --git a/package-lock.json b/package-lock.json index 8ea0942b..9fe7d768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "radix-vue": "1.7.4", "sortablejs": "1.15.2", "swiper": "11.1.1", + "unique-names-generator": "4.7.1", "uuid": "9.0.1", "vue": "3.4.27", "vue-i18n": "9.13.1", @@ -10399,6 +10400,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unique-names-generator": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.7.1.tgz", + "integrity": "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==", + "engines": { + "node": ">=8" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",