├── .gitignore
├── screen.png
├── src
├── img
│ ├── blue.png
│ ├── green.png
│ ├── icon.png
│ └── yellow.png
├── js
│ ├── log.js
│ ├── options.js
│ ├── yandex_api.js
│ ├── background.js
│ ├── downloader.js
│ └── utils.js
├── manifest.json
└── html
│ └── options.html
├── readme.md
└── song.svg
/.gitignore:
--------------------------------------------------------------------------------
1 | nbproject/
2 |
--------------------------------------------------------------------------------
/screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepkagr/yandex-music-fisher/HEAD/screen.png
--------------------------------------------------------------------------------
/src/img/blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepkagr/yandex-music-fisher/HEAD/src/img/blue.png
--------------------------------------------------------------------------------
/src/img/green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepkagr/yandex-music-fisher/HEAD/src/img/green.png
--------------------------------------------------------------------------------
/src/img/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepkagr/yandex-music-fisher/HEAD/src/img/icon.png
--------------------------------------------------------------------------------
/src/img/yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stepkagr/yandex-music-fisher/HEAD/src/img/yellow.png
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | #Yandex Music Fisher (0.1.8)
2 |
3 | Расширение Google Chrome для загрузки музыки с сервиса [Яндекс.Музыка](http://music.yandex.ru/).
4 |
5 | 
6 |
7 | ### Подключение
8 |
9 | [Скачайте](https://github.com/egoroof/yandex-music-fisher/archive/master.zip),
10 | перенесите папку __src__ на страницу расширений.
11 |
12 | ### Как пользоваться
13 |
14 | Открыть страницу на [Яндекс.Музыка](http://music.yandex.ru/) с нужным  треком,
15 |  альбомом или  плейлистом, нажать на появившуюся иконку.
16 | Далее пойдёт автоматический процесс скачивания. Можно покинуть сайт Яндекс Музыки.
17 |
--------------------------------------------------------------------------------
/src/js/log.js:
--------------------------------------------------------------------------------
1 | var log = {
2 | string: ''
3 | };
4 |
5 | log.addMessage = function (msg) {
6 | log.string += msg + "\r\n";
7 | };
8 |
9 | log.download = function () {
10 | log.addMessage(new Date());
11 | chrome.downloads.download({
12 | url: 'data:text/plain;charset=utf-8,' + encodeURIComponent(log.string),
13 | filename: 'log.txt'
14 | });
15 | };
16 |
17 | chrome.runtime.getPlatformInfo(function (platformInfo) {
18 | log.addMessage('Yandex Music Fisher ' + chrome.runtime.getManifest().version);
19 | log.addMessage(new Date());
20 | log.addMessage('Operating system: ' + platformInfo.os);
21 | log.addMessage('Architecture: ' + platformInfo.arch);
22 | });
23 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "Yandex Music Fisher",
4 | "description": "Скачивание музыки с сервиса Яндекс.Музыка",
5 | "version": "0.1.8",
6 | "permissions": [
7 | "tabs",
8 | "downloads",
9 | "https://music.yandex.ru/"
10 | ],
11 | "minimum_chrome_version": "31.0",
12 | "background": {
13 | "scripts": ["js/log.js", "js/utils.js", "js/yandex_api.js", "js/downloader.js", "js/background.js"],
14 | "persistent": true
15 | },
16 | "options_page": "html/options.html",
17 | "page_action": {
18 | "default_title": "Yandex Music Fisher"
19 | },
20 | "icons": {
21 | "16": "img/icon.png",
22 | "32": "img/icon.png",
23 | "48": "img/icon.png",
24 | "128": "img/icon.png"
25 | },
26 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
27 | }
28 |
--------------------------------------------------------------------------------
/src/js/options.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('DOMContentLoaded', function () {
2 | var threadCount = localStorage.getItem('downloadThreadCount');
3 | if (threadCount) {
4 | document.getElementById('downloadThreadCount').value = threadCount;
5 | }
6 | var coverSize = localStorage.getItem('albumCoverSize');
7 | if (coverSize) {
8 | document.getElementById('albumCoverSize').value = coverSize;
9 | }
10 | });
11 |
12 | document.getElementById('downloadThreadCount').addEventListener('change', function (e) {
13 | if (e.target.value > 10) {
14 | e.target.value = 10;
15 | } else if (e.target.value < 1) {
16 | e.target.value = 1;
17 | }
18 | localStorage.setItem('downloadThreadCount', e.target.value);
19 | });
20 |
21 | document.getElementById('albumCoverSize').addEventListener('change', function (e) {
22 | localStorage.setItem('albumCoverSize', e.target.value);
23 | });
24 |
25 | document.getElementById('btn-log').addEventListener('click', function (e) {
26 | e.preventDefault();
27 | chrome.runtime.getBackgroundPage(function (backgroundPage) {
28 | backgroundPage.log.download();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/html/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Yandex Music Fisher
5 |
6 |
7 |
8 |
9 |
30 |
31 | Скачать файл с отладочной информацией
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/song.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/js/yandex_api.js:
--------------------------------------------------------------------------------
1 | var yandex = {};
2 |
3 | yandex.getTrackLinks = function (storageDir, success, fail) {
4 | var url = '/api/v1.4/jsonp.xml?action=getTrackSrc&p=download-info/'
5 | + storageDir + '/2.mp3&r=' + Math.random();
6 | utils.ajax(url, function (jsonp) {
7 | // чистим jsonp до json
8 | var jsonStr = jsonp.replace(/^[^[]+/, '').replace(/\);$/, '');
9 | try {
10 | // json кривой, поэтому извлекаем через eval
11 | var json = eval(jsonStr)[0];
12 | } catch (e) {
13 | var message = 'Не удалось распарсить строку' + jsonp;
14 | console.error(message);
15 | log.addMessage(message);
16 | fail();
17 | return;
18 | }
19 | var hosts = json['regional-hosts'];
20 | hosts.push(json.host);
21 |
22 | var md5 = utils.md5('XGRlBW9FXlekgbPrRHuSiA' + json.path.substr(1) + json.s);
23 | var urlBody = '/get-mp3/' + md5 + '/' + json.ts + json.path;
24 | var links = hosts.map(function (host) {
25 | return 'http://' + host + urlBody;
26 | });
27 | success(links);
28 | }, fail);
29 | };
30 |
31 | yandex.getTrack = function (trackId, success, fail) {
32 | var url = '/handlers/track.jsx?track=' + trackId
33 | + '&r=' + Math.random();
34 | utils.ajax(url, success, fail);
35 | };
36 |
37 | yandex.getAlbum = function (albumId, success, fail) {
38 | var url = '/handlers/album.jsx?album=' + albumId
39 | + '&r=' + Math.random();
40 | utils.ajax(url, success, fail);
41 | };
42 |
43 | yandex.getPlaylist = function (username, playlistId, success, fail) {
44 | var url = '/handlers/playlist.jsx?owner=' + username
45 | + '&kinds=' + playlistId
46 | + '&r=' + Math.random();
47 | utils.ajax(url, function (json) {
48 | success(json.playlist);
49 | }, fail);
50 | };
51 |
--------------------------------------------------------------------------------
/src/js/background.js:
--------------------------------------------------------------------------------
1 | // todo: предварительная загрузка страницы может не вызвать это событие
2 | chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
3 | if (changeInfo.status === 'loading') {
4 | utils.addIconToTab(tab);
5 | }
6 | });
7 |
8 | chrome.pageAction.onClicked.addListener(function (tab) {
9 | chrome.pageAction.hide(tab.id);
10 | var page = utils.getUrlInfo(tab.url);
11 | if (page.isPlaylist) {
12 | yandex.getPlaylist(page.username, page.playlistId, downloader.downloadPlaylist, function () {
13 | // ajax transport fail
14 | utils.addIconToTab(tab);
15 | });
16 | } else if (page.isTrack) {
17 | yandex.getTrack(page.trackId, function (track) {
18 | downloader.add([track]);
19 | }, function () {
20 | // ajax transport fail
21 | utils.addIconToTab(tab);
22 | });
23 | } else if (page.isAlbum) {
24 | yandex.getAlbum(page.albumId, downloader.downloadAlbum, function () {
25 | // ajax transport fail
26 | utils.addIconToTab(tab);
27 | });
28 | }
29 | });
30 |
31 | chrome.downloads.onChanged.addListener(function (delta) {
32 | chrome.downloads.search({
33 | id: delta.id
34 | }, function (downloads) {
35 | var parts = downloads[0].filename.split('.');
36 | var name = downloads[0].byExtensionName;
37 | if (!name || name !== 'Yandex Music Fisher' || parts[parts.length - 1] !== 'mp3') {
38 | return;
39 | }
40 | if (delta.state) {
41 | switch (delta.state.current) {
42 | case 'complete':
43 | case 'interrupted':
44 | downloader.activeThreadCount--;
45 | if (!downloader.queue.length && !downloader.activeThreadCount) {
46 | chrome.downloads.show(delta.id);
47 | }
48 | chrome.downloads.erase({
49 | id: delta.id
50 | });
51 | downloader.download();
52 | break;
53 | }
54 | } else if (delta.paused) {
55 | if (delta.paused.current) {
56 | console.info('Приостановленна загрузка', delta);
57 | } else {
58 | console.info('Возобновленна загрузка', delta);
59 | }
60 | }
61 | });
62 | });
63 |
64 | chrome.runtime.onInstalled.addListener(function (details) {
65 | // todo: перейти с localStorage на chrome.storage
66 | if (!localStorage.getItem('downloadThreadCount')) {
67 | localStorage.setItem('downloadThreadCount', 4);
68 | }
69 | if (!localStorage.getItem('albumCoverSize')) {
70 | localStorage.setItem('albumCoverSize', '460x460');
71 | }
72 | chrome.tabs.query({
73 | url: '*://music.yandex.ru/*'
74 | }, function (tabs) {
75 | for (var i = 0; i < tabs.length; i++) {
76 | utils.addIconToTab(tabs[i]);
77 | }
78 | });
79 | });
80 |
--------------------------------------------------------------------------------
/src/js/downloader.js:
--------------------------------------------------------------------------------
1 | var downloader = {
2 | queue: [],
3 | activeThreadCount: 0
4 | };
5 |
6 | downloader.clearPath = function (path) {
7 | return path.replace(/[\\/:*?"<>|]/g, '_'); // Windows path illegals
8 | };
9 |
10 | downloader.getPrefix = function (i, max) {
11 | var prefix = '';
12 | max = max.toString();
13 | switch (max.length) {
14 | case 2:
15 | prefix = (i < 10) ? '0' + i : i;
16 | break;
17 | case 3:
18 | prefix = (i < 10) ? '00' + i : ((i < 100) ? '0' + i : i);
19 | break;
20 | case 4:
21 | // надеюсь 9999 хватит
22 | prefix = (i < 10) ? '000' + i : ((i < 100) ? '00' + i : ((i < 1000) ? '0' + i : i));
23 | break;
24 | default:
25 | prefix = i;
26 | }
27 | return prefix + ' - ';
28 | };
29 |
30 | downloader.download = function () {
31 | var track = downloader.queue.shift();
32 | if (!track) {
33 | return;
34 | }
35 | if (track.error) {
36 | var message = 'Ошибка: ' + track.error;
37 | console.error(message, track);
38 | log.addMessage(message);
39 | downloader.download();
40 | return;
41 | }
42 | downloader.activeThreadCount++;
43 | var artists = track.artists.map(function (artist) {
44 | return artist.name;
45 | }).join(', ');
46 | if (track.version) {
47 | track.title += ' (' + track.version + ')';
48 | }
49 | var savePath = downloader.clearPath(artists + ' - ' + track.title + '.mp3');
50 | if (track.namePrefix) {
51 | savePath = track.namePrefix + savePath;
52 | }
53 | if (track.saveDir) {
54 | savePath = track.saveDir + '/' + savePath;
55 | }
56 | yandex.getTrackLinks(track.storageDir, function (links) {
57 | if (links.length) {
58 | chrome.downloads.download({
59 | url: links[0],
60 | filename: savePath,
61 | saveAs: false,
62 | conflictAction: 'prompt'
63 | });
64 | } else {
65 | var message = 'Не удалось найти ссылки';
66 | console.error(message, track);
67 | log.addMessage(message);
68 | downloader.activeThreadCount--;
69 | downloader.download();
70 | }
71 | }, function () {
72 | // ajax transport fail или json не распарсили
73 | downloader.activeThreadCount--;
74 | downloader.download();
75 | });
76 | };
77 |
78 | downloader.add = function (tracks) {
79 | // todo: сделать страницу с обзором закачек
80 | downloader.queue = downloader.queue.concat(tracks);
81 | var newThreadCount = localStorage.getItem('downloadThreadCount') - downloader.activeThreadCount;
82 | for (var i = 0; i < newThreadCount; i++) {
83 | downloader.download();
84 | }
85 | };
86 |
87 | downloader.downloadAlbum = function (album) {
88 | var tracks = [];
89 | var artists = album.artists.map(function (artist) {
90 | return artist.name;
91 | }).join(', ');
92 | if (album.version) {
93 | album.title += ' (' + album.version + ')';
94 | }
95 | var saveDir = downloader.clearPath(artists + ' - ' + album.title);
96 | if (album.volumes.length > 1) {
97 | for (var i = 0; i < album.volumes.length; i++) {
98 | album.volumes[i].forEach(function (track, j) {
99 | track.saveDir = saveDir + '/CD' + (i + 1);
100 | track.namePrefix = downloader.getPrefix(j + 1, album.volumes[i].length);
101 | });
102 | tracks = tracks.concat(album.volumes[i]);
103 | }
104 | } else {
105 | album.volumes[0].forEach(function (track, i) {
106 | track.saveDir = saveDir;
107 | track.namePrefix = downloader.getPrefix(i + 1, album.volumes[0].length);
108 | });
109 | tracks = album.volumes[0];
110 | }
111 | downloader.add(tracks);
112 | chrome.downloads.download({
113 | url: 'https://' + album.coverUri.replace('%%', localStorage.getItem('albumCoverSize')),
114 | filename: saveDir + '/cover.jpg',
115 | saveAs: false,
116 | conflictAction: 'prompt'
117 | });
118 | };
119 |
120 | downloader.downloadPlaylist = function (playlist) {
121 | playlist.tracks.forEach(function (track, i) {
122 | track.saveDir = downloader.clearPath(playlist.title);
123 | track.namePrefix = downloader.getPrefix(i + 1, playlist.tracks.length);
124 | });
125 | downloader.add(playlist.tracks);
126 | };
127 |
--------------------------------------------------------------------------------
/src/js/utils.js:
--------------------------------------------------------------------------------
1 | var utils = {};
2 |
3 | utils.ajax = function (url, success, fail) {
4 | var xhr = new XMLHttpRequest();
5 | xhr.open('GET', 'https://music.yandex.ru' + url, true);
6 | xhr.timeout = 10000;
7 | xhr.onload = function () {
8 | if (xhr.status === 200) {
9 | if (xhr.responseXML) {
10 | success(xhr.responseXML); // xml
11 | } else {
12 | var reply;
13 | try {
14 | reply = JSON.parse(xhr.responseText); // json
15 | } catch (e) {
16 | reply = xhr.responseText; // plain text
17 | }
18 | success(reply);
19 | }
20 | } else {
21 | var message = 'HTTP error code: ' + xhr.status;
22 | console.error(message);
23 | log.addMessage(message);
24 | fail();
25 | }
26 | };
27 | xhr.onerror = function () {
28 | var message = 'AJAX error: ' + url;
29 | console.error(message);
30 | log.addMessage(message);
31 | fail();
32 | };
33 | xhr.ontimeout = function () {
34 | var message = 'AJAX timeout: ' + url;
35 | console.error(message);
36 | log.addMessage(message);
37 | fail();
38 | };
39 | xhr.send();
40 | };
41 |
42 | utils.getUrlInfo = function (url) {
43 | var info = {};
44 | var parts = url.replace(/\?.*/, '').split('/');
45 | //["http:", "", "music.yandex.ru", "users", "furfurmusic", "playlists", "1000"]
46 | info.isYandexMusic = (parts[2] === 'music.yandex.ru');
47 | if (!info.isYandexMusic) {
48 | return info;
49 | }
50 | info.isPlaylist = (parts[3] === 'users' && parts[5] === 'playlists' && !!parts[6]);
51 | info.isTrack = (parts[3] === 'album' && parts[5] === 'track' && !!parts[6]);
52 | info.isAlbum = (parts[3] === 'album' && !!parts[4]);
53 | if (info.isPlaylist) {
54 | info.username = parts[4];
55 | info.playlistId = parts[6];
56 | } else if (info.isTrack) {
57 | info.trackId = parts[6];
58 | } else if (info.isAlbum) {
59 | info.albumId = parts[4];
60 | }
61 | return info;
62 | };
63 |
64 | utils.addIconToTab = function (tab) {
65 | chrome.pageAction.hide(tab.id);
66 | var page = utils.getUrlInfo(tab.url);
67 | if (!page.isYandexMusic) {
68 | return;
69 | } else if (page.isPlaylist) {
70 | chrome.pageAction.setIcon({
71 | tabId: tab.id,
72 | path: 'img/green.png'
73 | });
74 | chrome.pageAction.show(tab.id);
75 | } else if (page.isTrack) {
76 | chrome.pageAction.setIcon({
77 | tabId: tab.id,
78 | path: 'img/blue.png'
79 | });
80 | chrome.pageAction.show(tab.id);
81 | } else if (page.isAlbum) {
82 | chrome.pageAction.setIcon({
83 | tabId: tab.id,
84 | path: 'img/yellow.png'
85 | });
86 | chrome.pageAction.show(tab.id);
87 | }
88 | };
89 |
90 | // источник: http://jquerymy.com/js/md5.js
91 | utils.md5 = (function () {
92 | function e(e, t) {
93 | var o = e[0], u = e[1], a = e[2], f = e[3];
94 | o = n(o, u, a, f, t[0], 7, -680876936);
95 | f = n(f, o, u, a, t[1], 12, -389564586);
96 | a = n(a, f, o, u, t[2], 17, 606105819);
97 | u = n(u, a, f, o, t[3], 22, -1044525330);
98 | o = n(o, u, a, f, t[4], 7, -176418897);
99 | f = n(f, o, u, a, t[5], 12, 1200080426);
100 | a = n(a, f, o, u, t[6], 17, -1473231341);
101 | u = n(u, a, f, o, t[7], 22, -45705983);
102 | o = n(o, u, a, f, t[8], 7, 1770035416);
103 | f = n(f, o, u, a, t[9], 12, -1958414417);
104 | a = n(a, f, o, u, t[10], 17, -42063);
105 | u = n(u, a, f, o, t[11], 22, -1990404162);
106 | o = n(o, u, a, f, t[12], 7, 1804603682);
107 | f = n(f, o, u, a, t[13], 12, -40341101);
108 | a = n(a, f, o, u, t[14], 17, -1502002290);
109 | u = n(u, a, f, o, t[15], 22, 1236535329);
110 | o = r(o, u, a, f, t[1], 5, -165796510);
111 | f = r(f, o, u, a, t[6], 9, -1069501632);
112 | a = r(a, f, o, u, t[11], 14, 643717713);
113 | u = r(u, a, f, o, t[0], 20, -373897302);
114 | o = r(o, u, a, f, t[5], 5, -701558691);
115 | f = r(f, o, u, a, t[10], 9, 38016083);
116 | a = r(a, f, o, u, t[15], 14, -660478335);
117 | u = r(u, a, f, o, t[4], 20, -405537848);
118 | o = r(o, u, a, f, t[9], 5, 568446438);
119 | f = r(f, o, u, a, t[14], 9, -1019803690);
120 | a = r(a, f, o, u, t[3], 14, -187363961);
121 | u = r(u, a, f, o, t[8], 20, 1163531501);
122 | o = r(o, u, a, f, t[13], 5, -1444681467);
123 | f = r(f, o, u, a, t[2], 9, -51403784);
124 | a = r(a, f, o, u, t[7], 14, 1735328473);
125 | u = r(u, a, f, o, t[12], 20, -1926607734);
126 | o = i(o, u, a, f, t[5], 4, -378558);
127 | f = i(f, o, u, a, t[8], 11, -2022574463);
128 | a = i(a, f, o, u, t[11], 16, 1839030562);
129 | u = i(u, a, f, o, t[14], 23, -35309556);
130 | o = i(o, u, a, f, t[1], 4, -1530992060);
131 | f = i(f, o, u, a, t[4], 11, 1272893353);
132 | a = i(a, f, o, u, t[7], 16, -155497632);
133 | u = i(u, a, f, o, t[10], 23, -1094730640);
134 | o = i(o, u, a, f, t[13], 4, 681279174);
135 | f = i(f, o, u, a, t[0], 11, -358537222);
136 | a = i(a, f, o, u, t[3], 16, -722521979);
137 | u = i(u, a, f, o, t[6], 23, 76029189);
138 | o = i(o, u, a, f, t[9], 4, -640364487);
139 | f = i(f, o, u, a, t[12], 11, -421815835);
140 | a = i(a, f, o, u, t[15], 16, 530742520);
141 | u = i(u, a, f, o, t[2], 23, -995338651);
142 | o = s(o, u, a, f, t[0], 6, -198630844);
143 | f = s(f, o, u, a, t[7], 10, 1126891415);
144 | a = s(a, f, o, u, t[14], 15, -1416354905);
145 | u = s(u, a, f, o, t[5], 21, -57434055);
146 | o = s(o, u, a, f, t[12], 6, 1700485571);
147 | f = s(f, o, u, a, t[3], 10, -1894986606);
148 | a = s(a, f, o, u, t[10], 15, -1051523);
149 | u = s(u, a, f, o, t[1], 21, -2054922799);
150 | o = s(o, u, a, f, t[8], 6, 1873313359);
151 | f = s(f, o, u, a, t[15], 10, -30611744);
152 | a = s(a, f, o, u, t[6], 15, -1560198380);
153 | u = s(u, a, f, o, t[13], 21, 1309151649);
154 | o = s(o, u, a, f, t[4], 6, -145523070);
155 | f = s(f, o, u, a, t[11], 10, -1120210379);
156 | a = s(a, f, o, u, t[2], 15, 718787259);
157 | u = s(u, a, f, o, t[9], 21, -343485551);
158 | e[0] = m(o, e[0]);
159 | e[1] = m(u, e[1]);
160 | e[2] = m(a, e[2]);
161 | e[3] = m(f, e[3]);
162 | }
163 | function t(e, t, n, r, i, s) {
164 | t = m(m(t, e), m(r, s));
165 | return m(t << i | t >>> 32 - i, n);
166 | }
167 | function n(e, n, r, i, s, o, u) {
168 | return t(n & r | ~n & i, e, n, s, o, u);
169 | }
170 | function r(e, n, r, i, s, o, u) {
171 | return t(n & i | r & ~i, e, n, s, o, u);
172 | }
173 | function i(e, n, r, i, s, o, u) {
174 | return t(n ^ r ^ i, e, n, s, o, u);
175 | }
176 | function s(e, n, r, i, s, o, u) {
177 | return t(r ^ (n | ~i), e, n, s, o, u);
178 | }
179 | function o(t) {
180 | var n = t.length, r = [1732584193, -271733879, -1732584194, 271733878], i;
181 | for (i = 64; i <= t.length; i += 64) {
182 | e(r, u(t.substring(i - 64, i)));
183 | }
184 | t = t.substring(i - 64);
185 | var s = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
186 | for (i = 0; i < t.length; i++)
187 | s[i >> 2] |= t.charCodeAt(i) << (i % 4 << 3);
188 | s[i >> 2] |= 128 << (i % 4 << 3);
189 | if (i > 55) {
190 | e(r, s);
191 | for (i = 0; i < 16; i++)
192 | s[i] = 0;
193 | }
194 | s[14] = n * 8;
195 | e(r, s);
196 | return r;
197 | }
198 | function u(e) {
199 | var t = [], n;
200 | for (n = 0; n < 64; n += 4) {
201 | t[n >> 2] = e.charCodeAt(n) + (e.charCodeAt(n + 1) << 8) + (e.charCodeAt(n + 2) << 16) + (e.charCodeAt(n + 3) << 24);
202 | }
203 | return t;
204 | }
205 | function c(e) {
206 | var t = "", n = 0;
207 | for (; n < 4; n++)
208 | t += a[e >> n * 8 + 4 & 15] + a[e >> n * 8 & 15];
209 | return t;
210 | }
211 | function h(e) {
212 | for (var t = 0; t < e.length; t++)
213 | e[t] = c(e[t]);
214 | return e.join("");
215 | }
216 | function d(e) {
217 | return h(o(unescape(encodeURIComponent(e))));
218 | }
219 | function m(e, t) {
220 | return e + t & 4294967295;
221 | }
222 | var a = "0123456789abcdef".split("");
223 | return d;
224 | })();
--------------------------------------------------------------------------------