From 5fc98d97feb3360e9d0dc93928c2866b7ddace38 Mon Sep 17 00:00:00 2001 From: Petru Paler Date: Sun, 1 Jan 2023 07:13:01 +0000 Subject: [PATCH] Initial version. --- .gitignore | 2 + config.js.example | 15 +++ db-schema.sql | 13 ++ delete-all.js | 12 ++ package.json | 13 ++ sync.js | 284 +++++++++++++++++++++++++++++++++++++++ update-author.js | 18 +++ yarn.lock | 328 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 685 insertions(+) create mode 100644 .gitignore create mode 100644 config.js.example create mode 100644 db-schema.sql create mode 100644 delete-all.js create mode 100644 package.json create mode 100644 sync.js create mode 100644 update-author.js create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2154779 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +ig-images/ +node_modules/ diff --git a/config.js.example b/config.js.example new file mode 100644 index 0000000..e546f27 --- /dev/null +++ b/config.js.example @@ -0,0 +1,15 @@ +module.exports = { + db_conn: { + host : process.argv[2] || "localhost", + user : 'user', + password : 'password', + database : 'database', + charset : 'utf8mb4' + }, + api: { + url: 'https://example.com', + version: "v5.0", + key: 'dead:beef' + }, + instagramToken: "lsdkfjsdaslkdfjdsklfj" +} diff --git a/db-schema.sql b/db-schema.sql new file mode 100644 index 0000000..98a01a4 --- /dev/null +++ b/db-schema.sql @@ -0,0 +1,13 @@ +CREATE TABLE ig_posts ( + instagram_id varchar(100) not null PRIMARY KEY, + parent_instagram_id varchar(100) not null, + post_id varchar(100), + caption varchar(1000), + media_type varchar(100), + media_url varchar(1000), + permalink varchar(1000), + datetime datetime, + INDEX (parent_instagram_id), + INDEX (post_id) +); + diff --git a/delete-all.js b/delete-all.js new file mode 100644 index 0000000..2cce0b0 --- /dev/null +++ b/delete-all.js @@ -0,0 +1,12 @@ +const api = new GhostAdminAPI(config.api); +const config = require('./config.js'); + +api.posts.browse({ + limit: 'all', + filter: 'tag:photo-post' +}).then((posts) => { + posts.forEach((post) => { + console.log(post.title); + api.posts.delete({id: post.id}); + }); +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..878f07e --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "instasync", + "version": "1.0.0", + "main": "sync.js", + "license": "MIT", + "dependencies": { + "@tryghost/admin-api": "^1.13.2", + "axios": "^1.2.1", + "mysql2": "^2.3.3", + "nodejs-file-downloader": "^4.10.3", + "slugify": "^1.6.5" + } +} diff --git a/sync.js b/sync.js new file mode 100644 index 0000000..2ed9b89 --- /dev/null +++ b/sync.js @@ -0,0 +1,284 @@ +const axios = require('axios').default; +const mysql = require('mysql2'); +const slugify = require('slugify'); +const GhostAdminAPI = require('@tryghost/admin-api'); +const Downloader = require('nodejs-file-downloader'); +const config = require('./config.js'); + +const db_conn = mysql.createConnection(config.db_conn).promise(); +const api = new GhostAdminAPI(config.api); + +async function getInstagramUrl(url) { + return axios.get(url, { + headers: { "Accept-Encoding": "gzip,deflate,compress" } + }); +} + +function processInstagramPosts(posts) { + var postData = []; + + for (post of posts) { + + postData.push([ + post.id, + post.id, + post.caption, + post.media_type, + post.media_url, + post.permalink, + post.timestamp.replace(/\+0000$/, "") + ]); + + if ('children' in post) { + + for (child of post.children.data) { + + const childId = child.id; + + var caption = null; + + postData.push([ + post.id, + child.id, + caption, + child.media_type, + child.media_url, + child.permalink, + child.timestamp + ]); + } + } + } + + return postData; +} + +async function pullInstagramPosts() { + + const url = "https://graph.instagram.com/me/media?fields=id,caption,media_type,media_url,permalink,thumbnail_url,timestamp,children{id,media_type,media_url,permalink,thumbnail_url,timestamp}&access_token=" + config.instagramToken; + + var data = await getInstagramUrl(url); + data = data.data; + var postData = []; + const paginate = process.argv[3] || false; + + while (data != null) { + postData.push(...processInstagramPosts(data.data)); + if (paginate && 'paging' in data && 'next' in data.paging) { + data = await getInstagramUrl(data.paging.next); + data = data.data; + } else { + data = null; + } + } + + const insertSql = "INSERT IGNORE INTO `ig_posts` (`parent_instagram_id`, `instagram_id`, `caption`, `media_type`, `media_url`, `permalink`, `datetime`) VALUES ?"; + + console.log("Inserting " + postData.length + " posts"); + await db_conn.query(insertSql, [postData]); +} + + +//Helper function that returns an appropriate mobiledoc card for the post type +function getAppropriateCard(post, uploadedMediaUrl) { + + if (post.media_type == "VIDEO") { + + return ["html",{"html":"

"}]; + + } else { + + return ["image",{"src": uploadedMediaUrl,"alt":"","title":""}]; + } +} + + +async function doWork() { + + await pullInstagramPosts(); + + //Get all the Instagram photos/videos that we have get to post to the blog + const [posts, _] = await db_conn.query("SELECT * FROM ig_posts WHERE post_id IS NULL AND instagram_id = parent_instagram_id UNION SELECT * FROM ig_posts WHERE post_id IS NULL AND instagram_id != parent_instagram_id"); + + const processedPosts = {}; + + var cardIndex = 0; + var lastParentId = null; + + console.log("Processing " + posts.length + " posts"); + for (post of posts) { + + //Download the media to our server + const parts = post.media_url.split('/'); + const fileName = parts[parts.length - 1].split('?')[0]; + + const downloader = new Downloader({ + url: post.media_url, + directory: "./ig-images", + fileName: fileName, + cloneFiles: false, + skipExistingFileName: true + }) + + await downloader.download();//Downloader.download() returns a promise. + + //Upload it to the Ghost blog file directory + console.log("Uploading " + fileName); + var upload; + if (fileName.endsWith('mp4')) { + upload = await api.media.upload({ + file: "ig-images/" + fileName, + }) + } else { + upload = await api.images.upload({ + file: "ig-images/" + fileName, + }) + } + + //This is the return uploaded media url + const uploadedMediaUrl = upload.url; + + //If the parent Instagram ID differs to the last one then we are working in relation to a different parent post + if (post.parent_instagram_id != lastParentId) { + + //Reset our media index + cardIndex = 0; + lastParentId = post.parent_instagram_id; + } + + const CARD_SECTION = 10; + if (post.parent_instagram_id in processedPosts) { + processedPosts[post.parent_instagram_id]["media_urls"].push(post.media_url); + processedPosts[post.parent_instagram_id]["rowIds"].push(post.instagram_id); + + //Mobiledoc data + processedPosts[post.parent_instagram_id]["cards"].push(getAppropriateCard(post, uploadedMediaUrl)); + processedPosts[post.parent_instagram_id]["sections"].push([CARD_SECTION, cardIndex]); + + cardIndex++; + //This is the first media for this parent ID + } else { + + //const caption = post.caption != null ? post.caption.replace(/(?:\r\n|\r|\n)/g, '
') : ""; + const caption = post.caption != null ? post.caption : ""; + const captionMobileDoc = ["html",{"html": caption}]; + + var captionLines = caption.split("\n") + captionLines = captionLines.filter(function(e){return e}); + + var mobileDocCards = []; + var mobileDocSections = []; + + for (line of captionLines) { + + mobileDocSections.push([1,"p",[[0,[],0,line]]]); + } + + processedPosts[post.parent_instagram_id] = {}; + processedPosts[post.parent_instagram_id]["featureImage"] = uploadedMediaUrl; + if (post.media_type == "VIDEO") { + processedPosts[post.parent_instagram_id]["featureImage"] = null; + + mobileDocCards.push(getAppropriateCard(post, uploadedMediaUrl)); + mobileDocSections.push([CARD_SECTION, cardIndex]); + cardIndex++; + } + + var indexOfFullStop = caption.indexOf('.'); + var indexOfNewLine = caption.indexOf('\n'); + indexOfFullStop = indexOfFullStop !== -1 ? indexOfFullStop : 1000; + indexOfNewLine = indexOfNewLine !== -1 ? indexOfNewLine : 1000; + + const indexOf = Math.min(indexOfFullStop, indexOfNewLine); + + const postTitle = caption.substr(0, indexOf !== 1000 ? indexOf : caption.length); + const postSlug = slugify("Photo " + postTitle); + + var postTags = []; + var matchedTags = caption.match(/#[A-Za-z0-9\-]+/gi); + matchedTags = matchedTags != null && matchedTags.length > 0 ? matchedTags.map(tag => tag.replace("#", "")) : []; + postTags.push("photo-post"); + postTags = postTags.concat(matchedTags); + + var publishedAt = post.datetime.toISOString(); + + processedPosts[post.parent_instagram_id]["publishedAt"] = publishedAt; + processedPosts[post.parent_instagram_id]["postTitle"] = postTitle; + processedPosts[post.parent_instagram_id]["postCaption"] = post.caption; + processedPosts[post.parent_instagram_id]["postSlug"] = postSlug; + processedPosts[post.parent_instagram_id]["postTags"] = postTags; + processedPosts[post.parent_instagram_id]["permalink"] = post.permalink; + processedPosts[post.parent_instagram_id]["caption"] = post.caption; + processedPosts[post.parent_instagram_id]["media_urls"] = [post.media_url]; + processedPosts[post.parent_instagram_id]["rowIds"] = [post.instagram_id]; + + //Mobiledoc data + processedPosts[post.parent_instagram_id]["cards"] = mobileDocCards; + processedPosts[post.parent_instagram_id]["sections"] = mobileDocSections; + } + } + + //We will now submit the posts to the blog.. + + console.log("Submitting " + posts.length + " posts"); + for (instagramParentId of Object.keys(processedPosts)) { + + const processedPost = processedPosts[instagramParentId]; + + let featureImage = processedPost["featureImage"]; + let publishedAt = processedPost["publishedAt"]; + let postTitle = processedPost["postTitle"]; + let postCaption = processedPost["postCaption"]; + let postSlug = processedPost["postSlug"]; + let postTags = processedPost["postTags"]; + let permalink = processedPost["permalink"]; + + let rowIds = processedPost["rowIds"]; + let cards = processedPost["cards"]; + let sections = processedPost["sections"]; + + console.log(cards); + console.log(sections); + + const mobiledoc = { + version: "0.3.2", + atoms: [], + markups: [], + cards: cards, + sections: sections, + } + + const res = await api.posts + .add( + { + title: postTitle, + slug: postSlug.substring(0, 190), + tags: postTags, + meta_description: postCaption.substring(0, 500), + meta_title: postTitle, + feature_image: featureImage, + status: "published", + published_at: publishedAt, + created_at: publishedAt, + updated_at: publishedAt, + authors: ["hello@alo.land"], + mobiledoc: JSON.stringify(mobiledoc) + }, + ) + + + const postId = res.id; + + console.log("POSTED: " + postId); + + //Mark the media rows submitted as part of this post as complete + const updateSql = "UPDATE ig_posts SET post_id = ? WHERE instagram_id IN (?)"; + const updateResponse = await db_conn.query(updateSql, [postId, rowIds]); + + console.log("MARKED AS POSTED"); + } + console.log("Done"); +} + +doWork() + .then(result => { process.exit() }); diff --git a/update-author.js b/update-author.js new file mode 100644 index 0000000..534186d --- /dev/null +++ b/update-author.js @@ -0,0 +1,18 @@ +const GhostAdminAPI = require('@tryghost/admin-api'); +const config = require('./config.js'); + +const api = new GhostAdminAPI(config.api); + +api.posts.browse({ + limit: 'all', + filter: 'tag:photo-post+author:petru' +}).then((posts) => { + posts.forEach((post) => { + console.log(post.title, post.published_at); + api.posts.edit({ + id: post.id, + updated_at: post.published_at, + authors: ["hello@alo.land"] + }); + }); +}); diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..27b303d --- /dev/null +++ b/yarn.lock @@ -0,0 +1,328 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@tryghost/admin-api@^1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@tryghost/admin-api/-/admin-api-1.13.2.tgz#acb6d9bc02f43ee6ad376055f954286fe23d0f84" + integrity sha512-H7zTOeoP7hSBca5QvhB0OcjjvsJHRc0uPB/kKMt09mzgf1BAAIo6L0S9QKVbMp5D7ko1a7MtnFlBOKIGO43c6g== + dependencies: + axios "^0.27.0" + form-data "^4.0.0" + jsonwebtoken "^8.4.0" + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^0.27.0: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + +axios@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.1.tgz#44cf04a3c9f0c2252ebd85975361c026cb9f864a" + integrity sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +debug@4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +denque@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +follow-redirects@^1.14.9, follow-redirects@^1.15.0, follow-redirects@^1.15.1: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +generate-function@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== + +jsonwebtoken@^8.4.0: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +lru-cache@^4.1.3: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.27: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mysql2@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-2.3.3.tgz#944f3deca4b16629052ff8614fbf89d5552545a0" + integrity sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA== + dependencies: + denque "^2.0.1" + generate-function "^2.3.1" + iconv-lite "^0.6.3" + long "^4.0.0" + lru-cache "^6.0.0" + named-placeholders "^1.1.2" + seq-queue "^0.0.5" + sqlstring "^2.3.2" + +named-placeholders@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.2.tgz#ceb1fbff50b6b33492b5cf214ccf5e39cef3d0e8" + integrity sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA== + dependencies: + lru-cache "^4.1.3" + +nodejs-file-downloader@^4.10.3: + version "4.10.3" + resolved "https://registry.yarnpkg.com/nodejs-file-downloader/-/nodejs-file-downloader-4.10.3.tgz#22f65c161ec66c4717ad4c41b5fafa2790e13be4" + integrity sha512-4WlUP2nefJVJOkzL1fze+n5yP4fxE6O1PdDsjpreRoOrKGFTlF4G6Xyg+XGpTyPFCdAq8psmGs9r1VRU6nr0Jg== + dependencies: + follow-redirects "^1.15.1" + https-proxy-agent "^5.0.0" + mime-types "^2.1.27" + sanitize-filename "^1.6.3" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sanitize-filename@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378" + integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== + dependencies: + truncate-utf8-bytes "^1.0.0" + +semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +seq-queue@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" + integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q== + +slugify@^1.6.5: + version "1.6.5" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.5.tgz#c8f5c072bf2135b80703589b39a3d41451fbe8c8" + integrity sha512-8mo9bslnBO3tr5PEVFzMPIWwWnipGS0xVbYf65zxDqfNwmzYn1LpiKNrR6DlClusuvo+hDHd1zKpmfAe83NQSQ== + +sqlstring@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c" + integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg== + +truncate-utf8-bytes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" + integrity sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ== + dependencies: + utf8-byte-length "^1.0.1" + +utf8-byte-length@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61" + integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==