Up and running
This commit is contained in:
71
index.js
71
index.js
@@ -3,20 +3,23 @@ const fs = require('fs')
|
||||
const youtubedl = require('youtube-dl-exec')
|
||||
const tmp = require('tmp')
|
||||
const playlist = require('./playlist-search')
|
||||
const scraper = require('./playlist-scrape')
|
||||
|
||||
const STREAM_URL = "https://www.youtube.com/henrikomagnifico/live"
|
||||
const DURATION_REGEX = /(\d{1,2}:\d{2})\/(\d{1,2}:\d{2})/
|
||||
const EXPIRATION_REGEX = /.+\/expire\/([0-9]{10})\/.+/
|
||||
let albumWhitelistCharacters = playlist.getValidAlbumCharacters()
|
||||
let trackWhitelistCharacters = playlist.getValidTrackCharacters()
|
||||
|
||||
let currentTrack = {}
|
||||
let nextTrackTimestamp = 0
|
||||
let readAttempts = 0
|
||||
|
||||
let resolvedUrl = {
|
||||
url: '',
|
||||
expires: 0
|
||||
}
|
||||
|
||||
|
||||
function readText(tmpfile, charWhitelist) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let command = `tesseract ${tmpfile.name} -`
|
||||
@@ -29,7 +32,7 @@ function readText(tmpfile, charWhitelist) {
|
||||
console.error(stderr)
|
||||
reject(error)
|
||||
} else {
|
||||
resolve(stdout)
|
||||
resolve(stdout.replace('\n', '').replace('\f', ''))
|
||||
}
|
||||
}))
|
||||
})
|
||||
@@ -97,6 +100,7 @@ function getYoutubeAudioUrl() {
|
||||
|
||||
function getVotes(ocrResults) {
|
||||
return ocrResults.map((text, index, arr) => { // Actually perform the search. Map returns array of search results, result.refIndex is of interest
|
||||
console.debug(`Worker ${index} input:`, text)
|
||||
const title = text[0].trim()
|
||||
const album = text[1].trim()
|
||||
const position_duration = text[2].trim()
|
||||
@@ -113,29 +117,55 @@ function getVotes(ocrResults) {
|
||||
function tallyVotes(votes) {
|
||||
const trackResults = {}
|
||||
const positionResults = {}
|
||||
votes.forEach(vote => {
|
||||
let overallVotes = 0
|
||||
let positionVotes = 0
|
||||
const voters = []
|
||||
const durationVotes = []
|
||||
votes.forEach((vote, index, arr) => {
|
||||
if (vote.result != null) {
|
||||
overallVotes++
|
||||
voters.push("v")
|
||||
if (trackResults.hasOwnProperty(vote.result.refIndex)) {
|
||||
trackResults[`${vote.result.refIndex}`] = trackResults[`${vote.result.refIndex}`] + 1
|
||||
} else {
|
||||
trackResults[`${vote.result.refIndex}`] = 1
|
||||
}
|
||||
} else {
|
||||
voters.push("x")
|
||||
}
|
||||
if (vote.position) {
|
||||
positionVotes++
|
||||
durationVotes.push("v")
|
||||
if (positionResults.hasOwnProperty(vote.position)) {
|
||||
positionResults[`${vote.position}`] = positionResults[`${vote.position}`] + 1
|
||||
} else {
|
||||
positionResults[`${vote.position}`] = 1
|
||||
}
|
||||
} else {
|
||||
durationVotes.push("x")
|
||||
}
|
||||
})
|
||||
if (trackResults.length === 0) {
|
||||
console.log("votes resulted in no results!")
|
||||
|
||||
console.debug("Voter status:")
|
||||
console.debug("Result vote:", voters)
|
||||
console.debug("Duration vote:", durationVotes)
|
||||
if (overallVotes === 0) {
|
||||
console.warn("no workers voted")
|
||||
return {
|
||||
position: null,
|
||||
positionConfidence: 0,
|
||||
result: null,
|
||||
resultConfidence: 0
|
||||
resultConfidence: 0,
|
||||
voterConfidence: overallVotes / votes.length
|
||||
}
|
||||
} else if (overallVotes / votes.length < 0.5) {
|
||||
console.warn("not enough workers voted")
|
||||
return {
|
||||
position: null,
|
||||
positionConfidence: 0,
|
||||
result: null,
|
||||
resultConfidence: 0,
|
||||
voterConfidence: overallVotes / votes.length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +174,7 @@ function tallyVotes(votes) {
|
||||
let trackMaxVotes = trackResults[trackIndex]
|
||||
let position = Object.keys(positionResults)[0]
|
||||
let positionMaxVotes = positionResults[position]
|
||||
console.debug("Initial index:", trackIndex, trackResults)
|
||||
for (const index in trackResults) {
|
||||
if (trackResults[index] > trackMaxVotes) {
|
||||
trackIndex = index
|
||||
@@ -158,13 +189,20 @@ function tallyVotes(votes) {
|
||||
}
|
||||
|
||||
// console.log(JSON.stringify(votes))
|
||||
console.debug(`Vote was index ${trackIndex}`)
|
||||
console.debug(`Vote was index`, trackIndex)
|
||||
console.debug("Voter results:")
|
||||
// console.debug("Track accuracy:", voters.map((v, i, a) => {
|
||||
// if (v === 'v') {
|
||||
|
||||
// }
|
||||
// }))
|
||||
const actualResult = votes.find(v => v.result != null && `${v.result.refIndex}` === trackIndex).result.item
|
||||
return {
|
||||
position: position,
|
||||
positionConfidence: positionMaxVotes / votes.length,
|
||||
result: actualResult,
|
||||
resultConfidence: trackMaxVotes / votes.length
|
||||
resultConfidence: trackMaxVotes / overallVotes,
|
||||
voterConfidence: overallVotes / votes.length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +219,7 @@ function performQuorumProcessing(titleCroppedPromise, albumCroppedPromise, durat
|
||||
return new Promise(((resolve, reject) => {
|
||||
Promise.all([titleCroppedPromise, albumCroppedPromise, durationCroppedPromise]) // Wait for all promises to be resolved, returns paths to cropped segments of frame
|
||||
.then(images => {
|
||||
const thresholds = [15, 12.5, 10, 7.5, 5]
|
||||
const thresholds = [20, 17.5, 15, 12.5, 10, 7.5, 5]
|
||||
Promise.all(thresholds.map(t => { // Threshold all segments at determined levels, returns text determined by each threshold level as [title, album, position/duration]
|
||||
return Promise.all([
|
||||
thresholdImage(images[0], t).then(file => readText(file)),
|
||||
@@ -197,8 +235,9 @@ function performQuorumProcessing(titleCroppedPromise, albumCroppedPromise, durat
|
||||
// Tally votes
|
||||
const winner = tallyVotes(votes)
|
||||
|
||||
console.debug(`Position confidence: ${winner.positionConfidence}`)
|
||||
console.debug(`Result confidence: ${winner.resultConfidence}`)
|
||||
console.debug('Position confidence:', winner.positionConfidence)
|
||||
console.debug('Result confidence:', winner.resultConfidence)
|
||||
console.debug('Voter confidence:', winner.voterConfidence)
|
||||
|
||||
resolve(winner)
|
||||
})
|
||||
@@ -220,7 +259,7 @@ async function updateTrackInfo() {
|
||||
} else if (urlExpiresSoon) {
|
||||
updateStreamUrl().then(() => console.log("Updated stream info in the background"))
|
||||
}
|
||||
console.log(`Now: ${Date.now()}\tExpiration: ${resolvedUrl.expires}`)
|
||||
console.log(`Now: ${Date.now()}\tExpiration: ${resolvedUrl.expires}\tAttempt: ${readAttempts}`)
|
||||
const frame = await getFrame(resolvedUrl.url)
|
||||
const frameTime = Date.now()
|
||||
|
||||
@@ -229,16 +268,18 @@ async function updateTrackInfo() {
|
||||
const durationPromise = getRegion(frame, 0, 1028, 235, 34)
|
||||
|
||||
const result = await performQuorumProcessing(titlePromise, albumPromise, durationPromise)
|
||||
if (result.resultConfidence < 0.5) {
|
||||
setTimeout(() => updateTrackInfo(), 250)
|
||||
if (result.resultConfidence < 0.4) {
|
||||
setTimeout(() => updateTrackInfo(), 0)
|
||||
readAttempts++
|
||||
return;
|
||||
}
|
||||
readAttempts = 0
|
||||
currentTrack = result.result
|
||||
console.log(`${currentTrack.album}: ${currentTrack.track} (${result.position}/${currentTrack.duration})`)
|
||||
const secondsUntilNext = timeUntilNextTrack(result.position, currentTrack.duration)
|
||||
const processingTime = Date.now() - frameTime
|
||||
nextTrackTimestamp = frameTime + (secondsUntilNext * 1000)
|
||||
setTimeout(() => updateTrackInfo(), (secondsUntilNext * 1000) - processingTime + 500)
|
||||
setTimeout(() => updateTrackInfo(), (secondsUntilNext * 1000) - processingTime + 250)
|
||||
console.log(`Frame processing took ${processingTime}ms`)
|
||||
console.log(`Next track should be at: ${new Date(nextTrackTimestamp).toLocaleString()}`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user