var ltx = require('ltx'); var http = require('http'); var fs = require('fs'); var url = require('url'); var running = 0, queue = []; var SUFFIXES = [".mp4", ".webm", ".mp3", "_hq.mp4"]; var WEB_SUFFIXES = [".mp4", ".webm", ".mp3"]; function resolveSizes(urls, cb) { if (running > 0) { queue.push(function() { resolveSizes(urls, cb); }); return; } running++; urls = urls.map(url.parse); var pending = 0, results = [], done = function() { if (pending < 1) { results = results.sort(function(r1, r2) { var ui1 = urls.indexOf(r1.url); var ui2 = urls.indexOf(r2.url); if (ui1 < ui2) return -1; else if (ui1 > ui2) return 1; else return 0; }); cb(results); running--; var f = queue.shift(); f && f(); } }; urls.forEach(function(u) { var req = http.request({ method: 'HEAD', path: u.pathname, hostname: u.host }); req.end(); req.on('response', function(res) { if (res.statusCode === 200) { results.push({ url: url.format(u), size: res.headers['content-length'] }); } else { console.error("HTTP " + res.statusCode + " " + u.pathname); } pending--; done(); }); pending++; }); done(); } function mimeByExt(url) { var ext = url.split('.').pop(); var type = ({ avi: 'video/avi', mp3: 'audio/mpeg', mp4: 'video/mp4', webm: 'video/webm', ogv: 'video/ogg', flv: 'video/x-flv' })[ext]; return type || 'application/octet-stream'; } function processEvent(event) { var base = 'http://ftp.c3d2.de/datenspuren/2013/' + event.outputName; var urls = WEB_SUFFIXES.map(function(fmt) { return base + fmt; }); resolveSizes(urls, function(infos) { infos = infos.filter(function(i) { return i.size !== undefined; }); if (infos.length < 1) { console.warn(""); return; } var resAttrs = { title: event.title, size: infos[0].size, type: mimeByExt(infos[0].url), url: infos[0].url, preview: base + ".gif", poster: base + ".jpg" }; if (event.id != "0000") { resAttrs['details-link'] = "http://datenspuren.de/2013/fahrplan/events/" + event.id + ".html"; resAttrs['feedback-link'] = "https://frab.cccv.de/en/DS2013/public/events/" + event.id + "/feedback/new"; } var res = new ltx.Element('resource', resAttrs); infos.slice(1).forEach(function(info) { res.c('alternative', { size: info.size, type: mimeByExt(info.url), url: info.url }); }); console.info(res.toString()); }); } function printMetadata(event) { var metaparameters = ""; function addMeta(k, v) { metaparameters += " -metadata " + k + "=\"" + v.replace(/"/g, '\\"') + "\""; } addMeta('title', event.title); addMeta('subtitle', event.subtitle); addMeta('album', "Datenspuren 2012"); addMeta('artist', "Datenspuren"); addMeta('author', "Datenspuren"); addMeta('language', "deu"); addMeta('year', "2012"); addMeta('composer', event.persons.join(", ")); addMeta('description', event.abstract || event.description); SUFFIXES.forEach(function(suffix) { console.log("avconv -i " + event.inputName + suffix + " -codec copy" + metaparameters + " -y " + event.outputName + suffix); }); } function slugify(title) { return title.toLowerCase(). replace(/\s*[–\-\:].*/, ""). replace(/[\.!\?\/]/g, ""). replace(/Ä/g, "Ae"). replace(/Ö/g, "Oe"). replace(/Ü/g, "Ue"). replace(/ä/g, "ae"). replace(/ö/g, "oe"). replace(/ü/g, "ue"). replace(/ß/g, "ss"). replace(/ẞ/g, "SS"). replace(/\s+/g, "_"); } function makeEvent(event) { event.inputName = event.id; event.outputName = event.id + "_" + slugify(event.title); return event; } var scheduleFile = fs.readFileSync('../content/static/datenspuren/2013/fahrplan/schedule.xml'); var schedule = ltx.parse(scheduleFile); schedule.getChildren('day').forEach(function(dayEl) { var dayEvents = []; var roomIdx = 0; dayEl.getChildren('room').forEach(function(roomEl) { roomEl.getChildren('event').forEach(function(eventEl) { var persons = []; eventEl.getChildren('persons').forEach(function(personsEl) { personsEl.getChildren('person').forEach(function(personEl) { persons.push(personEl.getText()); }); }); dayEvents.push(makeEvent( { id: eventEl.attrs.id, time: eventEl.getChildText('start'), room: roomIdx, title: eventEl.getChildText('title'), subtitle: eventEl.getChildText('subtitle'), slug: eventEl.getChildText('slug'), abstract: eventEl.getChildText('abstract'), description: eventEl.getChildText('description'), persons: persons })); }); roomIdx++; }); dayEvents = dayEvents.sort(function(e1, e2) { return (e1.time < e2.time) ? -1 : ((e1.time > e2.time) ? 1 : e1.room - e2.room); }); dayEvents.forEach(printMetadata); dayEvents.forEach(processEvent); });