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;