fix: support credential extraction from Emby
This commit is contained in:
@@ -8,6 +8,47 @@ interface Props {
|
|||||||
onCredentialsRetrieved: (credentials: AppState['settings']['jellyfin']) => void;
|
onCredentialsRetrieved: (credentials: AppState['settings']['jellyfin']) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CredentialEventData = {
|
||||||
|
credentials: {
|
||||||
|
Servers: {
|
||||||
|
ManualAddress: string,
|
||||||
|
ManualAddressOnly: boolean,
|
||||||
|
IsLocalServer: boolean,
|
||||||
|
DateLastAccessed: number,
|
||||||
|
LastConnectionMode: number,
|
||||||
|
Type: string,
|
||||||
|
Name: string,
|
||||||
|
Id: string,
|
||||||
|
UserId: string | null,
|
||||||
|
AccessToken: string | null,
|
||||||
|
Users: {
|
||||||
|
UserId: string,
|
||||||
|
AccessToken: string,
|
||||||
|
}[]
|
||||||
|
LocalAddress: string,
|
||||||
|
RemoteAddress: string,
|
||||||
|
}[]
|
||||||
|
},
|
||||||
|
deviceId: string,
|
||||||
|
type: 'emby',
|
||||||
|
} | {
|
||||||
|
credentials: {
|
||||||
|
Servers: {
|
||||||
|
ManualAddress: string,
|
||||||
|
manualAddressOnly: boolean,
|
||||||
|
DateLastAccessed: number,
|
||||||
|
LastConnectionMode: number,
|
||||||
|
Name: string,
|
||||||
|
Id: string,
|
||||||
|
UserId: string | null,
|
||||||
|
AccessToken: string | null,
|
||||||
|
LocalAddress: string,
|
||||||
|
}[]
|
||||||
|
},
|
||||||
|
deviceId: string,
|
||||||
|
type: 'jellyfin',
|
||||||
|
} | undefined;
|
||||||
|
|
||||||
class CredentialGenerator extends Component<Props> {
|
class CredentialGenerator extends Component<Props> {
|
||||||
ref = createRef<WebView>();
|
ref = createRef<WebView>();
|
||||||
|
|
||||||
@@ -18,12 +59,18 @@ class CredentialGenerator extends Component<Props> {
|
|||||||
|
|
||||||
checkIfCredentialsAreThere = debounce(() => {
|
checkIfCredentialsAreThere = debounce(() => {
|
||||||
// Inject some javascript to check if the credentials can be extracted
|
// Inject some javascript to check if the credentials can be extracted
|
||||||
// from localstore
|
// from localstore. We simultaneously attempt to extract credentials for
|
||||||
|
// any back-end.
|
||||||
this.ref.current?.injectJavaScript(`
|
this.ref.current?.injectJavaScript(`
|
||||||
try {
|
try {
|
||||||
let credentials = JSON.parse(window.localStorage.getItem('jellyfin_credentials'));
|
let credentials = JSON.parse(window.localStorage.getItem('jellyfin_credentials'));
|
||||||
let deviceId = window.localStorage.getItem('_deviceId2');
|
let deviceId = window.localStorage.getItem('_deviceId2');
|
||||||
window.ReactNativeWebView.postMessage(JSON.stringify({ credentials, deviceId }))
|
window.ReactNativeWebView.postMessage(JSON.stringify({ credentials, deviceId, type: 'jellyfin' }))
|
||||||
|
} catch(e) { }; true;
|
||||||
|
try {
|
||||||
|
let credentials = JSON.parse(window.localStorage.getItem('servercredentials3'));
|
||||||
|
let deviceId = window.localStorage.getItem('_deviceId2');
|
||||||
|
window.ReactNativeWebView.postMessage(JSON.stringify({ credentials, deviceId, type: 'emby' }))
|
||||||
} catch(e) { }; true;
|
} catch(e) { }; true;
|
||||||
`);
|
`);
|
||||||
}, 500);
|
}, 500);
|
||||||
@@ -35,35 +82,71 @@ class CredentialGenerator extends Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the content
|
// Parse the content
|
||||||
const data = JSON.parse(event.nativeEvent.data);
|
const data = JSON.parse(event.nativeEvent.data) as CredentialEventData;
|
||||||
|
if (__DEV__) {
|
||||||
|
console.log('Received credential event data: ', JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
if (!data.deviceId
|
// Since Jellyfin and Emby are similar, we'll attempt to extract the
|
||||||
|| !data.credentials?.Servers?.length
|
// credentials in a generic way.
|
||||||
|| !data.credentials?.Servers[0]?.UserId
|
let userId: string | undefined, accessToken: string | undefined;
|
||||||
|| !data.credentials?.Servers[0]?.AccessToken) {
|
|
||||||
|
// GUARD: Attempt to extract emby format credentials
|
||||||
|
if (data?.type === 'emby'
|
||||||
|
&& data.credentials?.Servers?.length
|
||||||
|
&& data.credentials?.Servers[0]?.Users?.length
|
||||||
|
) {
|
||||||
|
userId = data.credentials.Servers[0].Users[0].UserId;
|
||||||
|
accessToken = data.credentials.Servers[0].Users[0].AccessToken;
|
||||||
|
// GUARD: Attempt to extract jellyfin format credentials
|
||||||
|
} else if (data?.type === 'jellyfin'
|
||||||
|
&& data.credentials?.Servers?.length
|
||||||
|
) {
|
||||||
|
userId = data.credentials.Servers[0].UserId || undefined;
|
||||||
|
accessToken = data.credentials.Servers[0].AccessToken || undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can extract the deviceId and server address in the same way for
|
||||||
|
// both Jellyfin and Emby.
|
||||||
|
const deviceId = data?.deviceId;
|
||||||
|
const address = data?.credentials?.Servers?.length
|
||||||
|
&& data?.credentials.Servers[0].ManualAddress;
|
||||||
|
|
||||||
|
// GUARD: log extract credentials in dev
|
||||||
|
if (__DEV__) {
|
||||||
|
console.log('Extracted the following credentials:', { userId, accessToken, deviceId, address });
|
||||||
|
}
|
||||||
|
|
||||||
|
// GUARD: Check that all the required credentials are available
|
||||||
|
if (!userId || !accessToken || !deviceId || !address) {
|
||||||
|
if (__DEV__) {
|
||||||
|
console.error('Failed to extract credentials from event');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { credentials: { Servers: [ credentials ] }, deviceId } = data;
|
|
||||||
|
|
||||||
// Attempt to perform a request using the credentials to see if they're
|
// Attempt to perform a request using the credentials to see if they're
|
||||||
// good
|
// good
|
||||||
const response = await fetch(`${credentials.ManualAddress}/Users/Me`, {
|
const response = await fetch(`${address}/Users/${userId}`, {
|
||||||
headers: {
|
headers: {
|
||||||
'X-Emby-Authorization': `MediaBrowser Client="", Device="", DeviceId="", Version="", Token="${credentials.AccessToken}"`
|
'X-Emby-Authorization': `MediaBrowser Client="", Device="", DeviceId="", Version="", Token="${accessToken}"`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GUARD: The request must succeed
|
// GUARD: The request must succeed
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
|
if (__DEV__) {
|
||||||
|
const body = await response.text();
|
||||||
|
console.error('Failed to retrieve user object using credentials:', response.status, body);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a message is received, the credentials should be there
|
// If a message is received, the credentials should be there
|
||||||
this.props.onCredentialsRetrieved({
|
this.props.onCredentialsRetrieved({
|
||||||
uri: credentials.ManualAddress,
|
uri: address,
|
||||||
user_id: credentials.UserId,
|
user_id: userId,
|
||||||
access_token: credentials.AccessToken,
|
access_token: accessToken,
|
||||||
device_id: deviceId,
|
device_id: deviceId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user