// Q: why would you do this // A: the password on the netflix account I use is long // and it doesn't have proper text input support // Q: how do I use this // A: bravia.delaySequence(require('netflixkb.js').NetflixPW.sequence("yourpassword")) "use strict" const FibonacciHeap = require('@tyriar/fibonacci-heap') // priority-queue dijkstra, reference https://en.wikipedia.org/wiki/Dijkstra's_algorithm function find_path(target, prev) { const S = [] let u = {node: target} while(prev.has(u.node)) { u = prev.get(u.node) S.push(u) } return [S.reverse().map(x => x.label), S[S.length - 1].node] } function dijkstra(source, target) { const dist = new Map() const prev = new Map() const heapnodes = new Map() const Q = new FibonacciHeap() dist.set(source, 0) heapnodes.set(source, Q.insert(0, source)) while(!Q.isEmpty()) { const {value: u} = Q.extractMinimum() heapnodes.delete(u) if (u == target) { return find_path(target, prev) } if (u.transitions) { for(const [cost, v, label] of u.transitions()) { const alt = dist.get(u) + cost if (alt < (dist.has(v) ? dist.get(v) : Infinity)) { dist.set(v, alt) prev.set(v, Object.freeze({node: u, label})) if (heapnodes.has(v)) Q.decreaseKey(heapnodes.get(v), alt) else heapnodes.set(v, Q.insert(alt, v)) } } } } return undefined } // TODO: find better solution for the special keys // wraps to left and right // \x1: shift. \x2: symbols, \x11: backspace // 3: diacritics \x05: uppercase symbols, \x06: uppercase diacritics // \x10: too lazy, some sort of superscript a const netflixpw_kb = Object.freeze([[ "1234567890", "qwertyuiop", "asdfghjkl-", "\x01\x01zxcvbnm'", "\x02\x02\x03\x03 \x11\x11\x11" ],[ "1234567890", "QWERTYUIOP", "ASDFGHJKL-", "\x00\x00ZXCVBNM'", "\x05\x05\x06\x06 \x11\x11\x11" ],[ "`~!@#$%^&*", "()-_=+[]{}", "\\|;:'\",.<>", "/?¡¿\x10°¢€£¥", "\x00\x00\x03\x03 \x11\x11\x11" ] ]) // 0: standard. 1: shift. 2: special chars. 3: uppercase special chars // 11: backspace. 12: @gmail.com. 13: @hotmail.com. 14: @live.nl. 15: .com. // 16: .ca 17 .net 18 .edu 19 .org 1a .gov const netflixemail = Object.freeze([[ "1234567890", "qwertyuiop", "asdfghjkl-", "\x01\x01zxcvbnm_", "\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14", "\x02\x02@@.\x15\x15\x11\x11\x11", ],[ "1234567890", "QWERTYUIOP", "ASDFGHJKL-", "\x00\x00ZXCVBNM_", "\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14", "\x03\x03@@.\x15\x15\x11\x11\x11", ],[ "1234567890", "`~!@#$%^&*", "+-_{}|'./?", "\x16\x16\x17\x17\x18\x18\x19\x19\x1a\x1a", "\x12\x12\x12\x13\x13\x13\x13\x14\x14\x14", "\x00\x00@@.\x15\x15\x11\x11\x11", ] ]) const netflixsearch = Object.freeze([[ ' \x11\x11\x11', 'abcdef', 'ghijkl', 'mnopqr', 'stuvwx', 'yz1234', '567890' ]]) class Keyboard { constructor(chars, startpos, wraparound = true, centeroffs = true) { Object.assign(this, { chars, depth: chars.length, height: chars[0].length, width: chars[0][0].length, startpos, wraparound, centeroffs // workaround because the search keyboard is different }) this.nodes = chars.map((kb, kbid) => kb.map((row, rowid) => row.split('').map((letter, colid) => this.mkNode(kbid, rowid, colid)))) } findGroupInfo(kb, row, col) { const kbrow = this.chars[kb][row] const idxs = kbrow.split('').map((ltr,id) => ltr == kbrow[col] ? id : -1).filter(id => id>=0) return Object.freeze({left: idxs[0], size: idxs.length}) } findGroupCenter(kb, row, col) { col = (col + this.width) % this.width // fix wraparound const {left, size} = this.findGroupInfo(kb, row, col) if (!this.centeroffs) return this.nodes[kb][row][left] return this.nodes[kb][row][left + Math.ceil(size / 2) - 1] } mkNode(kb, row, col) { return Object.freeze({ letter: this.chars[kb][row][col], kb, row, col, transitions: () => { const {left, size} = this.findGroupInfo(kb, row, col) const transitions = { 'Left': this.findGroupCenter(kb, row, left - 1), 'Right': this.findGroupCenter(kb, row, left + size) } if (!this.wraparound && col == 0) delete transitions['Left'] if (!this.wraparound && col == this.width-1) delete transitions['Right'] if (row != 0) transitions['Up'] = this.nodes[kb][row-1][col] if (row != this.height-1) transitions['Down'] = this.nodes[kb][row+1][col] { const char = this.chars[kb][row][col] const cc = char.charCodeAt(0) if (cc < this.depth) transitions['DpadCenter'] = this.findGroupCenter(cc, row, col) else if (cc > 15) transitions['DpadCenter'] = char } return Object.keys(transitions).map(tname => [1, transitions[tname], tname]) } }) } lookupNode(z, y, x) { return this.nodes[z][y][x] } sequence(string, goback = false, start = undefined) { if (!start) start = this.lookupNode(...this.startpos) let pos = start const seq = [] for(const char of string) { let s2; [s2, pos] = dijkstra(pos, char) seq.push(s2) } if (goback) return [[].concat(...seq, dijkstra(pos, start)[0]), start] return [[].concat(...seq), pos] } } module.exports = { Keyboard, NetflixPW: new Keyboard(netflixpw_kb, [0,2,4]), NetflixMail: new Keyboard(netflixemail, [0,2,4]), NetflixSearch: new Keyboard(netflixsearch, [0, 1, 0], false, false) }