Last active
June 29, 2016 11:29
-
-
Save zlargon/17a7bb4d400b83cd22b54b47353d7fcb to your computer and use it in GitHub Desktop.
Node.js Google Text-To-Speech API Hacking
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var googleTTS = require('./google-tts'); | |
var opts = { | |
text: 'hello world', | |
lang: 'en', | |
speed: 1, | |
timeout: 10000 | |
}; | |
googleTTS(opts, function (err, url) { | |
if (err) { | |
console.error(err.stack); | |
return; | |
} | |
console.log(' Text => ' + opts.text); | |
console.log('Audio URL => ' + url) | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var https = require('https'); | |
var url = require('url'); | |
var host = 'translate.google.com'; | |
function XL (a, b) { | |
for (var c = 0; c < b.length - 2; c += 3) { | |
var d = b.charAt(c + 2); | |
d = d >= 'a' ? d.charCodeAt(0) - 87 : Number(d); | |
d = b.charAt(c + 1) == '+' ? a >>> d : a << d; | |
a = b.charAt(c) == '+' ? a + d & 4294967295 : a ^ d; | |
} | |
return a; | |
} | |
/** | |
* Generate API Token | |
* | |
* @param {String} text | |
* @param {String} key | |
* @return {String} token | |
*/ | |
function getGoogleApiToken (text, key) { | |
var a = text, b = key, d = b.split('.'); | |
b = Number(d[0]) || 0; | |
for (var e = [], f = 0, g = 0; g < a.length; g++) { | |
var m = a.charCodeAt(g); | |
128 > m ? e[f++] = m : (2048 > m ? e[f++] = m >> 6 | 192 : (55296 == (m & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (m = 65536 + ((m & 1023) << 10) + (a.charCodeAt(++g) & 1023), | |
e[f++] = m >> 18 | 240, | |
e[f++] = m >> 12 & 63 | 128) : e[f++] = m >> 12 | 224, | |
e[f++] = m >> 6 & 63 | 128), | |
e[f++] = m & 63 | 128); | |
} | |
a = b; | |
for (f = 0; f < e.length; f++) { | |
a += e[f]; | |
a = XL(a, '+-a^+6'); | |
} | |
a = XL(a, '+-3^+b+-f'); | |
a ^= Number(d[1]) || 0; | |
0 > a && (a = (a & 2147483647) + 2147483648); | |
a = a % 1E6; | |
return a.toString() + '.' + (a ^ b); | |
}; | |
/** | |
* Get Key from https://translate.google.com | |
* | |
* @param {Function} callback(err: Error, key: String) | |
* @param {Number!} timeout default is 10000ms | |
*/ | |
function getGoogleApiKey (callback, timeout) { | |
var cb = typeof callback === 'function' ? callback : function () {}; | |
var timeout = timeout || 10 * 1000; | |
var options = { | |
hostname: host, | |
port: 443, | |
path: '/', | |
method: 'GET' | |
}; | |
var req = https.request(options, function (res) { | |
if (res.statusCode !== 200) { | |
cb(new Error('request to ' + options.hostname + ' failed, status code = ' + res.statusCode + ' (' + res.statusMessage + ')')); | |
return; | |
} | |
var buffer = []; | |
res.on('data', function (chunk) { | |
buffer.push(chunk.toString()); | |
}); | |
res.on('end', function(){ | |
var TKK = null; | |
try { | |
var html = buffer.join(); | |
eval(html.match(/TKK=eval\(\'\(.*\)\'\);/g)[0]); // TKK = '405291.1334555331' | |
if (TKK === null) throw null; | |
} catch (e) { | |
cb(new Error('get key failed from google'), null); | |
return; | |
} | |
cb(null, TKK); | |
return; | |
}); | |
res.on('error', function (err) { | |
cb(err, null); | |
return; | |
}); | |
}); | |
req.setTimeout(timeout, function () { | |
req.abort(); | |
cb(new Error('request to ' + options.hostname + ' timeout'), null); | |
return; | |
}); | |
req.on('error', function(err) { | |
cb(err, null); | |
return; | |
}); | |
req.end(); | |
} | |
/** | |
* Generate "Google TTS" audio link | |
* | |
* @param {String} text | |
* @param {String} key | |
* @param {String!} lang default is 'en' | |
* @param {Number!} speed show = 0.24, default is 1 | |
* @return {String} url | |
*/ | |
function getSpeechUrl (text, key, lang, speed) { | |
if (typeof text !== 'string' || text.length === 0) { | |
throw new TypeError('text should be a string'); | |
} | |
if (typeof key !== 'string' || key.length === 0) { | |
throw new TypeError('key should be a string'); | |
} | |
if (typeof lang !== 'undefined' && (typeof lang !== 'string' || lang.length === 0)) { | |
throw new TypeError('lang should be a string'); | |
} | |
if (typeof speed !== 'undefined' && typeof speed !== 'number') { | |
throw new TypeError('speed should be a number'); | |
} | |
return 'https://' + host + '/translate_tts' + url.format({ | |
query: { | |
ie: 'UTF-8', | |
q: text, | |
tl: lang || 'en', | |
total: 1, | |
idx: 0, | |
textlen: text.length, | |
tk: getGoogleApiToken(text, key), | |
client: 't', | |
prev: 'input', | |
ttsspeed: speed || 1 | |
} | |
}); | |
}; | |
/** | |
* get "Google TTS" audio URL | |
* | |
* @param {Object} options | |
* {String} options.text | |
* {String!} options.lang default is 'en' | |
* {Number!} options.speed show = 0.24, default is 1 | |
* {Number!} options.timeout default is 10000ms | |
* | |
* @param {Function} callback(err: Error, url: String) | |
*/ | |
module.exports = function (options, callback) { | |
var cb = typeof callback === 'function' ? callback : function () {}; | |
if (typeof options !== 'object') { | |
cb(new TypeError('options should be an object'), null); | |
return; | |
} | |
getGoogleApiKey(function (err, apiKey) { | |
if (err) { | |
cb(err, null); | |
return; | |
} | |
// get API | |
try { | |
var url = getSpeechUrl(options.text, apiKey, options.lang, options.speed); | |
cb(null, url); | |
} catch (err) { | |
cb(err, null); | |
} | |
}, options.timeout); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment