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

View File

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

View File

@@ -1,24 +1,20 @@
import zip from 'lodash/zip'
import { promisify } from 'util'
import fetch from 'node-fetch'
import csv from 'csvtojson'
import { GoogleSpreadsheet } from 'google-spreadsheet'
const sleep = promisify(setTimeout)
function filterLive(data) {
return data.filter((d) => d.Link && d.Status === 'Live')
return data.filter((d) => d.status === 'Live')
}
export async function* pollPublicData() {
const publicDataURL = 'https://woke.net/csv'
const refreshInterval = 5 * 60 * 1000
const publicDataURL = 'https://woke.net/api/streams.json'
const refreshInterval = 5 * 1000
while (true) {
let data
try {
const resp = await fetch(publicDataURL)
const text = await resp.text()
data = await csv().fromString(text)
data = await resp.json()
} catch (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 {
constructor(parent) {
this.idMap = new Map(parent ? parent.idMap : null)
@@ -60,11 +32,11 @@ export class StreamIDGenerator {
process(streams) {
const { idMap, idSet } = this
for (const stream of streams) {
const { Link, Source, Label } = stream
if (!idMap.has(Link)) {
const { link, source, label } = stream
if (!idMap.has(link)) {
let counter = 0
let newId
const normalizedText = (Source || Label || Link)
const normalizedText = (source || label || link)
.toLowerCase()
.replace(/[^\w]/g, '')
.replace(/^the|^https?(www)?/, '')
@@ -74,10 +46,10 @@ export class StreamIDGenerator {
newId = `${textPart}${counterPart}`
counter++
} while (idSet.has(newId))
idMap.set(Link, newId)
idMap.set(link, newId)
idSet.add(newId)
}
stream._id = idMap.get(Link)
stream._id = idMap.get(link)
}
return streams
}

View File

@@ -3,7 +3,7 @@ import yargs from 'yargs'
import { app, shell, session, BrowserWindow } from 'electron'
import { ensureValidURL } from '../util'
import { pollPublicData, pollSpreadsheetData, StreamIDGenerator } from './data'
import { pollPublicData, StreamIDGenerator } from './data'
import StreamWindow from './StreamWindow'
import initWebServer from './server'
@@ -12,17 +12,6 @@ async function main() {
.config('config', (configPath) => {
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(
['webserver', 'cert-dir', 'cert-email', 'hostname', 'port'],
'Web Server Configuration',
@@ -151,14 +140,7 @@ async function main() {
broadcastState(clientState)
})
let dataGen
if (argv.gsCreds) {
dataGen = pollSpreadsheetData(argv.gsCreds, argv.gsId, argv.gsTab)
} else {
dataGen = pollPublicData()
}
for await (const rawStreams of dataGen) {
for await (const rawStreams of pollPublicData()) {
const streams = idGen.process(rawStreams)
clientState.streams = streams
streamWindow.send('state', clientState)

View File

@@ -41,7 +41,7 @@ function App({ wsEndpoint }) {
const allStreams = [...newStreams, ...newCustomStreams]
for (const viewState of views) {
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 state = State.from(viewState.state)
const isListening = state.matches(
@@ -79,8 +79,8 @@ function App({ wsEndpoint }) {
)
if (stream) {
const content = {
url: stream?.Link,
kind: stream?.Kind || 'video',
url: stream?.link,
kind: stream?.kind || 'video',
}
newSpaceIdxMap.set(idx, {
...newSpaceIdxMap.get(idx),
@@ -158,7 +158,7 @@ function App({ wsEndpoint }) {
const handleChangeCustomStream = useCallback((idx, customStream) => {
let newCustomStreams = [...customStreams]
newCustomStreams[idx] = customStream
newCustomStreams = newCustomStreams.filter((s) => s.Link)
newCustomStreams = newCustomStreams.filter((s) => s.kind)
wsRef.current.send(
JSON.stringify({
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.
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(
({ Link, Label, Kind }, idx) => (
{[...customStreams, { kind: '', label: '', kind: 'video' }].map(
({ link, label, kind }, idx) => (
<CustomStreamInput
key={idx}
idx={idx}
Link={Link}
Label={Label}
Kind={Kind}
link={link}
label={label}
kind={kind}
onChange={handleChangeCustomStream}
/>
),
@@ -261,7 +261,7 @@ function App({ wsEndpoint }) {
function StreamLine({
id,
row: { Label, Source, Title, Link, Notes },
row: { label, source, title, link, notes },
onClickId,
}) {
const handleClickId = useCallback(() => {
@@ -271,15 +271,15 @@ function StreamLine({
<StyledStreamLine>
<StyledId onClick={handleClickId}>{id}</StyledId>
<div>
{Label ? (
Label
{label ? (
label
) : (
<>
<strong>{Source}</strong>{' '}
<a href={Link} target="_blank">
{Title || Link}
<strong>{source}</strong>{' '}
<a href={link} target="_blank">
{title || link}
</a>{' '}
{Notes}
{notes}
</>
)}
</div>
@@ -385,19 +385,19 @@ function GridInput({
function CustomStreamInput({ idx, onChange, ...props }) {
const handleChangeLink = useCallback(
(ev) => {
onChange(idx, { ...props, Link: ev.target.value })
onChange(idx, { ...props, link: ev.target.value })
},
[onChange],
)
const handleChangeLabel = useCallback(
(ev) => {
onChange(idx, { ...props, Label: ev.target.value })
onChange(idx, { ...props, label: ev.target.value })
},
[onChange],
)
const handleChangeKind = useCallback(
(ev) => {
onChange(idx, { ...props, Kind: ev.target.value })
onChange(idx, { ...props, kind: ev.target.value })
},
[onChange],
)
@@ -406,14 +406,14 @@ function CustomStreamInput({ idx, onChange, ...props }) {
<input
onChange={handleChangeLink}
placeholder="https://..."
value={props.Link}
value={props.link}
/>
<input
onChange={handleChangeLabel}
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="web">web</option>
</select>