Use new public JSON streams endpoint

This commit is contained in:
Max Goodhart
2020-06-22 23:58:53 -07:00
parent 2517ec055e
commit 5b699f7c31
6 changed files with 44 additions and 274 deletions

196
package-lock.json generated
View File

@@ -2380,14 +2380,6 @@
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
}, },
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"accepts": { "accepts": {
"version": "1.3.7", "version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -2439,14 +2431,6 @@
"integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==",
"dev": true "dev": true
}, },
"agent-base": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz",
"integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==",
"requires": {
"debug": "4"
}
},
"aggregate-error": { "aggregate-error": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
@@ -2579,11 +2563,6 @@
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true "dev": true
}, },
"arrify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="
},
"asn1": { "asn1": {
"version": "0.2.4", "version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -2945,7 +2924,8 @@
"base64-js": { "base64-js": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
"dev": true
}, },
"basic-auth": { "basic-auth": {
"version": "2.0.1", "version": "2.0.1",
@@ -2970,11 +2950,6 @@
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true "dev": true
}, },
"bignumber.js": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
"integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ=="
},
"binary-extensions": { "binary-extensions": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
@@ -3192,11 +3167,6 @@
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
}, },
"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": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
},
"buffer-from": { "buffer-from": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
@@ -4182,16 +4152,6 @@
} }
} }
}, },
"csvtojson": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/csvtojson/-/csvtojson-2.0.10.tgz",
"integrity": "sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ==",
"requires": {
"bluebird": "^3.5.1",
"lodash": "^4.17.3",
"strip-bom": "^2.0.0"
}
},
"cyclist": { "cyclist": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@@ -4489,14 +4449,6 @@
"safer-buffer": "^2.1.0" "safer-buffer": "^2.1.0"
} }
}, },
"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"
}
},
"editorconfig": { "editorconfig": {
"version": "0.15.3", "version": "0.15.3",
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
@@ -4763,11 +4715,6 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true "dev": true
}, },
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"events": { "events": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
@@ -4914,7 +4861,8 @@
"extend": { "extend": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"dev": true
}, },
"extend-shallow": { "extend-shallow": {
"version": "3.0.2", "version": "3.0.2",
@@ -5111,11 +5059,6 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true "dev": true
}, },
"fast-text-encoding": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz",
"integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig=="
},
"fastq": { "fastq": {
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
@@ -5392,34 +5335,6 @@
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true "dev": true
}, },
"gaxios": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz",
"integrity": "sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA==",
"requires": {
"abort-controller": "^3.0.0",
"extend": "^3.0.2",
"https-proxy-agent": "^5.0.0",
"is-stream": "^2.0.0",
"node-fetch": "^2.3.0"
},
"dependencies": {
"is-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw=="
}
}
},
"gcp-metadata": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.5.0.tgz",
"integrity": "sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA==",
"requires": {
"gaxios": "^2.1.0",
"json-bigint": "^0.3.0"
}
},
"gensync": { "gensync": {
"version": "1.0.0-beta.1", "version": "1.0.0-beta.1",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
@@ -5587,40 +5502,6 @@
"slash": "^3.0.0" "slash": "^3.0.0"
} }
}, },
"google-auth-library": {
"version": "5.10.1",
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz",
"integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==",
"requires": {
"arrify": "^2.0.0",
"base64-js": "^1.3.0",
"ecdsa-sig-formatter": "^1.0.11",
"fast-text-encoding": "^1.0.0",
"gaxios": "^2.1.0",
"gcp-metadata": "^3.4.0",
"gtoken": "^4.1.0",
"jws": "^4.0.0",
"lru-cache": "^5.0.0"
}
},
"google-p12-pem": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz",
"integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==",
"requires": {
"node-forge": "^0.9.0"
}
},
"google-spreadsheet": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/google-spreadsheet/-/google-spreadsheet-3.0.11.tgz",
"integrity": "sha512-bkYUdsq4Nwg7klnFevG6MRHXAfGh9gYVymGp001YdtThct5uSIB/41lUOsGzFbQQSR5FcBTwAu9nZVYvaNdv9Q==",
"requires": {
"axios": "^0.19.1",
"google-auth-library": "^5.9.1",
"lodash": "^4.17.15"
}
},
"got": { "got": {
"version": "9.6.0", "version": "9.6.0",
"resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
@@ -5651,17 +5532,6 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"gtoken": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz",
"integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==",
"requires": {
"gaxios": "^2.1.0",
"google-p12-pem": "^2.0.0",
"jws": "^4.0.0",
"mime": "^2.2.0"
}
},
"har-schema": { "har-schema": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -5867,15 +5737,6 @@
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
"dev": true "dev": true
}, },
"https-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
"requires": {
"agent-base": "6",
"debug": "4"
}
},
"human-signals": { "human-signals": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
@@ -6225,11 +6086,6 @@
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true "dev": true
}, },
"is-utf8": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
"integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
},
"is-whitespace": { "is-whitespace": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz",
@@ -8171,14 +8027,6 @@
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
}, },
"json-bigint": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz",
"integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=",
"requires": {
"bignumber.js": "^7.0.0"
}
},
"json-buffer": { "json-buffer": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
@@ -8236,25 +8084,6 @@
"verror": "1.10.0" "verror": "1.10.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"
}
},
"keygrip": { "keygrip": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
@@ -8553,6 +8382,7 @@
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"requires": { "requires": {
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
@@ -8744,11 +8574,6 @@
} }
} }
}, },
"mime": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
"integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
},
"mime-db": { "mime-db": {
"version": "1.44.0", "version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
@@ -11117,14 +10942,6 @@
"ansi-regex": "^5.0.0" "ansi-regex": "^5.0.0"
} }
}, },
"strip-bom": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
"requires": {
"is-utf8": "^0.2.0"
}
},
"strip-eof": { "strip-eof": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
@@ -12384,7 +12201,8 @@
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
}, },
"yaml": { "yaml": {
"version": "1.10.0", "version": "1.10.0",

View File

@@ -12,10 +12,8 @@
"author": "Max Goodhart <c@chromakode.com>", "author": "Max Goodhart <c@chromakode.com>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"csvtojson": "^2.0.10",
"ejs": "^3.1.3", "ejs": "^3.1.3",
"electron": "^9.0.4", "electron": "^9.0.4",
"google-spreadsheet": "^3.0.11",
"koa": "^2.12.1", "koa": "^2.12.1",
"koa-basic-auth": "^4.0.0", "koa-basic-auth": "^4.0.0",
"koa-easy-ws": "^1.1.3", "koa-easy-ws": "^1.1.3",

View File

@@ -25,7 +25,7 @@ function Overlay({ views, streams, customStreams }) {
{activeViews.map((viewState) => { {activeViews.map((viewState) => {
const { content, pos } = viewState.context const { content, pos } = viewState.context
const data = [...streams, ...customStreams].find( const data = [...streams, ...customStreams].find(
(d) => content.url === d.Link, (d) => content.url === d.link,
) )
const isListening = viewState.matches( const isListening = viewState.matches(
'displaying.running.audio.listening', 'displaying.running.audio.listening',
@@ -39,11 +39,11 @@ function Overlay({ views, streams, customStreams }) {
<StreamTitle isListening={isListening}> <StreamTitle isListening={isListening}>
<StreamIcon url={content.url} /> <StreamIcon url={content.url} />
<span> <span>
{data.hasOwnProperty('Label') ? ( {data.hasOwnProperty('label') ? (
data.Label data.label
) : ( ) : (
<> <>
{data.Source} &ndash; {data.City} {data.State} {data.source} &ndash; {data.city} {data.state}
</> </>
)} )}
</span> </span>

View File

@@ -1,24 +1,20 @@
import zip from 'lodash/zip'
import { promisify } from 'util' import { promisify } from 'util'
import fetch from 'node-fetch' import fetch from 'node-fetch'
import csv from 'csvtojson'
import { GoogleSpreadsheet } from 'google-spreadsheet'
const sleep = promisify(setTimeout) const sleep = promisify(setTimeout)
function filterLive(data) { function filterLive(data) {
return data.filter((d) => d.Link && d.Status === 'Live') return data.filter((d) => d.status === 'Live')
} }
export async function* pollPublicData() { export async function* pollPublicData() {
const publicDataURL = 'https://woke.net/csv' const publicDataURL = 'https://woke.net/api/streams.json'
const refreshInterval = 5 * 60 * 1000 const refreshInterval = 5 * 1000
while (true) { while (true) {
let data let data
try { try {
const resp = await fetch(publicDataURL) const resp = await fetch(publicDataURL)
const text = await resp.text() data = await resp.json()
data = await csv().fromString(text)
} catch (err) { } catch (err) {
console.warn('error loading stream data', err) console.warn('error loading stream data', err)
} }
@@ -27,30 +23,6 @@ export async function* pollPublicData() {
} }
} }
export async function* pollSpreadsheetData(creds, sheetId, tabName) {
const refreshInterval = 10 * 1000
const doc = new GoogleSpreadsheet(sheetId)
await doc.useServiceAccountAuth(creds)
await doc.loadInfo()
const sheet = Object.values(doc.sheetsById).find((s) => s.title === tabName)
await sheet.loadHeaderRow()
while (true) {
let rows
try {
rows = await sheet.getRows()
const data = rows.map((row) =>
Object.fromEntries(zip(row._sheet.headerValues, row._rawData)),
)
yield filterLive(data)
} catch (err) {
console.warn('error fetching rows', err)
}
await sleep(refreshInterval)
}
}
export class StreamIDGenerator { export class StreamIDGenerator {
constructor(parent) { constructor(parent) {
this.idMap = new Map(parent ? parent.idMap : null) this.idMap = new Map(parent ? parent.idMap : null)
@@ -60,11 +32,11 @@ export class StreamIDGenerator {
process(streams) { process(streams) {
const { idMap, idSet } = this const { idMap, idSet } = this
for (const stream of streams) { for (const stream of streams) {
const { Link, Source, Label } = stream const { link, source, label } = stream
if (!idMap.has(Link)) { if (!idMap.has(link)) {
let counter = 0 let counter = 0
let newId let newId
const normalizedText = (Source || Label || Link) const normalizedText = (source || label || link)
.toLowerCase() .toLowerCase()
.replace(/[^\w]/g, '') .replace(/[^\w]/g, '')
.replace(/^the|^https?(www)?/, '') .replace(/^the|^https?(www)?/, '')
@@ -74,10 +46,10 @@ export class StreamIDGenerator {
newId = `${textPart}${counterPart}` newId = `${textPart}${counterPart}`
counter++ counter++
} while (idSet.has(newId)) } while (idSet.has(newId))
idMap.set(Link, newId) idMap.set(link, newId)
idSet.add(newId) idSet.add(newId)
} }
stream._id = idMap.get(Link) stream._id = idMap.get(link)
} }
return streams return streams
} }

View File

@@ -3,7 +3,7 @@ import yargs from 'yargs'
import { app, shell, session, BrowserWindow } from 'electron' import { app, shell, session, BrowserWindow } from 'electron'
import { ensureValidURL } from '../util' import { ensureValidURL } from '../util'
import { pollPublicData, pollSpreadsheetData, StreamIDGenerator } from './data' import { pollPublicData, StreamIDGenerator } from './data'
import StreamWindow from './StreamWindow' import StreamWindow from './StreamWindow'
import initWebServer from './server' import initWebServer from './server'
@@ -12,17 +12,6 @@ async function main() {
.config('config', (configPath) => { .config('config', (configPath) => {
return JSON.parse(fs.readFileSync(configPath, 'utf-8')) return JSON.parse(fs.readFileSync(configPath, 'utf-8'))
}) })
.group(['gs-creds', 'gs-id', 'gs-tab'], 'Spreadsheet Configuration')
.option('gs-creds', {
describe: 'credentials file for Google Spreadsheet access',
implies: ['gs-id', 'gs-tab'],
})
.option('gs-id', {
describe: 'Google Spreadsheet id',
})
.option('gs-tab', {
describe: 'Google Spreadsheet tab name',
})
.group( .group(
['webserver', 'cert-dir', 'cert-email', 'hostname', 'port'], ['webserver', 'cert-dir', 'cert-email', 'hostname', 'port'],
'Web Server Configuration', 'Web Server Configuration',
@@ -151,14 +140,7 @@ async function main() {
broadcastState(clientState) broadcastState(clientState)
}) })
let dataGen for await (const rawStreams of pollPublicData()) {
if (argv.gsCreds) {
dataGen = pollSpreadsheetData(argv.gsCreds, argv.gsId, argv.gsTab)
} else {
dataGen = pollPublicData()
}
for await (const rawStreams of dataGen) {
const streams = idGen.process(rawStreams) const streams = idGen.process(rawStreams)
clientState.streams = streams clientState.streams = streams
streamWindow.send('state', clientState) streamWindow.send('state', clientState)

View File

@@ -41,7 +41,7 @@ function App({ wsEndpoint }) {
const allStreams = [...newStreams, ...newCustomStreams] const allStreams = [...newStreams, ...newCustomStreams]
for (const viewState of views) { for (const viewState of views) {
const { pos, content } = viewState.context const { pos, content } = viewState.context
const stream = allStreams.find((d) => d.Link === content.url) const stream = allStreams.find((d) => d.link === content.url)
const streamId = stream?._id const streamId = stream?._id
const state = State.from(viewState.state) const state = State.from(viewState.state)
const isListening = state.matches( const isListening = state.matches(
@@ -79,8 +79,8 @@ function App({ wsEndpoint }) {
) )
if (stream) { if (stream) {
const content = { const content = {
url: stream?.Link, url: stream?.link,
kind: stream?.Kind || 'video', kind: stream?.kind || 'video',
} }
newSpaceIdxMap.set(idx, { newSpaceIdxMap.set(idx, {
...newSpaceIdxMap.get(idx), ...newSpaceIdxMap.get(idx),
@@ -158,7 +158,7 @@ function App({ wsEndpoint }) {
const handleChangeCustomStream = useCallback((idx, customStream) => { const handleChangeCustomStream = useCallback((idx, customStream) => {
let newCustomStreams = [...customStreams] let newCustomStreams = [...customStreams]
newCustomStreams[idx] = customStream newCustomStreams[idx] = customStream
newCustomStreams = newCustomStreams.filter((s) => s.Link) newCustomStreams = newCustomStreams.filter((s) => s.kind)
wsRef.current.send( wsRef.current.send(
JSON.stringify({ JSON.stringify({
type: 'set-custom-streams', type: 'set-custom-streams',
@@ -241,14 +241,14 @@ function App({ wsEndpoint }) {
Include an empty object at the end to create an extra input for a new custom stream. Include an empty object at the end to create an extra input for a new custom stream.
We need it to be part of the array (rather than JSX below) for DOM diffing to match the key and retain focus. We need it to be part of the array (rather than JSX below) for DOM diffing to match the key and retain focus.
*/} */}
{[...customStreams, { Link: '', Label: '', Kind: 'video' }].map( {[...customStreams, { kind: '', label: '', kind: 'video' }].map(
({ Link, Label, Kind }, idx) => ( ({ link, label, kind }, idx) => (
<CustomStreamInput <CustomStreamInput
key={idx} key={idx}
idx={idx} idx={idx}
Link={Link} link={link}
Label={Label} label={label}
Kind={Kind} kind={kind}
onChange={handleChangeCustomStream} onChange={handleChangeCustomStream}
/> />
), ),
@@ -261,7 +261,7 @@ function App({ wsEndpoint }) {
function StreamLine({ function StreamLine({
id, id,
row: { Label, Source, Title, Link, Notes }, row: { label, source, title, link, notes },
onClickId, onClickId,
}) { }) {
const handleClickId = useCallback(() => { const handleClickId = useCallback(() => {
@@ -271,15 +271,15 @@ function StreamLine({
<StyledStreamLine> <StyledStreamLine>
<StyledId onClick={handleClickId}>{id}</StyledId> <StyledId onClick={handleClickId}>{id}</StyledId>
<div> <div>
{Label ? ( {label ? (
Label label
) : ( ) : (
<> <>
<strong>{Source}</strong>{' '} <strong>{source}</strong>{' '}
<a href={Link} target="_blank"> <a href={link} target="_blank">
{Title || Link} {title || link}
</a>{' '} </a>{' '}
{Notes} {notes}
</> </>
)} )}
</div> </div>
@@ -385,19 +385,19 @@ function GridInput({
function CustomStreamInput({ idx, onChange, ...props }) { function CustomStreamInput({ idx, onChange, ...props }) {
const handleChangeLink = useCallback( const handleChangeLink = useCallback(
(ev) => { (ev) => {
onChange(idx, { ...props, Link: ev.target.value }) onChange(idx, { ...props, link: ev.target.value })
}, },
[onChange], [onChange],
) )
const handleChangeLabel = useCallback( const handleChangeLabel = useCallback(
(ev) => { (ev) => {
onChange(idx, { ...props, Label: ev.target.value }) onChange(idx, { ...props, label: ev.target.value })
}, },
[onChange], [onChange],
) )
const handleChangeKind = useCallback( const handleChangeKind = useCallback(
(ev) => { (ev) => {
onChange(idx, { ...props, Kind: ev.target.value }) onChange(idx, { ...props, kind: ev.target.value })
}, },
[onChange], [onChange],
) )
@@ -406,14 +406,14 @@ function CustomStreamInput({ idx, onChange, ...props }) {
<input <input
onChange={handleChangeLink} onChange={handleChangeLink}
placeholder="https://..." placeholder="https://..."
value={props.Link} value={props.link}
/> />
<input <input
onChange={handleChangeLabel} onChange={handleChangeLabel}
placeholder="Label (optional)" placeholder="Label (optional)"
value={props.Label} value={props.label}
/> />
<select onChange={handleChangeKind} value={props.Kind}> <select onChange={handleChangeKind} value={props.kind}>
<option value="video">video</option> <option value="video">video</option>
<option value="web">web</option> <option value="web">web</option>
</select> </select>