From fd87665b164fe70f5e8a59118235343819174e41 Mon Sep 17 00:00:00 2001 From: HexaField Date: Mon, 16 Oct 2023 14:35:01 +1100 Subject: [PATCH 1/3] simplify static resource parsing of scenes and uploads --- .env.local.default | 1 - packages/server-core/src/appconfig.ts | 2 - .../static-resource-helper.test.ts | 209 +----------------- .../static-resource/static-resource-helper.ts | 72 +----- .../upload-asset/upload-asset.service.ts | 11 +- .../src/projects/project/project-helper.ts | 3 +- .../src/projects/scene/scene-helper.ts | 98 +------- .../src/user/avatar/avatar-helper.ts | 6 +- 8 files changed, 25 insertions(+), 377 deletions(-) diff --git a/.env.local.default b/.env.local.default index 9e1d774e8c..bf812829cc 100644 --- a/.env.local.default +++ b/.env.local.default @@ -93,7 +93,6 @@ LOCAL_STORAGE_PROVIDER_PORT=8642 GOOGLE_ANALYTICS_TRACKING_ID= HUB_ENDPOINT=https://etherealengine.io INSTANCESERVER_UNREACHABLE_TIMEOUT_SECONDS=10 -CLONE_STATIC_RESOURCES=false MATCHMAKER_EMULATION_MODE=true diff --git a/packages/server-core/src/appconfig.ts b/packages/server-core/src/appconfig.ts index 4e0f570665..356f0b2c29 100755 --- a/packages/server-core/src/appconfig.ts +++ b/packages/server-core/src/appconfig.ts @@ -145,8 +145,6 @@ const server = { corsServerPort: process.env.CORS_SERVER_PORT!, storageProvider: process.env.STORAGE_PROVIDER!, storageProviderExternalEndpoint: process.env.STORAGE_PROVIDER_EXTERNAL_ENDPOINT!, - cloneProjectStaticResources: - typeof process.env.CLONE_STATIC_RESOURCES === 'undefined' ? true : process.env.CLONE_STATIC_RESOURCES === 'true', gaTrackingId: process.env.GOOGLE_ANALYTICS_TRACKING_ID!, hub: { endpoint: process.env.HUB_ENDPOINT! diff --git a/packages/server-core/src/media/static-resource/static-resource-helper.test.ts b/packages/server-core/src/media/static-resource/static-resource-helper.test.ts index 57fbe39dc1..bee6768029 100644 --- a/packages/server-core/src/media/static-resource/static-resource-helper.test.ts +++ b/packages/server-core/src/media/static-resource/static-resource-helper.test.ts @@ -25,19 +25,10 @@ Ethereal Engine. All Rights Reserved. import appRootPath from 'app-root-path' import assert from 'assert' -import fs from 'fs' import path from 'path' -import { destroyEngine } from '@etherealengine/engine/src/ecs/classes/Engine' - -import { StaticResourceType, staticResourcePath } from '@etherealengine/engine/src/schemas/media/static-resource.schema' -import { Paginated } from '@feathersjs/feathers' -import { Application } from '../../../declarations' import { mockFetch, restoreFetch } from '../../../tests/util/mockFetch' -import { createFeathersKoaApp } from '../../createApp' -import { getCachedURL } from '../storageprovider/getCachedURL' -import { getStorageProvider } from '../storageprovider/storageprovider' -import { addAssetFromProject, downloadResourceAndMetadata } from './static-resource-helper' +import { downloadResourceAndMetadata } from './static-resource-helper' describe('static-resource-helper', () => { before(() => { @@ -91,201 +82,3 @@ describe('static-resource-helper', () => { }) }) }) - -const testProject = 'test-project' - -describe('audio-upload', () => { - let app: Application - - before(async () => { - app = createFeathersKoaApp() - await app.setup() - const url = 'https://test.com/projects/default-project/assets/test.mp3' - const url2 = getCachedURL('/projects/default-project/assets/test.mp3', getStorageProvider().cacheDomain) - mockFetch({ - [url]: { - contentType: 'audio/mpeg', - response: fs.readFileSync( - path.join(appRootPath.path, '/packages/projects/default-project/assets/SampleAudio.mp3') - ) - }, - [url2]: { - contentType: 'audio/mpeg', - response: fs.readFileSync( - path.join(appRootPath.path, '/packages/projects/default-project/assets/SampleAudio.mp3') - ) - } - }) - }) - - afterEach(async () => { - const storageProvider = getStorageProvider() - if (await storageProvider.doesExist('test.mp3', 'static-resources/test-project/')) - await storageProvider.deleteResources(['static-resources/test-project/test.mp3']) - - const existingResource = (await app.service(staticResourcePath).find({ - query: { - mimeType: 'audio/mpeg' - }, - paginate: false - })) as StaticResourceType[] - await Promise.all(existingResource.map((resource) => app.service(staticResourcePath).remove(resource.id))) - }) - - after(() => { - restoreFetch() - return destroyEngine() - }) - - describe('addAssetFromProject', () => { - it('should download audio asset as a new static resource from external url when forced to download', async () => { - const storageProvider = getStorageProvider() - const url = 'https://test.com/projects/default-project/assets/test.mp3' - - const staticResource = await addAssetFromProject(app, url, testProject, true) - - assert(staticResource.id) - assert.equal(staticResource.url, getCachedURL(staticResource.key!, storageProvider.cacheDomain)) - assert.equal(staticResource.key, 'static-resources/test-project/test.mp3') - assert.equal(staticResource.mimeType, 'audio/mpeg') - assert.equal(staticResource.project, testProject) - - assert(await storageProvider.doesExist('test.mp3', 'static-resources/test-project/')) - - const file = await storageProvider.getObject(staticResource.key!) - assert.equal(file.ContentType, 'audio/mpeg') - }) - - it('should link audio asset as a new static resource from external url when not forced to download', async () => { - const storageProvider = getStorageProvider() - const url = 'https://test.com/projects/default-project/assets/test.mp3' - - const staticResource = await addAssetFromProject(app, url, testProject, false) - - assert(staticResource.id) - assert.equal(staticResource.url, 'https://test.com/projects/default-project/assets/test.mp3') - assert.equal(staticResource.key, 'https://test.com/projects/default-project/assets/test.mp3') - assert.equal(staticResource.mimeType, 'audio/mpeg') - assert.equal(staticResource.project, testProject) - - const fileExists = await storageProvider.doesExist('test.mp3', 'static-resources/test-project/') - assert(!fileExists) - }) - - it('should download audio asset as a new static resource from another project', async () => { - const storageProvider = getStorageProvider() - const url = getCachedURL('/projects/default-project/assets/test.mp3', storageProvider.cacheDomain) - - const staticResource = await addAssetFromProject(app, url, testProject, true) - - assert.equal(staticResource.key, 'static-resources/test-project/test.mp3') - assert.equal(staticResource.url, getCachedURL(staticResource.key!, storageProvider.cacheDomain)) - assert.equal(staticResource.mimeType, 'audio/mpeg') - assert.equal(staticResource.project, testProject) - - assert(await storageProvider.doesExist('test.mp3', 'static-resources/test-project/')) - - const file = await storageProvider.getObject(staticResource.key!) - assert.equal(file.ContentType, 'audio/mpeg') - }) - - it('should link audio asset as a new static resource from another project', async () => { - const storageProvider = getStorageProvider() - const url = getCachedURL('/projects/default-project/assets/test.mp3', storageProvider.cacheDomain) - - const staticResource = await addAssetFromProject(app, url, testProject, false) - - assert.equal(staticResource.key, url) - assert.equal(staticResource.url, url) - assert.equal(staticResource.mimeType, 'audio/mpeg') - assert.equal(staticResource.project, testProject) - - // should not exist under static resources - const fileExists = await storageProvider.doesExist('test.mp3', 'static-resources/test-project/') - assert(!fileExists) - }) - - it('should link audio asset as a new static resource from url if from the same project', async () => { - const storageProvider = getStorageProvider() - const url = getCachedURL('/projects/default-project/assets/test.mp3', storageProvider.cacheDomain) - - const staticResource = await addAssetFromProject(app, url, 'default-project', false) - - assert.equal(staticResource.key, 'projects/default-project/assets/test.mp3') - assert.equal(staticResource.url, url) - assert.equal(staticResource.mimeType, 'audio/mpeg') - assert.equal(staticResource.project, 'default-project') - - // should not exist under static resources - const fileExists = await storageProvider.doesExist('test.mp3', 'static-resources/test-project/') - assert(!fileExists) - }) - - it('should return existing static resource with the same hash and project', async () => { - const storageProvider = getStorageProvider() - const url = getCachedURL('/projects/default-project/assets/test.mp3', storageProvider.cacheDomain) - - const response = await addAssetFromProject(app, url, 'default-project', false) - const response2 = await addAssetFromProject(app, url, 'default-project', false) - - const staticResources = (await app.service(staticResourcePath).find({ - query: { - url - } - })) as Paginated - - assert.equal(staticResources.data.length, 1) - assert.equal(response.id, response2.id) - assert.equal(response.url, response2.url) - assert.equal(response.key, response2.key) - }) - - it('should return new static resource with the same hash exists in another project when forcing download', async () => { - const storageProvider = getStorageProvider() - const url = getCachedURL('/projects/default-project/assets/test.mp3', storageProvider.cacheDomain) - - const response = await addAssetFromProject(app, url, 'default-project', true) - const response2 = await addAssetFromProject(app, url, 'test-project', true) - - const staticResources = (await app.service(staticResourcePath).find({ - query: { - url: response.url - } - })) as Paginated - assert.equal(staticResources.data.length, 1) - - const staticResources2 = (await app.service(staticResourcePath).find({ - query: { - url: response2.url - } - })) as Paginated - assert.equal(staticResources2.data.length, 1) - - assert.notEqual(response.id, response2.id) - assert.notEqual(response.url, response2.url) - assert.notEqual(response.key, response2.key) - assert.equal(response.hash, response2.hash) - }) - - it('should different static resource with the same url and hash if it exists in another project when not forcing download', async () => { - const storageProvider = getStorageProvider() - const url = getCachedURL('/projects/default-project/assets/test.mp3', storageProvider.cacheDomain) - - const response = await addAssetFromProject(app, url, 'default-project', false) - const response2 = await addAssetFromProject(app, url, 'test-project', false) - - const staticResources = (await app.service(staticResourcePath).find({ - query: { - url: response.url - } - })) as Paginated - assert.equal(staticResources.data.length, 2) - - assert(response.id !== response2.id) - assert.equal(response.url, response2.url) - // key is different as it is a different project - assert.notEqual(response.key, response2.key) - assert.equal(response.hash, response2.hash) - }) - }) -}) diff --git a/packages/server-core/src/media/static-resource/static-resource-helper.ts b/packages/server-core/src/media/static-resource/static-resource-helper.ts index e595db7e1b..d505e41249 100644 --- a/packages/server-core/src/media/static-resource/static-resource-helper.ts +++ b/packages/server-core/src/media/static-resource/static-resource-helper.ts @@ -36,13 +36,7 @@ import { UploadFile } from '@etherealengine/common/src/interfaces/UploadAssetInt import { CommonKnownContentTypes } from '@etherealengine/common/src/utils/CommonKnownContentTypes' import multiLogger from '@etherealengine/engine/src/common/functions/logger' -import { sceneRelativePathIdentifier } from '@etherealengine/engine/src/common/functions/parseSceneJSON' -import { StaticResourceType, staticResourcePath } from '@etherealengine/engine/src/schemas/media/static-resource.schema' -import { Paginated } from '@feathersjs/feathers' -import { Application } from '../../../declarations' -import config from '../../appconfig' import { getStorageProvider } from '../storageprovider/storageprovider' -import { addAssetAsStaticResource, getFileMetadata } from '../upload-asset/upload-asset.service' const logger = multiLogger.child('static-resource-helper') @@ -65,10 +59,7 @@ export type MediaUploadArguments = { * @param download - if true, will download the file and return it as a buffer, otherwise will return the url * @returns */ -export const downloadResourceAndMetadata = async ( - url: string, - download = config.server.cloneProjectStaticResources -): Promise => { +export const downloadResourceAndMetadata = async (url: string, download = false): Promise => { // console.log('getResourceFiles', url, download) if (/http(s)?:\/\//.test(url)) { // if configured to clone project static resources, we need to fetch the file and upload it @@ -109,18 +100,19 @@ export const downloadResourceAndMetadata = async ( const absoluteProjectPath = path.join(appRootPath.path, '/packages/projects/projects') -export const isAssetFromProject = (url: string, project: string) => { +export const isAssetFromDomain = (url: string) => { const storageProvider = getStorageProvider() - const storageProviderPath = path.join(storageProvider.cacheDomain, 'projects/', project) - const originPath = path.join(storageProvider.originURLs[0], 'projects/', project) return ( - url.includes(storageProviderPath) || - url.includes(originPath) || - url.includes(path.join(absoluteProjectPath, project)) + url.includes(storageProvider.cacheDomain) || !!storageProvider.originURLs.find((origin) => url.includes(origin)) ) } -export const getKeyForAsset = (url: string, project: string, isFromProject: boolean) => { +/** + * Get the key for a given asset + * - if from project, will return the path relative to the project + * - if from external url, will return the path relative to the static-resources folder + */ +export const getKeyForAsset = (url: string, project: string) => { const storageProvider = getStorageProvider() const storageProviderPath = 'https://' + path.join(storageProvider.cacheDomain, 'projects/', project) const originPath = 'https://' + path.join(storageProvider.originURLs[0], 'projects/', project) @@ -131,51 +123,7 @@ export const getKeyForAsset = (url: string, project: string, isFromProject: bool .split('/') .slice(0, -1) .join('/') - return isFromProject ? `projects/${project}${projectPath}` : `static-resources/${project}/` -} - -/** - * install project - * if external url and clone static resources, clone to /static-resources/project - * if external url and not clone static resources, link new static resource - * if internal url, link new static resource - */ -export const addAssetFromProject = async ( - app: Application, - url: string, // expects all urls to be from the same location, and to be variants of the same asset - project: string, - download = config.server.cloneProjectStaticResources -) => { - // console.log('addAssetsFromProject', url, project, download) - const mainURL = decodeURI(url.replace(sceneRelativePathIdentifier, absoluteProjectPath)) - - const isFromProject = isAssetFromProject(mainURL, project) - - const { hash, mimeType } = await getFileMetadata({ file: mainURL }) - - const key = getKeyForAsset(mainURL, project, isFromProject) - - const existingResource = (await app.service(staticResourcePath).find({ - query: { - hash, - project, - mimeType, - $limit: 1 - } - })) as Paginated - - if (existingResource.data.length > 0) return existingResource.data[0] - - const forceDownload = isFromProject ? false : download - - const file = await downloadResourceAndMetadata(mainURL, forceDownload) - - return addAssetAsStaticResource(app, file, { - hash: hash, - // use key for when downloading the asset, otherwise pass the url directly to be inserted into the database - path: isFromProject || download ? key : mainURL, - project - }) + return `projects/${project}${projectPath}` } export const getStats = async (buffer: Buffer | string, mimeType: string): Promise> => { diff --git a/packages/server-core/src/media/upload-asset/upload-asset.service.ts b/packages/server-core/src/media/upload-asset/upload-asset.service.ts index c5fa9c62d0..78ee1a7bb6 100755 --- a/packages/server-core/src/media/upload-asset/upload-asset.service.ts +++ b/packages/server-core/src/media/upload-asset/upload-asset.service.ts @@ -115,7 +115,6 @@ export const getFileMetadata = async (data: { name?: string; file: UploadFile | const addFileToStorageProvider = async (file: Buffer, mimeType: string, key: string) => { logger.info(`Uploading ${key} to storage provider`) - console.log(file, mimeType, key) const provider = getStorageProvider() try { await provider.createInvalidation([key]) @@ -143,7 +142,11 @@ export type UploadAssetArgs = { } export const uploadAsset = async (app: Application, args: UploadAssetArgs) => { - console.log('uploadAsset', args) + logger.info('uploadAsset', { + project: args.project, + name: args.name, + path: args.path + }) const { hash } = await getFileMetadata({ file: args.file, name: args.file.originalname @@ -227,7 +230,9 @@ export const addAssetAsStaticResource = async ( file: UploadFile, args: AdminAssetUploadArgumentsType ): Promise => { - console.log('addAssetAsStaticResource', file, args) + logger.info('addAssetAsStaticResource %o', args) + // console.log(file) + const provider = getStorageProvider() const isFromOrigin = isFromOriginURL(args.path) diff --git a/packages/server-core/src/projects/project/project-helper.ts b/packages/server-core/src/projects/project/project-helper.ts index fec5d5cab9..d9efac8c3f 100644 --- a/packages/server-core/src/projects/project/project-helper.ts +++ b/packages/server-core/src/projects/project/project-helper.ts @@ -77,7 +77,6 @@ import { getContentType } from '../../util/fileUtils' import { copyFolderRecursiveSync, deleteFolderRecursive, getFilesRecursive } from '../../util/fsHelperFunctions' import { getGitConfigData, getGitHeadData, getGitOrigHeadData } from '../../util/getGitData' import { useGit } from '../../util/gitHelperFunctions' -import { uploadSceneToStaticResources } from '../scene/scene-helper' import { getAuthenticatedRepo, getOctokitForChecking, getUserRepos } from './github-helper' import { ProjectParams } from './project.class' @@ -1648,7 +1647,7 @@ export const uploadLocalProjectToProvider = async ( const results = [] as (string | null)[] for (const file of filtered) { try { - const fileResult = await uploadSceneToStaticResources(app, projectName, file) + const fileResult = fs.readFileSync(file) const filePathRelative = processFileName(file.slice(projectRootPath.length)) await storageProvider.putObject( { diff --git a/packages/server-core/src/projects/scene/scene-helper.ts b/packages/server-core/src/projects/scene/scene-helper.ts index f30bccac4b..998ebb513b 100644 --- a/packages/server-core/src/projects/scene/scene-helper.ts +++ b/packages/server-core/src/projects/scene/scene-helper.ts @@ -24,18 +24,12 @@ Ethereal Engine. All Rights Reserved. */ import koa from '@feathersjs/koa' -import fs from 'fs' -import { SceneData, SceneJson } from '@etherealengine/common/src/interfaces/SceneInterface' +import { SceneData } from '@etherealengine/common/src/interfaces/SceneInterface' import { Application } from '../../../declarations' -import config from '../../appconfig' -import { addAssetFromProject } from '../../media/static-resource/static-resource-helper' // import { addVolumetricAssetFromProject } from '../../media/volumetric/volumetric-upload.helper' -import { - cleanStorageProviderURLs, - parseStorageProviderURLs -} from '@etherealengine/engine/src/common/functions/parseSceneJSON' +import { parseStorageProviderURLs } from '@etherealengine/engine/src/common/functions/parseSceneJSON' import { getCacheDomain } from '../../media/storageprovider/getCacheDomain' import { getCachedURL } from '../../media/storageprovider/getCachedURL' import { getStorageProvider } from '../../media/storageprovider/storageprovider' @@ -103,91 +97,3 @@ export const getEnvMapBakeById = async (app, entityId: string) => { // ] // }) } - -export const uploadSceneToStaticResources = async (app: Application, projectName: string, file: string) => { - const fileResult = fs.readFileSync(file) - - // todo - how do we handle updating projects on local dev? - if (!config.kubernetes.enabled) return fileResult - - if (/.scene.json$/.test(file)) { - const sceneData = JSON.parse(fileResult.toString()) - const convertedSceneData = await downloadAssetsFromScene(app, projectName, sceneData) - cleanStorageProviderURLs(convertedSceneData) - const newFile = Buffer.from(JSON.stringify(convertedSceneData, null, 2)) - fs.writeFileSync(file, newFile) - return newFile - } - - return fileResult -} - -export const downloadAssetsFromScene = async (app: Application, project: string, sceneData: SceneJson) => { - // parallelizes each entity, serializes each component to avoid media playlists taking up gigs of memory when downloading - await Promise.all( - Object.values(sceneData!.entities).map(async (entity) => { - try { - for (const component of entity.components) { - switch (component.name) { - case 'media': { - let urls = [] as string[] - const paths = component.props.paths - if (paths) { - urls = paths - delete component.props.paths - } - const resources = component.props.resources - if (resources && resources.length > 0) { - if (typeof resources[0] === 'string') urls = resources - else urls = resources.map((resource) => resource.path) - } - - const isVolumetric = entity.components.find((component) => component.name === 'volumetric') - if (isVolumetric) { - const extensions = ['drcs', 'mp4', 'manifest'] - const newUrls = [] as string[] - for (const url of urls) { - const split = url.split('.') - const fileName = split.slice(0, split.length - 1).join('.') - for (const extension of extensions) { - newUrls.push(`${fileName}.${extension}`) - } - } - urls = newUrls - } - - const newUrls = [] as string[] - for (const url of urls) { - const newURL = await addAssetFromProject(app, url, project) - newUrls.push(newURL.url!) - } - if (isVolumetric) { - component.props.resources = newUrls.filter((url) => url.endsWith('.mp4')) - } else { - component.props.resources = newUrls - } - break - } - case 'gltf-model': { - if (component.props.src) { - const resource = await addAssetFromProject(app, component.props.src, project) - component.props.src = resource.url - } - break - } - case 'image': { - if (component.props.source) { - const resource = await addAssetFromProject(app, component.props.source, project) - component.props.source = resource.url - } - break - } - } - } - } catch (error) { - console.log(error) - } - }) - ) - return sceneData -} diff --git a/packages/server-core/src/user/avatar/avatar-helper.ts b/packages/server-core/src/user/avatar/avatar-helper.ts index ac6163bf94..62f92703ee 100644 --- a/packages/server-core/src/user/avatar/avatar-helper.ts +++ b/packages/server-core/src/user/avatar/avatar-helper.ts @@ -32,7 +32,7 @@ import { CommonKnownContentTypes } from '@etherealengine/common/src/utils/Common import { avatarPath, AvatarType } from '@etherealengine/engine/src/schemas/user/avatar.schema' import { Application } from '../../../declarations' -import { isAssetFromProject } from '../../media/static-resource/static-resource-helper' +import { isAssetFromDomain } from '../../media/static-resource/static-resource-helper' import { getStorageProvider } from '../../media/storageprovider/storageprovider' import { addAssetAsStaticResource } from '../../media/upload-asset/upload-asset.service' import logger from '../../ServerLogger' @@ -174,8 +174,8 @@ export const uploadAvatarStaticResource = async ( const name = data.avatarName ? data.avatarName : 'Avatar-' + Math.round(Math.random() * 100000) const staticResourceKey = `static-resources/avatar/${data.isPublic ? 'public' : params?.user!.id}/` - const isFromProject = !!data.project && !!data.path && isAssetFromProject(data.path, data.project) - const path = isFromProject ? data.path! : staticResourceKey + const isFromDomain = !!data.path && isAssetFromDomain(data.path) + const path = isFromDomain ? data.path! : staticResourceKey // const thumbnail = await generateAvatarThumbnail(data.avatar as Buffer) // if (!thumbnail) throw new Error('Thumbnail generation failed - check the model') From 3c715687d092f090a9d502a550385ef66def722d Mon Sep 17 00:00:00 2001 From: HexaField Date: Mon, 16 Oct 2023 16:29:00 +1100 Subject: [PATCH 2/3] add download button in file browser input --- .../src/components/inputs/ArrayInputGroup.tsx | 6 +-- .../components/inputs/FileBrowserInput.tsx | 44 +++++++++++++++++-- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/packages/editor/src/components/inputs/ArrayInputGroup.tsx b/packages/editor/src/components/inputs/ArrayInputGroup.tsx index 293d35e26c..889641bda8 100755 --- a/packages/editor/src/components/inputs/ArrayInputGroup.tsx +++ b/packages/editor/src/components/inputs/ArrayInputGroup.tsx @@ -23,8 +23,8 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ +import Icon from '@etherealengine/ui/src/primitives/mui/Icon' import AddIcon from '@mui/icons-material/Add' -import DeleteIcon from '@mui/icons-material/Delete' import IconButton from '@mui/material/IconButton' import React from 'react' import styles from './ArrayInputGroup.module.scss' @@ -105,7 +105,7 @@ const ArrayInputGroup = ({ padding: 0 }} > - + {values && @@ -125,7 +125,7 @@ const ArrayInputGroup = ({ }} onClick={() => deleteInput(index + 1)} > - + ))} diff --git a/packages/editor/src/components/inputs/FileBrowserInput.tsx b/packages/editor/src/components/inputs/FileBrowserInput.tsx index 98961c34dd..8b14a44140 100644 --- a/packages/editor/src/components/inputs/FileBrowserInput.tsx +++ b/packages/editor/src/components/inputs/FileBrowserInput.tsx @@ -23,9 +23,12 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ +import IconButton from '@mui/material/IconButton' import React from 'react' import { useDrop } from 'react-dnd' +import config from '@etherealengine/common/src/config' +import Icon from '@etherealengine/ui/src/primitives/mui/Icon' import { ItemTypes } from '../../constants/AssetTypes' import useUpload from '../assets/useUpload' import { ControlledStringInput, StringInputProps } from './StringInput' @@ -54,6 +57,32 @@ export function FileBrowserInput({ } const onUpload = useUpload(uploadOptions) + // todo fix for invalid URLs + const assetIsExternal = value && !value?.includes(config.client.fileServer) + const uploadExternalAsset = () => { + onUpload([ + { + isFile: true, + name: value?.split('/').pop(), + file: async (onSuccess, onFail) => { + try { + const asset = await fetch(value!) + const blob = await asset.blob() + const file = new File([blob], value!.split('/').pop()!) + onSuccess(file) + } catch (error) { + if (onFail) onFail(error) + else throw error + } + } + } as Partial + ] as any).then((assets) => { + if (assets) { + onChange?.(assets[0]) + } + }) + } + const [{ canDrop, isOver }, dropRef] = useDrop({ accept: [...acceptDropItems, ItemTypes.File], async drop(item: any, monitor) { @@ -73,9 +102,7 @@ export function FileBrowserInput({ onUpload(entries).then((assets) => { if (assets) { - for (let index = 0; index < assets.length; index++) { - onChange?.(assets[index]) - } + onChange?.(assets[0]) } }) } @@ -96,6 +123,17 @@ export function FileBrowserInput({ canDrop={isOver && canDrop} {...rest} /> + {assetIsExternal && ( + + + + )} ) } From 6bff7f9d870a1bea4f7edd031d4129ec19363827 Mon Sep 17 00:00:00 2001 From: HexaField Date: Tue, 17 Oct 2023 13:45:02 +1100 Subject: [PATCH 3/3] fix --- packages/server-core/src/projects/scene/scene.class.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/server-core/src/projects/scene/scene.class.ts b/packages/server-core/src/projects/scene/scene.class.ts index 88376d711b..08aefb5ff6 100644 --- a/packages/server-core/src/projects/scene/scene.class.ts +++ b/packages/server-core/src/projects/scene/scene.class.ts @@ -47,7 +47,7 @@ import { Application } from '../../../declarations' import logger from '../../ServerLogger' import { getStorageProvider } from '../../media/storageprovider/storageprovider' import { cleanString } from '../../util/cleanString' -import { downloadAssetsFromScene, getSceneData } from './scene-helper' +import { getSceneData } from './scene-helper' const NEW_SCENE_NAME = 'New-Scene' const sceneAssetFiles = ['.scene.json', '.thumbnail.ktx2', '.envmap.ktx2'] @@ -236,8 +236,6 @@ export class SceneService .find({ ...params, query: { name: project }, paginate: false })) as ProjectType[] if (projectResult.length === 0) throw new Error(`No project named ${project} exists`) - await downloadAssetsFromScene(this.app, project!, parsedSceneData!) - const newSceneJsonPath = `projects/${project}/${name}.scene.json` await storageProvider.putObject({ Key: newSceneJsonPath,