aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-09-09 20:47:34 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-09-09 20:47:34 -0400
commitb24c475d8cd36af860fc374b0c5621b0d096be1d (patch)
treee046019a983133a5abb38db08d2b9679adc7a18f
parent0c987a119fc6baa344cd6a8d229556c02af64898 (diff)
nearly finished transferring images between text notes and google docs
-rw-r--r--package.json2
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts23
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx2
-rw-r--r--src/client/views/MainView.tsx6
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx2
-rw-r--r--src/new_fields/RichTextUtils.ts67
-rw-r--r--src/server/RouteStore.ts3
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts2
-rw-r--r--src/server/apis/google/GooglePhotosUploadUtils.ts5
-rw-r--r--src/server/credentials/google_docs_token.json2
-rw-r--r--src/server/index.ts20
-rw-r--r--src/server/updateSearch.ts123
13 files changed, 112 insertions, 147 deletions
diff --git a/package.json b/package.json
index f56e34ce0..f0f2b467e 100644
--- a/package.json
+++ b/package.json
@@ -224,4 +224,4 @@
"xoauth2": "^1.2.0",
"youtube": "^0.1.0"
}
-} \ No newline at end of file
+}
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index 5f5b39b14..fddcf3aa5 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -51,17 +51,26 @@ export namespace GooglePhotosClientUtils {
description: string;
}
- export const UploadImageDocuments = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption") => {
+ export const UploadImages = async (sources: (Doc | string)[], album?: AlbumReference, descriptionKey = "caption") => {
if (album && "title" in album) {
album = await (await endpoint()).albums.create(album.title);
}
const media: MediaInput[] = [];
- sources.forEach(document => {
- const data = Cast(Doc.GetProto(document).data, ImageField);
- data && media.push({
- url: data.url.href,
- description: parseDescription(document, descriptionKey),
- });
+ sources.forEach(source => {
+ let url: string;
+ let description: string;
+ if (source instanceof Doc) {
+ const data = Cast(Doc.GetProto(source).data, ImageField);
+ if (!data) {
+ return;
+ }
+ url = data.url.href;
+ description = parseDescription(source, descriptionKey);
+ } else {
+ url = source;
+ description = Utils.GenerateGuid();
+ }
+ media.push({ url, description });
});
if (media.length) {
return PostToServer(RouteStore.googlePhotosMediaUpload, { media, album });
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 7693a388f..a19fd39b7 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -117,7 +117,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
console.log(`(${this.quota - this.remaining}/${this.quota}) ${upload.name}`);
}));
- await GooglePhotosClientUtils.UploadImageDocuments(docs, { title: directory });
+ await GooglePhotosClientUtils.UploadImages(docs, { title: directory });
console.log("Finished upload!");
for (let i = 0; i < docs.length; i++) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 326c13424..0c0ed9072 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -155,7 +155,7 @@ export class MainView extends React.Component {
doc.caption = "Well isn't this a nice cat image!";
let photos = await GooglePhotosClientUtils.endpoint();
let albumId = (await photos.albums.list(50)).albums.filter((album: any) => album.title === "This is a generically created album!")[0].id;
- console.log(await GooglePhotosClientUtils.UploadImageDocuments([doc], { id: albumId }));
+ console.log(await GooglePhotosClientUtils.UploadImages([doc], { id: albumId }));
}
componentWillUnMount() {
@@ -470,7 +470,7 @@ export class MainView extends React.Component {
// let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
// let addYoutubeSearcher = action(() => Docs.Create.YoutubeDocument(youtubeurl, { width: 600, height: 600, title: "youtube search" }));
- let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] });
+ // let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] });
let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc | Promise<Doc>][] = [
[React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
@@ -478,7 +478,7 @@ export class MainView extends React.Component {
[React.createRef<HTMLDivElement>(), "globe-asia", "Add Website", addWebNode],
[React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument],
[React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode],
- [React.createRef<HTMLDivElement>(), "object-group", "Test Google Photos Search", googlePhotosSearch],
+ // [React.createRef<HTMLDivElement>(), "object-group", "Test Google Photos Search", googlePhotosSearch],
[React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode
//[React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher],
];
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 99e5ab7b3..5fc4f36a7 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -253,7 +253,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName };
- let path = Utils.prepend(file);
+ let path = Utils.prepend(file.path);
Docs.Get.DocumentFromType(type, path, full).then(doc => doc && this.props.addDocument(doc));
}));
});
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4033ffd9c..cb9346a8b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -591,7 +591,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" });
cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" });
if (Cast(this.props.Document.data, ImageField)) {
- cm.addItem({ description: "Export to Google Photos", event: () => GooglePhotosClientUtils.UploadImageDocuments([this.props.Document]), icon: "caret-square-right" });
+ cm.addItem({ description: "Export to Google Photos", event: () => GooglePhotosClientUtils.UploadImages([this.props.Document]), icon: "caret-square-right" });
}
let existingMake = ContextMenu.Instance.findByDescription("Make...");
let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : [];
diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts
index 0aba50c0d..500b93676 100644
--- a/src/new_fields/RichTextUtils.ts
+++ b/src/new_fields/RichTextUtils.ts
@@ -7,6 +7,11 @@ import { FormattedTextBox } from "../client/views/nodes/FormattedTextBox";
import { Opt } from "./Doc";
import * as Color from "color";
import { sinkListItem } from "prosemirror-schema-list";
+import { Utils, PostToServer } from "../Utils";
+import { RouteStore } from "../server/RouteStore";
+import { Docs } from "../client/documents/Documents";
+import { schema } from "../client/util/RichTextSchema";
+import { GooglePhotosClientUtils } from "../client/apis/google_docs/GooglePhotosClientUtils";
export namespace RichTextUtils {
@@ -86,19 +91,36 @@ export namespace RichTextUtils {
export namespace GoogleDocs {
export const Export = (state: EditorState): GoogleApiClientUtils.Docs.Content => {
- let textNodes: Node<any>[] = [];
+ let nodes: { [type: string]: Node<any>[] } = {
+ text: [],
+ image: []
+ };
let text = ToPlainText(state);
let content = state.doc.content;
- content.forEach(node => node.content.forEach(node => node.type.name === "text" && textNodes.push(node)));
- let linkRequests = ExtractLinks(textNodes);
+ content.forEach(node => node.content.forEach(node => {
+ const type = node.type.name;
+ let existing = nodes[type];
+ if (existing) {
+ existing.push(node);
+ } else {
+ nodes[type] = [node];
+ }
+ }));
+ let linkRequests = ExtractLinks(nodes.text);
+ let imageRequests = ExtractImages(nodes.image);
return {
text,
- requests: [...linkRequests]
+ requests: [...linkRequests, ...imageRequests]
};
};
type BulletPosition = { value: number, sinks: number };
+ interface MediaItem {
+ baseUrl: string;
+ filename: string;
+ width: number;
+ }
export const Import = async (documentId: GoogleApiClientUtils.Docs.DocumentId): Promise<Opt<GoogleApiClientUtils.Docs.ImportResult>> => {
const document = await GoogleApiClientUtils.Docs.retrieve({ documentId });
if (!document) {
@@ -109,6 +131,17 @@ export namespace RichTextUtils {
const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document);
let state = FormattedTextBox.blankState();
let structured = parseLists(paragraphs);
+ const inline = document.inlineObjects;
+ let inlineUrls: MediaItem[] = [];
+ if (inline) {
+ inlineUrls = Object.keys(inline).map(key => {
+ const embedded = inline[key].inlineObjectProperties!.embeddedObject!;
+ const baseUrl = embedded.imageProperties!.contentUri!;
+ const filename = `upload_${Utils.GenerateGuid()}.png`;
+ const width = embedded.size!.width!.magnitude!;
+ return { baseUrl, filename, width };
+ });
+ }
let position = 3;
let lists: ListGroup[] = [];
@@ -151,6 +184,12 @@ export namespace RichTextUtils {
}
}
+ const uploads = await PostToServer(RouteStore.googlePhotosMediaDownload, { mediaItems: inlineUrls });
+ for (let i = 0; i < uploads.length; i++) {
+ const src = Utils.prepend(`/files/${uploads[i].fileNames.clean}`);
+ state = state.apply(state.tr.insert(0, schema.nodes.image.create({ src, width: inlineUrls[i].width })));
+ }
+
return { title, text, state };
};
@@ -252,6 +291,26 @@ export namespace RichTextUtils {
return links;
};
+ const ExtractImages = async (nodes: Node<any>[]) => {
+ const images: docs_v1.Schema$Request[] = [];
+ let position = 1;
+ for (let node of nodes) {
+ const length = node.nodeSize;
+ const attrs = node.attrs;
+ const uri = attrs.src;
+ const result = (await GooglePhotosClientUtils.UploadImages([uri])).newMediaItemResults;
+ images.push({
+ insertInlineImage: {
+ uri: result[0].mediaItem.productUrl,
+ objectSize: { width: { magnitude: parseFloat(attrs.width.replace("px", "")), unit: "PT" } },
+ location: { index: position + length }
+ }
+ });
+ position += length;
+ }
+ return images;
+ };
+
const Encode = (information: LinkInformation) => {
return {
updateTextStyle: {
diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts
index f65e6134c..ee9cd8a0e 100644
--- a/src/server/RouteStore.ts
+++ b/src/server/RouteStore.ts
@@ -34,6 +34,7 @@ export enum RouteStore {
googleDocs = "/googleDocs",
googlePhotosAccessToken = "/googlePhotosAccessToken",
googlePhotosMediaUpload = "/googlePhotosMediaUpload",
- googlePhotosMediaDownload = "/googlePhotosMediaDownload"
+ googlePhotosMediaDownload = "/googlePhotosMediaDownload",
+ googleDocsGet = "/googleDocsGet"
} \ No newline at end of file
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index ac8023ce1..e0bd8a800 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -42,7 +42,7 @@ export namespace GoogleApiServerUtils {
export type ApiResponse = Promise<GaxiosResponse>;
export type ApiRouter = (endpoint: Endpoint, parameters: any) => ApiResponse;
- export type ApiHandler = (parameters: any) => ApiResponse;
+ export type ApiHandler = (parameters: any, methodOptions?: any) => ApiResponse;
export type Action = "create" | "retrieve" | "update";
export type Endpoint = { get: ApiHandler, create: ApiHandler, batchUpdate: ApiHandler };
diff --git a/src/server/apis/google/GooglePhotosUploadUtils.ts b/src/server/apis/google/GooglePhotosUploadUtils.ts
index 35f986250..d1f1f81bd 100644
--- a/src/server/apis/google/GooglePhotosUploadUtils.ts
+++ b/src/server/apis/google/GooglePhotosUploadUtils.ts
@@ -151,6 +151,11 @@ export namespace DownloadUtils {
.on('close', resolve)
.on('error', reject);
});
+ if (!isLocal) {
+ await new Promise<void>(resolve => {
+ stream(url).pipe(fs.createWriteStream(uploadDirectory + resolved)).on('close', resolve);
+ });
+ }
}
resolve(information);
});
diff --git a/src/server/credentials/google_docs_token.json b/src/server/credentials/google_docs_token.json
index fabc18cfd..5c142fba1 100644
--- a/src/server/credentials/google_docs_token.json
+++ b/src/server/credentials/google_docs_token.json
@@ -1 +1 @@
-{"access_token":"ya29.Glx-BwgWcpQUukTNyuUqvSAYrDyxDNUhCLtrFDJAViROvicm0DrcRvCn4OaQdn2m2IZQYcG-19cvQYoOC3UJCtWXLRvKZzQCbZZSykpxYu_lflUyEnIGZOIHMbbEjA","refresh_token":"1/HTv_xFHszu2Nf3iiFrUTaeKzC_Vp2-6bpIB06xW_WHI","scope":"https://www.googleapis.com/auth/presentations.readonly https://www.googleapis.com/auth/documents.readonly https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/photoslibrary https://www.googleapis.com/auth/photoslibrary.appendonly https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/presentations https://www.googleapis.com/auth/photoslibrary.sharing","token_type":"Bearer","expiry_date":1568008211814} \ No newline at end of file
+{"access_token":"ya29.Glx_B6G7Q_FYs1LK5VcyV6P6Zg9JkoHO2aC_TsnN7AVxPYWHEpsBSC0WyWX7Ztr8HWhOUYA5JXqnZDkLrK1V3Hb-0GgtyApLRNtEPOWf1dJ7lOm_iKVw2tRvPe7XDQ","refresh_token":"1/HTv_xFHszu2Nf3iiFrUTaeKzC_Vp2-6bpIB06xW_WHI","scope":"https://www.googleapis.com/auth/presentations.readonly https://www.googleapis.com/auth/documents.readonly https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/photoslibrary https://www.googleapis.com/auth/photoslibrary.appendonly https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/presentations https://www.googleapis.com/auth/photoslibrary.sharing","token_type":"Bearer","expiry_date":1568078116605} \ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index baef94a59..8469770d5 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -38,6 +38,7 @@ import flash = require('connect-flash');
import { Search } from './Search';
import _ = require('lodash');
import * as Archiver from 'archiver';
+import * as request_promise from 'request-promise';
var AdmZip = require('adm-zip');
import * as YoutubeApi from "./apis/youtube/youtubeApiSample";
import { Response } from 'express-serve-static-core';
@@ -576,9 +577,9 @@ app.post(
form.parse(req, async (_err, _fields, files) => {
let results: FileResponse[] = [];
for (const key in files) {
- const { name, type, path: location } = files[key];
+ const { type, path: location, name } = files[key];
const filename = path.basename(location);
- await UploadUtils.UploadImage(uploadDirectory + filename, path.basename(name));
+ await UploadUtils.UploadImage(uploadDirectory + filename, filename);
results.push({ name, type, path: `/files/${filename}` });
console.log(path.basename(name));
}
@@ -790,10 +791,23 @@ const tokenPath = path.join(__dirname, "./credentials/google_docs_token.json");
const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerUtils.ApiRouter>([
["create", (api, params) => api.create(params)],
- ["retrieve", (api, params) => api.get(params)],
+ ["retrieve", (api, params) => api.get(params, { params: "fields=inlineObjects" })],
["update", (api, params) => api.batchUpdate(params)],
]);
+// app.post(RouteStore.googleDocsGet, async (req, res) => {
+// const token = await GoogleApiServerUtils.RetrieveAccessToken({ credentialsPath, tokenPath });
+// request_promise.get({
+// uri: `https://docs.googleapis.com/v1/documents/${req.body.documentId}?fields=inlineObjects`,
+// headers: {
+// 'Authorization': `Bearer ${token}`
+// }
+// }).then(response => {
+// console.log(response);
+// res.send(response);
+// });
+// });
+
app.post(RouteStore.googleDocs + "/:sector/:action", (req, res) => {
let sector: GoogleApiServerUtils.Service = req.params.sector;
let action: GoogleApiServerUtils.Action = req.params.action;
diff --git a/src/server/updateSearch.ts b/src/server/updateSearch.ts
deleted file mode 100644
index 906b795f1..000000000
--- a/src/server/updateSearch.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { Database } from "./database";
-import { Cursor } from "mongodb";
-import { Search } from "./Search";
-import pLimit from 'p-limit';
-
-const suffixMap: { [type: string]: (string | [string, string | ((json: any) => any)]) } = {
- "number": "_n",
- "string": "_t",
- "boolean": "_b",
- // "image": ["_t", "url"],
- "video": ["_t", "url"],
- "pdf": ["_t", "url"],
- "audio": ["_t", "url"],
- "web": ["_t", "url"],
- "date": ["_d", value => new Date(value.date).toISOString()],
- "proxy": ["_i", "fieldId"],
- "list": ["_l", list => {
- const results = [];
- for (const value of list.fields) {
- const term = ToSearchTerm(value);
- if (term) {
- results.push(term.value);
- }
- }
- return results.length ? results : null;
- }]
-};
-
-function ToSearchTerm(val: any): { suffix: string, value: any } | undefined {
- if (val === null || val === undefined) {
- return;
- }
- const type = val.__type || typeof val;
- let suffix = suffixMap[type];
- if (!suffix) {
- return;
- }
-
- if (Array.isArray(suffix)) {
- const accessor = suffix[1];
- if (typeof accessor === "function") {
- val = accessor(val);
- } else {
- val = val[accessor];
- }
- suffix = suffix[0];
- }
-
- return { suffix, value: val };
-}
-
-function getSuffix(value: string | [string, any]): string {
- return typeof value === "string" ? value : value[0];
-}
-
-const limit = pLimit(5);
-async function update() {
- // await new Promise(res => setTimeout(res, 5));
- console.log("update");
- await Search.Instance.clear();
- const cursor = await Database.Instance.query({});
- console.log("Cleared");
- const updates: any[] = [];
- let numDocs = 0;
- function updateDoc(doc: any) {
- numDocs++;
- if ((numDocs % 50) === 0) {
- console.log("updateDoc " + numDocs);
- }
- // console.log("doc " + numDocs);
- if (doc.__type !== "Doc") {
- return;
- }
- const fields = doc.fields;
- if (!fields) {
- return;
- }
- const update: any = { id: doc._id };
- let dynfield = false;
- for (const key in fields) {
- const value = fields[key];
- const term = ToSearchTerm(value);
- if (term !== undefined) {
- let { suffix, value } = term;
- update[key + suffix] = value;
- dynfield = true;
- }
- }
- if (dynfield) {
- updates.push(update);
- // console.log(updates.length);
- }
- }
- await cursor.forEach(updateDoc);
- console.log(`Updating ${updates.length} documents`);
- const result = await Search.Instance.updateDocuments(updates);
- try {
- console.log(JSON.parse(result).responseHeader.status);
- } catch {
- console.log("Error:");
- // console.log(updates[i]);
- console.log(result);
- console.log("\n");
- }
- // for (let i = 0; i < updates.length; i++) {
- // console.log(i);
- // const result = await Search.Instance.updateDocument(updates[i]);
- // try {
- // console.log(JSON.parse(result).responseHeader.status);
- // } catch {
- // console.log("Error:");
- // console.log(updates[i]);
- // console.log(result);
- // console.log("\n");
- // }
- // }
- // await Promise.all(updates.map(update => {
- // return limit(() => Search.Instance.updateDocument(update));
- // }));
- cursor.close();
-}
-
-update(); \ No newline at end of file