diff --git a/src/localization/en.json b/src/localization/en.json index df5c5219..73c236ad 100644 --- a/src/localization/en.json +++ b/src/localization/en.json @@ -384,7 +384,7 @@ "fetch_cancelled_graph_not_updated": "Fetch cancelled" }, "notifications": { - "start_fetching": "Start fetching", + "start_fetching": "Started fetching", "mutual_friend_graph_ready_title": "Mutual Friend Network Graph", "mutual_friend_graph_ready_message": "Mutual friend network graph is ready", "friend_list_changed_fetch_again": "Friend list changed. Please fetch the mutual friend network again." @@ -2082,6 +2082,12 @@ "image_changed": "Avatar image changed", "image_invalid": "Current avatar image invalid" }, + "avatar_lookup": { + "not_found": "Avatar not found in search providers", + "failed": "Avatar lookup failed", + "private_or_not_found": "Users avatar is private or not found in search providers", + "loading": "Searching for avatar using search providers" + }, "database": { "upgrade_complete": "Database upgrade complete" }, @@ -2091,7 +2097,7 @@ "file": { "not_image": "File isn't an image", "too_large": "File size too large", - "folder_missing": "Folder dosn't exist" + "folder_missing": "Folder doesn't exist" }, "group": { "load_failed": "Failed to load group" diff --git a/src/stores/avatar.js b/src/stores/avatar.js index 7fcd2abe..f1e7eb8b 100644 --- a/src/stores/avatar.js +++ b/src/stores/avatar.js @@ -69,6 +69,7 @@ export const useAvatarStore = defineStore('Avatar', () => { timeSpent: 0 }); const avatarHistory = ref([]); + const loadingToastId = ref(null); watch( () => watchState.isLoggedIn, @@ -463,10 +464,11 @@ export const useAvatarStore = defineStore('Avatar', () => { const avatars = new Map(); if (type === 'search') { try { + const url = `${ + avatarProviderStore.avatarRemoteDatabaseProvider + }?${type}=${encodeURIComponent(search)}&n=5000`; const response = await webApiService.execute({ - url: `${ - avatarProviderStore.avatarRemoteDatabaseProvider - }?${type}=${encodeURIComponent(search)}&n=5000`, + url, method: 'GET', headers: { Referer: 'https://vrcx.app', @@ -475,7 +477,7 @@ export const useAvatarStore = defineStore('Avatar', () => { }); const json = JSON.parse(response.data); if (AppDebug.debugWebRequests) { - console.log(json, response); + console.log(url, json, response); } if (response.status === 200 && typeof json === 'object') { json.forEach((avatar) => { @@ -522,11 +524,18 @@ export const useAvatarStore = defineStore('Avatar', () => { } async function lookupAvatarByImageFileId(authorId, fileId) { - const length = - avatarProviderStore.avatarRemoteDatabaseProviderList.length; - for (let i = 0; i < length; ++i) { - const url = avatarProviderStore.avatarRemoteDatabaseProviderList[i]; - const avatarArray = await lookupAvatarsByAuthor(url, authorId); + for (const providerUrl of avatarProviderStore.avatarRemoteDatabaseProviderList) { + const avatar = await lookupAvatarByFileId(providerUrl, fileId); + if (avatar?.id) { + return avatar.id; + } + } + + for (const providerUrl of avatarProviderStore.avatarRemoteDatabaseProviderList) { + const avatarArray = await lookupAvatarsByAuthor( + providerUrl, + authorId + ); for (const avatar of avatarArray) { if (extractFileId(avatar.imageUrl) === fileId) { return avatar.id; @@ -536,14 +545,11 @@ export const useAvatarStore = defineStore('Avatar', () => { return null; } - async function lookupAvatarsByAuthor(url, authorId) { - const avatars = []; - if (!url) { - return avatars; - } + async function lookupAvatarByFileId(providerUrl, fileId) { try { + const url = `${providerUrl}?fileId=${encodeURIComponent(fileId)}`; const response = await webApiService.execute({ - url: `${url}?authorId=${encodeURIComponent(authorId)}`, + url, method: 'GET', headers: { Referer: 'https://vrcx.app', @@ -552,7 +558,50 @@ export const useAvatarStore = defineStore('Avatar', () => { }); const json = JSON.parse(response.data); if (AppDebug.debugWebRequests) { - console.log(json, response); + console.log(url, json, response); + } + if (response.status === 200 && typeof json === 'object') { + const ref = { + authorId: '', + authorName: '', + name: '', + description: '', + id: '', + imageUrl: '', + thumbnailImageUrl: '', + created_at: '0001-01-01T00:00:00.0000000Z', + updated_at: '0001-01-01T00:00:00.0000000Z', + releaseStatus: 'public', + ...json + }; + return ref; + } else { + return null; + } + } catch (err) { + // ignore errors for now, not all providers support this lookup type + return null; + } + } + + async function lookupAvatarsByAuthor(providerUrl, authorId) { + const avatars = []; + if (!providerUrl) { + return avatars; + } + try { + const url = `${providerUrl}?authorId=${encodeURIComponent(authorId)}`; + const response = await webApiService.execute({ + url, + method: 'GET', + headers: { + Referer: 'https://vrcx.app', + 'VRCX-ID': vrcxUpdaterStore.vrcxId + } + }); + const json = JSON.parse(response.data); + if (AppDebug.debugWebRequests) { + console.log(url, json, response); } if (response.status === 200 && typeof json === 'object') { json.forEach((avatar) => { @@ -621,11 +670,21 @@ export const useAvatarStore = defineStore('Avatar', () => { async function checkAvatarCacheRemote(fileId, ownerUserId) { if (advancedSettingsStore.avatarRemoteDatabase) { - const avatarId = await lookupAvatarByImageFileId( - ownerUserId, - fileId - ); - return avatarId; + try { + toast.dismiss(loadingToastId.value); + loadingToastId.value = toast.loading( + t('message.avatar_lookup.loading') + ); + const avatarId = await lookupAvatarByImageFileId( + ownerUserId, + fileId + ); + return avatarId; + } catch (err) { + console.error('Failed to lookup avatar by image file id:', err); + } finally { + toast.dismiss(loadingToastId.value); + } } return null; } @@ -637,7 +696,7 @@ export const useAvatarStore = defineStore('Avatar', () => { ) { const fileId = extractFileId(currentAvatarImageUrl); if (!fileId) { - toast.error('Sorry, the author is unknown'); + toast.error(t('message.avatar_lookup.failed')); } else if (refUserId === userStore.currentUser.id) { showAvatarDialog(userStore.currentUser.currentAvatar); } else { @@ -646,22 +705,20 @@ export const useAvatarStore = defineStore('Avatar', () => { if (!avatarId) { avatarInfo = await getAvatarName(currentAvatarImageUrl); if (avatarInfo.ownerId === userStore.currentUser.id) { - userStore.refreshUserDialogAvatars(fileId); + await userStore.refreshUserDialogAvatars(fileId); + return; } } if (!avatarId) { - avatarId = await checkAvatarCacheRemote( - fileId, - avatarInfo.ownerId - ); + avatarId = await checkAvatarCacheRemote(fileId, ownerUserId); } if (!avatarId) { - if (avatarInfo.ownerId === refUserId) { + if (ownerUserId === refUserId) { toast.warning( - "It's personal (own) avatar or not found in avatar database" + t('message.avatar_lookup.private_or_not_found') ); } else { - toast.warning('Avatar not found in avatar database'); + toast.warning(t('message.avatar_lookup.not_found')); userStore.showUserDialog(avatarInfo.ownerId); } } diff --git a/src/stores/avatarProvider.js b/src/stores/avatarProvider.js index c8db27e7..cb1c854e 100644 --- a/src/stores/avatarProvider.js +++ b/src/stores/avatarProvider.js @@ -15,7 +15,7 @@ export const useAvatarProviderStore = defineStore('AvatarProvider', () => { const avatarRemoteDatabaseProvider = ref(''); const avatarRemoteDatabaseProviderList = ref([ - 'https://api.avtrdb.com/v2/avatar/search/vrcx' + 'https://api.avtrdb.com/v3/avatar/search/vrcx' ]); watch( () => watchState.isLoggedIn, @@ -29,21 +29,26 @@ export const useAvatarProviderStore = defineStore('AvatarProvider', () => { avatarRemoteDatabaseProviderList.value = JSON.parse( await configRepository.getString( 'VRCX_avatarRemoteDatabaseProviderList', - '[ "https://api.avtrdb.com/v2/avatar/search/vrcx" ]' + '[ "https://api.avtrdb.com/v3/avatar/search/vrcx" ]' ) ); + const deprecated = 'https://avtr.just-h.party/vrcx_search.php'; + const v1 = 'https://api.avtrdb.com/v1/avatar/search/vrcx'; + const v2 = 'https://api.avtrdb.com/v2/avatar/search/vrcx'; + const v3 = 'https://api.avtrdb.com/v3/avatar/search/vrcx'; + + const newList = avatarRemoteDatabaseProviderList.value + .filter((u) => u !== deprecated) + .map((u) => (u === v1 || u === v2 ? v3 : u)); + if ( - avatarRemoteDatabaseProviderList.value.includes( - 'https://avtr.just-h.party/vrcx_search.php' - ) + JSON.stringify(newList) !== + JSON.stringify(avatarRemoteDatabaseProviderList.value) ) { - removeFromArray( - avatarRemoteDatabaseProviderList.value, - 'https://avtr.just-h.party/vrcx_search.php' - ); + avatarRemoteDatabaseProviderList.value = newList; await configRepository.setString( 'VRCX_avatarRemoteDatabaseProviderList', - JSON.stringify(avatarRemoteDatabaseProviderList.value) + JSON.stringify(newList) ); } diff --git a/src/stores/settings/advanced.js b/src/stores/settings/advanced.js index 726352ce..fb7b09fd 100644 --- a/src/stores/settings/advanced.js +++ b/src/stores/settings/advanced.js @@ -458,7 +458,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { const data = JSON.parse(response.data); if (AppDebug.debugWebRequests) { - console.log('Models API response:', data); + console.log(modelsURL, data, response); } if (data.data && Array.isArray(data.data)) { @@ -685,10 +685,11 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { apiKey = youTubeApiKey.value; } try { + const url = `https://www.googleapis.com/youtube/v3/videos?id=${encodeURIComponent( + videoId + )}&part=snippet,contentDetails&key=${apiKey}`; const response = await webApiService.execute({ - url: `https://www.googleapis.com/youtube/v3/videos?id=${encodeURIComponent( - videoId - )}&part=snippet,contentDetails&key=${apiKey}`, + url, method: 'GET', headers: { Referer: 'https://vrcx.app' @@ -696,7 +697,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { }); const json = JSON.parse(response.data); if (AppDebug.debugWebRequests) { - console.log(json, response); + console.log(url, json, response); } if (response.status === 200) { data = json; @@ -725,8 +726,9 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { return null; } try { + const url = `https://translation.googleapis.com/language/translate/v2?key=${keyToUse}`; const response = await webApiService.execute({ - url: `https://translation.googleapis.com/language/translate/v2?key=${keyToUse}`, + url, method: 'POST', headers: { 'Content-Type': 'application/json', @@ -745,7 +747,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { } const data = JSON.parse(response.data); if (AppDebug.debugWebRequests) { - console.log(data, response); + console.log(url, data, response); } return data.data.translations[0].translatedText; } catch (err) { @@ -809,7 +811,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { const data = JSON.parse(response.data); if (AppDebug.debugWebRequests) { - console.log(data, response); + console.log(endpoint, data, response); } const translated = data?.choices?.[0]?.message?.content; diff --git a/src/stores/user.js b/src/stores/user.js index c6376043..24c40f5a 100644 --- a/src/stores/user.js +++ b/src/stores/user.js @@ -1208,7 +1208,7 @@ export const useUserStore = defineStore('User', () => { D.avatars = array; } - function refreshUserDialogAvatars(fileId) { + async function refreshUserDialogAvatars(fileId) { const D = userDialog.value; if (D.isAvatarsLoading) { return; @@ -1233,7 +1233,7 @@ export const useUserStore = defineStore('User', () => { } } const map = new Map(); - processBulk({ + await processBulk({ fn: avatarRequest.getAvatars, N: -1, params, diff --git a/src/stores/vrcxUpdater.js b/src/stores/vrcxUpdater.js index 0a9cad14..e9c8c49b 100644 --- a/src/stores/vrcxUpdater.js +++ b/src/stores/vrcxUpdater.js @@ -221,7 +221,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => { } pendingVRCXUpdate.value = false; if (AppDebug.debugWebRequests) { - console.log(json, response); + console.log(url, json, response); } if (json === Object(json) && json.name && json.published_at) { changeLogDialog.value.buildName = json.name; @@ -308,7 +308,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => { return; } if (AppDebug.debugWebRequests) { - console.log(json, response); + console.log(url, json, response); } const releases = []; if (typeof json !== 'object' || json.message) { diff --git a/src/views/Charts/components/MutualFriends.vue b/src/views/Charts/components/MutualFriends.vue index c5453ec7..9f0d1e3f 100644 --- a/src/views/Charts/components/MutualFriends.vue +++ b/src/views/Charts/components/MutualFriends.vue @@ -515,6 +515,7 @@ if (hasFetched.value && !status.needsRefetch) return; isLoadingSnapshot.value = true; + toast.dismiss(loadingToastId.value); loadingToastId.value = toast.loading(t('view.charts.mutual_friend.status.loading_cache')); try { @@ -561,10 +562,7 @@ console.error('[MutualNetworkGraph] Failed to load cached mutual graph', err); } finally { isLoadingSnapshot.value = false; - if (loadingToastId.value !== null && loadingToastId.value !== undefined) { - toast.dismiss(loadingToastId.value); - loadingToastId.value = null; - } + toast.dismiss(loadingToastId.value); } }