您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Import files from Github repository to PlayCanvas Editor
// ==UserScript== // @name PlayCanvas Github Integration // @namespace http://tampermonkey.net/ // @version 0.1 // @description Import files from Github repository to PlayCanvas Editor // @author BrandLab360 // @match https://playcanvas.com/editor/* // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== (function () { 'use strict'; function initPanel() { // Ensure pcui and the PlayCanvas Editor API are available if (typeof pcui === 'undefined' || typeof editor === 'undefined') { console.error('PlayCanvas Editor API or pcui is not available'); return; } // Create the UI panel const panel = new pcui.Panel({ headerText: 'Github Integration', collapsible: true, collapsed: true }); panel.style.position = 'absolute'; panel.style.bottom = '10px'; panel.style.right = '10px'; panel.style.width = '300px'; editor.call('layout.viewport').append(panel); // Create the input field for the GitHub personal access token const tokenField = new pcui.TextInput({ placeholder: 'Enter GitHub Access Token', width: '95%' }); panel.append(tokenField); // Create the dropdown input const dropdown = new pcui.SelectInput({ options: [ { v: 'https://github.com/lukesmith1024/PlaycanvasTest', t: 'PlaycanvasTest' } ] }); // Add the dropdown input to the panel panel.append(dropdown); // Create the input field for the GitHub repository URL const repoField = new pcui.TextInput({ placeholder: 'GitHub Repository URL', }); panel.append(repoField); // Add an event listener for when the dropdown value changes dropdown.on('change', value => { repoField.value = value; }); // Create the button for importing files const importButton = new pcui.Button({ text: 'Import from Github', }); panel.append(importButton); // Function to recursively import folders and files from the GitHub repository async function importFromGithub(token, repoUrl) { const apiUrl = 'https://api.github.com'; // Extract the owner and repository name from the URL const [, , , owner, repo] = repoUrl.split('/'); // Check if a folder with the name of the GitHub repository already exists const existingFolder = editor.call('assets:findOne', (asset) => { return asset.get('name') === repo && asset.get('type') === 'folder'; }); if (existingFolder) { var errorMsg = `A folder with the name '${repo}' already exists. Import aborted.`; console.error(errorMsg); alert(errorMsg); return; // Stop importing if the folder already exists } // Create a root folder with the name of the GitHub repository const rootFolderAsset = await new Promise((resolve) => { editor.on('assets:add', (asset) => { if (asset.get('name') === repo && asset.get('type') === 'folder') { resolve(asset); } }); editor.call('assets:create', { name: repo, type: 'folder' }, (err) => { if (err) { console.error(`Error creating root folder asset: ${err}`); } }); }); const rootFolderId = rootFolderAsset.get('id'); console.log(`Created root folder '${repo}' with ID '${rootFolderId}'`); async function getContents(path = '') { console.log(`getContents called for path: '${path}'`); const response = await fetch(`${apiUrl}/repos/${owner}/${repo}/contents/${path}`, { headers: { Authorization: `token ${token}` }, }); if (!response.ok) { throw new Error(`Error fetching contents from repository: ${response.statusText}`); } const contents = await response.json(); console.log(`Fetched contents of '${path}':`, contents); return contents; } function isPlayCanvasMaterial(obj) { if (typeof obj !== 'object' || obj === null) { return false; } // Check if the object has specific properties that are unique to PlayCanvas materials const materialProperties = [ 'ambient', 'diffuse', 'specular', 'shininess', 'opacity', ]; return materialProperties.every((property) => obj.hasOwnProperty(property)); } function applyMaterialProperties(material, materialData) { for (const key in materialData) { if (materialData.hasOwnProperty(key) && material.hasOwnProperty(key)) { material[key] = materialData[key]; } } material.update(); } async function uploadFile(file) { console.log(`Uploading file '${file.name}' with type '${file.type}'`); console.log('File object:', file); console.log('Download URL:', file.download_url); const downloadUrl = file.download_url; return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: downloadUrl, responseType: 'arraybuffer', headers: { 'Authorization': `token ${token}` }, onload: async (response) => { if (response.status !== 200) { reject(new Error(`Error fetching file content: ${response.statusText}`)); return; } let fileContent; if (file.type === 'application/json') { const decoder = new TextDecoder('utf-8'); const arrayBuffer = new Uint8Array(response.response).buffer; const responseText = decoder.decode(arrayBuffer); let parsedJson; try { parsedJson = JSON.parse(responseText); } catch (error) { console.error(`Error parsing JSON for '${file.name}', importing as plain text:`, error); fileContent = new Blob([responseText], { type: 'text/plain' }); } console.log("Parsed JSON:", parsedJson); if (isPlayCanvasMaterial(parsedJson)) { file.isMaterial = true; file.materialData = parsedJson; console.log(file.name + " is a material"); fileContent = new Blob([responseText], { type: 'text/plain' }); } else { fileContent = new Blob([JSON.stringify(parsedJson)], { type: 'application/json' }); } } else if (file.isMaterial) { const decoder = new TextDecoder('utf-8'); const arrayBuffer = new Uint8Array(response.response).buffer; const responseText = decoder.decode(arrayBuffer); fileContent = new Blob([responseText], { type: 'text/plain' }); } else { fileContent = new Blob([response.response], { type: file.type }); } console.log(`File content for '${file.name}':`, fileContent); // Create a File instance const fileInstance = new File([fileContent], file.name, { type: file.type }); // Call the createAssetInPlayCanvas function const parentFolder = file.parent ? editor.call('assets:get', file.parent) : editor.call('assets:panel:getHierarchy'); const fileType = getFileTypeFromExtension(file.name); await createAssetInPlayCanvas(file.name, fileInstance, file.type, fileType, parentFolder, file.isMaterial, file.materialData); resolve(); }, onerror: (error) => { reject(new Error(`Error fetching file content: ${error}`)); } }); }); } async function createAssetInPlayCanvas(fileName, file, mimeType, assetType, parentFolder, isMaterial, materialData) { let asset; isMaterial = isMaterial || false; console.log("Is material:", isMaterial); if (isMaterial === true) { assetType = 'material'; const materialName = fileName.replace('.json', ''); console.log("Material name:", materialName); console.log("Parent folder ID:", parentFolder.get('id')); console.log("Material data:", materialData); // Create material asset asset = await new Promise((resolve, reject) => { const assetAddedHandler = function (addedAsset) { if (addedAsset.get('type') === 'material') { console.log('Material asset created:', addedAsset); editor.off('assets:add', assetAddedHandler); resolve(addedAsset); } }; editor.on('assets:add', assetAddedHandler); console.log("Creating material asset with data:", { name: materialName, type: assetType, parent: parentFolder.get('id'), data: materialData }); editor.call('assets:create', { name: materialName, type: assetType, parent: parentFolder.get('id'), data: materialData, }, (materialAsset) => { if (!materialAsset) { editor.off('assets:add', assetAddedHandler); reject('Failed to create the material asset.'); } }); }); if (asset) { console.log("Material asset created:", asset); // Save changes to the material asset asset.save(); console.log("Created material asset:", asset); } else { console.error('Failed to create the material asset.'); } } else { const assetData = { name: fileName, type: assetType, file: file, parent: parentFolder.get('id') }; console.log('Asset data:', assetData); // Create the asset using 'assets:create' event asset = await new Promise((resolve, reject) => { editor.call('assets:create', assetData, (err, createdAsset) => { if (err) { console.error(`Error creating asset: ${err}`); reject(err); } else { resolve(createdAsset); } }); }); console.log('Asset:', asset); // Add the asset to the assets registry and the asset panel if (asset) { console.log('Uploading file for asset:', asset); editor.call('assets:panel:files:upload', asset, file); } else { console.error('Failed to create the asset.'); } } } function getMimeTypeFromExtension(filename) { const ext = filename.split('.').pop().toLowerCase(); const mimeTypes = { 'png': 'image/png', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 'json': 'application/json', 'txt': 'text/plain', 'csv': 'text/csv', 'html': 'text/html', 'css': 'text/css', 'js': 'application/javascript', 'fbx': 'model/vnd.fbx', 'obj': 'model/obj', 'mp3': 'audio/mpeg', 'wav': 'audio/wav', 'ogg': 'audio/ogg', 'mp4': 'video/mp4', 'webm': 'video/webm', 'hdr': 'image/vnd.radiance', }; return mimeTypes[ext] || 'application/octet-stream'; } function getFileTypeFromExtension(filename) { const ext = filename.split('.').pop().toLowerCase(); const fileTypes = { 'png': 'texture', 'jpg': 'texture', 'jpeg': 'texture', 'gif': 'texture', 'json': 'json', 'txt': 'text', 'csv': 'text', 'html': 'text', 'css': 'text', 'js': 'script', 'fbx': 'model', 'obj': 'model', 'mp3': 'audio', 'wav': 'audio', 'ogg': 'audio', 'mp4': 'video', 'webm': 'video', 'hdr': 'texture', }; return fileTypes[ext] || 'unknown'; } async function processFolder(contents, parentFolderId = null) { console.log(`Contents of '${parentFolderId || ''}':`, contents); // Wait for all folder creations and file uploads to complete await Promise.all(contents.map(async (item) => { if (item.type === 'dir') { console.log(`Processing folder: '${item.name}'`); const folderData = { name: item.name, type: 'folder', parent: parentFolderId }; const folderAsset = await new Promise((resolve) => { editor.on('assets:add', (asset) => { if (asset.get('name') === folderData.name && asset.get('type') === folderData.type) { resolve(asset); } }); editor.call('assets:create', folderData, (err) => { if (err) { console.error(`Error creating folder asset: ${err}`); } }); }); const folderId = folderAsset.get('id'); console.log(`Created folder '${item.name}' with ID '${folderId}'`); const subContents = await getContents(item.path); await processFolder(subContents, folderId); } else { console.log(`Processing file: '${item.name}'`); const file = { name: item.name, path: item.path, type: getMimeTypeFromExtension(item.path), download_url: item.download_url, parent: parentFolderId, }; await uploadFile(file); } })); } try { const rootContents = await getContents(); await processFolder(rootContents, rootFolderId, token); } catch (err) { console.error(`Error importing from Github: ${err.message}`); } } // Handle the click event on the import button importButton.on('click', () => { const token = tokenField.value; const repoUrl = repoField.value; if (!token || !repoUrl) { console.error('Please provide a GitHub personal access token and a repository URL'); return; } importFromGithub(token, repoUrl); }); } // Initialize the panel after a delay to make sure the editor is fully loaded setTimeout(() => { initPanel(); }, 5000); // Load scripts in Editor folder as extentions const importExtensionsFromAssets = () => { const extentionsFolder = editor.assets.list().find((data) => { const { type, name, path } = data.json(); return type === "folder" && name === "Editor"; }); if (!extentionsFolder) { console.warn("Create an Editor folder in the assets."); return; } const extentions = editor.assets.list().filter((data) => { const { path, type } = data.json(); return type === "script"; }); extentions.forEach((data) => { const { url } = data.json().file; var script = document.createElement("script"); script.src = url; console.log(url) document.head.appendChild(script); }); }; editor.on('assets:load', () => importExtensionsFromAssets()); })();