Repository: listen1/listen1-api Branch: master Commit: aa4b9d34aad5 Files: 37 Total size: 200.4 KB Directory structure: gitextract_kcqnihu1/ ├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── docs/ │ ├── .nojekyll │ ├── README.md │ ├── _sidebar.md │ ├── about.md │ ├── api.md │ ├── changelog.md │ ├── developer.md │ ├── index.html │ ├── quickstart.md │ └── thanks.md ├── package.json ├── src/ │ ├── crypto/ │ │ ├── aes.js │ │ ├── big-integer.js │ │ ├── crypto.js │ │ └── md5.js │ ├── hack_header.js │ ├── index.js │ ├── platform/ │ │ ├── browser.js │ │ └── node.js │ ├── provider/ │ │ ├── bilibili.js │ │ ├── kugou.js │ │ ├── kuwo.js │ │ ├── netease.js │ │ ├── qq.js │ │ └── xiami.js │ └── utils.js ├── test/ │ ├── index.spec.js │ └── mocha.opts └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": ["env"], "plugins": ["babel-plugin-add-module-exports"], "env": { "test": { "plugins": [ ["istanbul"] ] } } } ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = space indent_size = 2 end_of_line = LF charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false ================================================ FILE: .eslintignore ================================================ /dist ================================================ FILE: .eslintrc.json ================================================ { "extends": "airbnb-base", "rules": { "camelcase":"off", "import/named": "off" } } ================================================ FILE: .gitignore ================================================ # Logs logs *.log # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules # Remove some common IDE working directories .idea .vscode .DS_Store dist/ ================================================ FILE: .nvmrc ================================================ v6.10 ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2019 Listen 1 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # listen1-api listen1音乐资源API库,可运行在 Nodejs 和 浏览器环境(限chrome extension 或 electron) ## 特性 * 集成网易,QQ,虾米,酷狗,酷我,Bilibili平台的音乐资源API * 支持获取热门歌单,歌手歌单,专辑歌单 * 支持搜索歌曲 * 支持获取歌曲歌词信息 * 支持获取歌曲的播放地址 ## 编译 ``` $ git clone git@github.com:listen1/listen1-api.git $ cd listen1-api $ yarn install $ yarn build ``` 编译后文件在dist目录下,`listen1-api.js`和`listen1-api.min.js` ## 安装 浏览器环境 ```html ``` Nodejs环境 ```javascript const listen1Api = require('./listen1-api.min'); ``` ## 开始使用 (nodejs 环境) 下载压缩版本,或dist目录下的`listen1-api.min.js`到本地目录 ```javascript const listen1Api = require('./listen1-api.min'); const platform = 'netease'; // 获取网易平台的热门歌单列表 const url = '/show_playlist?source='+platform; listen1Api.apiGet(url).then((data) => { console.log(data); }); ``` ## 文档 https://listen1.github.io/listen1-api ## 项目技术 * 使用[webpack-library-starter](https://github.com/krasimir/webpack-library-starter.git)模板建立项目。 * 基于Webpack 4打包。 * ES6 语法。 * 导出[umd](https://github.com/umdjs/umd)格式的包,支持在浏览器环境和nodejs环境运行。 * ES6 测试基于 [Mocha](http://mochajs.org/) 和 [Chai](http://chaijs.com/)。 * 使用[ESLint](http://eslint.org/)进行语法检查。 ## 开发使用 1. 编译生成 * 运行 `yarn install` (推荐) 或 `npm install` ,安装依赖包。 * 运行 `yarn build` 或 `npm run build` 来生成压缩版本的库文件。 2. 开发者模式 * 运行 `yarn dev` 或 `npm run dev` 安装依赖包。生成一个非压缩版本的库文件并且在文件变化时自动重新编译。 3. 运行测试 * 运行 `yarn test` 或 `npm run test`。 ## 常用命令 * `yarn build` or `npm run build` - 在dist目录下编译生成正式版的库文件。 * `yarn dev` or `npm run dev` - 编译生成dev版本的库并实时更新。 * `yarn test` or `npm run test` - 运行测试。 * `yarn test:watch` or `npm run test:watch` - 在watch模式运行测试。 ================================================ FILE: docs/.nojekyll ================================================ ================================================ FILE: docs/README.md ================================================ # listen1-api listen1音乐资源API库,可运行在 Nodejs 和 浏览器环境(限chrome extension 或 electron) ## 特性 * 集成网易,QQ,虾米,酷狗,酷我,Bilibili平台的音乐资源API * 支持获取热门歌单,歌手歌单,专辑歌单 * 支持搜索歌曲 * 支持获取歌曲歌词信息 * 支持获取歌曲的播放地址 ## 编译 ``` $ git clone git@github.com:listen1/listen1-api.git $ cd listen1-api $ yarn install $ yarn build ``` 编译后文件在dist目录下,`listen1-api.js`和`listen1-api.min.js` ## 安装 浏览器环境 ```html ``` Nodejs环境 ```javascript const listen1Api = require('./listen1-api.min'); ``` ## 开始使用 (nodejs 环境) 下载压缩版本,或dist目录下的`listen1-api.min.js`到本地目录 ```javascript const listen1Api = require('./listen1-api.min'); const platform = 'netease'; // 获取网易平台的热门歌单列表 const url = '/show_playlist?source='+platform; listen1Api.apiGet(url).then((data) => { console.log(data); }); ``` ## 项目技术 * 使用[webpack-library-starter](https://github.com/krasimir/webpack-library-starter.git)模板建立项目。 * 基于Webpack 4打包。 * ES6 语法。 * 导出[umd](https://github.com/umdjs/umd)格式的包,支持在浏览器环境和nodejs环境运行。 * ES6 测试基于 [Mocha](http://mochajs.org/) 和 [Chai](http://chaijs.com/)。 * 使用[ESLint](http://eslint.org/)进行语法检查。 ## 常用命令 * `yarn build` or `npm run build` - 在dist目录下编译生成正式版的库文件。 * `yarn dev` or `npm run dev` - 编译生成dev版本的库并实时更新。 * `yarn test` or `npm run test` - 运行测试。 * `yarn test:watch` or `npm run test:watch` - 在watch模式运行测试。 ================================================ FILE: docs/_sidebar.md ================================================ - 开始使用 - [快速开始](quickstart.md) - 参考 - [API](api.md) - [开发指南](developer.md) - [关于](about.md) - [致谢](thanks.md) - [版本更新](changelog.md) ================================================ FILE: docs/about.md ================================================ # 关于 当前版本: 1.0.0 [listen-api](https://github.com/listen1/listen1-api) 是Listen1项目的一部分,主要提供其它项目访问音乐资源的API支持。 ## 联系方式 * 项目文档主页: https://listen1.github.io/listen1-api * Github: https://github.com/listen1/listen1-api * 联系邮箱:githublisten1@gmail.com ## Listen1 相关项目 [listen1/listen1](https://github.com/listen1/listen1): 最初的网页版播放器,使用Python开发Web服务器。可以直接在服务器运行,也可使用打包的Windows版和Mac版在本地运行Web服务器 [listen1/listen1_chrome_extension](https://github.com/listen1/listen1_chrome_extension): Chrome和Firefox插件版 [listen1/listen1_desktop](https://github.com/listen1/listen1_desktop): Windows,Mac,Linux桌面版。使用Electron框架,基于Listen 1 Chrome插件版JS库开发 ## 本文档生成工具 [docsify](https://docsify.js.org/#/) ## 开源协议 MIT ================================================ FILE: docs/api.md ================================================ # API ## apiGet > listen1Api.apiGet(url, httpFunction, promiseFunction, cookieProviderClass) ⇒ Promise resolve(json_object) 访问音乐资源API | 参数 | 类型 | 必须 | 描述 | | --- | --- | --- | --- | | url | string | 是| 音乐资源API, 详细参数规范见下面说明 | | httpFunction | object | 否 | HTTP请求实现函数 | | promiseFunction | object | 否 | Promise实现函数 | | cookieProviderClass | object | 否 | Cookie管理实现类 | 除url以外的三个参数是兼容自定义运行环境时需要传入的,详细情况请参考 运行环境兼容性 ### /show_playlist 获取平台热门歌单列表 | 参数 | 类型 | 必须 | 描述 | | --- | --- | --- | --- | | source | string | 是 | 平台名称(netease, qq, xiami, kugou, kuwo, bilibili) | | offset | number | 否 | 列表起始位置,默认值为0。如第一次offset=0, 获得30个歌单(具体数值根据平台不同而不同),offset=30获取下30个| 返回 返回结果为Promise,resolve后为JSON对象。 | 字段 | 类型 | 描述 | | --- | --- | --- | | result | [Playlist] | 歌单数组,包含歌单对象 | 例子 ```javascript const listen1Api = require('./listen1-api'); const source = 'netease'; const url = '/show_playlist?source='+source; listen1Api.apiGet(url).then((data) => { console.log(data); }); // 如果node环境支持await语法 async function fetchShowplaylist() { const data = await listen1Api.apiGet(`/show_playlist?source=${platform}`); console.log(data); } fetchShowplaylist(); ``` ### /get_playlist 获取歌单详情 | 参数 | 类型 | 必须 | 描述 | | --- | --- | --- | --- | | list_id | string | 是 | 歌单id,歌单id可以是播放列表,也可以是歌手id,专辑id | 返回 返回结果为Promise,resolve后为JSON对象。 | 字段 | 类型 | 描述 | | --- | --- | --- | | info | Playlist | 歌单数组,包含歌单对象 | | tracks | [Track] | 歌曲数组,包含歌曲对象 | 例子 ```javascript const listen1Api = require('./listen1-api'); const listId = 'neplaylist_762840531'; const url = '/get_playlist?list_id='+listId; listen1Api.apiGet(url).then((data) => { console.log(data); }); ``` ### /search 搜索歌曲 | 参数 | 类型 | 必须 | 描述 | | --- | --- | --- | --- | | source | string | 是 | 平台名称(netease, qq, xiami, kugou, kuwo, bilibili) | | keywords | string | 是 | 搜索关键字 | | curpage | number | 否 | 搜索页码,默认值为1 | 返回 返回结果为Promise,resolve后为JSON对象。 | 字段 | 类型 | 描述 | | --- | --- | --- | | total | number | 共搜索到的结果总数 | | result | [Track] | 歌曲数组,包含歌曲对象 | ### /lyric 获取歌曲歌词信息 | 参数 | 类型 | 必须 | 描述 | | --- | --- | --- | --- | | track_id | string | 是 | 歌曲id | 返回 返回结果为Promise,resolve后为JSON对象。 | 字段 | 类型 | 描述 | | --- | --- | --- | | lyric | string | lyric格式的歌词信息 | ### /bootstrap_track 获取歌曲的播放地址 | 参数 | 类型 | 必须 | 描述 | | --- | --- | --- | --- | | track_id | string | 是 | 歌曲id | 返回 返回结果为Promise,resolve后为JSON对象。 | 字段 | 类型 | 描述 | | --- | --- | --- | | url | string | 歌曲的播放地址 | ## APIGET 返回结果 JSON 对象定义 ### Playlist 歌单对象 | 字段 | 类型 | 描述 | | --- | --- | --- | | id | string | 歌单id,一般以平台前缀+playlist_[歌单id]作为标识,比如 qqplaylist_123456 | | cover_img_url | string | 歌单封面url | | title | string | 歌单标题 | | source_url | source_url | 歌单来源网页url | ### Track 歌曲对象 | 字段 | 类型 | 描述 | | --- | --- | --- | | id | string | 歌曲id,一般以平台前缀+track_[歌曲id]作为标识,比如 qqtrack_123456 | | img_url | string | 歌曲封面url | | title | string | 歌曲标题 | | source | string | 歌曲来源标识,比如netease | | source_url | string | 歌曲来源网页url | | artist | string | 歌手名 | | artist_id | string | 歌手id, 一般以平台前缀+artist_[歌手id]作为标识,比如 qqartist_123456 | | album | string | 专辑名 | | album_id | string | 专辑id, 一般以平台前缀+album_[专辑id]作为标识,比如 qqalbum_123456 | | lyric_url | string | 歌曲歌词url(可选,根据平台不同) | | url | string | 备用,现在值和歌曲id相同 | ## hackHeader > listen1Api.hackHeader(url) ⇒ {} 跨域header处理,修改header中的Referer字段和Origin字段,使其符合同源规则 返回结果 | 字段 | 类型 | 描述 | | --- | --- | --- | | replace_referer | bool | 是否替代referer字段 | | add_referer | bool | 是否增加referer字段 | | replace_origin | bool | 是否替代origin字段 | | add_origin | bool | 是否替代origin字段 | | referer_value | string | referer或origin的修正值 | ================================================ FILE: docs/changelog.md ================================================ # 更新日志 本文件记录项目的所有更新。 格式基于 [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 版本号规则基于 [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [1.0.0] - 2019-01-15 ### 新增 - 首次发布,基本功能和文档 ================================================ FILE: docs/developer.md ================================================ ## 开发使用 1. 编译生成 * 运行 `yarn install` (推荐) 或 `npm install` ,安装依赖包。 * 运行 `yarn build` 或 `npm run build` 来生成压缩版本的库文件。 2. 开发者模式 * 运行 `yarn dev` 或 `npm run dev` 安装依赖包。生成一个非压缩版本的库文件并且在文件变化时自动重新编译。 3. 运行测试 * 运行 `yarn test` 或 `npm run test`。 ## 运行环境兼容性 listen1-api本身是umd库,支持运行在nodejs和浏览器环境。但其中一些功能和运行环境相关。所以,如果需要兼容多种兼容环境时,请考虑按照一下接口定义扩展如下类: httpFunction, promiseFunction, cookieProviderClass。 listen1-api默认会根据程序环境选择默认的类,可以参考 `/platform` 目录下的代码 ### httpFunction > httpFunction(params) ⇒ Promise resolve(json_object) http请求函数 params是一个字典,包含参数如下 | 参数 | 类型 | 必须 | 描述 | | --- | --- | --- | --- | | url | string | 是| 访问url | | method | string | 是 | | | transformResponse | bool | 否 | 是否将结果转换为JSON对象 | | data | object | 否 | 请求为POST时,传递的表单内容 | | cookieProvider | object | 否 | cookie管理类 | ### promiseFunction > promiseFunction((resolve, reject)=>{}) ⇒ Promise api使用的promise函数。一般都为系统默认的 new Promise,但angularjs中需要传入`$q`,否则不会触发数据刷新。 ### cookieProviderClass cookie管理类 需要实现如下接口 getCookie | 参数 | 类型 | 必须 | 描述 | | --- | --- | --- | --- | | url | string | 是| 访问url | | name | string | 是| cookie名 | | callback | string | 是| 回调函数 | 其他接口根据运行环境中的httpFunction需要实现。 ================================================ FILE: docs/index.html ================================================ listen1-api - Produce universal library with webpack and es6
================================================ FILE: docs/quickstart.md ================================================ # 快速开始 ## 下载 * 压缩版本 [下载](https://github.com/listen1/listen1-api/releases/download/v1.0.0/listen1-api.min.js) * 非压缩版本[下载](https://github.com/listen1/listen1-api/releases/download/v1.0.0/listen1-api.js) ## 安装 浏览器环境 ```html ``` Nodejs环境 ```javascript const listen1Api = require('./listen1-api.min'); ``` ## 开始使用 (nodejs 环境) 下载压缩版本,或dist目录下的`listen1-api.min.js`到本地目录 ```javascript const listen1Api = require('./listen1-api.min'); const platform = 'netease'; // 获取网易平台的热门歌单列表 const url = '/show_playlist?source='+platform; listen1Api.apiGet(url).then((data) => { console.log(data); }); ``` ================================================ FILE: docs/thanks.md ================================================ ## 致谢 纪念 亚伦·斯沃茨 (Aaron Swartz) (1986-2013) 感谢开源社区的开发者,为Listen 1贡献了精彩的代码。感谢所有支持本开源项目的朋友。 特别感谢@Dumeng对Listen 1 ES6升级的贡献。 ================================================ FILE: package.json ================================================ { "name": "listen1-api", "version": "1.0.0", "description": "One API for all free music in China", "main": "dist/listen1-api.js", "scripts": { "prebuild": "webpack --env dev && webpack --env build", "build": "webpack --env dev && webpack --env build && npm run test", "dev": "webpack --progress --colors --watch --env dev", "test": "mocha --require babel-register --colors ./test/*.spec.js", "test:watch": "mocha --require babel-register --colors -w ./test/*.spec.js", "test:cover": "cross-env NODE_ENV=test nyc mocha --require babel-register --colors test/*.js", "repl": "node -i -e \"$(< ./lib/webpack-library-starter.js)\"" }, "repository": { "type": "git", "url": "https://github.com/listen1/listen1-api.git" }, "keywords": [ "listen1", "music" ], "author": "Listen 1", "license": "MIT", "bugs": { "url": "https://github.com/listen1/listen1-api/issues" }, "homepage": "https://listen1.github.io/listen1-api", "devDependencies": { "@babel/cli": "^7.0.0-beta.51", "@babel/core": "^7.0.0-beta.51", "@babel/preset-env": "^7.0.0-beta.51", "babel-eslint": "^8.0.3", "babel-loader": "^8.0.0-beta.4", "babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-istanbul": "^5.1.0", "babel-preset-env": "^7.0.0-beta.3", "babel-register": "^7.0.0-beta.3", "chai": "^4.1.2", "cross-env": "^5.2.0", "eslint": "^5.12.0", "eslint-config-airbnb-base": "^13.1.0", "eslint-loader": "^2.0.0", "eslint-plugin-import": "^2.14.0", "jsdom": "11.11.0", "jsdom-global": "3.0.2", "mocha": "^4.0.1", "nyc": "^13.1.0", "uglifyjs-webpack-plugin": "^1.2.7", "webpack": "^4.12.2", "webpack-cli": "^3.0.8", "yargs": "^10.0.3" }, "nyc": { "sourceMap": false, "instrument": false }, "dependencies": { "async": "^2.6.1", "cheerio": "^1.0.0-rc.2", "entities": "^1.1.2", "request": "^2.88.0", "text-encoding": "^0.7.0" } } ================================================ FILE: src/crypto/aes.js ================================================ "use strict"; (function(root) { var createBuffer = null, copyBuffer = null, convertBytesToString, convertStringToBytes = null; var slowCreateBuffer = function(arg) { // Passed in a single number, the length to pre-allocate if (typeof arg === 'number') { var result = []; for (var i = 0; i < arg; i++) { result.push(0); } return result; } else { // Make sure they are passing sensible data for (var i = 0; i < arg.length; i++) { if (arg[i] < 0 || arg[i] >= 256 || typeof arg[i] !== 'number') { throw new Error('invalid byte at index ' + i + '(' + arg[i] + ')'); } } // Most array-like things should support this if (arg.slice) { return arg.slice(0); } // Something *weird*; copy it into an array (see PR#2) var result = []; for (var i = 0; i < arg.length; i++) { result.push(arg[i]); } return result; } } if (typeof(Buffer) === 'undefined') { createBuffer = slowCreateBuffer; copyBuffer = function(sourceBuffer, targetBuffer, targetStart, sourceStart, sourceEnd) { if (targetStart == null) { targetStart = 0; } if (sourceStart == null) { sourceStart = 0; } if (sourceEnd == null) { sourceEnd = sourceBuffer.length; } for (var i = sourceStart; i < sourceEnd; i++) { targetBuffer[targetStart++] = sourceBuffer[i]; } } convertStringToBytes = function(text, encoding) { // "utf8", "utf-8", "utf 8", etc if (encoding == null || encoding.toLowerCase().replace(/ |-/g, "") == 'utf8') { var result = [], i = 0; text = encodeURI(text); while (i < text.length) { var c = text.charCodeAt(i++); // if it is a % sign, encode the following 2 bytes as a hex value if (c === 37) { result.push(parseInt(text.substr(i, 2), 16)) i += 2; // otherwise, just the actual byte } else { result.push(c) } } return result; // "hex" } else if (encoding.toLowerCase() == 'hex') { var result = []; for (var i = 0; i < text.length; i += 2) { result.push(parseInt(text.substr(i, 2), 16)); } return result; } // @TODO: Base64... return null; } // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html var Hex = '0123456789abcdef'; convertBytesToString = function(bytes, encoding) { // "utf8", "utf-8", "utf 8", etc if (encoding == null || encoding.toLowerCase().replace(/ |-/g, "") == 'utf8') { var result = [], i = 0; while (i < bytes.length) { var c = bytes[i]; if (c < 128) { result.push(String.fromCharCode(c)); i++; } else if (c > 191 && c < 224) { result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f))); i += 2; } else { result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f))); i += 3; } } return result.join(''); // "hex" } else if (encoding.toLowerCase() == 'hex') { var result = []; for (var i = 0; i < bytes.length; i++) { var v = bytes[i]; result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]); } return result.join(''); } return result } } else { createBuffer = function(arg) { return new Buffer(arg); } copyBuffer = function(sourceBuffer, targetBuffer, targetStart, sourceStart, sourceEnd) { sourceBuffer.copy(targetBuffer, targetStart, sourceStart, sourceEnd); } convertStringToBytes = function(text, encoding) { return new Buffer(text, encoding); } convertBytesToString = function(bytes, encoding) { return (new Buffer(bytes)).toString(encoding); } } // Number of rounds by keysize var numberOfRounds = {16: 10, 24: 12, 32: 14} // Round constant words var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91]; // S-box and Inverse S-box (S is for Substitution) var S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]; var Si =[0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]; // Transformations for encryption var T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a]; var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616]; var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16]; var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c]; // Transformations for decryption var T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742]; var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857]; var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8]; var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0]; // Transformations for decryption key expansion var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]; var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697]; var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46]; var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d]; function convertToInt32(bytes) { var result = []; for (var i = 0; i < bytes.length; i += 4) { result.push( (bytes[i ] << 24) | (bytes[i + 1] << 16) | (bytes[i + 2] << 8) | bytes[i + 3] ); } return result; } var AES = function(key) { this.key = createBuffer(key); this._prepare(); } AES.prototype._prepare = function() { var rounds = numberOfRounds[this.key.length]; if (rounds == null) { throw new Error('invalid key size (must be length 16, 24 or 32)'); } // encryption round keys this._Ke = []; // decryption round keys this._Kd = []; for (var i = 0; i <= rounds; i++) { this._Ke.push([0, 0, 0, 0]); this._Kd.push([0, 0, 0, 0]); } var roundKeyCount = (rounds + 1) * 4; var KC = this.key.length / 4; // convert the key into ints var tk = convertToInt32(this.key); // copy values into round key arrays var index; for (var i = 0; i < KC; i++) { index = i >> 2; this._Ke[index][i % 4] = tk[i]; this._Kd[rounds - index][i % 4] = tk[i]; } // key expansion (fips-197 section 5.2) var rconpointer = 0; var t = KC, tt; while (t < roundKeyCount) { tt = tk[KC - 1]; tk[0] ^= ((S[(tt >> 16) & 0xFF] << 24) ^ (S[(tt >> 8) & 0xFF] << 16) ^ (S[ tt & 0xFF] << 8) ^ S[(tt >> 24) & 0xFF] ^ (rcon[rconpointer] << 24)); rconpointer += 1; // key expansion (for non-256 bit) if (KC != 8) { for (var i = 1; i < KC; i++) { tk[i] ^= tk[i - 1]; } // key expansion for 256-bit keys is "slightly different" (fips-197) } else { for (var i = 1; i < (KC / 2); i++) { tk[i] ^= tk[i - 1]; } tt = tk[(KC / 2) - 1]; tk[KC / 2] ^= (S[ tt & 0xFF] ^ (S[(tt >> 8) & 0xFF] << 8) ^ (S[(tt >> 16) & 0xFF] << 16) ^ (S[(tt >> 24) & 0xFF] << 24)); for (var i = (KC / 2) + 1; i < KC; i++) { tk[i] ^= tk[i - 1]; } } // copy values into round key arrays var i = 0, r, c; while (i < KC && t < roundKeyCount) { r = t >> 2; c = t % 4; this._Ke[r][c] = tk[i]; this._Kd[rounds - r][c] = tk[i++]; t++; } } // inverse-cipher-ify the decryption round key (fips-197 section 5.3) for (var r = 1; r < rounds; r++) { for (var c = 0; c < 4; c++) { tt = this._Kd[r][c]; this._Kd[r][c] = (U1[(tt >> 24) & 0xFF] ^ U2[(tt >> 16) & 0xFF] ^ U3[(tt >> 8) & 0xFF] ^ U4[ tt & 0xFF]); } } } AES.prototype.encrypt = function(plaintext) { if (plaintext.length != 16) { return new Error('plaintext must be a block of size 16'); } var rounds = this._Ke.length - 1; var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key) var t = convertToInt32(plaintext); for (var i = 0; i < 4; i++) { t[i] ^= this._Ke[0][i]; } // apply round transforms for (var r = 1; r < rounds; r++) { for (var i = 0; i < 4; i++) { a[i] = (T1[(t[ i ] >> 24) & 0xff] ^ T2[(t[(i + 1) % 4] >> 16) & 0xff] ^ T3[(t[(i + 2) % 4] >> 8) & 0xff] ^ T4[ t[(i + 3) % 4] & 0xff] ^ this._Ke[r][i]); } t = a.slice(0); } // the last round is special var result = createBuffer(16), tt; for (var i = 0; i < 4; i++) { tt = this._Ke[rounds][i]; result[4 * i ] = (S[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff; result[4 * i + 1] = (S[(t[(i + 1) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff; result[4 * i + 2] = (S[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff; result[4 * i + 3] = (S[ t[(i + 3) % 4] & 0xff] ^ tt ) & 0xff; } return result; } AES.prototype.decrypt = function(ciphertext) { if (ciphertext.length != 16) { return new Error('ciphertext must be a block of size 16'); } var rounds = this._Kd.length - 1; var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key) var t = convertToInt32(ciphertext); for (var i = 0; i < 4; i++) { t[i] ^= this._Kd[0][i]; } // apply round transforms for (var r = 1; r < rounds; r++) { for (var i = 0; i < 4; i++) { a[i] = (T5[(t[ i ] >> 24) & 0xff] ^ T6[(t[(i + 3) % 4] >> 16) & 0xff] ^ T7[(t[(i + 2) % 4] >> 8) & 0xff] ^ T8[ t[(i + 1) % 4] & 0xff] ^ this._Kd[r][i]); } t = a.slice(0); } // the last round is special var result = createBuffer(16), tt; for (var i = 0; i < 4; i++) { tt = this._Kd[rounds][i]; result[4 * i ] = (Si[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff; result[4 * i + 1] = (Si[(t[(i + 3) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff; result[4 * i + 2] = (Si[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff; result[4 * i + 3] = (Si[ t[(i + 1) % 4] & 0xff] ^ tt ) & 0xff; } return result; } /** * Mode Of Operation - Electonic Codebook (ECB) */ var ModeOfOperationECB = function(key) { this.description = "Electronic Code Block"; this.name = "ecb"; this._aes = new AES(key); } ModeOfOperationECB.prototype.encrypt = function(plaintext) { return this._aes.encrypt(plaintext); } ModeOfOperationECB.prototype.decrypt = function(ciphertext, encoding) { return this._aes.decrypt(ciphertext); } /** * Mode Of Operation - Cipher Block Chaining (CBC) */ var ModeOfOperationCBC = function(key, iv) { this.description = "Cipher Block Chaining"; this.name = "cbc"; if (!iv) { iv = createBuffer([0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); } else if (iv.length != 16) { return new Error('initialation vector iv must be of length 16'); } this._lastCipherblock = createBuffer(iv); this._aes = new AES(key); } ModeOfOperationCBC.prototype.encrypt = function(plaintext) { if (plaintext.length != 16) { return new Error('plaintext must be a block of size 16'); } var precipherblock = createBuffer(plaintext); for (var i = 0; i < 16; i++) { precipherblock[i] ^= this._lastCipherblock[i]; } this._lastCipherblock = this._aes.encrypt(precipherblock); return this._lastCipherblock; } ModeOfOperationCBC.prototype.decrypt = function(ciphertext) { if (ciphertext.length != 16) { return new Error('ciphertext must be a block of size 16'); } var plaintext = this._aes.decrypt(ciphertext); for (var i = 0; i < 16; i++) { plaintext[i] ^= this._lastCipherblock[i]; } copyBuffer(ciphertext, this._lastCipherblock); return plaintext; } /** * Mode Of Operation - Cipher Feedback (CFB) */ var ModeOfOperationCFB = function(key, iv, segmentSize) { this.description = "Cipher Feedback"; this.name = "cfb"; if (!iv) { iv = createBuffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); } else if (iv.length != 16) { return new Error('initialation vector iv must be of length 16'); } if (!segmentSize) { segmentSize = 1; } this.segmentSize = segmentSize; this._shiftRegister = createBuffer(iv); this._aes = new AES(key); } ModeOfOperationCFB.prototype.encrypt = function(plaintext) { if ((plaintext.length % this.segmentSize) != 0) { return new Error('plaintext must be a block of size module segmentSize (' + this.segmentSize + ')'); } var encrypted = createBuffer(plaintext); var xorSegment; for (var i = 0; i < encrypted.length; i += this.segmentSize) { xorSegment = this._aes.encrypt(this._shiftRegister); for (var j = 0; j < this.segmentSize; j++) { encrypted[i + j] ^= xorSegment[j]; } // Shift the register copyBuffer(this._shiftRegister, this._shiftRegister, 0, this.segmentSize); copyBuffer(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize); } return encrypted; } ModeOfOperationCFB.prototype.decrypt = function(ciphertext) { if ((ciphertext.length % this.segmentSize) != 0) { return new Error('ciphertext must be a block of size module segmentSize (' + this.segmentSize + ')'); } var plaintext = createBuffer(ciphertext); var xorSegment; for (var i = 0; i < plaintext.length; i += this.segmentSize) { xorSegment = this._aes.encrypt(this._shiftRegister); for (var j = 0; j < this.segmentSize; j++) { plaintext[i + j] ^= xorSegment[j]; } // Shift the register copyBuffer(this._shiftRegister, this._shiftRegister, 0, this.segmentSize); copyBuffer(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize); } return plaintext; } /** * Mode Of Operation - Output Feedback (OFB) */ var ModeOfOperationOFB = function(key, iv) { this.description = "Output Feedback"; this.name = "ofb"; if (!iv) { iv = createBuffer([0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); } else if (iv.length != 16) { return new Error('initialation vector iv must be of length 16'); } this._lastPrecipher = createBuffer(iv); this._lastPrecipherIndex = 16; this._aes = new AES(key); } ModeOfOperationOFB.prototype.encrypt = function(plaintext) { var encrypted = createBuffer(plaintext); for (var i = 0; i < encrypted.length; i++) { if (this._lastPrecipherIndex === 16) { this._lastPrecipher = this._aes.encrypt(this._lastPrecipher); this._lastPrecipherIndex = 0; } encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++]; } return encrypted; } // Decryption is symetric ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt; /** * Counter object for CTR common mode of operation */ var Counter = function(initialValue) { // We allow 0, but anything false-ish uses the default 1 if (initialValue !== 0 && !initialValue) { initialValue = 1; } if (typeof(initialValue) === 'number') { this._counter = createBuffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); this.setValue(initialValue); } else { this.setBytes(initialValue); } } Counter.prototype.setValue = function(value) { if (typeof(value) !== 'number' || parseInt(value) != value) { throw new Error('value must be an integer'); } for (var index = 15; index >= 0; --index) { this._counter[index] = value % 256; value = value >> 8; } } Counter.prototype.setBytes = function(bytes) { if (bytes.length != 16) { throw new Error('invalid counter bytes size (must be 16)'); } this._counter = createBuffer(bytes); }; Counter.prototype.increment = function() { for (var i = 15; i >= 0; i--) { if (this._counter[i] === 255) { this._counter[i] = 0; } else { this._counter[i]++; break; } } } /** * Mode Of Operation - Counter (CTR) */ var ModeOfOperationCTR = function(key, counter) { this.description = "Counter"; this.name = "ctr"; if (!(counter instanceof Counter)) { counter = new Counter(counter) } this._counter = counter; this._remainingCounter = null; this._remainingCounterIndex = 16; this._aes = new AES(key); } ModeOfOperationCTR.prototype.encrypt = function(plaintext) { var encrypted = createBuffer(plaintext); for (var i = 0; i < encrypted.length; i++) { if (this._remainingCounterIndex === 16) { this._remainingCounter = this._aes.encrypt(this._counter._counter); this._remainingCounterIndex = 0; this._counter.increment(); } encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++]; } return encrypted; } // Decryption is symetric ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt; // The bsic modes of operation as a map var ModeOfOperation = { ecb: ModeOfOperationECB, cbc: ModeOfOperationCBC, cfb: ModeOfOperationCFB, ofb: ModeOfOperationOFB, ctr: ModeOfOperationCTR }; /////////////////////// // Exporting // The block cipher var aesjs = { AES: AES, Counter: Counter, ModeOfOperation: ModeOfOperation, util: { convertBytesToString: convertBytesToString, convertStringToBytes: convertStringToBytes, _slowCreateBuffer: slowCreateBuffer } }; // node.js if (typeof exports !== 'undefined') { module.exports = aesjs // RequireJS/AMD // http://www.requirejs.org/docs/api.html // https://github.com/amdjs/amdjs-api/wiki/AMD } else if (typeof(define) === 'function' && define.amd) { define(aesjs); // Web Browsers } else { // If there was an existing library at "aes" make sure it's still available if (root.aes) { aesjs._aes = root.aes; } root.aesjs = aesjs; } })(this); ================================================ FILE: src/crypto/big-integer.js ================================================ // modified version from https://github.com/peterolson/BigInteger.js // remove eval to avoid warning in chrome var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),DEFAULT_ALPHABET="0123456789abcdefghijklmnopqrstuvwxyz";var supportsNativeBigInt=typeof BigInt==="function";function Integer(v,radix,alphabet,caseSensitive){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10&&!alphabet?parseValue(v):parseBase(v,radix,alphabet,caseSensitive);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function NativeBigInt(value){this.value=value}NativeBigInt.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i=base?1:0;r[i]=sum-carry*base}while(i0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;NativeBigInt.prototype.add=function(v){return new NativeBigInt(this.value+parseValue(v).value)};NativeBigInt.prototype.plus=NativeBigInt.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;NativeBigInt.prototype.subtract=function(v){return new NativeBigInt(this.value-parseValue(v).value)};NativeBigInt.prototype.minus=NativeBigInt.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};NativeBigInt.prototype.negate=function(){return new NativeBigInt(-this.value)};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};NativeBigInt.prototype.abs=function(){return new NativeBigInt(this.value>=0?this.value:-this.value)};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;ib_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);if(supportsNativeBigInt){return[new NativeBigInt(self.value/n.value),new NativeBigInt(self.value%n.value)]}var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(absb.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};NativeBigInt.prototype.compareAbs=function(v){var a=this.value;var b=parseValue(v).value;a=a>=0?a:-a;b=b>=0?b:-b;return a===b?0:a>b?1:-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;NativeBigInt.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var a=this.value;var b=parseValue(v).value;return a===b?0:a>b?1:-1};NativeBigInt.prototype.compareTo=NativeBigInt.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};NativeBigInt.prototype.eq=NativeBigInt.prototype.equals=SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};NativeBigInt.prototype.neq=NativeBigInt.prototype.notEquals=SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};NativeBigInt.prototype.gt=NativeBigInt.prototype.greater=SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};NativeBigInt.prototype.lt=NativeBigInt.prototype.lesser=SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};NativeBigInt.prototype.geq=NativeBigInt.prototype.greaterOrEquals=SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};NativeBigInt.prototype.leq=NativeBigInt.prototype.lesserOrEquals=SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};NativeBigInt.prototype.isEven=function(){return(this.value&BigInt(1))===BigInt(0)};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};NativeBigInt.prototype.isOdd=function(){return(this.value&BigInt(1))===BigInt(1)};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};NativeBigInt.prototype.isPositive=SmallInteger.prototype.isPositive;BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};NativeBigInt.prototype.isNegative=SmallInteger.prototype.isNegative;BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};NativeBigInt.prototype.isUnit=function(){return this.abs().value===BigInt(1)};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};NativeBigInt.prototype.isZero=function(){return this.value===BigInt(0)};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);if(n.isZero())return false;if(n.isUnit())return true;if(n.compareAbs(2)===0)return this.isEven();return this.mod(n).isZero()};NativeBigInt.prototype.isDivisibleBy=SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(49))return true}function millerRabinTest(n,a){var nPrev=n.prev(),b=nPrev,r=0,d,t,i,x;while(b.isEven())b=b.divide(2),r++;next:for(i=0;i-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};NativeBigInt.prototype.prev=function(){return new NativeBigInt(this.value-BigInt(1))};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return Math.abs(n)<=BASE}BigInteger.prototype.shiftLeft=function(v){var n=parseValue(v).toJSNumber();if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}if(n<0)return this.shiftRight(-n);var result=this;if(result.isZero())return result;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};NativeBigInt.prototype.shiftLeft=SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(v){var remQuo;var n=parseValue(v).toJSNumber();if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero()||result.isNegative()&&result.isUnit())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};NativeBigInt.prototype.shiftRight=SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};NativeBigInt.prototype.not=SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};NativeBigInt.prototype.and=SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};NativeBigInt.prototype.or=SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};NativeBigInt.prototype.xor=SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:typeof v==="bigint"?v|BigInt(LOBMASK_I):v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function integerLogarithm(value,base){if(base.compareTo(value)<=0){var tmp=integerLogarithm(value,base.square(base));var p=tmp.p;var e=tmp.e;var t=p.multiply(base);return t.compareTo(value)<=0?{p:t,e:e*2+1}:{p:p,e:e*2}}return{p:bigInt(1),e:0}}BigInteger.prototype.bitLength=function(){var n=this;if(n.compareTo(bigInt(0))<0){n=n.negate().subtract(bigInt(1))}if(n.compareTo(bigInt(0))===0){return bigInt(0)}return bigInt(integerLogarithm(n,bigInt(2)).e).add(bigInt(1))};NativeBigInt.prototype.bitLength=SmallInteger.prototype.bitLength=BigInteger.prototype.bitLength;function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var digits=toBase(range,BASE).value;var result=[],restricted=true;for(var i=0;i=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i"&&i=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit,alphabet){alphabet=alphabet||DEFAULT_ALPHABET;if(digit"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return{value:[0],isNegative:false};throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return{value:[0],isNegative:false};if(n.isNegative())return{value:[].concat.apply([],Array.apply(null,Array(-n.toJSNumber())).map(Array.prototype.valueOf,[1,0])),isNegative:false};var arr=Array.apply(null,Array(n.toJSNumber()-1)).map(Array.prototype.valueOf,[0,1]);arr.unshift([1]);return{value:[].concat.apply([],arr),isNegative:false}}var neg=false;if(n.isNegative()&&base.isPositive()){neg=true;n=n.abs()}if(base.isUnit()){if(n.isZero())return{value:[0],isNegative:false};return{value:Array.apply(null,Array(n.toJSNumber())).map(Number.prototype.valueOf,1),isNegative:neg}}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(digit.toJSNumber())}out.push(left.toJSNumber());return{value:out.reverse(),isNegative:neg}}function toBaseString(n,base,alphabet){var arr=toBase(n,base);return(arr.isNegative?"-":"")+arr.value.map(function(x){return stringify(x,alphabet)}).join("")}BigInteger.prototype.toArray=function(radix){return toBase(this,radix)};SmallInteger.prototype.toArray=function(radix){return toBase(this,radix)};NativeBigInt.prototype.toArray=function(radix){return toBase(this,radix)};BigInteger.prototype.toString=function(radix,alphabet){if(radix===undefined)radix=10;if(radix!==10)return toBaseString(this,radix,alphabet);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix,alphabet){if(radix===undefined)radix=10;if(radix!=10)return toBaseString(this,radix,alphabet);return String(this.value)};NativeBigInt.prototype.toString=SmallInteger.prototype.toString;NativeBigInt.prototype.toJSON=BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return parseInt(this.toString(),10)};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;NativeBigInt.prototype.valueOf=NativeBigInt.prototype.toJSNumber=function(){return parseInt(this.toString(),10)};function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return supportsNativeBigInt?new NativeBigInt(BigInt(x)):new SmallInteger(x);throw new Error("Invalid integer: "+v)}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);if(supportsNativeBigInt){return new NativeBigInt(BigInt(sign?"-"+v:v))}var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(supportsNativeBigInt){return new NativeBigInt(BigInt(v))}if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}if(typeof v==="bigint"){return new NativeBigInt(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=parseValue(i);if(i>0)Integer[-i]=parseValue(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger||x instanceof NativeBigInt};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})} ================================================ FILE: src/crypto/crypto.js ================================================ /* eslint-disable import/named */ /* eslint-disable no-undef */ /* eslint-disable no-param-reassign */ import aesjs from './aes'; import { MD5 } from './md5'; const bigInt = require('./big-integer'); function aesEncrypt(text, secKey, ivString) { const pad = 16 - (text.length % 16); for (let i = 0; i < pad; i += 1) { text += String.fromCharCode(pad); } const key = aesjs.util.convertStringToBytes(secKey); // The initialization vector, which must be 16 bytes const iv = aesjs.util.convertStringToBytes(ivString); let textBytes = aesjs.util.convertStringToBytes(text); // eslint-disable-next-line new-cap const aesCbc = new aesjs.ModeOfOperation.cbc(key, iv); const cipherArray = []; while (textBytes.length !== 0) { const block = aesCbc.encrypt(textBytes.slice(0, 16)); Array.prototype.push.apply(cipherArray, block); textBytes = textBytes.slice(16); } let ciphertext = ''; for (let i = 0; i < cipherArray.length; i += 1) { ciphertext += String.fromCharCode(cipherArray[i]); } ciphertext = btoa(ciphertext); return ciphertext; } function hexify(text) { return text.split('').map(x => x.charCodeAt(0).toString(16)).join(''); } function zfill(num, size) { let s = `${num}`; while (s.length < size) s = `0${s}`; return s; } function expmod(base, exp, mymod) { let result; if (exp.eq(0)) return bigInt(1, 10); if (exp.mod(bigInt(2, 10)).eq(0)) { let newexp = bigInt(exp); newexp = newexp.shiftRight(1); result = expmod(base, newexp, mymod).modPow(2, mymod); return result; } result = expmod(base, exp.subtract(bigInt(1, 10)), mymod).multiply(base).mod(mymod); return result; } function rsaEncrypt(text, pubKey, modulus) { const reversedText = text.split('').reverse().join(''); const base = bigInt(hexify(reversedText), 16); const exp = bigInt(pubKey, 16); const mod = bigInt(modulus, 16); const bigNumber = expmod(base, exp, mod); const rs = bigNumber.toString(16); return zfill(rs, 256).toLowerCase(); } export default { aesEncrypt, rsaEncrypt, MD5 }; ================================================ FILE: src/crypto/md5.js ================================================ /** * * MD5 (Message-Digest Algorithm) * http://www.webtoolkit.info/ * refer to: https://github.com/david-sabata/web-scrobbler/blob/master/vendor/md5.js **/ function MD5(string) { function RotateLeft(lValue, iShiftBits) { return (lValue<>>(32-iShiftBits)); } function AddUnsigned(lX,lY) { var lX4,lY4,lX8,lY8,lResult; lX8 = (lX & 0x80000000); lY8 = (lY & 0x80000000); lX4 = (lX & 0x40000000); lY4 = (lY & 0x40000000); lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF); if (lX4 & lY4) { return (lResult ^ 0x80000000 ^ lX8 ^ lY8); } if (lX4 | lY4) { if (lResult & 0x40000000) { return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); } else { return (lResult ^ 0x40000000 ^ lX8 ^ lY8); } } else { return (lResult ^ lX8 ^ lY8); } } function F(x,y,z) { return (x & y) | ((~x) & z); } function G(x,y,z) { return (x & z) | (y & (~z)); } function H(x,y,z) { return (x ^ y ^ z); } function I(x,y,z) { return (y ^ (x | (~z))); } function FF(a,b,c,d,x,s,ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b); }; function GG(a,b,c,d,x,s,ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b); }; function HH(a,b,c,d,x,s,ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b); }; function II(a,b,c,d,x,s,ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b); }; function ConvertToWordArray(string) { var lWordCount; var lMessageLength = string.length; var lNumberOfWords_temp1=lMessageLength + 8; var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64; var lNumberOfWords = (lNumberOfWords_temp2+1)*16; var lWordArray=Array(lNumberOfWords-1); var lBytePosition = 0; var lByteCount = 0; while ( lByteCount < lMessageLength ) { lWordCount = (lByteCount-(lByteCount % 4))/4; lBytePosition = (lByteCount % 4)*8; lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<>>29; return lWordArray; }; function WordToHex(lValue) { var WordToHexValue="",WordToHexValue_temp="",lByte,lCount; for (lCount = 0;lCount<=3;lCount++) { lByte = (lValue>>>(lCount*8)) & 255; WordToHexValue_temp = "0" + lByte.toString(16); WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2); } return WordToHexValue; }; function Utf8Encode(string) { string = string.replace(/\r\n/g,"\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; }; var x=Array(); var k,AA,BB,CC,DD,a,b,c,d; var S11=7, S12=12, S13=17, S14=22; var S21=5, S22=9 , S23=14, S24=20; var S31=4, S32=11, S33=16, S34=23; var S41=6, S42=10, S43=15, S44=21; string = Utf8Encode(string); x = ConvertToWordArray(string); a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476; for (k=0;k new Promise(fn); } if (cookieProvider === undefined || cookieProvider === null) { // eslint-disable-next-line no-param-reassign cookieProvider = globalCookieProvider; } // router if (url.search('/show_playlist') !== -1) { const source = getParameterByName('source', url); const provider = getProviderByName(source); return provider.showPlaylist(url, httpClient, pfn, cookieProvider); } if (url.search('/playlist') !== -1) { const listId = getParameterByName('list_id', url); const provider = getProviderByItemId(listId); return provider.getPlaylist(url, httpClient, pfn, cookieProvider); } if (url.search('/search') !== -1) { const source = getParameterByName('source', url); const provider = getProviderByName(source); return provider.search(url, httpClient, pfn, cookieProvider); } if (url.search('/lyric') !== -1) { const trackId = getParameterByName('track_id', url); const provider = getProviderByItemId(trackId); return provider.lyric(url, httpClient, pfn, cookieProvider); } if (url.search('/bootstrap_track') !== -1) { const trackId = getParameterByName('track_id', url); const provider = getProviderByItemId(trackId); return provider.bootstrapTrack(trackId, httpClient, pfn, cookieProvider); } return null; } export default { getAllProviders, getProviderByItemId, getProviderByName, apiGet, hackHeader, httpParamEncode, platform, loadNodejsDefaults, loadBrowserDefaults, }; ================================================ FILE: src/platform/browser.js ================================================ // function getDomain(url) { // const matches = url.match(/^https?:\/\/([^/?#]+)(?:[/?#]|$)/i); // const domain = matches && matches[1]; // return domain; // } function getCookieProvider() { // const domain = 'https://www.xiami.com'; // const name = 'xm_sg_tk'; const cookieProvider = {}; if (typeof chrome !== 'undefined') { // chrome cookie provider // eslint-disable-next-line func-names cookieProvider.getCookie = function (url, name, callback) { // eslint-disable-next-line no-undef chrome.cookies.get({ url, name, }, (cookie) => { if (cookie == null) { return callback(''); } return callback(cookie.value); }); }; } else { // TODO: fix electron auto load, now webpack will require electron in any platform build. // // electron cookie provider // cookieProvider.getCookie = function getCookie(url, name, callback) { // // trick to avoid require electron not found in compile time // const remote = require('electron').remote; // eslint-disable-line // const domain = getDomain(url); // remote.session.defaultSession.cookies.get({ // domain, // name, // }, (err, cookie) => { // if (cookie.length === 0) { // return callback(''); // } // return callback(cookie[0].value); // }); // }; } return cookieProvider; } export default { CookieProvider: getCookieProvider() }; ================================================ FILE: src/platform/node.js ================================================ import { hackHeader } from '../hack_header'; // eslint-disable-next-line import/no-dynamic-require const request = require('request').defaults({ jar: true }); class CookieProvider { constructor() { this.data = {}; } getCookie(url, name, callback) { const domain = this.getDomain(url); if (this.data[domain] == null) { return callback(''); } return callback(this.data[domain][name]); } setCookie(url, name, value) { const domain = this.getDomain(url); if (this.data[domain] == null) { this.data[domain] = {}; } this.data[domain][name] = value; } getCookieForHTTPHeader(url) { const domain = this.getDomain(url); if (this.data[domain] == null) { return ''; } const kvArray = []; Object.keys(this.data[domain]).forEach((k) => { const v = this.data[domain][k]; kvArray.push(`${k}=${v}`); }); return `${kvArray.join(';')};`; } // eslint-disable-next-line class-methods-use-this getDomain(url) { const matches = url.match(/^https?:\/\/([^/?#]+)(?:[/?#]|$)/i); const domain = matches && matches[1]; return domain; } } function HTTPClient(params) { // console.log(params); // cookieProvider in params? should update it return new Promise((resolve, reject) => { let headers = { // eslint-disable-next-line max-len 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36', }; if (params.headers !== undefined) { headers = Object.assign(headers, params.headers); } const action = hackHeader(params.url); if (action.add_referer || (action.replace_referer && headers.Referer === undefined)) { headers.Referer = action.referer_value; } if (action.add_origin || (action.replace_origin && headers.Origin === undefined)) { headers.Origin = action.referer_value; } // if cookie provider is provided, send cookie related to this domain if (params.cookieProvider !== undefined) { headers.Cookie = params.cookieProvider.getCookieForHTTPHeader(params.url); } if (params.method === 'GET') { return request.get({ headers, uri: params.url, }, (error, response, body) => { if (error) { return reject(); } if (params.cookieProvider !== undefined && response.headers['set-cookie'] !== undefined) { response.headers['set-cookie'].forEach((cookieString) => { const kvString = cookieString.split(';')[0]; const k = kvString.split('=')[0]; const v = kvString.split('=')[1]; params.cookieProvider.setCookie(params.url, k, v); }); } if (params.transformResponse === false) { return resolve({ data: body, }); } return resolve({ data: JSON.parse(body), }); }); } if (params.method === 'POST') { return request.post({ headers, uri: params.url, form: params.data, }, (error, response, body) => { if (error) { return reject(); } if (params.transformResponse === false) { return resolve({ data: body, }); } return resolve({ data: JSON.parse(body), }); }); } return null; }); } export default { CookieProvider, HTTPClient }; ================================================ FILE: src/provider/bilibili.js ================================================ /* eslint-disable no-unused-vars */ /* Author: @71e6fd52 */ import { getParameterByName } from '../utils'; function build_bilibili() { function bi_convert_song(song_info) { const track = { id: `bitrack_${song_info.id}`, title: song_info.title, artist: song_info.uname, artist_id: `biartist_${song_info.uid}`, source: 'bilibili', source_url: `https://www.bilibili.com/audio/au${song_info.id}`, img_url: song_info.cover, url: song_info.id, lyric_url: song_info.lyric, }; return track; } function bi_show_playlist(url, hm, pfn) { let offset = getParameterByName('offset', url); if (offset === undefined) { offset = 0; } const page = offset / 20 + 1; const target_url = `https://www.bilibili.com/audio/music-service-c/web/menu/hit?ps=20&pn=${page}`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response.data.data; const result = data.map(item => ({ cover_img_url: item.cover, title: item.title, id: `biplaylist_${item.menuId}`, source_url: `https://www.bilibili.com/audio/am${item.menuId}`, })); return resolve({ result, }); }); }); } function bi_get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars const list_id = getParameterByName('list_id', url).split('_').pop(); const target_url = `https://www.bilibili.com/audio/music-service-c/web/menu/info?sid=${list_id}`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response.data; const info = { cover_img_url: data.cover, title: data.title, id: `biplaylist_${list_id}`, source_url: `https://www.bilibili.com/audio/am${list_id}`, }; const target = `https://www.bilibili.com/audio/music-service-c/web/song/of-menu?pn=1&ps=100&sid=${list_id}`; hm({ url: target, method: 'GET' }).then((res) => { const tracks = res.data.data.data.map(item => bi_convert_song(item)); return resolve({ info, tracks, }); }); }); }); } function bi_album(url, hm, pfn) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => resolve({ tracks: [], info: {}, })); // bilibili havn't album // const album_id = getParameterByName('list_id', url).split('_').pop(); // const target_url = ''; // hm({url:target_url, method:'GET'}).then((response) => { // const data = response.data; // const info = {}; // const tracks = []; // return fn({ // tracks, // info, // }); // }); } function bi_artist(url, hm, pfn) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const artist_id = getParameterByName('list_id', url).split('_').pop(); let target_url = 'https://space.bilibili.com/ajax/member/GetInfo'; hm({ url: target_url, method: 'POST', data: { mid: artist_id }, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }) .then((response) => { const { data } = response.data; const info = { cover_img_url: data.face, title: data.name, id: `biartist_${artist_id}`, source_url: `https://space.bilibili.com/${artist_id}/#/audio`, }; // eslint-disable-next-line max-len target_url = `https://api.bilibili.com/audio/music-service-c/web/song/upper?pn=1&ps=0&order=2&uid=${artist_id}`; hm({ url: target_url, method: 'GET' }).then((res) => { const tracks = res.data.data.data.map(item => bi_convert_song(item)); return resolve({ tracks, info, }); }); }); }); } function bi_parse_url(url) { let result; const match = /\/\/www.bilibili.com\/audio\/am([0-9]+)/.exec(url); if (match != null) { const playlist_id = match[1]; result = { type: 'playlist', id: `biplaylist_${playlist_id}`, }; } return result; } // eslint-disable-next-line no-unused-vars function bi_bootstrap_track(trackId, hm, pfn) { const song_id = trackId.slice('bitrack_'.length); const target_url = `https://www.bilibili.com/audio/music-service-c/web/url?sid=${song_id}`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response; if (data.code === 0) { resolve({ url: data.data.cdns[0] }); } else { reject(); } }); }); } function bi_search(url, hm, pfn) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const keyword = getParameterByName('keywords', url); const curpage = getParameterByName('curpage', url); const au = /\d+$/.exec(keyword); if (au != null) { const target_url = `https://www.bilibili.com/audio/music-service-c/web/song/info?sid=${au}`; hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response.data; const tracks = [bi_convert_song(data)]; return resolve({ result: tracks, total: 1, }); }); } else { return resolve({ result: [], total: 0, }); } // inferred, not implemented yet // eslint-disable-next-line max-len const target_url = `https://api.bilibili.com/x/web-interface/search/type?search_type=audio&keyword=${keyword}&page=${curpage}`; hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response.data; const tracks = data.result.map(item => bi_convert_song(item)); return resolve({ result: tracks, total: data.numResults, }); }); return null; }); } function bi_lyric(url, hm, pfn) { // eslint-disable-line no-unused-vars // const track_id = getParameterByName('track_id', url).split('_').pop(); const lyric_url = getParameterByName('lyric_url', url); return pfn((resolve, reject) => { hm({ url: lyric_url, method: 'GET' }).then((response) => { const { data } = response; return resolve({ lyric: data, }); }); }); } function get_playlist(url, hm, pfn) { const list_id = getParameterByName('list_id', url).split('_')[0]; switch (list_id) { case 'biplaylist': return bi_get_playlist(url, hm, pfn); case 'bialbum': return bi_album(url, hm, pfn); case 'biartist': return bi_artist(url, hm, pfn); default: return null; } } return { showPlaylist: bi_show_playlist, getPlaylist: get_playlist, parseUrl: bi_parse_url, bootstrapTrack: bi_bootstrap_track, search: bi_search, lyric: bi_lyric, }; } export default build_bilibili(); // eslint-disable-line no-unused-vars ================================================ FILE: src/provider/kugou.js ================================================ /* eslint-disable no-unused-vars */ /* Author: @WinterXMQ */ import async from 'async'; import { getParameterByName } from '../utils'; function build_kugou() { function kg_convert_song(song) { const track = { id: `kgtrack_${song.FileHash}`, title: song.SongName, artist: '', artist_id: '', album: song.AlbumName, album_id: `kgalbum_${song.AlbumID}`, source: 'kugou', source_url: `http://www.kugou.com/song/#hash=${song.FileHash}&album_id=${song.AlbumID}`, img_url: '', url: `kgtrack_${song.FileHash}`, lyric_url: song.FileHash, }; let singer_id = song.SingerId; let singer_name = song.SingerName; if (song.SingerId instanceof Array) { [singer_id] = singer_id; [singer_name] = singer_name.split('、'); } track.artist = singer_name; track.artist_id = `kgartist_${singer_id}`; return track; } function async_process_list(data_list, handler, handler_extra_param_list, callback) { const fnDict = {}; data_list.forEach((item, index) => { fnDict[index] = cb => handler(index, item, handler_extra_param_list, cb); }); async.parallel(fnDict, (_, results) => callback(null, data_list.map((item, index) => results[index]))); } function kg_render_search_result_item(index, item, params, callback) { const hm = params[0]; const track = kg_convert_song(item); // Add singer img const url = `${'http://www.kugou.com/yy/index.php?' + 'r=play/getdata&hash='}${track.lyric_url}`; hm({ url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = JSON.parse(data); track.img_url = data.data.img; callback(null, track); }); } function kg_search(url, hm, pfn) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const keyword = getParameterByName('keywords', url); const curpage = getParameterByName('curpage', url); const target_url = `${'http://songsearch.kugou.com/' + 'song_search_v2?keyword='}${keyword}&page=${curpage}`; hm({ url: target_url, method: 'GET', transformResponse: false, }) .then((response) => { let { data } = response; data = JSON.parse(data); async_process_list(data.data.lists, kg_render_search_result_item, [hm], (_, tracks) => resolve({ result: tracks, total: data.data.total, })); }); }); } function kg_render_playlist_result_item(index, item, params, callback) { const hm = params[0]; let target_url = `${'http://m.kugou.com/app/i/getSongInfo.php?' + 'cmd=playInfo&hash='}${item.hash}`; const track = { id: `kgtrack_${item.hash}`, title: '', artist: '', artist_id: '', album: '', album_id: `kgalbum_${item.album_id}`, source: 'kugou', source_url: `http://www.kugou.com/song/#hash=${ item.hash}&album_id=${item.album_id}`, img_url: '', url: `xmtrack_${item.hash}`, lyric_url: item.hash, }; // Fix song info hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response; track.title = data.songName; track.artist = data.singerId === 0 ? '未知' : data.singerName; track.artist_id = `kgartist_${data.singerId}`; if (data.imgUrl !== undefined) { track.img_url = data.imgUrl.replace('{size}', '400'); } else { // track['img_url'] = data.imgUrl.replace('{size}', '400'); } // Fix album target_url = `http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${ item.album_id}`; hm({ url: target_url, method: 'GET' }).then((res) => { const { data: res_data } = res; if (res_data.status && res_data.data !== undefined) { track.album = res_data.data.albumname; } else { track.album = ''; } return callback(null, track); }); }); } function kg_get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const list_id = getParameterByName('list_id', url).split('_').pop(); const target_url = `http://m.kugou.com/plist/list/${list_id}?json=true`; hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response; const info = { cover_img_url: data.info.list.imgurl ? data.info.list.imgurl.replace('{size}', '400') : '', title: data.info.list.specialname, id: `kgplaylist_${data.info.list.specialid}`, source_url: 'http://www.kugou.com/yy/special/single/{size}.html' .replace('{size}', data.info.list.specialid), }; async_process_list(data.list.list.info, kg_render_playlist_result_item, [hm], (_, tracks) => resolve({ tracks, info, })); }); }); } function kg_render_artist_result_item(index, item, params, callback) { const hm = params[0]; const info = params[1]; const track = { id: `kgtrack_${item.hash}`, title: '', artist: '', artist_id: info.id, album: '', album_id: `kgalbum_${item.album_id}`, source: 'kugou', source_url: `http://www.kugou.com/song/#hash=${ item.hash}&album_id=${item.album_id}`, img_url: '', url: `kgtrack_${item.hash}`, lyric_url: item.hash, }; const one = item.filename.split('-'); track.title = one[1].trim(); track.artist = one[0].trim(); // Fix album name and img const target_url = `${'http://www.kugou.com/yy/index.php?' + 'r=play/getdata&hash='}${item.hash}`; hm({ url: `http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${item.album_id}`, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = JSON.parse(data); if (data.status && data.data !== undefined) { track.album = data.data.albumname; } else { track.album = ''; } hm({ url: target_url, method: 'GET', transformResponse: false, }).then((res) => { const res_data = JSON.parse(res.data); track.img_url = res_data.data.img; callback(null, track); }); }); } function kg_artist(url, hm, pfn) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const artist_id = getParameterByName('list_id', url).split('_').pop(); let target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/info?singerid=${artist_id}`; hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = JSON.parse(data); const info = { cover_img_url: data.data.imgurl.replace('{size}', '400'), title: data.data.singername, id: `kgartist_${artist_id}`, source_url: 'http://www.kugou.com/singer/{id}.html'.replace('{id}', artist_id), }; target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/song?singerid=${ artist_id}&page=1&pagesize=30`; hm({ url: target_url, method: 'GET', transformResponse: false, }).then((res) => { let { data: res_data } = res; res_data = JSON.parse(res_data); async_process_list(res_data.data.info, kg_render_artist_result_item, [hm, info], (_, tracks) => resolve({ tracks, info, })); }); }); }); } function getTimestampString() { return (new Date()).getTime().toString(); } function getRandomIntString() { return (Math.random() * 100).toString().replace(/\D/g, ''); } // eslint-disable-next-line no-unused-vars function kg_bootstrap_track(trackId, hm, pfn) { let target_url = 'https://wwwapi.kugou.com/yy/index.php?r=play/getdata'; const jQueryHeader = `jQuery1910${getRandomIntString()}_${getTimestampString()}`; const song_id = trackId.slice('kgtrack_'.length); target_url = `${target_url}&callback=${jQueryHeader}&hash=${song_id}&_=${getTimestampString()}`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { let data = response.data.slice(jQueryHeader.length + 1, response.data.length - 2); data = JSON.parse(data); if (data.status === 1) { resolve({ url: data.data.play_url }); } else { reject(); } }); }); } function kg_lyric(url, hm, pfn) { // eslint-disable-line no-unused-vars const track_id = getParameterByName('track_id', url).split('_').pop(); const lyric_url = `http://www.kugou.com/yy/index.php?r=play/getdata&hash=${ track_id}`; return pfn((resolve, reject) => { hm({ url: lyric_url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = JSON.parse(data); return resolve({ lyric: data.data.lyrics, }); }); }); } function kg_render_album_result_item(index, item, params, callback) { const hm = params[0]; const info = params[1]; const album_id = params[2]; const track = { id: `kgtrack_${item.hash}`, title: '', artist: '', artist_id: '', album: info.title, album_id: `kgalbum_${album_id}`, source: 'kugou', source_url: `http://www.kugou.com/song/#hash=${ item.hash}&album_id=${album_id}`, img_url: '', url: `xmtrack_${item.hash}`, lyric_url: item.hash, }; // Fix other data const target_url = `${'http://m.kugou.com/app/i/getSongInfo.php?' + 'cmd=playInfo&hash='}${item.hash}`; hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { const data = JSON.parse(response.data); track.title = data.songName; track.artist = data.singerId === 0 ? '未知' : data.singerName; track.artist_id = `kgartist_${data.singerId}`; track.img_url = data.imgUrl.replace('{size}', '400'); callback(null, track); }); } function kg_album(url, hm, pfn) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const album_id = getParameterByName('list_id', url).split('_').pop(); let target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/info?' + 'albumid='}${album_id}`; let info; // info hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = JSON.parse(data); info = { cover_img_url: data.data.imgurl.replace('{size}', '400'), title: data.data.albumname, id: `kgalbum_${data.data.albumid}`, source_url: 'http://www.kugou.com/album/{id}.html' .replace('{id}', data.data.albumid), }; target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/song?' + 'albumid='}${album_id}&page=1&pagesize=-1`; hm({ url: target_url, method: 'GET', transformResponse: false, }).then((res) => { let res_data = res.data; res_data = JSON.parse(res_data); async_process_list(res_data.data.info, kg_render_album_result_item, [hm, info, album_id], (_, tracks) => resolve({ tracks, info, })); }); }); }); } function kg_show_playlist(url, hm, pfn) { let offset = getParameterByName('offset', url); if (offset === undefined) { offset = 0; } // const page = offset / 30 + 1; const target_url = `${'http://m.kugou.com/plist/index' + '&json=true&page='}${offset}`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response; // const total = data.plist.total; const result = data.plist.list.info.map(item => ({ cover_img_url: item.imgurl ? item.imgurl.replace('{size}', '400') : '', title: item.specialname, id: `kgplaylist_${item.specialid}`, source_url: 'http://www.kugou.com/yy/special/single/{size}.html'.replace('{size}', item.specialid), })); return resolve({ result, }); }); }); } function kg_parse_url(url) { let result; const match = /\/\/www.kugou.com\/yy\/special\/single\/([0-9]+).html/.exec(url); if (match != null) { const playlist_id = match[1]; result = { type: 'playlist', id: `kgplaylist_${playlist_id}`, }; } return result; } function get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars const list_id = getParameterByName('list_id', url).split('_')[0]; switch (list_id) { case 'kgplaylist': return kg_get_playlist(url, hm, pfn); case 'kgalbum': return kg_album(url, hm, pfn); case 'kgartist': return kg_artist(url, hm, pfn); default: return null; } } return { showPlaylist: kg_show_playlist, getPlaylist: get_playlist, parseUrl: kg_parse_url, bootstrapTrack: kg_bootstrap_track, search: kg_search, lyric: kg_lyric, }; } export default build_kugou(); // eslint-disable-line no-unused-vars ================================================ FILE: src/provider/kuwo.js ================================================ /* eslint-disable no-unused-vars */ /* Author: @WinterXMQ */ import async from 'async'; import { getParameterByName } from '../utils'; function build_kuwo() { // Convert html code function html_decode(str) { return str.replace(/( )/g, ' '); } // Fix single quote in json function fix_json(data) { return data.replace(/(')/g, '"'); } function num2str(num) { // const t = parseInt(num, 10); return parseInt(num / 10, 10).toString() + (num % 10).toString(); } function kw_convert_song(item) { const song_id = item.MUSICRID.split('_').pop(); const track = { id: `kwtrack_${song_id}`, title: html_decode(item.SONGNAME), artist: item.ARTIST, artist_id: `kwartist_${item.ARTISTID}`, album: html_decode(item.ALBUM), album_id: `kwalbum_${item.ALBUMID}`, source: 'kuwo', source_url: `http://www.kuwo.cn/yinyue/${song_id}`, img_url: '', url: `xmtrack_${song_id}`, lyric_url: song_id, }; return track; } function async_process_list(data_list, handler, handler_extra_param_list, callback) { const fnDict = {}; data_list.forEach((item, index) => { fnDict[index] = cb => handler(index, item, handler_extra_param_list, cb); }); async.parallel(fnDict, (_, results) => { callback(null, data_list.map((item, index) => results[index])); }); } function kw_add_song_pic_in_track(track, params, callback) { const hm = params[0]; // Add song picture image const target_url = `${'http://artistpicserver.kuwo.cn/pic.web?' + 'type=rid_pic&pictype=url&size=240&rid='}${track.lyric_url}`; hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { const { data } = response; track.img_url = data; // eslint-disable-line no-param-reassign callback(null, track); }); } function kw_render_search_result_item(index, item, params, callback) { // const hm = params[0]; const track = kw_convert_song(item); kw_add_song_pic_in_track(track, params, callback); } function kw_render_artist_result_item(index, item, params, callback) { // const hm = params[0]; const track = { id: `kwtrack_${item.musicrid}`, title: html_decode(item.name), artist: item.artist, artist_id: `kwartist_${item.artistid}`, album: html_decode(item.album), album_id: `kwalbum_${item.albumid}`, source: 'kuwo', source_url: `http://www.kuwo.cn/yinyue/${item.musicrid}`, img_url: '', url: `xmtrack_${item.musicrid}`, lyric_url: item.musicrid, }; kw_add_song_pic_in_track(track, params, callback); } function kw_render_album_result_item(index, item, params, callback) { // const hm = params[0]; const info = params[1]; const track = { id: `kwtrack_${item.id}`, title: html_decode(item.name), artist: item.artist, artist_id: `kwartist_${item.artistid}`, album: info.title, album_id: `kwalbum_${info.id}`, source: 'kuwo', source_url: `http://www.kuwo.cn/yinyue/${item.id}`, img_url: '', url: `xmtrack_${item.id}`, lyric_url: item.id, }; kw_add_song_pic_in_track(track, params, callback); } function kw_render_playlist_result_item(index, item, params, callback) { // const hm = params[0]; const track = { id: `kwtrack_${item.id}`, title: html_decode(item.name), artist: item.artist, artist_id: `kwartist_${item.artistid}`, album: html_decode(item.album), album_id: `kwalbum_${item.albumid}`, source: 'kuwo', source_url: `http://www.kuwo.cn/yinyue/${item.id}`, img_url: '', url: `xmtrack_${item.id}`, lyric_url: item.id, }; kw_add_song_pic_in_track(track, params, callback); } function kw_search(url, hm, pfn) { // eslint-disable-line no-unused-vars // API From https://blog.csdn.net/u011354613/article/details/52756467 const keyword = getParameterByName('keywords', url); let curpage = getParameterByName('curpage', url); // page number is started from 0 curpage -= 1; const target_url = `${'http://search.kuwo.cn/r.s?' + 'ft=music&itemset=web_2013&client=kt' + '&rformat=json&encoding=utf8'}${ '&all={0}&pn={1}&rn=20'.replace('{0}', keyword).replace('{1}', curpage)}`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = JSON.parse(fix_json(data)); async_process_list(data.abslist, kw_render_search_result_item, [hm], (_, tracks) => resolve({ result: tracks, total: parseInt(data.TOTAL, 10), })); }); }); } // eslint-disable-next-line no-unused-vars function kw_bootstrap_track(trackId, hm, pfn) { const song_id = trackId.slice('kwtrack_'.length); const target_url = `${'http://antiserver.kuwo.cn/anti.s?' + 'type=convert_url&format=aac|mp3|wma&response=url&rid=MUSIC_'}${song_id}`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { const { data } = response; if (data.length > 0) { resolve({ url: data }); } else { reject(); } }); }); } function kw_lyric(url, hm, pfn) { // eslint-disable-line no-unused-vars const track_id = getParameterByName('lyric_url', url); const target_url = `http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=${track_id}`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = JSON.parse(data); const lyric = data.data.lrclist.reduce((str, item) => { const t = parseFloat(item.time); const m = parseInt(t / 60, 10); const s = parseInt(t - m * 60, 10); const ms = parseInt((t - m * 60 - s) * 100, 10); return `${str}[${num2str(m)}:${num2str(parseInt(s, 10))}.${num2str(ms)}]${item.lineLyric}\n`; }, ''); return resolve({ lyric, }); }); }); } function kw_artist(url, hm, pfn) { // eslint-disable-line no-unused-vars const artist_id = getParameterByName('list_id', url).split('_').pop(); return pfn((resolve, reject) => { let target_url = `${'http://search.kuwo.cn/r.s?stype=artistinfo' + '&artistid='}${artist_id}&encoding=utf8`; hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = JSON.parse(fix_json(data)); const info = { cover_img_url: `http://img1.sycdn.kuwo.cn/star/starheads/${data.pic}`, title: html_decode(data.name), id: `kwartist_${data.id}`, source_url: `http://www.kuwo.cn/artist/content?name=${data.name}`, }; // Get songs target_url = `${'http://search.kuwo.cn/r.s?stype=artist2music' + '&sortby=0&alflac=1&pcmp4=1&encoding=utf8' + '&artistid='}${artist_id}&pn=0&rn=100`; hm({ url: target_url, method: 'GET', transformResponse: false, }).then((res) => { let { data: res_data } = res; res_data = JSON.parse(fix_json(res_data)); async_process_list(res_data.musiclist, kw_render_artist_result_item, [hm], (_, tracks) => resolve({ tracks, info, })); }); }); }); } function kw_album(url, hm, pfn) { // eslint-disable-line no-unused-vars const album_id = getParameterByName('list_id', url).split('_').pop(); return pfn((resolve, reject) => { const target_url = `${'http://search.kuwo.cn/r.s?pn=0&rn=1000&stype=albuminfo' + '&albumid='}${album_id }&alflac=1&pcmp4=1&encoding=utf8&vipver=MUSIC_8.7.7.0_W4`; hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = JSON.parse(fix_json(data)); const info = { cover_img_url: `http://img1.sycdn.kuwo.cn/star/albumcover/${data.pic}`, title: html_decode(data.name), id: data.albumid, source_url: `http://www.kuwo.cn/album/${data.albumid}`, }; // Get songs async_process_list(data.musiclist, kw_render_album_result_item, [hm, info], (_, tracks) => resolve({ tracks, info, })); }); }); } function kw_show_playlist(url, hm, pfn) { let offset = getParameterByName('offset', url); if (!offset) { offset = 0; } /* const id_available = { 1265: '经典', 577: '纯音乐', 621: '网络', 155: '怀旧', 1879: '网红', 220: '佛乐', 180: '影视', 578: '器乐', 1877: '游戏', 181: '二次元', 882: 'KTV', 216: '喊麦', 1366: '3D', 146: '伤感', 62: '放松', 58: '励志', 143: '开心', 137: '甜蜜', 139: '兴奋', 67: '安静', 66: '治愈', 147: '寂寞', 160: '四年', 366: '运动', 354: '睡前', 378: '跳舞', 1876: '学习', 353: '清晨', 359: '夜店', 382: '校园', 544: '亲热', 363: '咖啡店', 375: '旅行', 371: '散步', 386: '工作', 336: '婚礼', 637: '70后', 638: '80后', 639: '90后', 640: '00后', 268: '10后', 393: '流行', 391: '电子', 389: '摇滚', 1921: '民歌', 392: '民谣', 399: '乡村', 35: '欧洲', 37: '华语', }; */ const target_url = `${'http://www.kuwo.cn/www/categoryNew/getPlayListInfoUnderCategory?' + 'type=taglist&digest=10000&id='}${37}&start=${offset}&count=50`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response.data; if (!data[0]) { return resolve([]); } const result = data[0].data.map(item => ({ cover_img_url: item.img, title: item.name, id: `kwplaylist_${item.id}`, source_url: `http://www.kuwo.cn/playlist/index?pid=${item.id}`, })); return resolve({ result, }); }); }); } function kw_get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars const list_id = getParameterByName('list_id', url).split('_').pop(); const target_url = `${'http://nplserver.kuwo.cn/pl.svc?' + 'op=getlistinfo&pn=0&rn=200&encode=utf-8&keyset=pl2012&pcmp4=1' + '&pid='}${list_id}&vipver=MUSIC_9.0.2.0_W1&newver=1`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET' }).then((response) => { const { data } = response; const info = { cover_img_url: data.pic, title: data.title, id: `kwplaylist_${data.id}`, source_url: `http://www.kuwo.cn/playlist/index?pid=${data.id}`, }; async_process_list(data.musiclist, kw_render_playlist_result_item, [hm], (_, tracks) => resolve({ tracks, info, })); }); }); } function kw_parse_url(url) { let result; const match = /\/\/www.kuwo.cn\/playlist\/index\?pid=([0-9]+)/.exec(url); if (match != null) { const playlist_id = match[1]; result = { type: 'playlist', id: `kwplaylist_${playlist_id}`, }; } return result; } function get_playlist(url, hm, pfn) { const list_id = getParameterByName('list_id', url).split('_')[0]; switch (list_id) { case 'kwplaylist': return kw_get_playlist(url, hm, pfn); case 'kwalbum': return kw_album(url, hm, pfn); case 'kwartist': return kw_artist(url, hm, pfn); default: return null; } } return { showPlaylist: kw_show_playlist, getPlaylist: get_playlist, parseUrl: kw_parse_url, bootstrapTrack: kw_bootstrap_track, search: kw_search, lyric: kw_lyric, }; } export default build_kuwo(); // eslint-disable-line no-unused-vars ================================================ FILE: src/provider/netease.js ================================================ /* eslint-disable import/named */ /* eslint-disable no-unused-vars */ /* eslint-disable no-param-reassign */ import { getParameterByName, getRandomHexString } from '../utils'; import { aesEncrypt, rsaEncrypt } from '../crypto/crypto'; const $ = require('cheerio'); function encryptedRequest(text) { const modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b72' + '5152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbd' + 'a92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe48' + '75d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'; const nonce = '0CoJUm6Qyw8W8jud'; const pubKey = '010001'; text = JSON.stringify(text); const secKey = getRandomHexString(16); const ivString = '0102030405060708'; const encText = aesEncrypt(aesEncrypt(text, nonce, ivString), secKey, ivString); const encSecKey = rsaEncrypt(secKey, pubKey, modulus); const data = { params: encText, encSecKey, }; return data; } function NeteaseFactory() { function neShowPlaylist(url, hm, pfn) { const order = 'hot'; const offset = getParameterByName('offset', url); let targetUrl; if (offset != null) { targetUrl = `http://music.163.com/discover/playlist/?order=${order}&limit=35&offset=${offset}`; } else { targetUrl = `http://music.163.com/discover/playlist/?order=${order}`; } return pfn((resolve, reject) => { const result = []; hm({ url: targetUrl, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; // TODO: replace jquery to raw document api data = $.parseHTML(data); // eslint-disable-next-line func-names $(data).find('.m-cvrlst li').each(function () { const defaultPlaylist = { cover_img_url: '', title: '', id: '', source_url: '', }; defaultPlaylist.cover_img_url = $(this).find('img')[0].attribs.src; defaultPlaylist.title = $(this).find('div a')[0].attribs.title; const url2 = $(this).find('div a')[0].attribs.href; const listId = getParameterByName('id', url2); defaultPlaylist.id = `neplaylist_${listId}`; defaultPlaylist.source_url = `http://music.163.com/#/playlist?id=${listId}`; result.push(defaultPlaylist); }); return resolve({ result, }); }); }); } // function neEnsureCookie(callback) { // var domain = 'https://music.163.com'; // var nuidName = '_ntes_nuid'; // var nnidName = '_ntes_nnid'; // // TODO: replace chrome for dependency injection // var chrome = {}; // chrome.cookies.get({ // 'url': domain, // 'name': nuidName // }, function (cookie) { // if (cookie == null) { // var nuidValue = getRandomHexString(32); // var nnidValue = nuidValue + ',' + (new Date()).getTime(); // // netease default cookie expire time: 100 years // var expire = ((new Date()).getTime() + 1e3 * 60 * 60 * 24 * 365 * 100) / 1000; // chrome.cookies.set({ // 'url': domain, // 'name': nuidName, // 'value': nuidValue, // 'expirationDate': expire // }, function (cookie) { // chrome.cookies.set({ // 'url': domain, // 'name': nnidName, // 'value': nnidValue, // 'expirationDate': expire // }, function (cookie2) { // callback(cookie.value); // }); // }); // } else { // callback(cookie.value); // } // }); // } function neGetPlaylist(url, hm, pfn) { // special thanks for @Binaryify // https://github.com/Binaryify/NeteaseCloudMusicApi return pfn((resolve, reject) => { const listId = getParameterByName('list_id', url).split('_').pop(); const targetUrl = 'http://music.163.com/weapi/v3/playlist/detail'; const d = { id: listId, offset: 0, total: true, limit: 1000, n: 1000, csrf_token: '', }; const data = encryptedRequest(d); // neEnsureCookie(function () { hm({ url: targetUrl, method: 'POST', data, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }).then((response) => { const { data: res_data } = response; const info = { id: `neplaylist_${listId}`, cover_img_url: res_data.playlist.coverImgUrl, title: res_data.playlist.name, source_url: `http://music.163.com/#/playlist?id=${listId}`, }; const tracks = []; Array.prototype.forEach.call(res_data.playlist.tracks, (trackJson, index) => { const defaultTrack = { id: '0', title: '', artist: '', artist_id: 'neartist_0', album: '', album_id: 'nealbum_0', source: 'netease', source_url: 'http://www.xiami.com/song/0', img_url: '', url: '', }; defaultTrack.id = `netrack_${trackJson.id}`; defaultTrack.title = trackJson.name; defaultTrack.artist = trackJson.ar[0].name; defaultTrack.artist_id = `neartist_${trackJson.ar[0].id}`; defaultTrack.album = trackJson.al.name; defaultTrack.album_id = `nealbum_${trackJson.al.id}`; defaultTrack.source_url = `http://music.163.com/#/song?id=${trackJson.id}`; defaultTrack.img_url = trackJson.al.picUrl; defaultTrack.url = defaultTrack.id; tracks.push(defaultTrack); }); return resolve({ info, tracks, }); }); }); } function neBootstrapTrack(songId, hm, pfn) { const targetUrl = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token='; const csrf = ''; songId = songId.slice('netrack_'.length); const d = { ids: [songId], br: 320000, csrf_token: csrf, }; const data = encryptedRequest(d); return pfn((resolve, reject) => { hm({ url: targetUrl, method: 'POST', data, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }).then((response) => { const { data: res_data } = response; const { url } = res_data.data[0]; if (url != null) { resolve({ url }); } else { reject({}); } }); }); } function isPlayable(song) { return ((song.status >= 0) && (song.fee !== 4)); } function neSearch(url, hm, pfn) { // use chrome extension to modify referer. const targetUrl = 'http://music.163.com/api/search/pc'; const keyword = getParameterByName('keywords', url); const curpage = getParameterByName('curpage', url); const reqData = { s: keyword, offset: 20 * (curpage - 1), limit: 20, type: 1, }; return pfn((resolve, reject) => { hm({ url: targetUrl, method: 'POST', data: reqData, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }).then((response) => { const { data } = response; const tracks = []; Array.prototype.forEach.call(data.result.songs, (songInfo, index) => { const defaultTrack = { id: `netrack_${songInfo.id}`, title: songInfo.name, artist: songInfo.artists[0].name, artist_id: `neartist_${songInfo.artists[0].id}`, album: songInfo.album.name, album_id: `nealbum_${songInfo.album.id}`, source: 'netease', source_url: `http://music.163.com/#/song?id=${songInfo.id}`, img_url: songInfo.album.picUrl, url: `netrack_${songInfo.id}`, }; if (!isPlayable(songInfo)) { defaultTrack.disabled = true; } else { defaultTrack.disabled = false; } tracks.push(defaultTrack); }); return resolve({ result: tracks, total: data.result.songCount, }); }); }); } function neAlbum(url, hm, pfn) { const albumId = getParameterByName('list_id', url).split('_').pop(); // use chrome extension to modify referer. const targetUrl = `http://music.163.com/api/album/${albumId}`; return pfn((resolve, reject) => { hm({ url: targetUrl, method: 'GET', }).then((response) => { const { data } = response; const info = { cover_img_url: data.album.picUrl, title: data.album.name, id: `nealbum_${data.album.id}`, source_url: `http://music.163.com/#/album?id=${data.album.id}`, }; const tracks = []; Array.prototype.forEach.call(data.album.songs, (songInfo, index) => { const defaultTrack = { id: `netrack_${songInfo.id}`, title: songInfo.name, artist: songInfo.artists[0].name, artist_id: `neartist_${songInfo.artists[0].id}`, album: songInfo.album.name, album_id: `nealbum_${songInfo.album.id}`, source: 'netease', source_url: `http://music.163.com/#/song?id=${songInfo.id}`, img_url: songInfo.album.picUrl, url: `netrack_${songInfo.id}`, }; if (!isPlayable(songInfo)) { defaultTrack.disabled = true; } else { defaultTrack.disabled = false; } tracks.push(defaultTrack); }); return resolve({ tracks, info, }); }); }); } function neArtist(url, hm, pfn) { const artistId = getParameterByName('list_id', url).split('_').pop(); // use chrome extension to modify referer. const targetUrl = `http://music.163.com/api/artist/${artistId}`; return pfn((resolve, reject) => { hm({ url: targetUrl, method: 'GET', }).then((response) => { const { data } = response; const info = { cover_img_url: data.artist.picUrl, title: data.artist.name, id: `neartist_${data.artist.id}`, source_url: `http://music.163.com/#/artist?id=${data.artist.id}`, }; const tracks = []; Array.prototype.forEach.call(data.hotSongs, (songInfo, i) => { const defaultTrack = { id: `netrack_${songInfo.id}`, title: songInfo.name, artist: songInfo.artists[0].name, artist_id: `neartist_${songInfo.artists[0].id}`, album: songInfo.album.name, album_id: `nealbum_${songInfo.album.id}`, source: 'netease', source_url: `http://music.163.com/#/song?id=${songInfo.id}`, img_url: songInfo.album.picUrl, url: `netrack_${songInfo.id}`, }; if (!isPlayable(songInfo)) { defaultTrack.disabled = true; } else { defaultTrack.disabled = false; } tracks.push(defaultTrack); }); return resolve({ tracks, info, }); }); }); } function neLyric(url, hm, pfn) { const trackId = getParameterByName('track_id', url).split('_').pop(); // use chrome extension to modify referer. const targetUrl = 'http://music.163.com/weapi/song/lyric?csrf_token='; const csrf = ''; const d = { id: trackId, lv: -1, tv: -1, csrf_token: csrf, }; const data = encryptedRequest(d); return pfn((resolve, reject) => { hm({ url: targetUrl, method: 'POST', data, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }).then((response) => { const { data: res_data } = response; let lrc = ''; if (res_data.lrc != null) { lrc = res_data.lrc.lyric; } return resolve({ lyric: lrc, }); }); }); } function neParseUrl(url) { let result; url = url.replace('music.163.com/#/my/m/music/playlist?', 'music.163.com/#/playlist?'); if (url.search('//music.163.com/#/m/playlist') !== -1 || url.search('//music.163.com/#/playlist') !== -1 || url.search('//music.163.com/playlist') !== -1) { result = { type: 'playlist', id: `neplaylist_${getParameterByName('id', url)}`, }; } return result; } function getPlaylist(url, hm, pfn) { const listId = getParameterByName('list_id', url).split('_')[0]; if (listId === 'neplaylist') { return neGetPlaylist(url, hm, pfn); } if (listId === 'nealbum') { return neAlbum(url, hm, pfn); } if (listId === 'neartist') { return neArtist(url, hm, pfn); } return null; } return { showPlaylist: neShowPlaylist, getPlaylist, parseUrl: neParseUrl, bootstrapTrack: neBootstrapTrack, search: neSearch, lyric: neLyric, }; } export default NeteaseFactory(); ================================================ FILE: src/provider/qq.js ================================================ /* eslint-disable no-unused-vars */ /* global atob */ import entities from 'entities'; import encoding from 'text-encoding'; import { getParameterByName } from '../utils'; function build_qq() { function htmlDecode(value) { return entities.decodeHTML(value); } function qq_show_playlist(url, hm, pfn) { const offset = Number(getParameterByName('offset', url)) || 0; const target_url = `${'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg' + '?rnd=0.4781484879517406&g_tk=732560869&jsonpCallback=MusicJsonCallback' + '&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8' + '¬ice=0&platform=yqq&needNewCode=0' + '&categoryId=10000000&sortId=5&sin='}${offset}&ein=${49 + offset}`; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = data.slice('MusicJsonCallback('.length, -')'.length); data = JSON.parse(data); const playlists = data.data.list.map(item => ({ cover_img_url: item.imgurl, title: htmlDecode(item.dissname), id: `qqplaylist_${item.dissid}`, source_url: `http://y.qq.com/#type=taoge&id=${item.dissid}`, })); return resolve({ result: playlists, }); }); }); } function qq_get_image_url(qqimgid, img_type) { if (qqimgid == null) { return ''; } let category = ''; if (img_type === 'artist') { category = 'mid_singer_300'; } if (img_type === 'album') { category = 'mid_album_300'; } const s = [category, qqimgid[qqimgid.length - 2], qqimgid[qqimgid.length - 1], qqimgid].join('/'); const url = `http://imgcache.qq.com/music/photo/${s}.jpg`; return url; } function qq_is_playable(song) { const switch_flag = song.switch.toString(2).split(''); switch_flag.pop(); switch_flag.reverse(); // flag switch table meaning: // ["play_lq", "play_hq", "play_sq", "down_lq", "down_hq", "down_sq", "soso", // "fav", "share", "bgm", "ring", "sing", "radio", "try", "give"] const play_flag = switch_flag[0]; const try_flag = switch_flag[13]; return ((play_flag === '1') || ((play_flag === '1') && (try_flag === '1'))); } function qq_convert_song(song) { const d = { id: `qqtrack_${song.songmid}`, title: htmlDecode(song.songname), artist: htmlDecode(song.singer[0].name), artist_id: `qqartist_${song.singer[0].mid}`, album: htmlDecode(song.albumname), album_id: `qqalbum_${song.albummid}`, img_url: qq_get_image_url(song.albummid, 'album'), source: 'qq', source_url: `http://y.qq.com/#type=song&mid=${song.songmid}&tpl=yqq_song_detail`, url: `qqtrack_${song.songmid}`, disabled: !qq_is_playable(song), }; return d; } function qq_get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars const list_id = getParameterByName('list_id', url).split('_').pop(); return pfn((resolve, reject) => { const target_url = `${'http://i.y.qq.com/qzone-music/fcg-bin/fcg_ucc_getcdinfo_' + 'byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&jsonpCallback=' + 'jsonCallback&nosign=1&disstid='}${list_id}&g_tk=5381&loginUin=0&hostUin=0` + '&format=jsonp&inCharset=GB2312&outCharset=utf-8¬ice=0' + '&platform=yqq&jsonpCallback=jsonCallback&needNewCode=0'; hm({ url: target_url, method: 'GET', transformResponse: false, }) .then((response) => { let { data } = response; data = data.slice('jsonCallback('.length, -')'.length); data = JSON.parse(data); const info = { cover_img_url: data.cdlist[0].logo, title: data.cdlist[0].dissname, id: `qqplaylist_${list_id}`, source_url: `http://y.qq.com/#type=taoge&id=${list_id}`, }; const tracks = data.cdlist[0].songlist.map(item => qq_convert_song(item)); return resolve({ tracks, info, }); }); }); } function qq_album(url, hm, pfn) { const album_id = getParameterByName('list_id', url).split('_').pop(); return pfn((resolve, reject) => { const target_url = `${'http://i.y.qq.com/v8/fcg-bin/fcg_v8_album_info_cp.fcg' + '?platform=h5page&albummid='}${album_id}&g_tk=938407465` + '&uin=0&format=jsonp&inCharset=utf-8&outCharset=utf-8' + '¬ice=0&platform=h5&needNewCode=1&_=1459961045571' + '&jsonpCallback=asonglist1459961045566'; hm({ url: target_url, method: 'GET', transformResponse: false, }) .then((response) => { let { data } = response; data = data.slice(' asonglist1459961045566('.length, -')'.length); data = JSON.parse(data); const info = { cover_img_url: qq_get_image_url(album_id, 'album'), title: data.data.name, id: `qqalbum_${album_id}`, source_url: `http://y.qq.com/#type=album&mid=${album_id}`, }; const tracks = data.data.list.map(item => qq_convert_song(item)); return resolve({ tracks, info, }); }); }); } function qq_artist(url, hm, pfn) { const artist_id = getParameterByName('list_id', url).split('_').pop(); return pfn((resolve, reject) => { const target_url = `${'http://i.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg' + '?platform=h5page&order=listen&begin=0&num=50&singermid='}${artist_id}` + '&g_tk=938407465&uin=0&format=jsonp&' + 'inCharset=utf-8&outCharset=utf-8¬ice=0&platform=' + 'h5&needNewCode=1&from=h5&_=1459960621777&' + 'jsonpCallback=ssonglist1459960621772'; hm({ url: target_url, method: 'GET', transformResponse: false, }) .then((response) => { let { data } = response; data = data.slice(' ssonglist1459960621772('.length, -')'.length); data = JSON.parse(data); const info = { cover_img_url: qq_get_image_url(artist_id, 'artist'), title: data.data.singer_name, id: `qqartist_${artist_id}`, source_url: `http://y.qq.com/#type=singer&mid=${artist_id}`, }; const tracks = data.data.list.map(item => qq_convert_song(item.musicData)); return resolve({ tracks, info, }); }); }); } function qq_search(url, hm, pfn) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const keyword = getParameterByName('keywords', url); const curpage = getParameterByName('curpage', url); const target_url = `${'http://i.y.qq.com/s.music/fcgi-bin/search_for_qq_cp?' + 'g_tk=938407465&uin=0&format=jsonp&inCharset=utf-8' + '&outCharset=utf-8¬ice=0&platform=h5&needNewCode=1' + '&w='}${keyword}&zhidaqu=1&catZhida=1` + `&t=0&flag=1&ie=utf-8&sem=1&aggr=0&perpage=20&n=20&p=${curpage }&remoteplace=txt.mqq.all&_=1459991037831&jsonpCallback=jsonp4`; hm({ url: target_url, method: 'GET', transformResponse: false, }) .then((response) => { let { data } = response; data = data.slice('jsonp4('.length, -')'.length); data = JSON.parse(data); const tracks = data.data.song.list.map(item => qq_convert_song(item)); return resolve({ result: tracks, total: data.data.song.totalnum, }); }); }); } // eslint-disable-next-line no-unused-vars function qq_bootstrap_track(trackId, hm, pfn) { const songId = trackId.slice('qqtrack_'.length); const target_url = `${'https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&' + 'hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&' + 'platform=yqq.json&needNewCode=0&data=%7B%22req_0%22%3A%7B%22' + 'module%22%3A%22vkey.GetVkeyServer%22%2C%22method%22%3A%22' + 'CgiGetVkey%22%2C%22param%22%3A%7B%22guid%22%3A%2210000%22%2C%22songmid%22%3A%5B%22'}${ songId}%22%5D%2C%22songtype%22%3A%5B0%5D%2C%22uin%22%3A%220%22%2C%22loginflag%22` + '%3A1%2C%22platform%22%3A%2220%22%7D%7D%2C%22comm%22%3A%7B%22uin%22%3A0%2C%22' + 'format%22%3A%22json%22%2C%22ct%22%3A20%2C%22cv%22%3A0%7D%7D'; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET', transformResponse: false, }) .then((response) => { let { data } = response; data = JSON.parse(data); const url = data.req_0.data.sip[0] + data.req_0.data.midurlinfo[0].purl; resolve({ url }); }); }); } function str2ab(str) { // string to array buffer. const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i += 1) { bufView[i] = str.charCodeAt(i); } return buf; } function qq_lyric(url, hm, pfn) { // eslint-disable-line no-unused-vars const track_id = getParameterByName('track_id', url).split('_').pop(); // use chrome extension to modify referer. const target_url = `${'http://i.y.qq.com/lyric/fcgi-bin/fcg_query_lyric.fcg?' + 'songmid='}${track_id}&loginUin=0&hostUin=0&format=jsonp&inCharset=GB2312` + '&outCharset=utf-8¬ice=0&platform=yqq&jsonpCallback=MusicJsonCallback&needNewCode=0'; return pfn((resolve, reject) => { hm({ url: target_url, method: 'GET', transformResponse: false, }).then((response) => { let { data } = response; data = data.slice('MusicJsonCallback('.length, -')'.length); data = JSON.parse(data); let lrc = ''; if (data.lyric != null) { // const td = new TextDecoder('utf8'); const td = new encoding.TextDecoder(); lrc = td.decode(str2ab(atob(data.lyric))); } return resolve({ lyric: lrc, }); }); }); } function qq_parse_url(url) { let result; let match = /\/\/y.qq.com\/n\/yqq\/playlist\/([0-9]+)/.exec(url); if (match != null) { const playlist_id = match[1]; result = { type: 'playlist', id: `qqplaylist_${playlist_id}`, }; } match = /\/\/y.qq.com\/n\/yqq\/playsquare\/([0-9]+)/.exec(url); if (match != null) { const playlist_id = match[1]; result = { type: 'playlist', id: `qqplaylist_${playlist_id}`, }; } match = /\/\/y.qq.com\/n\/m\/detail\/taoge\/index.html\?id=([0-9]+)/.exec(url); if (match != null) { const playlist_id = match[1]; result = { type: 'playlist', id: `qqplaylist_${playlist_id}`, }; } return result; } function get_playlist(url, hm, pfn) { const list_id = getParameterByName('list_id', url).split('_')[0]; switch (list_id) { case 'qqplaylist': return qq_get_playlist(url, hm, pfn); case 'qqalbum': return qq_album(url, hm, pfn); case 'qqartist': return qq_artist(url, hm, pfn); default: return null; } } return { showPlaylist: qq_show_playlist, getPlaylist: get_playlist, parseUrl: qq_parse_url, bootstrapTrack: qq_bootstrap_track, search: qq_search, lyric: qq_lyric, }; } export default build_qq(); // eslint-disable-line no-unused-vars ================================================ FILE: src/provider/xiami.js ================================================ /* eslint-disable no-unused-vars */ /* global parseInt */ /* eslint-disable no-param-reassign */ import { MD5 } from '../crypto/crypto'; import { getParameterByName } from '../utils'; function build_xiami() { function caesar(location) { const num = location[0]; const avg_len = Math.floor(location.slice(1).length / num); const remainder = location.slice(1).length % num; const result = []; for (let i = 0; i < remainder; i += 1) { const line = location.slice(i * (avg_len + 1) + 1, (i + 1) * (avg_len + 1) + 1); result.push(line); } for (let i = 0; i < num - remainder; i += 1) { const line = location.slice((avg_len + 1) * remainder) .slice(i * avg_len + 1, (i + 1) * avg_len + 1); result.push(line); } const s = []; for (let i = 0; i < avg_len; i += 1) { for (let j = 0; j < num; j += 1) { s.push(result[j][i]); } } for (let i = 0; i < remainder; i += 1) { s.push(result[i].slice(-1)); } return unescape(s.join('')).replace(/\^/g, '0'); } function handleProtocolRelativeUrl(url) { const regex = /^.*?\/\//; const result = url.replace(regex, 'http://'); return result; } function xm_retina_url(s) { if (s.slice(-6, -4) === '_1') { return s.slice(0, -6) + s.slice(-4); } return s; } function xm_get_api_url(api, params, token) { const params_string = JSON.stringify(params); const origin = `${token.split('_')[0]}_xmMain_${api}_${params_string}`; const sign = MD5(origin); const baseUrl = 'https://www.xiami.com'; return encodeURI(`${baseUrl + api}?_q=${params_string}&_s=${sign}`); } function xm_api_get(hm, api, params, cookieProvider, callback) { const domain = 'https://www.xiami.com'; const name = 'xm_sg_tk'; cookieProvider.getCookie(domain, name, (token) => { const url = xm_get_api_url(api, params, token); hm({ method: 'GET', url, cookieProvider }).then((response) => { if (response.data.code === 'SG_TOKEN_EMPTY' || response.data.code === 'SG_TOKEN_EXPIRED' || response.data.code === 'SG_INVALID') { // token expire, refetch token and start get url cookieProvider.getCookie(domain, name, (token2) => { const url2 = xm_get_api_url(api, params, token2); hm({ method: 'GET', url: url2, cookieProvider }).then((res) => { callback(res); }); }); } else { callback(response); } }); }); } function xm_get_low_quality_img_url(url) { return `${url}?x-oss-process=image/resize,m_fill,limit_0,s_330/quality,q_80`; } function xm_show_playlist(url, hm, pfn, cookieProvider) { const offset = getParameterByName('offset', url); const page = offset / 30 + 1; const pageSize = 60; return pfn((resolve, reject) => { const api = '/api/list/collect'; const params = { pagingVO: { page, pageSize, }, dataType: 'system', }; xm_api_get(hm, api, params, cookieProvider, (response) => { const result = response.data.result.data.collects.map((d) => { const default_playlist = { cover_img_url: '', title: '', id: '', source_url: '', }; default_playlist.cover_img_url = xm_get_low_quality_img_url(d.collectLogo); default_playlist.title = d.collectName; const list_id = d.listId; default_playlist.id = `xmplaylist_${list_id}`; default_playlist.source_url = `http://www.xiami.com/collect/${list_id}`; return default_playlist; }); return resolve({ result, }); }); }); } // eslint-disable-next-line no-unused-vars function xm_bootstrap_track(trackId, hm, pfn) { const target_url = `http://www.xiami.com/song/playlist/id/${trackId.slice('xmtrack_'.length) }/object_name/default/object_id/0/cat/json`; return pfn((resolve, reject) => { hm({ method: 'GET', url: target_url }).then((response) => { const { data } = response; if (data.data.trackList == null) { return reject(); } const { location } = data.data.trackList[0]; // eslint-disable-next-line const url = handleProtocolRelativeUrl(caesar(location)); const img_url = xm_retina_url(handleProtocolRelativeUrl(data.data.trackList[0].pic)); const album = data.data.trackList[0].album_name; const album_id = `xmalbum_${data.data.trackList[0].album_id}`; const lyric_url = handleProtocolRelativeUrl(data.data.trackList[0].lyric_url); return resolve({ url, img_url, album, album_id, lyric_url, }); }); }); } function xm_convert_song(song_info, artist_field_name) { const track = { id: `xmtrack_${song_info.song_id}`, title: song_info.song_name, artist: song_info[artist_field_name], artist_id: `xmartist_${song_info.artist_id}`, album: song_info.album_name, album_id: `xmalbum_${song_info.album_id}`, source: 'xiami', source_url: `http://www.xiami.com/song/${song_info.song_id}`, img_url: song_info.album_logo, url: `xmtrack_${song_info.song_id}`, lyric_url: song_info.lyric_file, }; return track; } function xm_convert_song2(song_info, artist_field_name) { // eslint-disable-line no-unused-vars const track = { id: `xmtrack_${song_info.songId}`, title: song_info.songName, artist: song_info.artistName, artist_id: `xmartist_${song_info.artistId}`, album: song_info.albumName, album_id: `xmalbum_${song_info.albumId}`, source: 'xiami', source_url: `http://www.xiami.com/song/${song_info.songId}`, img_url: song_info.albumLogo, url: `xmtrack_${song_info.songId}`, // 'lyric_url': song_info.lyricInfo.lyricFile }; if (song_info.lyricInfo) { track.lyric_url = song_info.lyricInfo.lyricFile; } return track; } function xm_get_playlist(url, hm, pfn, cookieProvider) { // eslint-disable-line no-unused-vars const list_id = getParameterByName('list_id', url).split('_').pop(); return pfn((resolve, reject) => { const api = '/api/collect/initialize'; const params = { listId: parseInt(list_id, 10), }; xm_api_get(hm, api, params, cookieProvider, (response) => { const collect = response.data.result.data.collectDetail; const info = { cover_img_url: xm_get_low_quality_img_url(collect.collectLogo), title: collect.collectName, id: `xmplaylist_${list_id}`, source_url: `http://www.xiami.com/collect/${list_id}`, }; const tracks = response.data.result.data.collectSongs.map(item => xm_convert_song2(item, 'artist_name')); return resolve({ tracks, info, }); }); }); } function xm_search(url, hm, pfn, cookieProvider) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const api = '/api/search/searchSongs'; const keyword = getParameterByName('keywords', url); const curpage = getParameterByName('curpage', url); const pageSize = 60; const params = { pagingVO: { page: curpage, pageSize, }, key: keyword, }; xm_api_get(hm, api, params, cookieProvider, (response) => { const tracks = response.data.result.data.songs.map(item => xm_convert_song2(item, 'artistName')); return resolve({ result: tracks, total: response.data.result.data.pagingVO.pages, }); }); }); } function xm_album(url, hm, pfn, cookieProvider) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const album_id = getParameterByName('list_id', url).split('_').pop(); const target_url = `http://api.xiami.com/web?v=2.0&app_key=1&id=${album_id }&page=1&limit=20&callback=jsonp217&r=album/detail`; hm({ url: target_url, method: 'GET', transformResponse: false, }) .then((response) => { let { data } = response; data = data.slice('jsonp217('.length, -')'.length); data = JSON.parse(data); const info = { cover_img_url: data.data.album_logo, title: data.data.album_name, id: `xmalbum_${data.data.album_id}`, source_url: `http://www.xiami.com/album/${data.data.album_id}`, }; const tracks = data.data.songs.map(item => xm_convert_song(item, 'singers')); return resolve({ tracks, info, }); }); }); } function xm_artist(url, hm, pfn, cookieProvider) { // eslint-disable-line no-unused-vars return pfn((resolve, reject) => { const artist_id = getParameterByName('list_id', url).split('_').pop(); let target_url = `http://api.xiami.com/web?v=2.0&app_key=1&id=${artist_id }&page=1&limit=20&_ksTS=1459931285956_216` + '&callback=jsonp217&r=artist/detail'; hm({ url: target_url, method: 'GET', transformResponse: false, }) .then((response) => { let { data } = response; data = data.slice('jsonp217('.length, -')'.length); data = JSON.parse(data); const info = { cover_img_url: xm_retina_url(data.data.logo), title: data.data.artist_name, id: `xmartist_${artist_id}`, source_url: `http://www.xiami.com/artist/${artist_id}`, }; target_url = `http://api.xiami.com/web?v=2.0&app_key=1&id=${artist_id }&page=1&limit=20&callback=jsonp217&r=artist/hot-songs`; hm({ url: target_url, method: 'GET', transformResponse: false, }) .then((res) => { let { data: res_data } = res; res_data = res_data.slice('jsonp217('.length, -')'.length); res_data = JSON.parse(res_data); const tracks = res_data.data.map((item) => { const track = xm_convert_song(item, 'singers'); track.artist_id = `xmartist_${artist_id}`; return track; }); return resolve({ tracks, info, }); }); }); }); } function xm_lyric(url, hm, pfn, cookieProvider) { // eslint-disable-line no-unused-vars // const track_id = getParameterByName('track_id', url).split('_').pop(); const lyric_url = getParameterByName('lyric_url', url); return pfn((resolve, reject) => { hm({ url: lyric_url, method: 'GET', transformResponse: false, }).then((response) => { const { data } = response; return resolve({ lyric: data, }); }); }); } function xm_parse_url(url) { let result; const match = /\/\/www.xiami.com\/collect\/([0-9]+)/.exec(url); if (match != null) { const playlist_id = match[1]; result = { type: 'playlist', id: `xmplaylist_${playlist_id}`, }; } return result; } function get_playlist(url, hm, pfn, cookieProvider) { const list_id = getParameterByName('list_id', url).split('_')[0]; switch (list_id) { case 'xmplaylist': return xm_get_playlist(url, hm, pfn, cookieProvider); case 'xmalbum': return xm_album(url, hm, pfn, cookieProvider); case 'xmartist': return xm_artist(url, hm, pfn, cookieProvider); default: return null; } } return { showPlaylist: xm_show_playlist, getPlaylist: get_playlist, parseUrl: xm_parse_url, bootstrapTrack: xm_bootstrap_track, search: xm_search, lyric: xm_lyric, }; } export default build_xiami(); // eslint-disable-line no-unused-vars ================================================ FILE: src/utils.js ================================================ function getParameterByName(name, url) { // if (!url) url = window.location.href; const replacedName = name.replace(/[[\]]/g, '\\$&'); const regex = new RegExp(`[?&]${replacedName}(=([^&#]*)|&|#|$)`); const results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, ' ')); } function getRandomHexString(size) { const result = []; const choice = '012345679abcdef'.split(''); for (let i = 0; i < size; i += 1) { const index = Math.floor(Math.random() * choice.length); result.push(choice[index]); } return result.join(''); } function httpParamEncode(obj) { const str = []; Object.keys(obj).forEach((key) => { str.push(`${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`); }); return str.join('&'); } export default { getParameterByName, getRandomHexString, httpParamEncode }; ================================================ FILE: test/index.spec.js ================================================ /* global describe, it */ import chai from 'chai'; const listen1Api = require('../dist/listen1-api'); chai.expect(); const { expect } = chai; // force load nodejs, because mocha will use chrome setting by default listen1Api.loadNodejsDefaults(); const { HTTPClient, CookieProvider } = listen1Api.platform.nodejs; const globalCookieProvider = new CookieProvider(); function testPlatformShowList(platform) { describe(`Test Listen1 api ${platform}`, () => { describe(`when I need show playlist in ${platform}`, () => { it('should return the playlist info', () => listen1Api.apiGet(`/show_playlist?source=${platform}`, HTTPClient, null, globalCookieProvider).then((data) => { // console.log(data); expect(data).to.be.a('object'); expect(data.result.length).to.be.above(0); const propertyArray = ['cover_img_url', 'title', 'id', 'source_url']; propertyArray.forEach((p) => { expect(data.result[0]).have.property(p); }); })); }); }); } function testPlatformPlaylist(platform) { const platformTestList = { netease: ['neplaylist_762840531', 'neartist_31226', 'nealbum_501208'], qq: ['qqplaylist_4892242121', 'qqartist_003kBQDN4EZbEP', 'qqalbum_004Yw5KE09NoYI'], xiami: ['xmplaylist_377226457', 'xmartist_24182', 'xmalbum_172228'], kugou: ['kgplaylist_585003', 'kgartist_151896', 'kgalbum_1718145'], kuwo: ['kwplaylist_2661217147', 'kwartist_451', 'kwalbum_6865'], bilibili: ['biplaylist_10624', 'biartist_13943828'], }; const listIds = platformTestList[platform]; listIds.forEach((listId) => { describe(`Test Listen1 api ${platform}`, () => { describe(`when I need playlist ${listId} info`, () => { it('should return the playlist', () => listen1Api.apiGet(`/playlist?list_id=${listId}`, HTTPClient, null, globalCookieProvider).then((data) => { // console.log(data); expect(data).to.be.a('object'); expect(data.info).to.be.a('object'); expect(data.tracks).to.be.a('array'); expect(data.tracks.length).to.be.above(0); const propertyArray = ['id', 'title', 'artist', 'artist_id', 'img_url', 'source', 'source_url', 'url']; if (platform !== 'bilibili') { propertyArray.concat(['album', 'album_id']); } propertyArray.forEach((p) => { expect(data.tracks[0]).have.property(p); }); })); }); }); }); } function testSearch(platform) { describe(`Test Listen1 api ${platform}`, () => { describe(`when I search by ${platform}`, () => { it('should return the search result', () => listen1Api.apiGet(`/search?source=${platform}&keywords=123&curpage=1`, HTTPClient, null, globalCookieProvider).then((data) => { // console.log(data); expect(data).to.be.a('object'); expect(data.total).to.be.a('number'); expect(data.result).to.be.a('array'); expect(data.result.length).to.be.above(0); const propertyArray = ['id', 'title', 'artist', 'artist_id', 'album', 'album_id', 'img_url', 'source', 'source_url', 'url']; propertyArray.forEach((p) => { expect(data.result[0]).have.property(p); }); })); }); }); } function testLyric(platform) { const trackIdsMapping = { netease: ['netrack_25642119'], qq: ['qqtrack_004J80Df0WKD7L'], // xiami lyric is from url lyric_url field, skip test it xiami: [], kugou: ['kgtrack_5078BEA97CC7F9F7ED8C8CB1071BB9CF'], kuwo: ['kwtrack_320411'], bilibili: ['bitrack_697129'], }; const trackIds = trackIdsMapping[platform]; trackIds.forEach((trackId) => { describe(`Test Listen1 api ${platform}`, () => { describe(`when I need the ${trackId} lyric`, () => { it('should return the lyric info', () => listen1Api.apiGet(`/lyric?track_id=${trackId}`, HTTPClient, null, globalCookieProvider).then((data) => { // console.log(data); expect(data).to.be.a('object'); expect(data).have.property('lyric'); })); }); }); }); } function testBootstrapTrack(platform) { const trackIdsMapping = { netease: ['netrack_25642119'], qq: ['qqtrack_004J80Df0WKD7L'], xiami: ['xmtrack_1769683699'], kugou: ['kgtrack_5078BEA97CC7F9F7ED8C8CB1071BB9CF'], kuwo: ['kwtrack_320411'], bilibili: ['bitrack_697129'], }; const trackIds = trackIdsMapping[platform]; trackIds.forEach((trackId) => { describe(`Test Listen1 api ${platform}`, () => { describe(`when I need the ${trackId} info`, () => { it('should return the track info', () => listen1Api.apiGet(`/bootstrap_track?track_id=${trackId}`, HTTPClient, null, globalCookieProvider).then((data) => { // console.log(data); expect(data).to.be.a('object'); expect(data).have.property('url'); })); }); }); }); } const platformList = ['netease', 'qq', 'xiami', 'kugou', 'kuwo', 'bilibili']; platformList.forEach((name) => { testPlatformShowList(name); testPlatformPlaylist(name); if (name !== 'bilibili') { testSearch(name); } testBootstrapTrack(name); if (name !== 'xiami' && name !== 'kuwo' && name !== 'bilibili') { testLyric(name); } }); ================================================ FILE: test/mocha.opts ================================================ -r jsdom-global/register ================================================ FILE: webpack.config.js ================================================ /* global __dirname, require, module */ // eslint-disable-next-line no-unused-vars const webpack = require('webpack'); const path = require('path'); const { env } = require('yargs').argv; // use --env with webpack 2 const pkg = require('./package.json'); const libraryName = pkg.name; let outputFile; let mode; if (env === 'build') { mode = 'production'; outputFile = `${libraryName}.min.js`; } else { mode = 'development'; outputFile = `${libraryName}.js`; } const config = { mode, entry: `${__dirname}/src/index.js`, // open devtool only when debug // it will increase lib size // devtool: 'inline-source-map', output: { path: `${__dirname}/dist`, filename: outputFile, // library: libraryName, library: 'listen1Api', libraryTarget: 'umd', umdNamedDefine: true, globalObject: "typeof self !== 'undefined' ? self : this", }, module: { rules: [ { test: /(\.jsx|\.js)$/, loader: 'babel-loader', exclude: /(node_modules|bower_components)/, }, { test: /(\.jsx|\.js)$/, loader: 'eslint-loader', exclude: /node_modules|src/, }, ], }, resolve: { modules: [path.resolve('./node_modules'), path.resolve('./src')], extensions: ['.json', '.js'], }, node: { fs: 'empty', net: 'empty', tls: 'empty', }, externals: { // request not working with browser, so we exclude it from bundle request: 'request', electron: 'electron', }, }; module.exports = config;