mirror of
https://gitflic.ru/project/photopea-v2/photopea-v-2.git
synced 2025-07-01 18:35:33 +00:00
Support the new storages backend, and patched dropboxStorage.html
This commit is contained in:
parent
bdc8f33c08
commit
00db37e3fe
20
Updater.py
20
Updater.py
@ -25,7 +25,10 @@ urls = [
|
||||
"plugins/tpls/templates.css",
|
||||
"plugins/tpls/templates.js",
|
||||
"papi/tpls.json",
|
||||
"rsrc/fonts/fonts.png"
|
||||
"rsrc/fonts/fonts.png",
|
||||
"code/storages/deviceStorage.html",
|
||||
"code/storages/googledriveStorage.html",
|
||||
"code/storages/dropboxStorage.html"
|
||||
]
|
||||
|
||||
#Update files
|
||||
@ -107,24 +110,27 @@ for font in decompress_font_list(db["FNTS"]["list"]):
|
||||
#Delete any unused fonts
|
||||
fonts_db=[root+'rsrc/fonts/'+font.url for font in decompress_font_list(db["FNTS"]["list"])]
|
||||
|
||||
fonts_local=glob.glob(root + 'rsrc/fonts/'+'/**/*.{otf,ttf,ttc}', recursive=True)
|
||||
fonts_local=glob.glob(root + 'rsrc/fonts/' + '/**/*.{otf,ttf,ttc}', recursive=True)
|
||||
|
||||
for font_file in list(set(fonts_local)-set(fonts_db)):
|
||||
print('Removing ' + font_file)
|
||||
os.remove(font_file)
|
||||
|
||||
def find_and_replace(file,find,replace):
|
||||
with open(file,'r') as pp:
|
||||
with open(os.path.join(root,file),'r') as pp:
|
||||
file1=pp.read()
|
||||
file1=file1.replace(find,replace)
|
||||
with open(file,'w') as pp:
|
||||
with open(os.path.join(root,file),'w') as pp:
|
||||
pp.write(file1)
|
||||
|
||||
#Allow any port to be used
|
||||
find_and_replace(root+'/code/pp/pp.js','8887','')
|
||||
find_and_replace('code/pp/pp.js','8887','')
|
||||
|
||||
#Don't load Google Analytics
|
||||
find_and_replace(root+'/index.html','//www.google-analytics.com/analytics.js','')
|
||||
find_and_replace('index.html','//www.google-analytics.com/analytics.js','')
|
||||
|
||||
#Allow the import of pictures of URLs (bypassing mirror.php)
|
||||
find_and_replace(root+'/code/pp/pp.js','"mirror.php?url="+encodeURIComponent','')
|
||||
find_and_replace('code/pp/pp.js','"mirror.php?url="+encodeURIComponent','')
|
||||
|
||||
#Allow Dropbox to load from dropboxStorage.html
|
||||
find_and_replace('code/storages/dropboxStorage.html', 'var redirectUri = window.location.href;', 'var redirectUri = "https://www.photopea.com/code/storages/dropboxStorage.html";')
|
||||
|
267
www.photopea.com/code/storages/deviceStorage.html
Normal file
267
www.photopea.com/code/storages/deviceStorage.html
Normal file
@ -0,0 +1,267 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
var lastMsg, allowed=false, dir=null;
|
||||
|
||||
var locStor = {};
|
||||
|
||||
function go() {
|
||||
console.log("deviceStorage");
|
||||
var free = false;
|
||||
|
||||
window.addEventListener("message", onMessage,false);
|
||||
|
||||
|
||||
var idb = { req: window["indexedDB"].open("deviceStorage") };
|
||||
idb.req["onupgradeneeded"] = function(e) {
|
||||
//console.log("onupgradeneeded");
|
||||
var db = e.target.result;
|
||||
var os = db["createObjectStore"]("rsrc", { "keyPath": "k" });
|
||||
}
|
||||
idb.req["onsuccess"] = function(e) {
|
||||
var db = locStor.db = e.target.result; //console.log(db);
|
||||
var os = db["transaction"](["rsrc"], "readwrite")["objectStore"]("rsrc");
|
||||
|
||||
var req = os["get"]("fs0");
|
||||
req["onsuccess"] = function(e) {
|
||||
if(e.target.result) {
|
||||
if(localStorage.getItem("forget")=="1") {
|
||||
localStorage.removeItem("forget");
|
||||
}
|
||||
else {
|
||||
locStor.fset = e.target.result["fset"];
|
||||
//console.log(locStor.fset);
|
||||
dir = locStor.fset;
|
||||
}
|
||||
//allowed=true; send("ready",true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
allowed=false; send("ready",false);
|
||||
//allowed = free;
|
||||
}
|
||||
|
||||
async function makeFolder(path) {
|
||||
path = __getPath(path); //console.log(path);
|
||||
var fld = dir; for(var i=0; i<path.length-1; i++) fld = await fld.getDirectoryHandle(path[i]);
|
||||
|
||||
var dirHandle = await fld.getDirectoryHandle(path[path.length-1], { create: true });
|
||||
send("0","");
|
||||
}
|
||||
|
||||
async function deleteFile(path) {
|
||||
path = __getPath(path); //console.log(path);
|
||||
var fld = dir; for(var i=0; i<path.length-1; i++) fld = await fld.getDirectoryHandle(path[i]);
|
||||
|
||||
await fld.removeEntry(path[path.length-1], { recursive: true });
|
||||
send("0","");
|
||||
}
|
||||
|
||||
async function saveFile (path, buff) {
|
||||
path = __getPath(path); //console.log(path);
|
||||
var fld = dir; for(var i=0; i<path.length-1; i++) fld = await fld.getDirectoryHandle(path[i]);
|
||||
|
||||
var han = await fld.getFileHandle(path[path.length-1],{create:true});
|
||||
|
||||
var wrt = await han.createWritable();//;.then(function(e) {e.write(buff); return e;}).then(function(e) {e.close()});
|
||||
await wrt.write(buff);
|
||||
await wrt.close();
|
||||
send("0","");
|
||||
}
|
||||
async function printFile(path) {
|
||||
path = __getPath(path); //console.log(path);
|
||||
var fld = dir; for(var i=0; i<path.length-1; i++) fld = await fld.getDirectoryHandle(path[i]);
|
||||
|
||||
var han = await fld.getFileHandle(path[path.length-1]);
|
||||
var fil = await han.getFile();
|
||||
var buf = await fil.arrayBuffer();
|
||||
window.parent.postMessage(buf,"*");
|
||||
}
|
||||
|
||||
var fmap,left,fls, gtime, timg;
|
||||
async function printFolder(path) {
|
||||
path = __getPath(path); //console.log(path);
|
||||
var fld = dir; for(var i=0; i<path.length; i++) fld = await fld.getDirectoryHandle(path[i]);
|
||||
|
||||
fls = []; fmap={}; left=0; timg=0; gtime=Date.now();
|
||||
var time = Date.now(), cnt=0, gotFile=false;
|
||||
for await (var entry of fld.values()) {
|
||||
var file = [entry.name,-1]; fls.push(file);
|
||||
|
||||
//if(entry.kind=="file--") { file[1]=100; file.push(0); }
|
||||
if(entry.kind=="file") {
|
||||
gotFile=true;
|
||||
fmap[entry.name]=fls.length-1; left++;
|
||||
entry.getFile().then(fileLoaded);
|
||||
//var fil = await entry.getFile(); makeFile(fil, file);
|
||||
}
|
||||
}
|
||||
if(!gotFile) send("0",fls);
|
||||
}
|
||||
async function fileLoaded(fil) {
|
||||
file = fls[fmap[fil.name]];
|
||||
await makeFile(fil,file); left--;
|
||||
if(left==0) {
|
||||
console.log(Date.now()-gtime, timg);
|
||||
send("0",fls);
|
||||
}
|
||||
}
|
||||
|
||||
async function makeFile(fil, file) {
|
||||
var np = fil.name.split("."), ext = (np.length==1 ? "" : np.pop()).toLowerCase();
|
||||
file[1]=fil.size;
|
||||
file.push(~~(fil.lastModified/1000)); //return;
|
||||
if(ext=="jpeg") ext="jpg";
|
||||
if(ext=="tiff") ext="tif";
|
||||
if(["dng","cr2","nef","arw","3fr","fff","gpr"].indexOf(ext)!=-1) ext="tif";
|
||||
var thmb=null;
|
||||
if(thmb==null && (ext=="jpg" || ext=="tif")) { // || ext=="--psd"
|
||||
var lim = 130000;
|
||||
var buf = await fil.slice(0,(lim*1.5>fil.size) ? fil.size : lim).arrayBuffer();
|
||||
if(ext=="jpg" && buf.byteLength==fil.size) thmb = bufToUrl(buf, "jpg");
|
||||
else thmb=findJPG(fil.name,buf);
|
||||
}
|
||||
//*
|
||||
if(thmb==null && (ext=="psd" || ext=="psb")) {
|
||||
var buf = await fil.slice(0,4096).arrayBuffer();
|
||||
buf = new Uint8Array(buf); //console.log(buf);
|
||||
var off = 26;
|
||||
var clen = readUint(buf,off); off+=4; //console.log(clen);
|
||||
off+=clen;
|
||||
var rlen = readUint(buf,off); off+=4; //console.log(rlen);
|
||||
|
||||
if(off+rlen<=4096) buf=buf.slice(off,off+rlen);
|
||||
else buf = new Uint8Array(await fil.slice(off,off+rlen).arrayBuffer());
|
||||
|
||||
off = 0;
|
||||
while(off<buf.length) {
|
||||
var sign = readASCII(buf,off,4); off+=4;
|
||||
var resID = readUshort(buf,off); off+=2;
|
||||
var sz = buf[off];
|
||||
off+=sz+(1-(sz&1))+1;
|
||||
|
||||
var asiz = readUint(buf,off); off+=4;
|
||||
if(resID==1036) {
|
||||
//console.log(off, rlen);
|
||||
var tsz = readUint(buf,off+20);
|
||||
var io = off+28; //console.log(buf.slice(io,io+tsz));
|
||||
thmb = bufToUrl(buf.slice(io,io+tsz).buffer,"jpg");
|
||||
break;
|
||||
}
|
||||
off+=asiz+(asiz&1);
|
||||
}
|
||||
}//*/
|
||||
if(thmb==null && fil.size<100000 && ["png","svg","bmp","gif","webp"].indexOf(ext)!=-1) {
|
||||
var buf = await fil.arrayBuffer();
|
||||
if(ext=="png") {
|
||||
var data=new Uint8Array(buf), off=16;
|
||||
var bf = new Uint8Array(4), dim=[];
|
||||
for(var i=0; i<2; i++) {
|
||||
for(var j=0; j<4; j++) bf[j] = data[off+3-j];
|
||||
dim.push((new Uint32Array(bf.buffer))[0]);
|
||||
}
|
||||
if(dim[0]*dim[1]<1e6) thmb=bufToUrl(buf,ext);
|
||||
}
|
||||
else thmb=bufToUrl(buf,ext);
|
||||
}
|
||||
if(thmb) { file.push(thmb); timg++; }
|
||||
}
|
||||
|
||||
function findJPG(name, buf) {
|
||||
buf = new Uint8Array(buf); //console.log(buf);
|
||||
var d8=-1, d9=-1, end=buf.length-1;
|
||||
var ints = [];
|
||||
for(var i=2; i<end; i++) {
|
||||
if(buf[i]==255) {
|
||||
var b1=buf[i+1], b2=buf[i+2], b3 = buf[i+3];
|
||||
//if(b1==0xd8 && b2==0xff) console.log(name);
|
||||
if (d8==-1 && b1==0xd8 && b2==0xff) d8=i;
|
||||
else if(d8!=-1 && b1==0xd9) { d9=i; ints.push(d8,d9); d8=d9=-1; }
|
||||
}
|
||||
}
|
||||
if(ints.length!=0) {
|
||||
d8=ints[0]; d9=ints[1];
|
||||
return bufToUrl(buf.slice(d8, d9+2).buffer, "jpg");
|
||||
}
|
||||
}
|
||||
|
||||
var u8 = new Uint8Array(4);
|
||||
var u16 = new Uint16Array(u8.buffer);
|
||||
var u32 = new Uint32Array(u8.buffer);
|
||||
function readASCII (b, o, l) { var out=""; for(var i=0; i<l; i++) out+=String.fromCharCode(b[o+i]); return out; }
|
||||
function readUshort(b, o) { u8[0]=b[o+1]; u8[1]=b[o]; return u16[0]; }
|
||||
function readUint (b, o) { u8[0]=b[o+3]; u8[1]=b[o+2]; u8[2]=b[o+1]; u8[3]=b[o]; return u32[0]; }
|
||||
function bufToUrl(buf, ext) {
|
||||
var str=[]; buf=new Uint8Array(buf);
|
||||
for(var i=0; i<buf.length; i++) str.push(String.fromCharCode(buf[i]));
|
||||
return "data:image/"+ext+(ext=="svg"?"+xml":"")+";base64,"+btoa(str.join(""))
|
||||
}
|
||||
|
||||
async function askPermissions() {
|
||||
if(dir) {
|
||||
if(await dir.requestPermission({mode:"readwrite"})!="granted") return;
|
||||
}
|
||||
else {
|
||||
dir = await window.showDirectoryPicker();
|
||||
locStor.fset = dir;
|
||||
|
||||
var os = locStor.db["transaction"](["rsrc"], "readwrite")["objectStore"]("rsrc");
|
||||
var ooo=os["put"]({ "k":"fs0", "fset":locStor.fset });
|
||||
ooo.onerror = function(e) { console.log(e); alert("Storing failed. Browser says: "+e["target"]["error"]["message"],7000); }
|
||||
}
|
||||
allowed = true; printFolder("/");
|
||||
}
|
||||
|
||||
function onMessage(e) {
|
||||
if((typeof e.data) == "string") {
|
||||
//console.log(e.data);
|
||||
|
||||
if(window.showDirectoryPicker==null) { send("1","Your browser can not perform this feature."); return; }
|
||||
|
||||
var msg = null;
|
||||
try { msg = JSON.parse(e.data); } catch(e) { return; }
|
||||
if(msg.code==null) return;
|
||||
|
||||
if(!allowed) { askPermissions(); return; }
|
||||
|
||||
|
||||
if(msg.code=="show") {
|
||||
printFolder(msg.prm);
|
||||
}
|
||||
else if(msg.code=="load") {
|
||||
printFile(msg.prm);
|
||||
}
|
||||
else if(msg.code=="delete") {
|
||||
deleteFile(msg.prm);
|
||||
}
|
||||
else if(msg.code=="save" && msg.prm.endsWith("/")) makeFolder(msg.prm.slice(0,-1)); // making a folder
|
||||
else if(msg.code=="forget") { console.log("forget message"); localStorage.setItem("forget", "1"); send("0",""); }
|
||||
else if(msg.code=="rename") {
|
||||
send("1","We can not rename files in your coputer yet.");
|
||||
}
|
||||
|
||||
lastMsg=msg;
|
||||
}
|
||||
else {
|
||||
if(lastMsg.code=="save") saveFile(lastMsg.prm, e.data);
|
||||
}
|
||||
}
|
||||
|
||||
function __getPath(path) {
|
||||
var pth = []; if(path.length>1) pth=path.slice(1).split("/");
|
||||
return pth;
|
||||
}
|
||||
|
||||
function send(code,prm) {
|
||||
var time = Date.now();
|
||||
var str = JSON.stringify({"code":code, "prm":prm});
|
||||
//console.log("json", Date.now()-time, str.length);
|
||||
window.parent.postMessage(str,"*");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()"></body>
|
||||
</html>
|
328
www.photopea.com/code/storages/dropboxStorage.html
Normal file
328
www.photopea.com/code/storages/dropboxStorage.html
Normal file
@ -0,0 +1,328 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/dropbox@10.9.0/dist/Dropbox-sdk.min.js"></script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
|
||||
<script type="text/javascript" >
|
||||
|
||||
var sentFalse = false;
|
||||
var authDone = false;
|
||||
var lastMsg, authUrl;
|
||||
|
||||
var DROPBOX_APP_KEY = "ylv5pcromn2df1o"; //'7rlydkifdtp5gnb';
|
||||
var redirectUri = "https://www.photopea.com/code/storages/dropboxStorage.html";
|
||||
var dbx = new Dropbox.Dropbox({ clientId: DROPBOX_APP_KEY });
|
||||
|
||||
function go() {
|
||||
if (window.name == "authwin")
|
||||
{
|
||||
authDone = false;
|
||||
window.setTimeout(function()
|
||||
{
|
||||
var searchParams = new URLSearchParams(window.location.search.substr(1));
|
||||
var dropboxCode = searchParams.get('code');
|
||||
window.opener.saveCode(dropboxCode);
|
||||
send("ready",true);
|
||||
window.setTimeout(function()
|
||||
{
|
||||
window.close();
|
||||
}, 400);
|
||||
}, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbx.auth.accessToken = localStorage.getItem('dropboxAccessToken');
|
||||
window.addEventListener("message", onMessage,false);
|
||||
doAction(checkUser);
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(e)
|
||||
{
|
||||
console.log(e);
|
||||
if(e.error && e.error == "multiple_colons") { send(1,"Rename path contains more than one colon."); }
|
||||
else
|
||||
{
|
||||
send("ready",false);
|
||||
sentFalse = true;
|
||||
}
|
||||
}
|
||||
|
||||
function saveCode(code)
|
||||
{
|
||||
dbx.auth.getAccessTokenFromCode(redirectUri, code)
|
||||
.then(function({ result })
|
||||
{
|
||||
dbx.auth.accessToken = result.access_token;
|
||||
localStorage.setItem('dropboxAccessToken', result.access_token);
|
||||
authDone = true;
|
||||
if(retryAction) { doAction(retryAction.action, retryAction.prms, 1); retryAction = null; }
|
||||
else send("ready",true);
|
||||
})
|
||||
.catch(function(error) { handleError(error); });
|
||||
}
|
||||
|
||||
function signOut()
|
||||
{
|
||||
var w = window.open("https://www.dropbox.com/logout", 'logoutwin', 'width=700,height=800');
|
||||
if(!w) { handleError("Popup window blocked.") }
|
||||
else
|
||||
{
|
||||
dbx.authTokenRevoke()
|
||||
.then(function({ result })
|
||||
{
|
||||
send(0,"");
|
||||
})
|
||||
.catch(function(error) { handleError(error); });
|
||||
window.setTimeout(function()
|
||||
{
|
||||
w.close();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
//check valid accessToken and proceed, or try to reauthenticate
|
||||
function doAction(action, prms)
|
||||
{
|
||||
if(action == signOut) { signOut(); }
|
||||
else if(sentFalse) { authenticate(action,prms); }
|
||||
else if(dbx.auth.accessToken)
|
||||
{
|
||||
dbx.checkUser({"query": ""})
|
||||
.then(function(e) {
|
||||
action(prms);
|
||||
})
|
||||
.catch(function(e)
|
||||
{
|
||||
handleError(e);
|
||||
});
|
||||
}
|
||||
else { handleError("No Access Token present."); }
|
||||
}
|
||||
|
||||
function handleListResult(response, prms, out)
|
||||
{
|
||||
var entries = response.result.entries;
|
||||
if (!out) { out = []; };
|
||||
for (var i = 0; i<entries.length; ++i)
|
||||
{
|
||||
if (entries[i][".tag"] == "file")
|
||||
{
|
||||
out.push([entries[i].name,entries[i].size, Date.parse(entries[i].server_modified)/1000]);
|
||||
}
|
||||
else out.push([entries[i].name,-1, 0]);
|
||||
}
|
||||
if(response.result.has_more)
|
||||
{
|
||||
prms.cursor = response.result.cursor;
|
||||
listDir(prms, out);
|
||||
}
|
||||
else
|
||||
{
|
||||
getThumbnailPage(prms, 0, out);
|
||||
}
|
||||
}
|
||||
|
||||
function listDir(prms, out)
|
||||
//recursively listdir until no more .has_more, passing out array with every call
|
||||
{
|
||||
if(prms.path == "/") { prms.path = ""; }
|
||||
if(prms.cursor)
|
||||
{
|
||||
dbx.filesListFolderContinue({cursor: prms.cursor})
|
||||
.then(function(response) { handleListResult(response,prms,out); })
|
||||
.catch(function(error) { handleError(error); });
|
||||
}
|
||||
else
|
||||
{
|
||||
dbx.filesListFolder({path: prms.path})
|
||||
.then(function(response) { handleListResult(response,prms,out); })
|
||||
.catch(function(error) { handleError(error); });
|
||||
}
|
||||
}
|
||||
|
||||
function getThumbnailPage(prms, i, out)
|
||||
{
|
||||
var ii = i;
|
||||
var thumbnailExtenstions = ["jpg", "jpeg", "png", "tiff", "tif", "gif", "webp", "ppm", "bmp",
|
||||
"JPG", "JPEG", "PNG", "TIFF", "TIF", "GIF", "WEBP", "PPM", "BMP"];
|
||||
var query = [];
|
||||
while(query.length < 25 && ii < out.length)
|
||||
{
|
||||
if(thumbnailExtenstions.includes(out[ii][0].split('.').pop()))
|
||||
{
|
||||
query.push({"path" : prms.path + "/" + out[ii][0], "format" : "png"});
|
||||
}
|
||||
++ii;
|
||||
}
|
||||
dbx.filesGetThumbnailBatch({entries:query})
|
||||
.then(function(response) {
|
||||
ii = i;
|
||||
var j = 0;
|
||||
while(j < 25 && ii < out.length)
|
||||
{
|
||||
//could just check names from metadata, probably faster than .includes
|
||||
if(thumbnailExtenstions.includes(out[ii][0].split('.').pop()))
|
||||
{
|
||||
out[ii].push(response.result.entries[j].thumbnail)
|
||||
++j
|
||||
}
|
||||
++ii;
|
||||
}
|
||||
if(ii < out.length) { getThumbnailPage(prms, ii, out); }
|
||||
else { convertThumbnail(prms, 0, out) }
|
||||
})
|
||||
.catch(function(e) { console.log(e); })
|
||||
}
|
||||
|
||||
function convertThumbnail(prms, i, out)
|
||||
{
|
||||
if(i == out.length) { send(0,out); }
|
||||
else if(out[i].length == 4)
|
||||
{
|
||||
fetch("data:image/png;base64,"+out[i][3])
|
||||
.then(function(result) {
|
||||
result.blob()
|
||||
.then(function(blob) {
|
||||
out[i][3] = URL.createObjectURL(blob);
|
||||
convertThumbnail(prms, i+1, out);
|
||||
})
|
||||
.catch(function(e) { handleError(e); });
|
||||
})
|
||||
.catch(function(e) { handleError(e); });
|
||||
}
|
||||
else { convertThumbnail(prms, i+1, out); }
|
||||
}
|
||||
|
||||
function downloadFile(prms)
|
||||
{
|
||||
dbx.filesDownload({"path" : prms.path})
|
||||
.then(function(response) {
|
||||
response.result.fileBlob.arrayBuffer()
|
||||
.then(function(buffer) { window.parent.postMessage(buffer,"*"); })
|
||||
.catch(function(e) { handleError(e); });
|
||||
})
|
||||
.catch(function(response) {
|
||||
handleError(response);
|
||||
});
|
||||
}
|
||||
|
||||
function downloadThumbnail(prms)
|
||||
{
|
||||
dbx.filesGetThumbnail({"path" : prms.path, "format" : "png"})
|
||||
.then(function(response) {
|
||||
var blobURL = URL.createObjectURL(response.result.fileBlob);
|
||||
console.log(blobURL);
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', blobURL, true);
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.onload = function(e) {window.parent.postMessage(e.target.response,"*"); };
|
||||
xhr.onerror = function(e) { console.error(e); };
|
||||
xhr.send();
|
||||
})
|
||||
.catch(function(response) {
|
||||
handleError(response);
|
||||
});
|
||||
}
|
||||
|
||||
function uploadFile(prms)
|
||||
{
|
||||
var file = new Blob([prms.buffer]);
|
||||
dbx.filesUpload({"path": prms.path, "contents": file, "mode" : "overwrite"})
|
||||
.then(function(response) { send(0,""); })
|
||||
.catch(function(error) { send(1,error); });
|
||||
}
|
||||
|
||||
function deleteFile(prms)
|
||||
{
|
||||
dbx.filesDelete({ "path" : prms.path })
|
||||
.then(function(response) { send(0,""); })
|
||||
.catch(function(error) { send(1,error); });
|
||||
}
|
||||
|
||||
function makeFolder(prms)
|
||||
{
|
||||
dbx.filesCreateFolderV2({ "path" : prms.path.slice(0, -1) })
|
||||
.then(function(response) { send(0,""); })
|
||||
.catch(function(error) { send(1,error); });
|
||||
}
|
||||
|
||||
function renameFile(prms)
|
||||
{
|
||||
dbx.filesMoveV2({ "from_path": prms.from_path, "to_path": prms.to_path })
|
||||
.then(function(response) {send(0,""); })
|
||||
.catch(function(error) { send(1,error); });
|
||||
}
|
||||
|
||||
function authenticate(action, prms)
|
||||
{
|
||||
sentFalse = false;
|
||||
if(action) { retryAction = { "action": action, "prms": prms }; }
|
||||
dbx.auth.getAuthenticationUrl(
|
||||
redirectUri,
|
||||
'',
|
||||
'code',
|
||||
'',
|
||||
[
|
||||
'account_info.read',
|
||||
'files.content.write',
|
||||
'files.content.read',
|
||||
],
|
||||
'user',
|
||||
true,)
|
||||
.then(function(url)
|
||||
{
|
||||
var w = window.open(url, 'authwin', 'width=700,height=800');
|
||||
if(!w) { handleError("Popup window blocked.") }
|
||||
else
|
||||
{
|
||||
var popupTick = setInterval(function()
|
||||
{
|
||||
if (w.closed)
|
||||
{
|
||||
clearInterval(popupTick);
|
||||
if (!authDone) { handleError("Authorization failed (window closed without authorization completing.") }
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkUser(prms)
|
||||
{
|
||||
send("ready",true);
|
||||
}
|
||||
|
||||
function onMessage(e) {
|
||||
if((typeof e.data) == "string") {
|
||||
var msg = JSON.parse(e.data);
|
||||
console.log(msg);
|
||||
if(msg.code=="show") { doAction(listDir, { "path" : msg.prm }); }
|
||||
else if(msg.code=="load") { doAction(downloadFile, { "path" : msg.prm }); }
|
||||
//else if(msg.code=="load") { doAction(downloadThumbnail, { "path" : msg.prm }); }
|
||||
else if(msg.code=="delete") { doAction(deleteFile, { "path" : msg.prm }); }
|
||||
else if(msg.code=="save" && msg.prm.endsWith("/")) { doAction(makeFolder, { "path" : msg.prm }); }
|
||||
else if(msg.code=="forget") { doAction(signOut); }
|
||||
else if(msg.code=="rename")
|
||||
{
|
||||
var prmSplit = msg.prm.split(":");
|
||||
if (prmSplit.length > 2) { handleError({"error" : "multiple_colons"}); return; }
|
||||
else { doAction(renameFile, { "from_path" : prmSplit[0], "to_path" : prmSplit[0].substring(0,prmSplit[0].lastIndexOf("/")+1)+prmSplit[1] }); }
|
||||
}
|
||||
lastMsg=msg;
|
||||
}
|
||||
else {
|
||||
if(lastMsg.code=="save") doAction(uploadFile, { "path": lastMsg.prm, "buffer" : e.data });
|
||||
}
|
||||
}
|
||||
|
||||
function send(code,prm) {
|
||||
window.parent.postMessage(JSON.stringify({"code":code, "prm":prm}),"*");
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
384
www.photopea.com/code/storages/googledriveStorage.html
Normal file
384
www.photopea.com/code/storages/googledriveStorage.html
Normal file
@ -0,0 +1,384 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="https://apis.google.com/js/api.js"></script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var sentFalse = false;
|
||||
var clientId = "463342976776-04ub3ijsr7i5qobn8ha32ap6vsaae75a.apps.googleusercontent.com";
|
||||
var scope = 'https://www.googleapis.com/auth/drive';
|
||||
var discoveryDocs = ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'];
|
||||
var oauthToken;
|
||||
var GoogleAuth;
|
||||
|
||||
function go()
|
||||
{
|
||||
gapi.load('client:auth2', {'callback': onAuthApiLoad});
|
||||
}
|
||||
|
||||
function handleError(e)
|
||||
{
|
||||
console.log(e);
|
||||
if(e.result && e.result.error && e.result.error.code)
|
||||
{
|
||||
if(e.result.error.code == 403) {send("ready",false); sentFalse = true;}
|
||||
if(e.result.error.code == 401) {send("ready",false); sentFalse = true;}
|
||||
}
|
||||
else if (e.error)
|
||||
{
|
||||
if(e.error == "access_denied") {send("ready",false); sentFalse = true;}
|
||||
if(e.error == "popup_closed_by_user") {send("ready",false); sentFalse = true;}
|
||||
if(e.error == "popup_blocked_by_browser") {send("ready",false); sentFalse = true;}
|
||||
if(e.error == "multiple_colons") {send(1,"Rename path contains more than one colon.");}
|
||||
}
|
||||
}
|
||||
|
||||
function authorize(action,prms)
|
||||
{
|
||||
sentFalse = false;
|
||||
GoogleAuth.signIn()
|
||||
.then(function () {
|
||||
send("ready",true);
|
||||
if(action) { findFile(action, prms.path.slice(1).split("/"), null, null, prms.buffer); }
|
||||
})
|
||||
.catch(function(e) {
|
||||
handleError(e);
|
||||
});
|
||||
}
|
||||
|
||||
function onAuthApiLoad()
|
||||
{
|
||||
window.addEventListener("message", onMessage, false);
|
||||
gapi.client.init({
|
||||
clientId: clientId,
|
||||
scope: scope,
|
||||
discoveryDocs :discoveryDocs
|
||||
})
|
||||
.then(function () {
|
||||
GoogleAuth = gapi.auth2.getAuthInstance();
|
||||
if(!GoogleAuth.isSignedIn.get()) { sentFalse = true; send("ready",false); }
|
||||
else { send("ready",true); }
|
||||
})
|
||||
.catch(handleError);
|
||||
}
|
||||
|
||||
function findFile(action, path, id, pageToken, buffer)
|
||||
{
|
||||
if(!id) id = "root";
|
||||
if(path.length == 0 || path[0] == '')
|
||||
{
|
||||
if(action == listDir)
|
||||
{
|
||||
var query = "'" + id + "' in parents";
|
||||
action(null, query, []);
|
||||
}
|
||||
else { action(id); }
|
||||
return;
|
||||
}
|
||||
if(path.length == 1 && action == createDir)
|
||||
{
|
||||
action(id,path[0]);
|
||||
return;
|
||||
}
|
||||
if(path.length == 1 && action == checkFile)
|
||||
{
|
||||
action(id, path[0], null, buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
var newFile = null;
|
||||
if(path.length == 1 && action == renameFile)
|
||||
{
|
||||
var prmSplit = path[0].split(":");
|
||||
if (prmSplit.length > 2) { handleError({"error" : "multiple_colons"}); return; }
|
||||
else { path[0] = prmSplit[0]; newFile = prmSplit[1]; }
|
||||
}
|
||||
|
||||
var query = "'" + id + "' in parents";
|
||||
|
||||
gapi.client.drive.files.list({
|
||||
'q': query,
|
||||
'pageToken' : pageToken,
|
||||
'pageSize': 1000,
|
||||
'fields': "nextPageToken, files(id, name, size, modifiedTime, kind, mimeType)",})
|
||||
.then(function(response) {
|
||||
var files = response.result.files;
|
||||
pageToken = response.result.nextPageToken;
|
||||
var found = false;
|
||||
for (var i = 0; i < files.length; ++i)
|
||||
{
|
||||
if(files[i].name == path[0])
|
||||
{
|
||||
found = true;
|
||||
if(newFile && action == renameFile) { renameFile(files[i].id, newFile); return; }
|
||||
else { findFile(action, path.slice(1), files[i].id, null, buffer); return; }
|
||||
}
|
||||
}
|
||||
if(!found && pageToken) findFile(action, path, id, pageToken, buffer);
|
||||
else if(action == checkFile) newFile(id,path[0],buffer);
|
||||
else if(action == renameFile) { handleError("Could not find file to rename '"+path[0]+"'"); send(1, "Could not find file to rename '"+path[0]+"'"); }
|
||||
})
|
||||
.catch(handleError);
|
||||
}
|
||||
|
||||
function listDir(pageToken, query, out)
|
||||
{
|
||||
gapi.client.drive.files.list({
|
||||
'q': query,
|
||||
'pageToken' : pageToken,
|
||||
'pageSize': 1000,
|
||||
'fields': "nextPageToken, files(id, name, size, modifiedTime, kind, mimeType, thumbnailLink)",})
|
||||
.then(function(response) {
|
||||
var files = response.result.files;
|
||||
pageToken = response.result.nextPageToken;
|
||||
for(var i = 0; i<files.length; ++i)
|
||||
{
|
||||
if(files[i].mimeType == "application/vnd.google-apps.folder") { out.push([files[i].name, -1, Math.round(Date.parse(files[i].modifiedTime)/1000)]); }
|
||||
else
|
||||
{
|
||||
if(files[i].thumbnailLink) { out.push([files[i].name,files[i].size ? parseInt(files[i].size) : 0, Math.round(Date.parse(files[i].modifiedTime)/1000), files[i].thumbnailLink]); }
|
||||
//but maybe we should only show thumbnail to files which Photopea can open - Gdrive also makes thumbnails for spreadsheets etc.
|
||||
else { out.push([files[i].name,parseInt(files[i].size), Math.round(Date.parse(files[i].modifiedTime)/1000)]); }
|
||||
}
|
||||
}
|
||||
if(pageToken) listDir(pageToken,query,out);
|
||||
else { send(0,out); }
|
||||
})
|
||||
.catch(handleError);
|
||||
}
|
||||
|
||||
function downloadFile(fileId)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', "https://www.googleapis.com/drive/v3/files/" + fileId + "?alt=media");
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + GoogleAuth.currentUser.get().getAuthResponse().access_token);
|
||||
xhr.onload = function(e) {window.parent.postMessage(e.target.response,"*"); };
|
||||
xhr.onerror = function(e) { console.error(e); };
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function downloadThumbnail(fileId)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', "https://www.googleapis.com/drive/v3/files/" + fileId + "?fields=thumbnailLink");
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + GoogleAuth.currentUser.get().getAuthResponse().access_token);
|
||||
xhr.onload = function(e)
|
||||
{
|
||||
var link = JSON.parse(e.target.response).thumbnailLink;
|
||||
console.log(JSON.parse(e.target.response));
|
||||
if (link)
|
||||
{
|
||||
var xhr2 = new XMLHttpRequest();
|
||||
xhr2.open('GET', link);
|
||||
xhr2.responseType = "arraybuffer";
|
||||
xhr2.onload = function(e) {window.parent.postMessage(e.target.response,"*"); };
|
||||
xhr2.onerror = function(e) { console.error(e); };
|
||||
xhr2.send();
|
||||
}
|
||||
else { console.error("Could not get thumbnailLink."); }
|
||||
};
|
||||
xhr.onerror = function(e) { console.error(e); };
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function deleteFile(fileId)
|
||||
{
|
||||
gapi.client.drive.files.delete({
|
||||
'fileId' : fileId,
|
||||
})
|
||||
.then(function(response) {
|
||||
send(0,"");
|
||||
})
|
||||
.catch(function(response) {
|
||||
send(1,response);
|
||||
handleError(response);
|
||||
});
|
||||
}
|
||||
|
||||
function createDir(parentId, name)
|
||||
{
|
||||
gapi.client.drive.files.create(
|
||||
{
|
||||
'resource' :
|
||||
{
|
||||
'name' : name,
|
||||
'parents' : [parentId],
|
||||
'mimeType': 'application/vnd.google-apps.folder',
|
||||
}
|
||||
})
|
||||
.then(function(response) {
|
||||
send(0,"");
|
||||
})
|
||||
.catch(function(response) {
|
||||
send(1,response);
|
||||
handleError(response);
|
||||
});
|
||||
}
|
||||
|
||||
function renameFile(fileId, newName)
|
||||
{
|
||||
gapi.client.drive.files.update({
|
||||
'fileId' : fileId,
|
||||
'resource' :
|
||||
{
|
||||
'name' : newName,
|
||||
}
|
||||
})
|
||||
.then(function(response) {
|
||||
send(0,"");
|
||||
})
|
||||
.catch(function(response) {
|
||||
send(1,response);
|
||||
handleError(response);
|
||||
});
|
||||
}
|
||||
|
||||
//check if file exists, then overwrite, otherwise create new file.
|
||||
function checkFile(parentId, name, pageToken, buffer)
|
||||
{
|
||||
var query = "'" + parentId + "' in parents";
|
||||
gapi.client.drive.files.list({
|
||||
'q': query,
|
||||
'pageToken' : pageToken,
|
||||
'pageSize': 1000,
|
||||
'fields': "nextPageToken, files(id, name)",})
|
||||
.then(function(response) {
|
||||
var files = response.result.files;
|
||||
pageToken = response.result.nextPageToken;
|
||||
var found = false;
|
||||
for (var i = 0; i < files.length; ++i)
|
||||
{
|
||||
if(files[i].name == name)
|
||||
{
|
||||
found = true;
|
||||
uploadFile(files[i].id,parentId,name,buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(!found && pageToken) checkFile(parentId, name, pageToken, buffer);
|
||||
else uploadFile(null,parentId,name,buffer);
|
||||
})
|
||||
.catch(handleError);
|
||||
}
|
||||
|
||||
function uploadFile(fileId, parentId, name, buffer)
|
||||
{
|
||||
var file = new Blob([buffer]);
|
||||
var metadata = { 'name': name, };
|
||||
if(!fileId) metadata['parents'] = [parentId];
|
||||
var xhr = new XMLHttpRequest();
|
||||
if(buffer.byteLength > 5000000)
|
||||
{
|
||||
var contentType = file.type || 'application/octet-stream';
|
||||
metadata['mimeType'] = contentType;
|
||||
metadata['Content-Type'] = contentType;
|
||||
metadata['Content-Length'] = file.size;
|
||||
|
||||
if(!fileId) xhr.open("POST", "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable");
|
||||
else xhr.open("PATCH", "https://www.googleapis.com/upload/drive/v3/files/" + fileId + "?uploadType=resumable");
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + GoogleAuth.currentUser.get().getAuthResponse().access_token);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.setRequestHeader('X-Upload-Content-Length', file.size);
|
||||
xhr.setRequestHeader('X-Upload-Content-Type', contentType);
|
||||
xhr.onreadystatechange = function() {
|
||||
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
|
||||
var locationUrl = xhr.getResponseHeader('Location');
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
|
||||
var xhr2 = new XMLHttpRequest();
|
||||
|
||||
xhr2.open('PUT', locationUrl, true);
|
||||
xhr2.setRequestHeader('Content-Type', contentType);
|
||||
xhr2.setRequestHeader('X-Upload-Content-Type', contentType);
|
||||
xhr2.onreadystatechange = function() {
|
||||
if(xhr2.readyState === XMLHttpRequest.DONE && xhr2.status === 200) {
|
||||
send(0,"");
|
||||
}
|
||||
};
|
||||
xhr2.onerror = function(e) { send(1,e); handleError(e)};
|
||||
xhr2.send(reader.result);
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
};
|
||||
xhr.onerror = function(e) { send(1,e); handleError(e)};
|
||||
xhr.send(JSON.stringify(metadata));
|
||||
}
|
||||
else
|
||||
{
|
||||
var form = new FormData();
|
||||
form.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
|
||||
form.append('file', file);
|
||||
if(!fileId) xhr.open('POST', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id');
|
||||
else xhr.open('PATCH', 'https://www.googleapis.com/upload/drive/v3/files/' + fileId + '?uploadType=multipart&fields=id');
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + GoogleAuth.currentUser.get().getAuthResponse().access_token);
|
||||
xhr.onload = function(response) { send(0,""); };
|
||||
xhr.onerror = function(e) { send(1,e); handleError(e)};
|
||||
xhr.send(form);
|
||||
}
|
||||
}
|
||||
|
||||
function signOutUser()
|
||||
{
|
||||
GoogleAuth.signOut().then(function () {
|
||||
//console.log('User signed out.');
|
||||
send(0,"");
|
||||
})
|
||||
.catch(handleError);
|
||||
}
|
||||
|
||||
function disconnectUser()
|
||||
{
|
||||
GoogleAuth.disconnect().then(function () {
|
||||
//console.log('User account disconnected.');
|
||||
send(0,"");
|
||||
})
|
||||
.catch(handleError);
|
||||
}
|
||||
|
||||
function doAction(action, prms)
|
||||
{
|
||||
if(action == signOutUser) { signOutUser(); }
|
||||
//if(action == signOutUser) { disconnectUser(); }
|
||||
else if(sentFalse) { authorize(action, prms); }
|
||||
else if(!GoogleAuth.isSignedIn.get()) { send("ready", false); sentFalse = true; }
|
||||
else if(!GoogleAuth.currentUser.get().hasGrantedScopes(scope)) { send("ready", false); sentFalse = true; }
|
||||
else { findFile(action, prms.path.slice(1).split("/"), null, null, prms.buffer); }
|
||||
}
|
||||
|
||||
function onMessage(e) {
|
||||
if(e.origin && (e.origin == "https://accounts.google.com" || e.origin == "https://content.googleapis.com")) { return; }
|
||||
if((typeof e.data) == "string") {
|
||||
var msg = JSON.parse(e.data);
|
||||
console.log(msg);
|
||||
//TODO: cannot list directory if name contains forwardslash
|
||||
if(msg.code=="show") { doAction(listDir, { "path" : msg.prm }) }
|
||||
else if(msg.code=="load") { doAction(downloadFile, { "path" : msg.prm }); }
|
||||
//else if(msg.code=="load") { doAction(downloadThumbnail, { "path" : msg.prm }); }
|
||||
else if(msg.code=="delete") { doAction(deleteFile, { "path" : msg.prm }); }
|
||||
else if(msg.code=="save" && msg.prm.endsWith("/")) { doAction(createDir, { "path" : msg.prm.slice(0, -1) }); }
|
||||
//TODO: will not work if filename contains colon(s)
|
||||
else if(msg.code=="rename") { doAction(renameFile, {"path" : msg.prm }); }
|
||||
else if(msg.code=="forget") { doAction(signOutUser); }
|
||||
lastMsg=msg;
|
||||
}
|
||||
else {
|
||||
if(lastMsg.code=="save") doAction(checkFile, { "path": lastMsg.prm, "buffer" : e.data });
|
||||
}
|
||||
}
|
||||
|
||||
function send(code,prm) {
|
||||
window.parent.postMessage(JSON.stringify({"code":code, "prm":prm}),"*");
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user