Commit 1e81927d authored by A. Koch's avatar A. Koch

add session grouping service, add config vars for external api keys and local...

add session grouping service, add config vars for external api keys and local api conf, add & update deps
parent a30114f7
......@@ -16,5 +16,10 @@
"url": "MONGODB_PROFILES_URL",
"dbName": "MONGODB_PROFILES_DBNAME"
}
},
"apiKeys": {
"youtube": "YOUTUBE_API_KEY",
"vimeo": "VIMEO_ACCESS_TOKEN",
"opengraph": "OPENGRAPH_API_KEY"
}
}
{
"api": {
"uriBase": "https://app.motionbank.org",
"apiHost": "https://api.motionbank.org"
},
"http": {
"host": "0.0.0.0",
"port": 3030
......@@ -36,5 +40,10 @@
"algorithms": ["RS256"],
"credentialsRequired": false
}
},
"apiKeys": {
"youtube": null,
"vimeo": null,
"opengraph": null
}
}
{
"api": {
"apiHost": "http://localhost:3030"
},
"http": {
"host": "localhost",
"port": 3030
},
"resources": {
"mongodb": {
"prefix": "res_",
"url": "mongodb://localhost:27017/motionbank-api-dev",
"dbName": "motionbank-api-dev"
}
......@@ -18,23 +20,13 @@
},
"profiles": {
"mongodb": {
"prefix": "sys_",
"url": "mongodb://localhost:27017/motionbank-api-sys-dev",
"dbName": "motionbank-api-sys-dev"
}
},
"auth": {
"jwks": {
"cache": true,
"rateLimit": true,
"jwksRequestsPerMinute": 5,
"jwksUri": "https://motionbank.eu.auth0.com/.well-known/jwks.json"
},
"jwt": {
"audience": "http://localhost:3030",
"issuer": "https://motionbank.eu.auth0.com/",
"algorithms": ["RS256"],
"credentialsRequired": false
"audience": "http://localhost:3030"
}
}
}
This diff is collapsed.
......@@ -107,6 +107,15 @@ const setup = async function () {
const documents = new Service('documents', app, models.Document, winston, acl)
documents.on('message', message => primus.write(message))
/**
* Configure sessions
*/
const
Sessions = require('./sessions'),
sessions = new Sessions(app, maps, annotations)
sessions.on('message', message => primus.write(message))
/**
* Configure archives
*/
......
const
config = require('config'),
send = require('@polka/send-type'),
TinyEmitter = require('tiny-emitter'),
constants = require('mbjs-data-models/src/constants'),
{ ObjectUtil } = require('mbjs-utils'),
{ parseSelector, Sorting } = require('mbjs-data-models/src/lib'),
{ getMetaData } = require('mbjs-media/src/util/metadata'),
{ DateTime } = require('luxon')
class SessionHelpers {
static annotationToSessionTime (seconds, annotation, session) {
const offset = (session.start.toMillis() - annotation.target.selector.value.start.toMillis()) * 0.001
return offset + seconds
}
}
const resurrectAnnotation = function (annotation) {
if (typeof annotation.created === 'string') annotation.created = DateTime.fromISO(annotation.created)
if (typeof annotation.updated === 'string') annotation.updated = DateTime.fromISO(annotation.updated)
if (annotation.target && annotation.target.selector) {
if (typeof annotation.target.selector.value === 'string') {
const parsedSelector = parseSelector(annotation.target.selector.value)
if (parsedSelector.end) {
annotation.target.selector.value = {
start: parsedSelector.start,
end: parsedSelector.end
}
}
else {
annotation.target.selector.value = parsedSelector
}
}
}
return annotation
}
const fetchMetaData = async (videos, user, annotationsService) => {
for (let v of videos) {
try {
const meta = await getMetaData(v.annotation, async query => {
const results = await annotationsService.findHandler({
query: {
query: JSON.stringify(query)
},
user
})
return results.data
}, config.apiKeys)
Object.assign(v.meta, meta)
}
catch (e) { console.error('fetchMetaData', e.message, e.stack) }
}
return videos
}
const groupBySessions = async function (annotations, user, annotationsService, secondsDist = constants.SESSION_DISTANCE_SECONDS) {
let millisDist = secondsDist * 1000
annotations = annotations.map(annotation => resurrectAnnotation(annotation)).sort(Sorting.sortOnTarget)
const videos = annotations.filter(anno => { return anno.body.type === 'Video' })
.map(annotation => {
return {
meta: {},
annotation: annotation
}
})
await fetchMetaData(videos, user, annotationsService)
annotations = annotations.filter(anno => { return anno.body.type === 'TextualBody' })
const sessions = []
const defaultSession = { start: undefined, end: undefined, duration: undefined, annotations: [] }
let lastDatetime, session = ObjectUtil.merge({}, defaultSession)
for (let i = 0; i < annotations.length; i++) {
const a = annotations[i]
const select = a.target.selector.value.start
if (!session.start) {
session.start = select
}
let duration = select.toMillis() - session.start.toMillis()
if (lastDatetime) {
const dist = select.toMillis() - lastDatetime.toMillis()
if (dist >= millisDist || i === annotations.length - 1) {
session.end = select
session.duration = (session.end.toMillis() - session.start.toMillis()) * 0.001 // TimelineSelector.timeBetween(, ).as('seconds')
videos.forEach(video => {
// FIXME: end value stays wrong
const s = SessionHelpers.annotationToSessionTime(video.meta.duration, video.annotation, session)
session.duration = Math.max(session.duration, s)
})
if (isNaN(session.duration)) {
console.error('duration NaN', session)
session.duration = 0
}
session.annotations.push({ annotation: a, duration, active: false })
sessions.push(session)
session = ObjectUtil.merge({}, defaultSession)
}
else {
session.annotations.push({ annotation: a, duration, active: false })
}
}
else session.annotations.push({ annotation: a, duration, active: false })
lastDatetime = select
}
return { sessions, videos }
}
class Sessions extends TinyEmitter {
constructor (app, mapsService, annotationsService) {
super()
this._maps = mapsService
this._annotations = annotationsService
const _this = this
app.get('/sessions/:id', async (req, res) => {
let results = await _this._maps.getHandler(req)
const map = results.data
if (!map) return _this._errorResponse(res, 404)
results = await _this._annotations.findHandler({
query: {
query: JSON.stringify({
'target.id': `${config.api.uriBase}/piecemaker/timelines/${map.uuid}`
})
},
user: req.user,
headers: req.headers
})
let annotations = results.data.items
const sessions = await groupBySessions(annotations, req.user, _this._annotations)
_this._response(req, res, sessions)
})
}
_response (req, res, data = {}) {
this.emit('message', { method: req.method, id: data.uuid })
if (typeof res === 'function') res({ data })
else if (typeof res === 'undefined') return Promise.resolve({ data })
else send(res, 200, data)
}
_errorResponse (res, code, message = undefined) {
if (typeof res === 'function') res({ error: true, code })
else if (typeof res === 'undefined') return Promise.resolve({ error: true, code })
else send(res, code, message)
}
}
module.exports = Sessions
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment