diff --git a/.env b/.env
deleted file mode 100644
index 7bffd96..0000000
--- a/.env
+++ /dev/null
@@ -1,2 +0,0 @@
-SOURCE_DIR=../bes-lyrics/verified
-OUT_DIR=./pp7-songs
diff --git a/.env.local b/.env.local
new file mode 100644
index 0000000..98a4093
--- /dev/null
+++ b/.env.local
@@ -0,0 +1,4 @@
+TZ=Europe/Bucharest
+LOCAL_SOURCE_DIR=../bes-lyrics/verified
+LOCAL_OUT_DIR=./out_temp_for_local
+CONNECT_TO_G_DRIVE=false
diff --git a/.env.remote b/.env.remote
index 29b1234..517ca0f 100644
--- a/.env.remote
+++ b/.env.remote
@@ -1,2 +1,5 @@
-SOURCE_DIR=../bes-lyrics/verified
-OUT_DIR=/Users/ilucut/WORK/BES/CLOUD DATA/ProPresenter_Generated/ProPresenter_Generated_Version_10
+TZ=Europe/Bucharest
+LOCAL_SOURCE_DIR=../bes-lyrics/verified
+LOCAL_OUT_DIR=./out_temp_for_remote
+GDRIVE_ROOT_FOLDER_ID=1jjGoKWLbYskXWsCcOgjtQiynCUs-lslB
+CONNECT_TO_G_DRIVE=true
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..9e9ef5b
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,7 @@
+#### Motivation and context
+
+
+
+#### Checklist:
+
+- [x] I covered my changes with tests
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..4fbdcd2
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,22 @@
+on: [pull_request]
+
+jobs:
+ Build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone repository
+ uses: actions/checkout@v3
+
+ - name: Use Node.js 20.x
+ uses: actions/setup-node@v3
+ with:
+ node-version: 20.x
+
+ - name: Install dependencies
+ run: npm i
+
+ - name: Build
+ run: npm run test
+ env:
+ CI: true
+ FORCE_COLOR: 2
diff --git a/.gitignore b/.gitignore
index 965c5fb..28cb537 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
node_modules/
.idea
-pp7-songs
+out_temp_for_local
+.env.debug
+out_temp_for_remote
diff --git a/.run/Run migrator.run.xml b/.run/Run migrator.run.xml
deleted file mode 100644
index df0b636..0000000
--- a/.run/Run migrator.run.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.run/[Debug] Run converter.run.xml b/.run/[Debug] Run converter.run.xml
new file mode 100644
index 0000000..f4a4566
--- /dev/null
+++ b/.run/[Debug] Run converter.run.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/[Local] Run converter.run.xml b/.run/[Local] Run converter.run.xml
new file mode 100644
index 0000000..373750e
--- /dev/null
+++ b/.run/[Local] Run converter.run.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index d0f472e..70839af 100644
--- a/README.md
+++ b/README.md
@@ -36,26 +36,26 @@ Domn al veșniciei, în veci! Amin!
- Assuming that the following config is fine:
```dotenv
-SOURCE_DIR=directory-with-txt-songs
-OUT_DIR=directory-with-pro-songs
+LOCAL_SOURCE_DIR=directory-with-txt-songs
+LOCAL_OUT_DIR=directory-with-pro-songs
```
-Simply run the `npm run migrate:local`
+Simply run the `npm run convert:local`
### How to customize the runner
- Pass the following env variables with your source and out directories
```dotenv
-SOURCE_DIR=directory-with-txt-songs
-OUT_DIR=directory-with-pro-songs
+LOCAL_SOURCE_DIR=directory-with-txt-songs
+LOCAL_OUT_DIR=directory-with-pro-songs
```
-- Use the `migrateSongsToPP7Format` method to do the conversion as follows:
+- Use the `convertSongsToPP7FormatLocally` method to do the conversion as follows:
```typescript
import dotenv from 'dotenv';
-import { Config, migrateSongsToPP7Format } from './';
+import { Config, convertSongsToPP7FormatLocally } from './';
import { Presentation_CCLI } from './proto/presentation';
import { Graphics_Text_Attributes_Font } from './proto/graphicsData';
@@ -87,10 +87,9 @@ const CONFIG = {
'A macro name for referencing the macro id in the first intro blank slide',
};
-migrateSongsToPP7Format({
- sourceDir: process.env.SOURCE_DIR as string,
- outDir: process.env.OUT_DIR as string,
- clearOutputDirFirst: true,
+convertSongsToPP7FormatLocally({
+ sourceDir: process.env.LOCAL_SOURCE_DIR as string,
+ baseLocalDir: process.env.LOCAL_OUT_DIR as string,
config: CONFIG,
});
```
diff --git a/environment.d.ts b/environment.d.ts
index 29b6b3b..518c100 100644
--- a/environment.d.ts
+++ b/environment.d.ts
@@ -3,6 +3,13 @@ declare global {
interface ProcessEnv {
CANDIDATES_DIR: string;
VERIFIED_DIR: string;
+ GDRIVE_ROOT_FOLDER_ID: string;
+ CONNECT_TO_G_DRIVE: string;
+
+ // Injected from GH Secrets
+ GDRIVE_BES_CLIENT_ID: string;
+ GDRIVE_BES_CLIENT_SECRET: string;
+ GDRIVE_BES_CLIENT_REFRESH_TOKEN: string;
}
}
}
diff --git a/package-lock.json b/package-lock.json
index 0c8f645..30e55f0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,11 +8,13 @@
"name": "migrator_propres7-node",
"version": "1.0.0",
"dependencies": {
+ "@googleapis/drive": "^8.0.0",
"dotenv": "^16.0.3",
"dotenv-cli": "^7.2.1",
"fs-extra": "^11.1.1",
"google-protobuf": "^3.21.2",
"lodash": "^4.17.21",
+ "p-map": "^4.0.0",
"protobufjs": "^7.2.3",
"recursive-readdir": "^2.2.3",
"ts-proto": "^1.147.1",
@@ -756,6 +758,17 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
+ "node_modules/@googleapis/drive": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.0.0.tgz",
+ "integrity": "sha512-RMa1gto0Dwmy3pi7o8IkqAXiDUHl9ENn/VPm5kPfCl3lxa/oRRzhHlbQ7DAeJz+ukIaVm0lbtS7wYcT7jxAA1g==",
+ "dependencies": {
+ "googleapis-common": "^6.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
@@ -1702,6 +1715,29 @@
"node": ">=0.4.0"
}
},
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -1794,6 +1830,14 @@
"node": ">=8"
}
},
+ "node_modules/arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/babel-jest": {
"version": "29.6.1",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.1.tgz",
@@ -1890,6 +1934,33 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/bignumber.js": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
+ "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1960,12 +2031,29 @@
"node-int64": "^0.4.0"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
+ "node_modules/call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2061,6 +2149,14 @@
"integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==",
"dev": true
},
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -2148,7 +2244,6 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -2282,6 +2377,14 @@
"detect-libc": "^1.0.3"
}
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.4.341",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.341.tgz",
@@ -2682,6 +2785,11 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2728,6 +2836,11 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
+ "node_modules/fast-text-encoding": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
+ "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
+ },
"node_modules/fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
@@ -2838,8 +2951,33 @@
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
- "dev": true
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "node_modules/gaxios": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
+ "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^5.0.0",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.6.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/gcp-metadata": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
+ "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
+ "dependencies": {
+ "gaxios": "^5.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
@@ -2859,6 +2997,20 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/get-intrinsic": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+ "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/get-package-type": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
@@ -2941,11 +3093,76 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/google-auth-library": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz",
+ "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==",
+ "dependencies": {
+ "arrify": "^2.0.0",
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "fast-text-encoding": "^1.0.0",
+ "gaxios": "^5.0.0",
+ "gcp-metadata": "^5.3.0",
+ "gtoken": "^6.1.0",
+ "jws": "^4.0.0",
+ "lru-cache": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/google-auth-library/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/google-auth-library/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
+ "node_modules/google-p12-pem": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz",
+ "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==",
+ "dependencies": {
+ "node-forge": "^1.3.1"
+ },
+ "bin": {
+ "gp12-pem": "build/src/bin/gp12-pem.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/google-protobuf": {
"version": "3.21.2",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz",
"integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA=="
},
+ "node_modules/googleapis-common": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-6.0.4.tgz",
+ "integrity": "sha512-m4ErxGE8unR1z0VajT6AYk3s6a9gIMM6EkDZfkPnES8joeOlEtFEJeF8IyZkb0tjPXkktUfYrE4b3Li1DNyOwA==",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "gaxios": "^5.0.1",
+ "google-auth-library": "^8.0.2",
+ "qs": "^6.7.0",
+ "url-template": "^2.0.8",
+ "uuid": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -2957,11 +3174,23 @@
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"dev": true
},
+ "node_modules/gtoken": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz",
+ "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==",
+ "dependencies": {
+ "gaxios": "^5.0.1",
+ "google-p12-pem": "^4.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
@@ -2978,12 +3207,46 @@
"node": ">=8"
}
},
+ "node_modules/has-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+ "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@@ -3055,6 +3318,14 @@
"node": ">=0.8.19"
}
},
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -3185,7 +3456,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true,
"engines": {
"node": ">=8"
},
@@ -4027,6 +4297,14 @@
"node": ">=4"
}
},
+ "node_modules/json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "dependencies": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -4068,6 +4346,25 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "dependencies": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -4246,8 +4543,7 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/natural-compare": {
"version": "1.4.0",
@@ -4261,6 +4557,33 @@
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
"dev": true
},
+ "node_modules/node-fetch": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
+ "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-forge": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
+ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
+ "engines": {
+ "node": ">= 6.13.0"
+ }
+ },
"node_modules/node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -4302,6 +4625,14 @@
"node": ">= 0.10.0"
}
},
+ "node_modules/object-inspect": {
+ "version": "1.12.3",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+ "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -4385,6 +4716,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -4615,6 +4960,20 @@
}
]
},
+ "node_modules/qs": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
+ "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -4756,6 +5115,25 @@
"queue-microtask": "^1.2.2"
}
},
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/semver": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
@@ -4808,6 +5186,19 @@
"node": ">=8"
}
},
+ "node_modules/side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -5015,6 +5406,11 @@
"node": ">=8.0"
}
},
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
"node_modules/ts-jest": {
"version": "29.1.0",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz",
@@ -5311,6 +5707,11 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/url-template": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
+ "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="
+ },
"node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
@@ -5364,6 +5765,20 @@
"makeerror": "1.0.12"
}
},
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6029,6 +6444,14 @@
"integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==",
"dev": true
},
+ "@googleapis/drive": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.0.0.tgz",
+ "integrity": "sha512-RMa1gto0Dwmy3pi7o8IkqAXiDUHl9ENn/VPm5kPfCl3lxa/oRRzhHlbQ7DAeJz+ukIaVm0lbtS7wYcT7jxAA1g==",
+ "requires": {
+ "googleapis-common": "^6.0.3"
+ }
+ },
"@humanwhocodes/config-array": {
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
@@ -6789,6 +7212,23 @@
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
"dev": true
},
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "requires": {
+ "debug": "4"
+ }
+ },
+ "aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "requires": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ }
+ },
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -6856,6 +7296,11 @@
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true
},
+ "arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="
+ },
"babel-jest": {
"version": "29.6.1",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.1.tgz",
@@ -6931,6 +7376,16 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+ },
+ "bignumber.js": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz",
+ "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig=="
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -6979,12 +7434,26 @@
"node-int64": "^0.4.0"
}
},
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -7036,6 +7505,11 @@
"integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==",
"dev": true
},
+ "clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
+ },
"cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -7110,7 +7584,6 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
"requires": {
"ms": "2.1.2"
}
@@ -7203,6 +7676,14 @@
"detect-libc": "^1.0.3"
}
},
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"electron-to-chromium": {
"version": "1.4.341",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.341.tgz",
@@ -7488,6 +7969,11 @@
"jest-util": "^29.6.1"
}
},
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ },
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -7530,6 +8016,11 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
+ "fast-text-encoding": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz",
+ "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w=="
+ },
"fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
@@ -7618,8 +8109,27 @@
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
- "dev": true
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "gaxios": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
+ "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
+ "requires": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^5.0.0",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.6.9"
+ }
+ },
+ "gcp-metadata": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
+ "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
+ "requires": {
+ "gaxios": "^5.0.0",
+ "json-bigint": "^1.0.0"
+ }
},
"gensync": {
"version": "1.0.0-beta.2",
@@ -7633,6 +8143,17 @@
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
+ "get-intrinsic": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+ "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3"
+ }
+ },
"get-package-type": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
@@ -7688,11 +8209,63 @@
"slash": "^3.0.0"
}
},
+ "google-auth-library": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz",
+ "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==",
+ "requires": {
+ "arrify": "^2.0.0",
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "fast-text-encoding": "^1.0.0",
+ "gaxios": "^5.0.0",
+ "gcp-metadata": "^5.3.0",
+ "gtoken": "^6.1.0",
+ "jws": "^4.0.0",
+ "lru-cache": "^6.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ }
+ }
+ },
+ "google-p12-pem": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz",
+ "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==",
+ "requires": {
+ "node-forge": "^1.3.1"
+ }
+ },
"google-protobuf": {
"version": "3.21.2",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz",
"integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA=="
},
+ "googleapis-common": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-6.0.4.tgz",
+ "integrity": "sha512-m4ErxGE8unR1z0VajT6AYk3s6a9gIMM6EkDZfkPnES8joeOlEtFEJeF8IyZkb0tjPXkktUfYrE4b3Li1DNyOwA==",
+ "requires": {
+ "extend": "^3.0.2",
+ "gaxios": "^5.0.1",
+ "google-auth-library": "^8.0.2",
+ "qs": "^6.7.0",
+ "url-template": "^2.0.8",
+ "uuid": "^9.0.0"
+ }
+ },
"graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -7704,11 +8277,20 @@
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"dev": true
},
+ "gtoken": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz",
+ "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==",
+ "requires": {
+ "gaxios": "^5.0.1",
+ "google-p12-pem": "^4.0.0",
+ "jws": "^4.0.0"
+ }
+ },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dev": true,
"requires": {
"function-bind": "^1.1.1"
}
@@ -7719,12 +8301,31 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
+ "has-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+ "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
+ },
+ "has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+ },
"html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
+ "https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
"human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@@ -7771,6 +8372,11 @@
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true
},
+ "indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="
+ },
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -7871,8 +8477,7 @@
"is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
},
"isexe": {
"version": "2.0.0",
@@ -8494,6 +9099,14 @@
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
+ "json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "requires": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
"json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -8527,6 +9140,25 @@
"universalify": "^2.0.0"
}
},
+ "jwa": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+ "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "requires": {
+ "jwa": "^2.0.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -8671,8 +9303,7 @@
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"natural-compare": {
"version": "1.4.0",
@@ -8686,6 +9317,19 @@
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
"dev": true
},
+ "node-fetch": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
+ "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "node-forge": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
+ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
+ },
"node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -8718,6 +9362,11 @@
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz",
"integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA=="
},
+ "object-inspect": {
+ "version": "1.12.3",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
+ "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
+ },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -8779,6 +9428,14 @@
}
}
},
+ "p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -8934,6 +9591,14 @@
"integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==",
"dev": true
},
+ "qs": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
+ "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
+ "requires": {
+ "side-channel": "^1.0.4"
+ }
+ },
"queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -9016,6 +9681,11 @@
"queue-microtask": "^1.2.2"
}
},
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
"semver": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
@@ -9055,6 +9725,16 @@
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
+ "side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "requires": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ }
+ },
"signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -9213,6 +9893,11 @@
"is-number": "^7.0.0"
}
},
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
"ts-jest": {
"version": "29.1.0",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz",
@@ -9409,6 +10094,11 @@
"punycode": "^2.1.0"
}
},
+ "url-template": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
+ "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="
+ },
"uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
@@ -9458,6 +10148,20 @@
"makeerror": "1.0.12"
}
},
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/package.json b/package.json
index 4729a47..81bcaf3 100644
--- a/package.json
+++ b/package.json
@@ -1,22 +1,24 @@
{
"name": "migrator_propres7-node",
"version": "1.0.0",
- "description": "A migrator from .txt song lyrics to PP7 pro format.",
+ "description": "A converter from .txt song lyrics to PP7 pro format.",
"main": "index.ts",
"scripts": {
- "test:ci": "NODE_ENV=test jest --runInBand --no-cache",
- "test:watch": "NODE_ENV=test TZ='Europe/Berlin' jest --watch --logHeapUsage",
+ "test:ci": "NODE_ENV=test TZ='Europe/Bucharest' jest --runInBand --no-cache",
+ "test:watch": "NODE_ENV=test TZ='Europe/Bucharest' jest --watch --logHeapUsage",
"test": "is-ci-cli test:ci test:watch",
- "migrate:local": "ts-node runner.ts",
- "migrate:remote": "dotenv -e .env.remote ts-node runner.ts"
+ "convert:local": "dotenv -e .env.local ts-node runner.ts",
+ "convert:remote": "dotenv -e .env.remote ts-node runner.ts"
},
"author": "Ioan Lucut",
"dependencies": {
+ "@googleapis/drive": "^8.0.0",
"dotenv": "^16.0.3",
"dotenv-cli": "^7.2.1",
"fs-extra": "^11.1.1",
"google-protobuf": "^3.21.2",
"lodash": "^4.17.21",
+ "p-map": "^4.0.0",
"protobufjs": "^7.2.3",
"recursive-readdir": "^2.2.3",
"ts-proto": "^1.147.1",
diff --git a/runner.ts b/runner.ts
index 715349b..d910731 100644
--- a/runner.ts
+++ b/runner.ts
@@ -1,5 +1,9 @@
import dotenv from 'dotenv';
-import { Config, migrateSongsToPP7Format } from './';
+import {
+ Config,
+ convertSongsToPP7FormatLocally,
+ convertSongsToPP7FormatRemotely,
+} from './';
import { Presentation_CCLI } from './proto/presentation';
import { Graphics_Text_Attributes_Font } from './proto/graphicsData';
@@ -27,13 +31,18 @@ const CONFIG = {
presentationCategory: `Worship Songs ~ BES ${new Date().getFullYear()}`,
refMacroId: '3ffd01b7-104f-499f-aac9-a13135006d0e',
refMacroName: 'Songs',
-};
+} as Config;
(async () => {
- await migrateSongsToPP7Format({
- sourceDir: process.env.SOURCE_DIR as string,
- outDir: process.env.OUT_DIR as string,
- clearOutputDirFirst: true,
+ const deploymentArgs = {
+ sourceDir: process.env.LOCAL_SOURCE_DIR as string,
+ baseLocalDir: process.env.LOCAL_OUT_DIR as string,
config: CONFIG,
- });
+ };
+
+ if (process.env.CONNECT_TO_G_DRIVE !== 'true') {
+ await convertSongsToPP7FormatLocally(deploymentArgs);
+ } else {
+ await convertSongsToPP7FormatRemotely(deploymentArgs);
+ }
})();
diff --git a/src/constants.ts b/src/constants.ts
index 788a6c8..b7263e7 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -12,6 +12,8 @@ export const SLASH = '/';
export const HASH = '#';
+export const DASH = '-';
+
export const TXT_EXTENSION = '.txt';
export const TEST_FILE = 'TEMPLATE.txt';
@@ -34,3 +36,5 @@ export const TEST_ENV = 'test';
export const EMPTY_SPACE = ' ';
export const PRO_EXTENSION = `.pro`;
+
+export const MANIFEST_FILE_NAME = 'manifest.json';
diff --git a/src/converterService.ts b/src/converterService.ts
new file mode 100644
index 0000000..5ca012f
--- /dev/null
+++ b/src/converterService.ts
@@ -0,0 +1,151 @@
+import fs from 'fs';
+import path from 'path';
+import { isEqual } from 'lodash';
+import { Presentation } from '../proto/presentation';
+import {
+ Config,
+ convertSongToProPresenter7,
+} from './proPresenter7SongConverter';
+import { MANIFEST_FILE_NAME, PRO_EXTENSION, TXT_EXTENSION } from './constants';
+import {
+ getClosestVersionedDir,
+ getVersionedDir,
+ parseDateFromVersionedDir,
+} from './core';
+import {
+ ConvertedFileStats,
+ DeployableSong,
+ SongManifest,
+ SongsInventoryManifest,
+} from './types';
+import recursive from 'recursive-readdir';
+import { parseSong } from './songsParser';
+import { generateManifest } from './manifestGenerator';
+
+export const getConvertedAndWrittenToLocalOutDirSongs = (
+ songsToGenerate: DeployableSong[],
+ dir: string,
+ config: Config,
+) =>
+ songsToGenerate.map(({ fileName, song }) => {
+ const songInProPresenter7Format = convertSongToProPresenter7(song, config);
+
+ const songFileName = fileName.replace(TXT_EXTENSION, PRO_EXTENSION);
+ const songFilePath = path.join(dir, fileName);
+
+ fs.writeFileSync(
+ songFilePath,
+ Buffer.from(Presentation.encode(songInProPresenter7Format).finish()),
+ );
+
+ console.log(
+ `Successfully converted "${songFilePath}" in the "${dir}" directory.`,
+ );
+
+ return {
+ songFileName,
+ songFilePath,
+ } as ConvertedFileStats;
+ });
+
+export const getPreviousDeploymentDirectory = (
+ deploymentDate: Date,
+ allPreviousDeploymentDirs: {
+ deploymentDirDate: Date;
+ deploymentDir: string;
+ }[],
+) => {
+ const previousDeploymentDirDate = getClosestVersionedDir(
+ deploymentDate,
+ allPreviousDeploymentDirs.map(({ deploymentDirDate }) => deploymentDirDate),
+ );
+
+ return getVersionedDir(previousDeploymentDirDate);
+};
+
+export const transformManifestToHashMapForFasterRetrievals = (
+ manifest: SongManifest[],
+) =>
+ manifest.reduce(
+ (accumulator, entry) => ({
+ ...accumulator,
+ [entry.id]: entry,
+ }),
+ {} as {
+ [id: string]: SongManifest;
+ },
+ );
+
+export const getSongDiffFromManifest = (
+ currentManifest: SongsInventoryManifest,
+ previousManifest: SongsInventoryManifest,
+) => {
+ const previousManifestHashMap = transformManifestToHashMapForFasterRetrievals(
+ previousManifest.inventory,
+ );
+ const currentManifestHashMap = transformManifestToHashMapForFasterRetrievals(
+ currentManifest.inventory,
+ );
+
+ const newOrUpdatedSongs = currentManifest.inventory.filter(
+ ({ id, fileName, contentHash }) =>
+ // Is new song
+ !previousManifestHashMap[id] ||
+ // Is an existing-updated song
+ !isEqual(previousManifestHashMap[id]?.contentHash, contentHash),
+ );
+
+ const toBeRemovedFileNames = previousManifest.inventory
+ .filter(
+ ({ id, fileName, contentHash }) =>
+ // File-name has changed
+ !isEqual(currentManifestHashMap[id]?.fileName, fileName),
+ )
+ .map(({ id }) => previousManifestHashMap[id]?.fileName);
+
+ return {
+ newOrUpdatedSongs,
+ toBeRemovedFileNames,
+ };
+};
+
+export const getDeployableSongs = async (sourceDir: string) =>
+ (await recursive(sourceDir, ['.DS_Store'])).map((filePath) => {
+ const fileAsText = fs.readFileSync(filePath).toString();
+ const fileName = path.basename(filePath);
+ const song = parseSong(fileAsText);
+
+ return { song, fileName, fileAsText } as DeployableSong;
+ });
+
+export const getBasicDeploymentInfo = async (
+ sourceDir: string,
+ baseLocalDir: string,
+) => {
+ const versionedDir = getVersionedDir();
+ const deploymentDate = parseDateFromVersionedDir(versionedDir);
+ const deploymentVersionedDir = `${baseLocalDir}/${versionedDir}`;
+
+ // ---
+ // Current deployment
+ const deployableSongs = await getDeployableSongs(sourceDir);
+
+ const currentManifest = {
+ inventory: await generateManifest(deployableSongs),
+ updatedOn: versionedDir,
+ } as SongsInventoryManifest;
+
+ const localManifestFilePath = path.join(
+ deploymentVersionedDir,
+ MANIFEST_FILE_NAME,
+ );
+
+ return {
+ versionedDir,
+ deploymentDate,
+ deploymentVersionedDir,
+ deployableSongs,
+ currentManifest,
+ localManifestFilePath,
+ };
+};
diff --git a/src/core.spec.ts b/src/core.spec.ts
index 7c2f7a3..ab98b37 100644
--- a/src/core.spec.ts
+++ b/src/core.spec.ts
@@ -1,10 +1,17 @@
import {
+ parseDateFromVersionedDir,
getMetaSectionsFromTitle,
getSongInSectionTuples,
getTitleWithoutMeta,
+ getVersionedDir,
+ getClosestVersionedDir,
} from './core';
import { SIMPLE_SONG_MOCK_FILE_CONTENT } from '../mocks';
+jest
+ .useFakeTimers()
+ .setSystemTime(new Date('2023-02-19T11:12:13.000Z').getTime());
+
describe('core', () => {
describe('getSongInSectionTuples', () => {
it('should work correctly', () => {
@@ -74,4 +81,38 @@ describe('core', () => {
`);
});
});
+
+ describe('getClosestVersionedDir', () => {
+ it('should work correctly', () => {
+ expect(getVersionedDir()).toMatchInlineSnapshot(`"2023-02-19-13:12:13"`);
+ });
+ });
+
+ describe('parseDateFromVersionedDir', () => {
+ it('should work correctly', () => {
+ expect(
+ parseDateFromVersionedDir('2023-02-19-11:12:13'),
+ ).toMatchInlineSnapshot(`2023-02-19T09:12:13.000Z`);
+
+ expect(
+ parseDateFromVersionedDir('2023-08-08-19:17:25'),
+ ).toMatchInlineSnapshot(`2023-08-08T16:17:25.000Z`);
+ });
+ });
+
+ describe('getClosestVersionedDir', () => {
+ it('should work correctly', () => {
+ expect(
+ getClosestVersionedDir(
+ parseDateFromVersionedDir('2023-26-20-11:12:13'),
+ [
+ parseDateFromVersionedDir('2023-02-20-11:12:13'),
+ parseDateFromVersionedDir('2023-02-19-11:12:13'),
+ parseDateFromVersionedDir('2023-02-23-11:12:13'),
+ parseDateFromVersionedDir('2023-02-24-11:12:13'),
+ ],
+ ),
+ ).toMatchInlineSnapshot(`2023-02-24T09:12:13.000Z`);
+ });
+ });
});
diff --git a/src/core.ts b/src/core.ts
index 810cd4d..5e4ffde 100644
--- a/src/core.ts
+++ b/src/core.ts
@@ -1,15 +1,26 @@
-import { first, last, parseInt, trim } from 'lodash';
import chalk from 'chalk';
-import { SequenceChar } from './types';
+import {
+ filter,
+ first,
+ includes,
+ last,
+ parseInt,
+ size,
+ trim,
+ uniq,
+} from 'lodash';
+import { SequenceChar, SongMeta } from './types';
import {
COLON,
COMMA,
+ DASH,
DOUBLE_LINE_TUPLE,
EMPTY_STRING,
HASH,
NEW_LINE_TUPLE,
TEST_ENV,
} from './constants';
+import assert from 'node:assert';
const MISSING_SEQUENCE_NUMBER = 1;
@@ -84,7 +95,7 @@ export const getMetaSectionsFromTitle = (titleContent: string) => {
const [sequence, content] = entry.split(COLON);
return { ...accumulator, [sequence]: trim(content) };
- }, {});
+ }, {}) as Record;
};
export const createSongMock = (
@@ -119,3 +130,47 @@ ${tuples
export const convertSequenceToNumber = (sequenceOrderQualifier: string) =>
parseInt(sequenceOrderQualifier) || MISSING_SEQUENCE_NUMBER;
+
+export const getVersionedDir = (now = new Date()) =>
+ now.getFullYear() +
+ DASH +
+ ('0' + (now.getMonth() + 1)).slice(-2) +
+ DASH +
+ ('0' + now.getDate()).slice(-2) +
+ DASH +
+ ('0' + now.getHours()).slice(-2) +
+ COLON +
+ ('0' + now.getMinutes()).slice(-2) +
+ COLON +
+ ('0' + now.getSeconds()).slice(-2);
+
+export const parseDateFromVersionedDir = (versionFolder: string) => {
+ const [year, month, day, time] = versionFolder.split(DASH);
+ const [hour, minute, second] = time.split(COLON);
+
+ return new Date(
+ parseInt(year),
+ parseInt(month) - 1,
+ parseInt(day),
+ parseInt(hour),
+ parseInt(minute),
+ parseInt(second),
+ );
+};
+
+export const getClosestVersionedDir = (diffDate: Date, dates: Date[]) =>
+ first(
+ dates.sort((a, b) => {
+ // @ts-ignore
+ return Math.abs(diffDate - a) - Math.abs(diffDate - b); // sort a before b when the distance is smaller
+ }),
+ );
+
+export const assertUniqueness = (array: string[]) =>
+ assert.equal(
+ size(uniq(array)),
+ size(array),
+ `There are duplicates: ${filter(array, (value, index, iteratee) =>
+ includes(iteratee, value, index + 1),
+ ).join(COMMA)}`,
+ );
diff --git a/src/gDriveConverterRunner.ts b/src/gDriveConverterRunner.ts
new file mode 100644
index 0000000..f988f62
--- /dev/null
+++ b/src/gDriveConverterRunner.ts
@@ -0,0 +1,156 @@
+import fs from 'fs';
+import fsExtra from 'fs-extra';
+import assert from 'node:assert';
+import { isEmpty, isEqual } from 'lodash';
+import { Config } from './proPresenter7SongConverter';
+import { parseDateFromVersionedDir } from './core';
+import {
+ getBasicDeploymentInfo,
+ getConvertedAndWrittenToLocalOutDirSongs,
+ getPreviousDeploymentDirectory,
+ getSongDiffFromManifest,
+} from './converterService';
+import {
+ getExistingFoldersFromRoot,
+ getPreviousManifestFileBy,
+ uploadSongsAndManifestToGDrive,
+} from './gDriveService';
+
+const getPreviousRemoteInventoryManifest = async (
+ deploymentDate: Date,
+ allPreviousDeploymentDirs: {
+ deploymentDirDate: Date;
+ deploymentDir: string;
+ id: string;
+ }[],
+) => {
+ const previousDeploymentDirectory = getPreviousDeploymentDirectory(
+ deploymentDate,
+ allPreviousDeploymentDirs,
+ );
+
+ const previousDeploymentDirFileId = allPreviousDeploymentDirs.find(
+ ({ deploymentDir }) => isEqual(deploymentDir, previousDeploymentDirectory),
+ )?.id as string;
+
+ return getPreviousManifestFileBy(previousDeploymentDirFileId);
+};
+
+export const convertSongsToPP7FormatRemotely = async ({
+ sourceDir,
+ baseLocalDir,
+ config,
+}: {
+ sourceDir: string;
+ baseLocalDir: string;
+ config: Config;
+}) => {
+ assert.ok(
+ process.env.GDRIVE_ROOT_FOLDER_ID,
+ 'No `GDRIVE_ROOT_FOLDER_ID` env variable set.',
+ );
+ assert.ok(
+ process.env.GDRIVE_BES_CLIENT_ID,
+ 'No `GDRIVE_BES_CLIENT_ID` env variable set.',
+ );
+ assert.ok(
+ process.env.GDRIVE_BES_CLIENT_SECRET,
+ 'No `GDRIVE_BES_CLIENT_SECRET` env variable set.',
+ );
+ assert.ok(
+ process.env.GDRIVE_BES_CLIENT_REFRESH_TOKEN,
+ 'No `GDRIVE_BES_CLIENT_REFRESH_TOKEN` env variable set.',
+ );
+ assert.ok(
+ process.env.CONNECT_TO_G_DRIVE,
+ 'No `CONNECT_TO_G_DRIVE` env variable set.',
+ );
+
+ const {
+ versionedDir,
+ deploymentDate,
+ deploymentVersionedDir,
+ deployableSongs,
+ currentManifest,
+ localManifestFilePath,
+ } = await getBasicDeploymentInfo(sourceDir, baseLocalDir);
+
+ assert.ok(
+ !fsExtra.pathExistsSync(deploymentVersionedDir),
+ `The out directory "${deploymentVersionedDir}" does exists already.`,
+ );
+
+ // ---
+ // Create directory
+ fsExtra.ensureDirSync(deploymentVersionedDir);
+
+ fs.writeFileSync(localManifestFilePath, JSON.stringify(currentManifest));
+
+ // ---
+ // Existing deployments
+ const allPreviousDeploymentDirs = (await getExistingFoldersFromRoot()).map(
+ ({ id, folderName: deploymentDir }) => ({
+ id,
+ deploymentDir,
+ deploymentDirDate: parseDateFromVersionedDir(deploymentDir),
+ }),
+ );
+ const isAFirstDeployment = isEmpty(allPreviousDeploymentDirs);
+
+ if (isAFirstDeployment) {
+ console.log(
+ `[Remote]: No previous deployment found in. Skip incremental deployments by doing a full deployment. Please proceed with a full manual import in PP7.`,
+ );
+
+ await uploadSongsAndManifestToGDrive(
+ getConvertedAndWrittenToLocalOutDirSongs(
+ deployableSongs,
+ deploymentVersionedDir,
+ config,
+ ),
+ versionedDir,
+ localManifestFilePath,
+ );
+
+ return;
+ }
+
+ const previousManifest = await getPreviousRemoteInventoryManifest(
+ deploymentDate,
+ allPreviousDeploymentDirs,
+ );
+
+ const { newOrUpdatedSongs, toBeRemovedFileNames } = getSongDiffFromManifest(
+ currentManifest,
+ previousManifest,
+ );
+
+ if (isEmpty(newOrUpdatedSongs)) {
+ console.log(
+ `[Remote]: Skip incremental local deployments as no changes have been found between the last two versions.`,
+ );
+ return;
+ }
+
+ const partialDeployableSongs = deployableSongs.filter(({ song: { id } }) =>
+ newOrUpdatedSongs
+ .map(({ id: newOrUpdatedSongId }) => newOrUpdatedSongId)
+ .includes(id),
+ );
+
+ await uploadSongsAndManifestToGDrive(
+ getConvertedAndWrittenToLocalOutDirSongs(
+ partialDeployableSongs,
+ deploymentVersionedDir,
+ config,
+ ),
+ versionedDir,
+ localManifestFilePath,
+ );
+
+ if (!isEmpty(toBeRemovedFileNames)) {
+ console.log(
+ `[Remote]: The following songs have been removed: ${toBeRemovedFileNames} manually.`,
+ );
+ }
+};
diff --git a/src/gDriveService.ts b/src/gDriveService.ts
new file mode 100644
index 0000000..5001299
--- /dev/null
+++ b/src/gDriveService.ts
@@ -0,0 +1,147 @@
+import fs from 'fs';
+import { auth, drive } from '@googleapis/drive';
+import pMap from 'p-map';
+import { drive_v3 } from '@googleapis/drive/v3';
+import { logProcessingFile } from './core';
+import { ConvertedFileStats, SongsInventoryManifest } from './types';
+import assert from 'node:assert';
+import { first, size } from 'lodash';
+
+const MANIFEST_FILE_NAME = 'manifest.json';
+const FOLDER_MIME_TYPE = 'application/vnd.google-apps.folder';
+const DEFAULT_PP7_MIME_FILE = 'application/octet-stream';
+const JSON_MIME_TYPE = 'application/json';
+const GDRIVE_FIELD_ID = 'id';
+
+let gDriveClient: drive_v3.Drive;
+
+const getGoogleDriveClient: () => Promise = async () => {
+ if (gDriveClient) {
+ return gDriveClient as drive_v3.Drive;
+ }
+
+ const oAuth2Client = new auth.OAuth2(
+ process.env.GDRIVE_BES_CLIENT_ID,
+ process.env.GDRIVE_BES_CLIENT_SECRET,
+ );
+
+ oAuth2Client.setCredentials({
+ refresh_token: process.env.GDRIVE_BES_CLIENT_REFRESH_TOKEN,
+ });
+
+ gDriveClient = drive({
+ version: 'v3',
+ auth: oAuth2Client,
+ });
+
+ return gDriveClient;
+};
+
+export const uploadSongsAndManifestToGDrive = async (
+ convertedFilesStats: ConvertedFileStats[],
+ versionedDir: string,
+ localManifestFilePath: string,
+) => {
+ // ---
+ // New GDrive folder
+ const newGDriveFolder = await (
+ await getGoogleDriveClient()
+ ).files.create({
+ requestBody: {
+ name: versionedDir,
+ mimeType: FOLDER_MIME_TYPE,
+ parents: [process.env.GDRIVE_ROOT_FOLDER_ID],
+ },
+ media: {
+ mimeType: FOLDER_MIME_TYPE,
+ },
+ fields: GDRIVE_FIELD_ID,
+ });
+
+ // ---
+ // Upload manifest
+ await (
+ await getGoogleDriveClient()
+ ).files.create({
+ requestBody: {
+ name: MANIFEST_FILE_NAME,
+ mimeType: JSON_MIME_TYPE,
+ parents: [newGDriveFolder.data.id as string],
+ },
+ media: {
+ mimeType: JSON_MIME_TYPE,
+ body: fs.createReadStream(localManifestFilePath),
+ },
+ });
+
+ (
+ await pMap(
+ convertedFilesStats,
+ async ({ songFileName, songFilePath }) =>
+ (
+ await getGoogleDriveClient()
+ ).files.create({
+ requestBody: {
+ name: songFileName,
+ mimeType: DEFAULT_PP7_MIME_FILE,
+ parents: [newGDriveFolder.data.id as string],
+ },
+ media: {
+ mimeType: DEFAULT_PP7_MIME_FILE,
+ body: fs.createReadStream(songFilePath),
+ },
+ }),
+ { concurrency: 5, stopOnError: true },
+ )
+ ).forEach(({ data }) => {
+ logProcessingFile(data.name as string, 'Uploaded successfully to GDrive.');
+ });
+};
+
+export const getExistingFoldersFromRoot = async () => {
+ const newGDriveFolder = await (
+ await getGoogleDriveClient()
+ ).files.list({
+ q: `'${process.env.GDRIVE_ROOT_FOLDER_ID}' in parents and mimeType = '${FOLDER_MIME_TYPE}'`,
+ fields: 'nextPageToken, files(id, name)',
+ spaces: 'drive',
+ });
+
+ return newGDriveFolder.data.files?.map(({ id, name: folderName }) => ({
+ id,
+ folderName,
+ })) as {
+ id: string;
+ folderName: string;
+ }[];
+};
+
+export const getPreviousManifestFileBy = async (
+ previousDeploymentDirFileId: string,
+) => {
+ const previousGDriveManifestFileInDeploymentFolder = await (
+ await getGoogleDriveClient()
+ ).files.list({
+ q: `'${previousDeploymentDirFileId}' in parents and name = '${MANIFEST_FILE_NAME}'`,
+ fields: 'nextPageToken, files(id, name)',
+ spaces: 'drive',
+ });
+
+ assert.ok(
+ size(previousGDriveManifestFileInDeploymentFolder.data.files) === 1,
+ 'There should be only one manifest file in the deployment folder.',
+ );
+
+ const manifestFileId = first(
+ previousGDriveManifestFileInDeploymentFolder.data.files,
+ )?.id as string;
+
+ const { data } = await (
+ await getGoogleDriveClient()
+ ).files.get({
+ fileId: manifestFileId,
+ alt: 'media',
+ });
+
+ return data as SongsInventoryManifest;
+};
diff --git a/src/index.ts b/src/index.ts
index f979d07..bb3ebb2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,5 @@
-export * from './migratorRunner';
+export * from './gDriveConverterRunner';
+export * from './localConverterRunner';
export * from './proPresenter7SongConverter';
export * from './songsParser';
export * from './types';
diff --git a/src/localConverterRunner.ts b/src/localConverterRunner.ts
new file mode 100644
index 0000000..bdf9c87
--- /dev/null
+++ b/src/localConverterRunner.ts
@@ -0,0 +1,139 @@
+import fs from 'fs';
+import fsExtra from 'fs-extra';
+import path from 'path';
+import assert from 'node:assert';
+import { isEmpty } from 'lodash';
+import { Config } from './proPresenter7SongConverter';
+import { MANIFEST_FILE_NAME } from './constants';
+import {
+ logFileWithLinkInConsole,
+ logProcessingFile,
+ parseDateFromVersionedDir,
+} from './core';
+import { SongsInventoryManifest } from './types';
+import {
+ getBasicDeploymentInfo,
+ getConvertedAndWrittenToLocalOutDirSongs,
+ getPreviousDeploymentDirectory,
+ getSongDiffFromManifest,
+} from './converterService';
+
+const getPreviousLocalInventoryManifest = (
+ deploymentDate: Date,
+ allPreviousDeploymentDirs: {
+ deploymentDirDate: Date;
+ deploymentDir: string;
+ }[],
+ baseLocalDir: string,
+) => {
+ const previousDeploymentManifestFilePath = path.join(
+ baseLocalDir,
+ getPreviousDeploymentDirectory(deploymentDate, allPreviousDeploymentDirs),
+ MANIFEST_FILE_NAME,
+ );
+
+ console.log(
+ `Previous deployment manifest file path: ${previousDeploymentManifestFilePath}.`,
+ );
+
+ const previousInventoryManifest = JSON.parse(
+ fs.readFileSync(previousDeploymentManifestFilePath).toString(),
+ ) as SongsInventoryManifest;
+
+ logProcessingFile(
+ previousDeploymentManifestFilePath,
+ 'Previous manifest inspection',
+ );
+ logFileWithLinkInConsole(previousDeploymentManifestFilePath);
+
+ return previousInventoryManifest;
+};
+
+export const convertSongsToPP7FormatLocally = async ({
+ sourceDir,
+ baseLocalDir,
+ config,
+}: {
+ sourceDir: string;
+ baseLocalDir: string;
+ config: Config;
+}) => {
+ const {
+ deploymentDate,
+ deploymentVersionedDir,
+ deployableSongs,
+ currentManifest,
+ localManifestFilePath,
+ } = await getBasicDeploymentInfo(sourceDir, baseLocalDir);
+
+ assert(
+ !fsExtra.pathExistsSync(deploymentVersionedDir),
+ `The out directory "${deploymentVersionedDir}" does exists already.`,
+ );
+
+ // ---
+ // Create directory
+ fsExtra.ensureDirSync(deploymentVersionedDir);
+
+ fs.writeFileSync(localManifestFilePath, JSON.stringify(currentManifest));
+
+ // ---
+ // Existing deployments
+ const allPreviousDeploymentDirs = fsExtra
+ .readdirSync(baseLocalDir)
+ .map((deploymentDir) => ({
+ deploymentDir,
+ deploymentDirDate: parseDateFromVersionedDir(deploymentDir),
+ }));
+ const isAFirstDeployment = isEmpty(allPreviousDeploymentDirs);
+
+ if (isAFirstDeployment) {
+ console.log(
+ `No previous deployment found in "${baseLocalDir}". Skip incremental deployments by doing a full deployment. Please proceed with a full manual import in PP7.`,
+ );
+
+ getConvertedAndWrittenToLocalOutDirSongs(
+ deployableSongs,
+ deploymentVersionedDir,
+ config,
+ );
+
+ return;
+ }
+
+ const previousManifest = getPreviousLocalInventoryManifest(
+ deploymentDate,
+ allPreviousDeploymentDirs,
+ baseLocalDir,
+ );
+
+ const { newOrUpdatedSongs, toBeRemovedFileNames } = getSongDiffFromManifest(
+ currentManifest,
+ previousManifest,
+ );
+
+ if (isEmpty(newOrUpdatedSongs)) {
+ console.log(
+ `Skip incremental local deployments as no changes have been found between the last two versions.`,
+ );
+ return;
+ }
+
+ const partialDeployableSongs = deployableSongs.filter(({ song: { id } }) =>
+ newOrUpdatedSongs
+ .map(({ id: newOrUpdatedSongId }) => newOrUpdatedSongId)
+ .includes(id),
+ );
+
+ getConvertedAndWrittenToLocalOutDirSongs(
+ partialDeployableSongs,
+ deploymentVersionedDir,
+ config,
+ );
+
+ if (!isEmpty(toBeRemovedFileNames)) {
+ console.log(
+ `The following songs have been removed: ${toBeRemovedFileNames} manually.`,
+ );
+ }
+};
diff --git a/src/manifestGenerator.ts b/src/manifestGenerator.ts
new file mode 100644
index 0000000..a8949cf
--- /dev/null
+++ b/src/manifestGenerator.ts
@@ -0,0 +1,26 @@
+import dotenv from 'dotenv';
+import { assertUniqueness } from './core';
+import { Song, SongManifest } from './types';
+
+dotenv.config();
+
+export const generateManifest = async (
+ deployableSongs: Array<{
+ song: Song;
+ fileName: string;
+ fileAsText: string;
+ }>,
+) => {
+ assertUniqueness(deployableSongs.map(({ song }) => song.id));
+
+ return deployableSongs
+ .map(
+ ({ song: { id, contentHash }, fileName }) =>
+ ({
+ id,
+ fileName,
+ contentHash,
+ } as SongManifest),
+ )
+ .sort((a, b) => a.fileName.localeCompare(b.fileName));
+};
diff --git a/src/migratorRunner.ts b/src/migratorRunner.ts
deleted file mode 100644
index 4d99958..0000000
--- a/src/migratorRunner.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import fs from 'fs';
-import fsExtra from 'fs-extra';
-import path from 'path';
-import recursive from 'recursive-readdir';
-import { Presentation } from '../proto/presentation';
-import { parseSong } from './songsParser';
-import {
- Config,
- convertSongToProPresenter7,
-} from './proPresenter7SongConverter';
-import { EMPTY_STRING, PRO_EXTENSION, TXT_EXTENSION } from './constants';
-
-/**
- * Removes all the files from the out directory
- */
-export const migrateSongsToPP7Format = async ({
- sourceDir,
- outDir,
- clearOutputDirFirst,
- config,
-}: {
- sourceDir: string;
- outDir: string;
- clearOutputDirFirst?: boolean;
- config: Config;
-}) => {
- if (clearOutputDirFirst) {
- fsExtra.emptydirSync(outDir);
- }
-
- await fsExtra.ensureDirSync(outDir);
-
- (await recursive(sourceDir, ['.DS_Store'])).forEach((filePath) => {
- const fileAsText = fs.readFileSync(filePath).toString();
- const fileName = path.basename(filePath);
-
- console.log(`Processing "${filePath}"...`);
-
- const song = parseSong(fileAsText);
- const presentation = convertSongToProPresenter7(song, config);
-
- const outFile = `${outDir}/${fileName.replace(
- TXT_EXTENSION,
- EMPTY_STRING,
- )}${PRO_EXTENSION}`;
-
- fs.writeFileSync(
- outFile,
- Buffer.from(Presentation.encode(presentation).finish()),
- );
-
- console.log(`Successfully generated "${outFile}".`);
- });
-};
diff --git a/src/songsParser.spec.ts b/src/songsParser.spec.ts
index 0649229..0af7f98 100644
--- a/src/songsParser.spec.ts
+++ b/src/songsParser.spec.ts
@@ -116,6 +116,9 @@ describe('songsParser', () => {
expect(parseSong(SONG_WITH_SUBSECTIONS_MOCK_FILE_CONTENT))
.toMatchInlineSnapshot(`
{
+ "author": "CustomAuthor",
+ "contentHash": "#customHash",
+ "id": "customId",
"sequence": [
"[v1.1]",
"[v1.2]",
@@ -287,6 +290,9 @@ describe('songsParser', () => {
expect(parseSong(SONG_WITH_SUBSECTIONS_MOCK_FILE_CONTENT))
.toMatchInlineSnapshot(`
{
+ "author": "CustomAuthor",
+ "contentHash": "#customHash",
+ "id": "customId",
"sequence": [
"[v1.1]",
"[v1.2]",
diff --git a/src/songsParser.ts b/src/songsParser.ts
index a79fd98..f8463af 100644
--- a/src/songsParser.ts
+++ b/src/songsParser.ts
@@ -20,6 +20,7 @@ export const parseSong = (songContent: string): Song => {
const hashMap = {} as Record;
const sections = [] as Section[];
+ let rawTitle = '';
for (
let sectionIndex = 0;
@@ -32,7 +33,8 @@ export const parseSong = (songContent: string): Song => {
hashMap[sectionIdentifier] = songSectionContent;
if (sectionIdentifier === SongSection.TITLE) {
- hashMap[SongSection.TITLE] = first(
+ rawTitle = songSectionContent;
+ hashMap[sectionIdentifier] = first(
getTitleBySections(songSectionContent),
) as string;
}
@@ -71,9 +73,10 @@ export const parseSong = (songContent: string): Song => {
}
const titleContent = hashMap[SongSection.TITLE];
- const metaSectionsFromTitle = getMetaSectionsFromTitle(
- titleContent,
- ) as Record;
+ const metaSectionsFromTitle = getMetaSectionsFromTitle(rawTitle) as Record<
+ SongMeta,
+ string
+ >;
return pickBy(
{
diff --git a/src/types.ts b/src/types.ts
index f3c3bb6..87a8e01 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -115,3 +115,25 @@ export type Song = {
verses: Section[];
version?: string;
};
+
+export type SongManifest = {
+ id: string;
+ fileName: string;
+ contentHash: string;
+};
+
+export type SongsInventoryManifest = {
+ updatedOn: string;
+ inventory: SongManifest[];
+};
+
+export type DeployableSong = {
+ song: Song;
+ fileName: string;
+ fileAsText: string;
+};
+
+export type ConvertedFileStats = {
+ songFileName: string;
+ songFilePath: string;
+};