From ec27b5c2a3959c5da735189be21ab24450ae726c Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Sat, 21 May 2022 23:27:25 -0500 Subject: [PATCH 1/6] Use project token to download projects --- src/common/errors.js | 10 ++++++++++ src/p4/SelectProject.svelte | 16 +++++++++------- src/p4/get-project-meta.js | 17 ----------------- src/p4/get-project-metadata.js | 26 ++++++++++++++++++++++++++ src/packager/load-project.js | 5 ++++- 5 files changed, 49 insertions(+), 25 deletions(-) delete mode 100644 src/p4/get-project-meta.js create mode 100644 src/p4/get-project-metadata.js diff --git a/src/common/errors.js b/src/common/errors.js index f0641d7..49c216f 100644 --- a/src/common/errors.js +++ b/src/common/errors.js @@ -49,3 +49,13 @@ export class HTTPError extends Error { this.name = 'HTTPError'; } } + +/** + * Error indicating a project cannot be accessed, perhaps because it's unshared, doesn't exist, or has an invalid ID. + */ +export class CannotAccessProjectError extends Error { + constructor (message) { + super(message); + this.name = 'CannotAccessProjectError'; + } +} diff --git a/src/p4/SelectProject.svelte b/src/p4/SelectProject.svelte index a49ebce..6881a78 100644 --- a/src/p4/SelectProject.svelte +++ b/src/p4/SelectProject.svelte @@ -9,7 +9,7 @@ import writablePersistentStore from './persistent-store'; import {progress, currentTask} from './stores'; import {UserError} from '../common/errors'; - import getProjectTitle from './get-project-meta'; + import getProjectMetadata from './get-project-metadata'; import loadProject from '../packager/load-project'; import {extractProjectId, isValidURL, getTitleFromURL} from './url-utils'; import Task from './task'; @@ -133,13 +133,15 @@ throw new UserError($_('select.invalidId')); } uniqueId = `#${id}`; - task.setProgressText($_('progress.loadingProjectData')); - const {promise: loadProjectPromise, terminate} = await loadProject.fromID(id, progressCallback); + + task.setProgressText($_('progress.loadingProjectMetadata')); + const metadata = await getProjectMetadata(id); + const token = metadata.token; + projectTitle = metadata.title; + + const {promise: loadProjectPromise, terminate} = await loadProject.fromID(id, token, progressCallback); task.whenAbort(terminate); - [projectTitle, project] = await Promise.all([ - getProjectTitle(id), - loadProjectPromise - ]); + project = await loadProjectPromise; } else if ($type === 'file') { const files = fileInputElement.files; const file = files && files[0]; diff --git a/src/p4/get-project-meta.js b/src/p4/get-project-meta.js deleted file mode 100644 index 76d40f2..0000000 --- a/src/p4/get-project-meta.js +++ /dev/null @@ -1,17 +0,0 @@ -import request from '../common/request'; - -const getProjectTitle = async (id) => { - try { - const meta = await request({ - url: `https://trampoline.turbowarp.org/proxy/projects/${id}`, - timeout: 10000, - type: 'json' - }); - return meta.title; - } catch (e) { - // Happens commonly when loading unshared projects, not something to worry about - return ''; - } -}; - -export default getProjectTitle; diff --git a/src/p4/get-project-metadata.js b/src/p4/get-project-metadata.js new file mode 100644 index 0000000..4e5c493 --- /dev/null +++ b/src/p4/get-project-metadata.js @@ -0,0 +1,26 @@ +import {CannotAccessProjectError} from '../common/errors'; +import request from '../common/request'; + +const getProjectMetadata = async (id) => { + try { + const meta = await request({ + url: [ + // Hopefully one of these URLs won't be blocked. + `https://trampoline.turbowarp.org/proxy/projects/${id}`, + `https://trampoline.turbowarp.xyz/proxy/projects/${id}` + ], + type: 'json' + }); + return { + title: meta.title, + token: meta.project_token + }; + } catch (e) { + if (e && e.status === 404) { + throw new CannotAccessProjectError(`Cannot access project ${id}`); + } + throw e; + } +}; + +export default getProjectMetadata; diff --git a/src/packager/load-project.js b/src/packager/load-project.js index e27541b..a68aa5f 100644 --- a/src/packager/load-project.js +++ b/src/packager/load-project.js @@ -39,7 +39,10 @@ const fromURL = async (url, progressCallback) => { return downloadProject(buffer, progressCallback); }; -const fromID = (id, progressCallback) => fromURL(`https://projects.scratch.mit.edu/${id}`, progressCallback); +const fromID = (id, token, progressCallback) => fromURL( + `https://projects.scratch.mit.edu/${id}?token=${token}`, + progressCallback +); const fromFile = async (file, progressCallback) => { const buffer = await readAsArrayBuffer(file); From 1f069a43855de79584fc66841f525145d9d99e74 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Sat, 21 May 2022 23:31:02 -0500 Subject: [PATCH 2/6] Update interface for unshared projects not being accessible --- src/locales/en.json | 21 ++++++++++++++++----- src/p4/Modals.svelte | 24 +++++++++++++++++++++++- src/p4/SelectProject.svelte | 4 ++-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 5775127..f0e4fc6 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -33,6 +33,14 @@ "string": "This version of the packager appears to be outdated. Please refresh and try again.", "context": "Error displayed when the packager detects that you are using an old version and that you must refresh to continue." }, + "cannotAccessProject": { + "string": "Can't access project metadata. This probably means the project is unshared, never existed, or the ID is invalid.", + "context": "Part of the error modal when loading an unshared project." + }, + "cannotAccessProjectCaching": { + "string": "If the project was shared recently, it may take up to an hour for this message to go away.", + "context": "Part of the error modal when loading an unshared project." + }, "close": { "string": "Close" }, @@ -84,6 +92,9 @@ "compressingProject": { "string": "Compressing project" }, + "loadingProjectMetadata": { + "string": "Downloading project metadata" + }, "loadingProjectData": { "string": "Downloading project data" }, @@ -137,13 +148,13 @@ "string": "No file selected", "context": "Error message" }, - "uncertainty": { - "string": "May 8, 2022 - The future of packaging unshared projects is unknown due to upcoming changes to the Scratch API. {moreInformation}.", - "context": "The Scratch Team is probably going to prevent third-party sites from accessing unshared projects. This describes the situation. The date is when this notice was added so that people can understand that this is a new notice." + "unsharedProjects": { + "string": "Unshared projects are no longer accessible using their project ID due to Scratch API changes. Instead, you can save the project to your computer (File > Save to your computer) and load the file in the packager. {moreInformation}.", + "context": "Warning message about unshared projects no longer being accessible." }, - "uncertaintyMore": { + "unsharedProjectsMore": { "string": "More information", - "context": "Link to more information about the Scratch Team probably going to prevent third-party sites from accessing unshared projects." + "context": "Link to more information about unshared projects no longer being accessible." } }, "options": { diff --git a/src/p4/Modals.svelte b/src/p4/Modals.svelte index 9dbf51b..01199b2 100644 --- a/src/p4/Modals.svelte +++ b/src/p4/Modals.svelte @@ -1,7 +1,7 @@