diff --git a/css/player.css b/css/player.css
index ee7f93c..7ba9ecb 100644
--- a/css/player.css
+++ b/css/player.css
@@ -496,7 +496,7 @@ em, i {
     _bottom: 90px;
     right: 0px;
     _right: 0px;
-    height: 330px;
+    height: 349px;
     width: 100%;
     background-color: #121212;
 }
@@ -504,7 +504,7 @@ em, i {
 .m-playbar .menu ul {
     display: inline-block;
     padding-left: 0px;
-    height: 290px;
+    height: 308px;
     overflow-y: scroll;
     width: 60%;
     margin-bottom: 0px;
@@ -520,7 +520,7 @@ em, i {
     text-align: center;
     width: 39%;
     display: inline-block;
-    height: 290px;
+    height: 308px;
     overflow-y: scroll;
     position: relative;
 }
diff --git a/js/app.js b/js/app.js
index 3262634..57f3340 100644
--- a/js/app.js
+++ b/js/app.js
@@ -37,6 +37,18 @@
     };
   });
 
+  function getSourceName(sourceId) {
+    if (sourceId == 0) {
+      return 'netease';
+    }
+    if (sourceId == 1) {
+      return 'xiami';
+    }
+    if (sourceId == 2) {
+      return 'qq';
+    }
+  }
+
   // control main view of page, it can be called any place
   app.controller('NavigationController', ['$scope', '$http',
     '$httpParamSerializerJQLike', '$timeout',
@@ -389,7 +401,17 @@
         if (angularPlayer.getRepeatStatus() == false) {
             angularPlayer.repeatToggle();
         }
-        $timeout(function(){angularPlayer.setBootstrapTrack(loWeb.bootstrapTrack);}, 0);
+        $timeout(
+          function(){
+            angularPlayer.setBootstrapTrack(
+              loWeb.bootstrapTrack(
+                function(){}, 
+                function(){
+                  Notification.info('版权原因无法播放,请搜索其他平台');
+                })
+            );
+          }, 0
+        );
 
         if (track_id == -1) {
           return;
@@ -535,7 +557,10 @@
             var content = line.replace(/\[(\d{2,})\:(\d{2})(?:\.(\d{1,3}))?\]/g, '');
             var min = parseInt(timeRegResult[1]);
             var sec = parseInt(timeRegResult[2]);
-            var microsec = parseInt(rightPadding(timeRegResult[3], 3, '0'));
+            var microsec = 0;
+            if (timeRegResult[3] != null) {
+              microsec = parseInt(rightPadding(timeRegResult[3], 3, '0'));
+            }
             var seconds = min * 60 * 1000 + sec*1000 + microsec;
             var lyricObject = {};
             lyricObject.content = content;
@@ -618,7 +643,7 @@
       $scope.changeTab = function(newTab){
         $scope.tab = newTab;
         $scope.result = [];
-        loWeb.get('/search?source='+ $scope.tab + '&keywords=' + $scope.keywords).success(function(data) {
+        loWeb.get('/search?source=' + getSourceName($scope.tab) + '&keywords=' + $scope.keywords).success(function(data) {
             // update the textarea
             $scope.result = data.result; 
         });
@@ -637,7 +662,7 @@
         // go ahead and retrieve the data
         if (tmpStr === $scope.keywords)
         {
-          loWeb.get('/search?source='+ $scope.tab + '&keywords=' + $scope.keywords).success(function(data) {
+          loWeb.get('/search?source=' + getSourceName($scope.tab) + '&keywords=' + $scope.keywords).success(function(data) {
             // update the textarea
             $scope.result = data.result; 
           });
@@ -848,7 +873,7 @@
       $scope.tab = newTab;
       $scope.result = [];
 
-      loWeb.get('/show_playlist?source=' + $scope.tab).success(function(data) {
+      loWeb.get('/show_playlist?source=' + getSourceName($scope.tab)).success(function(data) {
         $scope.result = data.result;
       });
 
@@ -863,7 +888,7 @@
 
 
     $scope.loadPlaylist = function(){
-      loWeb.get('/show_playlist?source=' + $scope.tab).success(function(data) {
+      loWeb.get('/show_playlist?source=' + getSourceName($scope.tab)).success(function(data) {
         $scope.result = data.result;
       });
     };
diff --git a/js/loweb.js b/js/loweb.js
index 5db8bef..e9e2e23 100644
--- a/js/loweb.js
+++ b/js/loweb.js
@@ -1,100 +1,87 @@
 var ngloWebManager = angular.module('loWebManager', []);
 
+function getProviderByName(sourceName) {
+    if (sourceName == 'netease') {
+        return netease;
+    }
+    if (sourceName == 'xiami') {
+        return xiami;
+    }
+    if (sourceName == 'qq') {
+        return qq;
+    }
+}
+
+function getProviderByItemId(itemId) {
+    var prefix = itemId.slice(0,2);
+    if (prefix == 'ne') {
+        return netease;
+    }
+    if (prefix == 'xm') {
+        return xiami;
+    }
+    if (prefix == 'qq') {
+        return qq;
+    }
+}
+
 ngloWebManager.factory('loWeb', ['$rootScope', '$log', '$http', '$httpParamSerializerJQLike',
 function($rootScope, $log, $http, $httpParamSerializerJQLike) {
-    var response = null;
     return {
         get: function(url) {
             if (url.search('/show_playlist') != -1) {
                 var source = getParameterByName('source', url);
-                if (source == '0') {
-                    return ne_show_playlist(url, $http);
-                }
-                if (source == '1') {
-                    return xm_show_playlist(url, $http);
-                }
-                if (source == '2') {
-                    return qq_show_playlist(url, $http);
-                }
+                var provider = getProviderByName(source);
+                return provider.show_playlist(url, $http);
             }
             if (url.search('/playlist') != -1) {
                 var list_id = getParameterByName('list_id', url);
-                if (list_id.search('xmplaylist')!= -1) {
-                    return xm_get_playlist(url, $http);
-                }
-                if (list_id.search('neplaylist')!= -1) {
-                    return ne_get_playlist(url, $http);
-                }
-                 if (list_id.search('qqplaylist')!= -1) {
-                    return qq_get_playlist(url, $http);
-                }
-
+                var provider = getProviderByItemId(list_id);
+                return provider.get_playlist(url, $http);
             }
             if (url.search('/search') != -1) {
                 var source = getParameterByName('source', url);
-                if (source == '0') {
-                    return ne_search(url, $http, $httpParamSerializerJQLike);
-                }
-                if (source == '1') {
-                    return xm_search(url, $http, $httpParamSerializerJQLike);
-                }
-                if (source == '2') {
-                    return qq_search(url, $http, $httpParamSerializerJQLike);
-                }
+                var provider = getProviderByName(source);
+                return provider.search(url, $http, $httpParamSerializerJQLike);
             }
             if (url.search('/album') != -1) {
                 var album_id = getParameterByName('album_id', url);
-                if (album_id.search('xmalbum')!= -1) {
-                    return xm_album(url, $http, $httpParamSerializerJQLike);
-                }
-                if (album_id.search('nealbum')!= -1) {
-                    return ne_album(url, $http, $httpParamSerializerJQLike);
-                }
-                if (album_id.search('qqalbum')!= -1) {
-                    return qq_album(url, $http, $httpParamSerializerJQLike);
-                }
+                var provider = getProviderByItemId(album_id);
+                return provider.album(url, $http, $httpParamSerializerJQLike);
             }
             if (url.search('/artist') != -1) {
                 var artist_id = getParameterByName('artist_id', url);
-                if (artist_id.search('xmartist')!= -1) {
-                    return xm_artist(url, $http, $httpParamSerializerJQLike);
-                }
-                if (artist_id.search('neartist')!= -1) {
-                    return ne_artist(url, $http, $httpParamSerializerJQLike);
-                }
-                if (artist_id.search('qqartist')!= -1) {
-                    return qq_artist(url, $http, $httpParamSerializerJQLike);
-                }
+                var provider = getProviderByItemId(artist_id);
+                return provider.artist(url, $http, $httpParamSerializerJQLike);
             }
             if (url.search('/lyric') != -1) {
                 var track_id = getParameterByName('track_id', url);
-                if (track_id.search('xmtrack')!= -1) {
-                    return xm_lyric(url, $http, $httpParamSerializerJQLike);
-                }
-                if (track_id.search('netrack')!= -1) {
-                    return ne_lyric(url, $http, $httpParamSerializerJQLike);
-                }
-                if (track_id.search('qqtrack')!= -1) {
-                    return qq_lyric(url, $http, $httpParamSerializerJQLike);
-                }
+                var provider = getProviderByItemId(track_id);
+                return provider.lyric(url, $http, $httpParamSerializerJQLike);
             }
 
         },
-        bootstrapTrack: function(sound, track, callback){
-            // always refresh url, becaues url will expires
-            // if (sound.url.search('http') != -1){
-            //     callback();
-            //     return;
-            // }
-            if(track.source == 'xiami') {
-                xm_bootstrap_track(sound, track, callback, $http, $httpParamSerializerJQLike);
-            }
-            if(track.source == 'netease') {
-                ne_bootstrap_track(sound, track, callback, $http, $httpParamSerializerJQLike);
-            }
-            if(track.source == 'qq') {
-                qq_bootstrap_track(sound, track, callback, $http, $httpParamSerializerJQLike);
-            }
+        bootstrapTrack: function(success, failure) {
+            return function(sound, track, callback){
+                // always refresh url, becaues url will expires
+                // if (sound.url.search('http') != -1){
+                //     callback();
+                //     return;
+                // }
+                function successCallback() {
+                    callback();
+                    success();
+                }
+
+                function failureCallback() {
+                    failure();
+                }
+                var source = track.source;
+                var provider = getProviderByName(source);
+
+                provider.bootstrap_track(sound, track, successCallback, failureCallback, $http, $httpParamSerializerJQLike);
+
+            };
         }
     };
 }]);
diff --git a/js/netease.js b/js/netease.js
index ba28a13..f2747fe 100644
--- a/js/netease.js
+++ b/js/netease.js
@@ -1,348 +1,370 @@
-function ne_show_playlist(url, hm) {
-    var target_url = 'http://music.163.com/discover/playlist/';
+var netease = (function() {
+    'use strict';
 
-    return {
-        success: function(fn) {
-            var result = [];
-            hm.get(target_url).success(function(data) {
-                data = $.parseHTML(data);
-                $(data).find('.m-cvrlst li').each(function(){
-                    var default_playlist = {
-                        'cover_img_url' : '',
-                        'title': '',
-                        'list_id': '',
-                        'source_url': ''
-                    };
-                    default_playlist.cover_img_url = $(this).find('img')[0].src;
-                    default_playlist.title = $(this).find('div a')[0].title;
-                    var url = $(this).find('div a')[0].href;
-                    var list_id = getParameterByName('id',url);
-                    default_playlist.list_id = 'neplaylist_' + list_id;
-                    default_playlist.source_url = 'http://music.163.com/#/playlist?id=' + list_id;
-                    result.push(default_playlist);
+    var ne_show_playlist = function(url, hm) {
+        var target_url = 'http://music.163.com/discover/playlist/';
+
+        return {
+            success: function(fn) {
+                var result = [];
+                hm.get(target_url).success(function(data) {
+                    data = $.parseHTML(data);
+                    $(data).find('.m-cvrlst li').each(function(){
+                        var default_playlist = {
+                            'cover_img_url' : '',
+                            'title': '',
+                            'list_id': '',
+                            'source_url': ''
+                        };
+                        default_playlist.cover_img_url = $(this).find('img')[0].src;
+                        default_playlist.title = $(this).find('div a')[0].title;
+                        var url = $(this).find('div a')[0].href;
+                        var list_id = getParameterByName('id',url);
+                        default_playlist.list_id = 'neplaylist_' + list_id;
+                        default_playlist.source_url = 'http://music.163.com/#/playlist?id=' + list_id;
+                        result.push(default_playlist);
+                    });
+                    return fn({"result":result});
                 });
-                return fn({"result":result});
-            });
-        }
-    };
-}
+            }
+        };
+    }
 
-function ne_get_playlist(url, hm) {
-    var list_id = getParameterByName('list_id', url).split('_').pop();
-    var target_url = 'http://music.163.com/playlist?id=' + list_id;
-    return {
-        success: function(fn) {
-            hm.get(target_url).success(function(data) {
-                data = $.parseHTML(data);
-                var dataObj = $(data);
-                var info = {
-                    'list_id': list_id,
-                    'cover_img_url': dataObj.find('.u-cover img').attr('src'),
-                    'title': dataObj.find('.tit h2').text(),
-                    'source_url': 'http://music.163.com/#/playlist?id=' + list_id
-                };
-                var tracks = [];
-                var json_string = dataObj.find('textarea').val();
-                var track_json_list = JSON.parse(json_string);
-                $.each(track_json_list, function(index, track_json){
-                    var default_track = {
-                        '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': ''
+    var ne_get_playlist = function(url, hm) {
+        var list_id = getParameterByName('list_id', url).split('_').pop();
+        var target_url = 'http://music.163.com/playlist?id=' + list_id;
+        return {
+            success: function(fn) {
+                hm.get(target_url).success(function(data) {
+                    data = $.parseHTML(data);
+                    var dataObj = $(data);
+                    var info = {
+                        'list_id': list_id,
+                        'cover_img_url': dataObj.find('.u-cover img').attr('src'),
+                        'title': dataObj.find('.tit h2').text(),
+                        'source_url': 'http://music.163.com/#/playlist?id=' + list_id
                     };
-                    default_track.id = 'netrack_' + track_json.id;
-                    default_track.title = track_json.name;
-                    default_track.artist = track_json.artists[0].name;
-                    default_track.artist_id = 'neartist_' + track_json.artists[0].id;
-                    default_track.album = track_json.album.name;
-                    default_track.album_id = 'nealbum_' + track_json.album.id;
-                    default_track.source_url = 'http://music.163.com/#/song?id=' +  track_json.id;
-                    default_track.img_url = track_json.album.picUrl;
-                    default_track.url = default_track.id;
-                    tracks.push(default_track);
+                    var tracks = [];
+                    var json_string = dataObj.find('textarea').val();
+                    var track_json_list = JSON.parse(json_string);
+                    $.each(track_json_list, function(index, track_json){
+                        var default_track = {
+                            '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': ''
+                        };
+                        default_track.id = 'netrack_' + track_json.id;
+                        default_track.title = track_json.name;
+                        default_track.artist = track_json.artists[0].name;
+                        default_track.artist_id = 'neartist_' + track_json.artists[0].id;
+                        default_track.album = track_json.album.name;
+                        default_track.album_id = 'nealbum_' + track_json.album.id;
+                        default_track.source_url = 'http://music.163.com/#/song?id=' +  track_json.id;
+                        default_track.img_url = track_json.album.picUrl;
+                        default_track.url = default_track.id;
+                        tracks.push(default_track);
+                    });
+                    return fn({"info":info,"tracks":tracks,'is_mine':'0'});
                 });
-                return fn({"info":info,"tracks":tracks,'is_mine':'0'});
-            });
+            }
+        };
+    }
+
+    function _create_secret_key(size) {
+        var result = [];
+        var choice = '012345679abcdef'.split('');
+        for (var i=0; i<size; i++) {
+            var index = Math.floor(Math.random() * choice.length);
+            result.push(choice[index]);
         }
-    };
-}
-
-function _create_secret_key(size) {
-    var result = [];
-    var choice = '012345679abcdef'.split('');
-    for (var i=0; i<size; i++) {
-        var index = Math.floor(Math.random() * choice.length);
-        result.push(choice[index]);
+        return result.join('');
     }
-    return result.join('');
-}
 
 
-function _aes_encrypt(text, sec_key) {
-    pad = 16 - text.length % 16;
-    for (var i=0; i<pad; i++) {
-        text = text + String.fromCharCode(pad);
-    }
-    var key = aesjs.util.convertStringToBytes(sec_key);
-    // The initialization vector, which must be 16 bytes
-    var iv = aesjs.util.convertStringToBytes("0102030405060708");
-    var textBytes = aesjs.util.convertStringToBytes(text);
-    var aesCbc = new aesjs.ModeOfOperation.cbc(key, iv);
-    var cipherArray = [];
-    while(textBytes.length != 0) {
-        var block = aesCbc.encrypt(textBytes.slice(0, 16));
-        Array.prototype.push.apply(cipherArray,block);
-        textBytes = textBytes.slice(16);
-    }
-    var ciphertext = '';
-    for (var i=0; i<cipherArray.length; i++) {
-        ciphertext = ciphertext + String.fromCharCode(cipherArray[i]);
-    }
-    ciphertext = btoa(ciphertext)
-    return ciphertext;
-}
-
-function hexify(text) {
-    return text.split('').map(function(x){return x.charCodeAt(0).toString(16)}).join('');
-}
-
-function zfill(num, size) {
-    var s = num+"";
-    while (s.length < size) s = "0" + s;
-    return s;
-}
-
-
-function expmod( base, exp, mymod ) {
-  if ( equalsInt(exp, 0) == 1) return int2bigInt(1,10);
-  if ( equalsInt(mod(exp, int2bigInt(2,10) ), 0) ) {
-    var newexp = dup(exp);
-    rightShift_(newexp,1);
-    var result = powMod(expmod( base, newexp, mymod), [2,0], mymod);
-    return result;
-  }
-  else {
-    var result = mod(mult(expmod( base, sub(exp, int2bigInt(1,10)), mymod), base), mymod);
-    return result;
-  }
-}
-
-function _rsa_encrypt(text, pubKey, modulus) {
-    text = text.split('').reverse().join('');
-    var base = str2bigInt(hexify(text), 16);
-    var exp = str2bigInt(pubKey, 16);
-    var mod = str2bigInt(modulus, 16);
-    var bigNumber = expmod(base, exp, mod);
-    var rs = bigInt2str(bigNumber, 16);
-    return zfill(rs, 256).toLowerCase();
-}
-
-function _encrypted_request(text) {
-    var modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b72' + 
-    '5152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbd' + 
-    'a92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe48' + 
-    '75d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7';
-    var nonce = '0CoJUm6Qyw8W8jud';
-    var pubKey = '010001';
-    text = JSON.stringify(text);
-    var sec_key = _create_secret_key(16);
-    var enc_text = _aes_encrypt(_aes_encrypt(text, nonce), sec_key);
-    var enc_sec_key = _rsa_encrypt(sec_key, pubKey, modulus);
-    var data = {
-        'params': enc_text,
-        'encSecKey': enc_sec_key
-    };
-
-    return data;
-}
-
-function ne_bootstrap_track(sound, track, callback, hm, se) {
-    var target_url = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token=';
-    var csrf = '';
-    var song_id = track.id;
-
-    song_id = song_id.slice('netrack_'.length);
-    var d = {
-        "ids": [song_id],
-        "br": 320000,
-        "csrf_token": csrf
-    }
-    var data = _encrypted_request(d);
-
-    hm({
-        url: target_url,
-        method: 'POST',
-        data: se(data),
-        headers: {
-          'Content-Type': 'application/x-www-form-urlencoded'
+    function _aes_encrypt(text, sec_key) {
+        var pad = 16 - text.length % 16;
+        for (var i=0; i<pad; i++) {
+            text = text + String.fromCharCode(pad);
         }
-      }).success(function(data) {
-        sound.url = data.data[0].url;
-        callback();
-      });
-}
+        var key = aesjs.util.convertStringToBytes(sec_key);
+        // The initialization vector, which must be 16 bytes
+        var iv = aesjs.util.convertStringToBytes("0102030405060708");
+        var textBytes = aesjs.util.convertStringToBytes(text);
+        var aesCbc = new aesjs.ModeOfOperation.cbc(key, iv);
+        var cipherArray = [];
+        while(textBytes.length != 0) {
+            var block = aesCbc.encrypt(textBytes.slice(0, 16));
+            Array.prototype.push.apply(cipherArray,block);
+            textBytes = textBytes.slice(16);
+        }
+        var ciphertext = '';
+        for (var i=0; i<cipherArray.length; i++) {
+            ciphertext = ciphertext + String.fromCharCode(cipherArray[i]);
+        }
+        ciphertext = btoa(ciphertext)
+        return ciphertext;
+    }
 
-function ne_search(url, hm, se) {
-    // use chrome extension to modify referer.
-    var target_url = 'http://music.163.com/api/search/pc';
-    var keyword = getParameterByName('keywords', url);
-    var req_data = {
-        's': keyword,
-        'offset': 0,
-        'limit': 20,
-        'type': 1
-    };
-    return {
-        success: function(fn) {
-            hm({
-                url: target_url,
-                method: 'POST',
-                data: se(req_data),
-                headers: {
-                  'Content-Type': 'application/x-www-form-urlencoded'
-                }
-            }).success(function(data) {
-                var tracks = [];
-                $.each(data.result.songs, function(index, song_info) {
-                    if (song_info.status == -1) {
-                        return;
+    function hexify(text) {
+        return text.split('').map(function(x){return x.charCodeAt(0).toString(16)}).join('');
+    }
+
+    function zfill(num, size) {
+        var s = num+"";
+        while (s.length < size) s = "0" + s;
+        return s;
+    }
+
+
+    function expmod( base, exp, mymod ) {
+      if ( equalsInt(exp, 0) == 1) return int2bigInt(1,10);
+      if ( equalsInt(mod(exp, int2bigInt(2,10) ), 0) ) {
+        var newexp = dup(exp);
+        rightShift_(newexp,1);
+        var result = powMod(expmod( base, newexp, mymod), [2,0], mymod);
+        return result;
+      }
+      else {
+        var result = mod(mult(expmod( base, sub(exp, int2bigInt(1,10)), mymod), base), mymod);
+        return result;
+      }
+    }
+
+    function _rsa_encrypt(text, pubKey, modulus) {
+        text = text.split('').reverse().join('');
+        var base = str2bigInt(hexify(text), 16);
+        var exp = str2bigInt(pubKey, 16);
+        var mod = str2bigInt(modulus, 16);
+        var bigNumber = expmod(base, exp, mod);
+        var rs = bigInt2str(bigNumber, 16);
+        return zfill(rs, 256).toLowerCase();
+    }
+
+    function _encrypted_request(text) {
+        var modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b72' + 
+        '5152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbd' + 
+        'a92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe48' + 
+        '75d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7';
+        var nonce = '0CoJUm6Qyw8W8jud';
+        var pubKey = '010001';
+        text = JSON.stringify(text);
+        var sec_key = _create_secret_key(16);
+        var enc_text = _aes_encrypt(_aes_encrypt(text, nonce), sec_key);
+        var enc_sec_key = _rsa_encrypt(sec_key, pubKey, modulus);
+        var data = {
+            'params': enc_text,
+            'encSecKey': enc_sec_key
+        };
+
+        return data;
+    }
+
+    var ne_bootstrap_track = function(sound, track, success, failure, hm, se) {
+        var target_url = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token=';
+        var csrf = '';
+        var song_id = track.id;
+
+        song_id = song_id.slice('netrack_'.length);
+        var d = {
+            "ids": [song_id],
+            "br": 320000,
+            "csrf_token": csrf
+        }
+        var data = _encrypted_request(d);
+
+        hm({
+            url: target_url,
+            method: 'POST',
+            data: se(data),
+            headers: {
+              'Content-Type': 'application/x-www-form-urlencoded'
+            }
+          }).success(function(data) {
+            var url = data.data[0].url;
+            if (url != null) {
+                sound.url = url;
+                success();
+            }
+            else {
+                failure();
+            }
+          });
+    }
+
+    var ne_search = function(url, hm, se) {
+        // use chrome extension to modify referer.
+        var target_url = 'http://music.163.com/api/search/pc';
+        var keyword = getParameterByName('keywords', url);
+        var req_data = {
+            's': keyword,
+            'offset': 0,
+            'limit': 20,
+            'type': 1
+        };
+        return {
+            success: function(fn) {
+                hm({
+                    url: target_url,
+                    method: 'POST',
+                    data: se(req_data),
+                    headers: {
+                      'Content-Type': 'application/x-www-form-urlencoded'
                     }
-                    var default_track = {
-                        'id': 'netrack_' + song_info.id,
-                        'title': song_info.name,
-                        'artist': song_info.artists[0].name,
-                        'artist_id': 'neartist_' + song_info.artists[0].id,
-                        'album': song_info.album.name,
-                        'album_id': 'nealbum_' + song_info.album.id,
-                        'source': 'netease',
-                        'source_url': 'http://music.163.com/#/song?id=' + song_info.id,
-                        'img_url': song_info.album.picUrl,
-                        'url': 'netrack_' + song_info.id
-                    };
-                    tracks.push(default_track);
+                }).success(function(data) {
+                    var tracks = [];
+                    $.each(data.result.songs, function(index, song_info) {
+                        if (song_info.status == -1) {
+                            return;
+                        }
+                        var default_track = {
+                            'id': 'netrack_' + song_info.id,
+                            'title': song_info.name,
+                            'artist': song_info.artists[0].name,
+                            'artist_id': 'neartist_' + song_info.artists[0].id,
+                            'album': song_info.album.name,
+                            'album_id': 'nealbum_' + song_info.album.id,
+                            'source': 'netease',
+                            'source_url': 'http://music.163.com/#/song?id=' + song_info.id,
+                            'img_url': song_info.album.picUrl,
+                            'url': 'netrack_' + song_info.id
+                        };
+                        tracks.push(default_track);
+                    });
+                    return fn({"result":tracks});
                 });
-                return fn({"result":tracks});
-            });
-        }
-    };
-}
-
-
-function ne_album(url, hm, se) {
-    var album_id = getParameterByName('album_id', url).split('_').pop();
-    // use chrome extension to modify referer.
-    var target_url = 'http://music.163.com/api/album/' + album_id;
-
-    return {
-        success: function(fn) {
-            hm({
-                url: target_url,
-                method: 'GET'
-            }).success(function(data) {
-                var 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
-                };
-
-                var tracks = [];
-                $.each(data.album.songs, function(index, song_info) {
-                    var default_track = {
-                        'id': 'netrack_' + song_info.id,
-                        'title': song_info.name,
-                        'artist': song_info.artists[0].name,
-                        'artist_id': 'neartist_' + song_info.artists[0].id,
-                        'album': song_info.album.name,
-                        'album_id': 'nealbum_' + song_info.album.id,
-                        'source': 'netease',
-                        'source_url': 'http://music.163.com/#/song?id=' + song_info.id,
-                        'img_url': song_info.album.picUrl,
-                        'url': 'netrack_' + song_info.id
-                    };
-                    tracks.push(default_track);
-                });
-                return fn({"tracks":tracks, "info":info, "is_mine": 0});
-            });
-        }
-    };
-}
-
-function ne_artist(url, hm, se) {
-    var artist_id = getParameterByName('artist_id', url).split('_').pop();
-    // use chrome extension to modify referer.
-    var target_url = 'http://music.163.com/api/artist/' + artist_id;
-
-    return {
-        success: function(fn) {
-            hm({
-                url: target_url,
-                method: 'GET'
-            }).success(function(data) {
-                var 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
-                };
-
-                var tracks = [];
-                $.each(data.hotSongs, function(index, song_info) {
-                    var default_track = {
-                        'id': 'netrack_' + song_info.id,
-                        'title': song_info.name,
-                        'artist': song_info.artists[0].name,
-                        'artist_id': 'neartist_' + song_info.artists[0].id,
-                        'album': song_info.album.name,
-                        'album_id': 'nealbum_' + song_info.album.id,
-                        'source': 'netease',
-                        'source_url': 'http://music.163.com/#/song?id=' + song_info.id,
-                        'img_url': song_info.album.picUrl,
-                        'url': 'netrack_' + song_info.id
-                    };
-                    tracks.push(default_track);
-                });
-                return fn({"tracks":tracks, "info":info, "is_mine": 0});
-            });
-        }
-    };
-}
-
-function ne_lyric(url, hm, se) {
-    var track_id = getParameterByName('track_id', url).split('_').pop();
-    // use chrome extension to modify referer.
-    var target_url = 'http://music.163.com/weapi/song/lyric?csrf_token=';
-    var csrf = '';
-    var d = {
-        'id': track_id,
-        'lv': -1,
-        'tv': -1,
-        'csrf_token': csrf
+            }
+        };
     }
-    var data = _encrypted_request(d);
-    return {
-        success: function(fn) {
-            hm({
-                url: target_url,
-                method: 'POST',
-                data: se(data),
-                headers: {
-                  'Content-Type': 'application/x-www-form-urlencoded'
-                }
-            }).success(function(data) {
-                var lrc = '';
-                if (data.lrc != null) {
-                    lrc = data.lrc.lyric;
-                }
-                return fn({"lyric":lrc});
-            });
+
+
+    var ne_album = function(url, hm, se) {
+        var album_id = getParameterByName('album_id', url).split('_').pop();
+        // use chrome extension to modify referer.
+        var target_url = 'http://music.163.com/api/album/' + album_id;
+
+        return {
+            success: function(fn) {
+                hm({
+                    url: target_url,
+                    method: 'GET'
+                }).success(function(data) {
+                    var 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
+                    };
+
+                    var tracks = [];
+                    $.each(data.album.songs, function(index, song_info) {
+                        var default_track = {
+                            'id': 'netrack_' + song_info.id,
+                            'title': song_info.name,
+                            'artist': song_info.artists[0].name,
+                            'artist_id': 'neartist_' + song_info.artists[0].id,
+                            'album': song_info.album.name,
+                            'album_id': 'nealbum_' + song_info.album.id,
+                            'source': 'netease',
+                            'source_url': 'http://music.163.com/#/song?id=' + song_info.id,
+                            'img_url': song_info.album.picUrl,
+                            'url': 'netrack_' + song_info.id
+                        };
+                        tracks.push(default_track);
+                    });
+                    return fn({"tracks":tracks, "info":info, "is_mine": 0});
+                });
+            }
+        };
+    }
+
+    var ne_artist = function(url, hm, se) {
+        var artist_id = getParameterByName('artist_id', url).split('_').pop();
+        // use chrome extension to modify referer.
+        var target_url = 'http://music.163.com/api/artist/' + artist_id;
+
+        return {
+            success: function(fn) {
+                hm({
+                    url: target_url,
+                    method: 'GET'
+                }).success(function(data) {
+                    var 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
+                    };
+
+                    var tracks = [];
+                    $.each(data.hotSongs, function(index, song_info) {
+                        var default_track = {
+                            'id': 'netrack_' + song_info.id,
+                            'title': song_info.name,
+                            'artist': song_info.artists[0].name,
+                            'artist_id': 'neartist_' + song_info.artists[0].id,
+                            'album': song_info.album.name,
+                            'album_id': 'nealbum_' + song_info.album.id,
+                            'source': 'netease',
+                            'source_url': 'http://music.163.com/#/song?id=' + song_info.id,
+                            'img_url': song_info.album.picUrl,
+                            'url': 'netrack_' + song_info.id
+                        };
+                        tracks.push(default_track);
+                    });
+                    return fn({"tracks":tracks, "info":info, "is_mine": 0});
+                });
+            }
+        };
+    }
+
+    var ne_lyric = function(url, hm, se) {
+        var track_id = getParameterByName('track_id', url).split('_').pop();
+        // use chrome extension to modify referer.
+        var target_url = 'http://music.163.com/weapi/song/lyric?csrf_token=';
+        var csrf = '';
+        var d = {
+            'id': track_id,
+            'lv': -1,
+            'tv': -1,
+            'csrf_token': csrf
         }
-    };
-}
+        var data = _encrypted_request(d);
+        return {
+            success: function(fn) {
+                hm({
+                    url: target_url,
+                    method: 'POST',
+                    data: se(data),
+                    headers: {
+                      'Content-Type': 'application/x-www-form-urlencoded'
+                    }
+                }).success(function(data) {
+                    var lrc = '';
+                    if (data.lrc != null) {
+                        lrc = data.lrc.lyric;
+                    }
+                    return fn({"lyric":lrc});
+                });
+            }
+        };
+    }
+
+return {
+    show_playlist: ne_show_playlist,
+    get_playlist: ne_get_playlist,
+    bootstrap_track: ne_bootstrap_track,
+    search: ne_search,
+    album: ne_album,
+    artist: ne_artist,
+    lyric: ne_lyric,
+};
+
+})();
+
diff --git a/js/qq.js b/js/qq.js
index 5f4a0ad..8cf0863 100644
--- a/js/qq.js
+++ b/js/qq.js
@@ -1,271 +1,305 @@
-function htmlDecode(value){ 
-  return $('<div/>').html(value).text(); 
-}
+var qq = (function() {
+    'use strict';
 
-function qq_show_playlist(url, hm) {
-    var target_url = 'http://i.y.qq.com/s.plcloud/fcgi-bin/fcg_get_diss_by_tag' + 
-        '.fcg?categoryId=10000000&sortId=1&sin=0&ein=49&' + 
-        'format=jsonp&g_tk=5381&loginUin=0&hostUin=0&' + 
-        'format=jsonp&inCharset=GB2312&outCharset=utf-8' + 
-        '&notice=0&platform=yqq&jsonpCallback=' + 
-        'MusicJsonCallback&needNewCode=0';
+    function htmlDecode(value){ 
+      return $('<div/>').html(value).text(); 
+    }
 
-    return {
-        success: function(fn) {
-            var result = [];
-            hm({
-                url:target_url,
-                method: 'GET',
-                transformResponse: undefined
-            }).success(function(data) {
-                data = data.slice('MusicJsonCallback('.length, -')'.length);
-                data = JSON.parse(data);
+    var qq_show_playlist = function(url, hm) {
+        var target_url = 'http://i.y.qq.com/s.plcloud/fcgi-bin/fcg_get_diss_by_tag' + 
+            '.fcg?categoryId=10000000&sortId=1&sin=0&ein=49&' + 
+            'format=jsonp&g_tk=5381&loginUin=0&hostUin=0&' + 
+            'format=jsonp&inCharset=GB2312&outCharset=utf-8' + 
+            '&notice=0&platform=yqq&jsonpCallback=' + 
+            'MusicJsonCallback&needNewCode=0';
 
-                var playlists = [];
-                $.each(data.data.list, function(index, item){
-                    var d = {
-                        'cover_img_url': item.imgurl,
-                        'title': htmlDecode(item.dissname),
-                        'list_id':'qqplaylist_' + item.dissid,
-                        'source_url': 'http://y.qq.com/#type=taoge&id=' + item.dissid
+        return {
+            success: function(fn) {
+                var result = [];
+                hm({
+                    url:target_url,
+                    method: 'GET',
+                    transformResponse: undefined
+                }).success(function(data) {
+                    data = data.slice('MusicJsonCallback('.length, -')'.length);
+                    data = JSON.parse(data);
 
+                    var playlists = [];
+                    $.each(data.data.list, function(index, item){
+                        var d = {
+                            'cover_img_url': item.imgurl,
+                            'title': htmlDecode(item.dissname),
+                            'list_id':'qqplaylist_' + item.dissid,
+                            'source_url': 'http://y.qq.com/#type=taoge&id=' + item.dissid
+
+                        };
+                        playlists.push(d);
+                    });
+
+                    return fn({"result":playlists});
+                });
+            }
+        };
+    }
+
+    function qq_get_image_url(qqimgid, img_type) {
+        var category = '';
+        if(img_type == 'artist') {
+            category = 'mid_singer_300'
+        }
+        if(img_type == 'album') {
+            category = 'mid_album_300';
+        }
+        var s = [category, qqimgid[qqimgid.length - 2], qqimgid[qqimgid.length - 1], qqimgid].join('/');
+        var url = 'http://imgcache.qq.com/music/photo/' + s + '.jpg';
+        return url;
+    }
+
+    function qq_convert_song(song) {
+        var 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
+        }
+        return d
+    }
+
+    function qq_is_playable(song) {
+        var 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"]
+        var play_flag = switch_flag[0];
+        var try_flag = switch_flag[13];
+        return ((play_flag == 1) || ((play_flag == 1) && (try_flag == 1)));
+    }
+
+    var qq_get_playlist = function(url, hm) {
+        var list_id = getParameterByName('list_id', url).split('_').pop();
+
+        return {
+            success: function(fn) {
+                var 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&notice=0' + 
+                    '&platform=yqq&jsonpCallback=jsonCallback&needNewCode=0';
+                hm({
+                    url:target_url,
+                    method: 'GET',
+                    transformResponse: undefined
+                })
+                .success(function(data) {
+                    data = data.slice('jsonCallback('.length, -')'.length);
+                    data = JSON.parse(data);
+
+                    var 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
                     };
-                    playlists.push(d);
+
+                    var tracks = [];
+                    $.each(data.cdlist[0].songlist, function(index, item){
+                        if (qq_is_playable(item)) {
+                            var track = qq_convert_song(item);
+                            tracks.push(track);
+                        }
+                    });
+                    return fn({"tracks":tracks, "info":info});
                 });
-
-                return fn({"result":playlists});
-            });
-        }
-    };
-}
-
-function qq_get_image_url(qqimgid, img_type) {
-    var category = '';
-    if(img_type == 'artist') {
-        category = 'mid_singer_300'
+            }
+        };
     }
-    if(img_type == 'album') {
-        category = 'mid_album_300';
+
+    var qq_album = function(url, hm) {
+        var album_id = getParameterByName('album_id', url).split('_').pop();
+
+        return {
+            success: function(fn) {
+                var 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' + 
+                    '&notice=0&platform=h5&needNewCode=1&_=1459961045571' + 
+                    '&jsonpCallback=asonglist1459961045566';
+                hm({
+                    url: target_url,
+                    method: 'GET',
+                    transformResponse: undefined
+                })
+                .success(function(data) {
+                    data = data.slice(' asonglist1459961045566('.length, -')'.length);
+                    data = JSON.parse(data);
+
+                    var 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
+                    };
+
+                    var tracks = [];
+                    $.each(data.data.list, function(index, item){
+                        if (qq_is_playable(item)) {
+                            var track = qq_convert_song(item);
+                            tracks.push(track);
+                        }
+                    });
+                    return fn({"tracks":tracks, "info":info});
+                });
+            }
+        };
     }
-    var s = [category, qqimgid[qqimgid.length - 2], qqimgid[qqimgid.length - 1], qqimgid].join('/');
-    var url = 'http://imgcache.qq.com/music/photo/' + s + '.jpg';
-    return url;
-}
 
-function qq_convert_song(song) {
-    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
+    var qq_artist = function(url, hm) {
+        var artist_id = getParameterByName('artist_id', url).split('_').pop();
+
+        return {
+            success: function(fn) {
+                var 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&notice=0&platform=' + 
+                    'h5&needNewCode=1&from=h5&_=1459960621777&' + 
+                    'jsonpCallback=ssonglist1459960621772';
+                hm({
+                    url: target_url,
+                    method: 'GET',
+                    transformResponse: undefined
+                })
+                .success(function(data) {
+                    data = data.slice(' ssonglist1459960621772('.length, -')'.length);
+                    data = JSON.parse(data);
+
+                    var 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
+                    };
+
+                    var tracks = [];
+                    $.each(data.data.list, function(index, item){
+                        if (qq_is_playable(item.musicData)) {
+                            var track = qq_convert_song(item.musicData);
+                            tracks.push(track);
+                        }
+                    });
+                    return fn({"tracks":tracks, "info":info});
+                });
+            }
+        };
     }
-    return d
-}
 
-function qq_get_playlist(url, hm) {
-    var list_id = getParameterByName('list_id', url).split('_').pop();
-
-    return {
-        success: function(fn) {
-            var 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&notice=0' + 
-                '&platform=yqq&jsonpCallback=jsonCallback&needNewCode=0';
-            hm({
-                url:target_url,
-                method: 'GET',
-                transformResponse: undefined
-            })
-            .success(function(data) {
-                data = data.slice('jsonCallback('.length, -')'.length);
-                data = JSON.parse(data);
-
-                var 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
-                };
-
-                var tracks = [];
-                $.each(data.cdlist[0].songlist, function(index, item){
-                    var track = qq_convert_song(item);
-                    tracks.push(track);
+    var qq_search = function(url, hm, se) {
+        return {
+            success: function(fn) {
+                var keyword = getParameterByName('keywords', url);
+                var 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&notice=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=1' + 
+                '&remoteplace=txt.mqq.all&_=1459991037831&jsonpCallback=jsonp4';
+                hm({
+                    url:target_url,
+                    method: 'GET',
+                    transformResponse: undefined
+                })
+                .success(function(data) {
+                    data = data.slice('jsonp4('.length, -')'.length);
+                    data = JSON.parse(data);
+                    var tracks = [];
+                    $.each(data.data.song.list, function(index, item){
+                        if (qq_is_playable(item)) {
+                            var track = qq_convert_song(item);
+                            tracks.push(track);
+                        }
+                    });
+                    return fn({"result":tracks});
                 });
-                return fn({"tracks":tracks, "info":info});
-            });
-        }
-    };
-}
+            }
+        };
+    }
 
-function qq_album(url, hm) {
-    var album_id = getParameterByName('album_id', url).split('_').pop();
+    var qq_bootstrap_track = function(sound, track, success, failure, hm, se) {
+        var target_url ='http://base.music.qq.com/fcgi-bin/fcg_musicexpress.fcg?' + 
+            'json=3&guid=780782017&g_tk=938407465&loginUin=0&hostUin=0&' + 
+            'format=jsonp&inCharset=GB2312&outCharset=GB2312&notice=0&' + 
+            'platform=yqq&jsonpCallback=jsonCallback&needNewCode=0';
 
-    return {
-        success: function(fn) {
-            var 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' + 
-                '&notice=0&platform=h5&needNewCode=1&_=1459961045571' + 
-                '&jsonpCallback=asonglist1459961045566';
-            hm({
-                url: target_url,
-                method: 'GET',
-                transformResponse: undefined
-            })
-            .success(function(data) {
-                data = data.slice(' asonglist1459961045566('.length, -')'.length);
-                data = JSON.parse(data);
+        hm({
+            url:target_url,
+            method: 'GET',
+            transformResponse: undefined
+        })
+        .success(function(data) {
+            data = data.slice('jsonCallback('.length, -');'.length);
+            data = JSON.parse(data);
+            var token = data.key;
+            var url = 'http://cc.stream.qqmusic.qq.com/C200' +  track.id.slice('qqtrack_'.length)  + '.m4a?vkey=' +
+                token + '&fromtag=0&guid=780782017';
+            sound.url = url;
+            success();
+        });
+    }
 
-                var 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
-                };
+    function str2ab(str) {
+      // string to array buffer.
+      var buf = new ArrayBuffer(str.length);
+      var bufView = new Uint8Array(buf);
+      for (var i=0, strLen=str.length; i<strLen; i++) {
+        bufView[i] = str.charCodeAt(i);
+      }
+      return buf;
+    }
 
-                var tracks = [];
-                $.each(data.data.list, function(index, item){
-                    var track = qq_convert_song(item);
-                    tracks.push(track);
+    var qq_lyric = function(url, hm, se) {
+        var track_id = getParameterByName('track_id', url).split('_').pop();
+        // use chrome extension to modify referer.
+        var 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&notice=0&platform=yqq&jsonpCallback=MusicJsonCallback&needNewCode=0';
+        return {
+            success: function(fn) {
+                hm({
+                    url:target_url,
+                    method: 'GET',
+                    transformResponse: undefined
+                }).success(function(data) {
+                    data = data.slice('MusicJsonCallback('.length, -')'.length);
+                    data = JSON.parse(data);
+                    var lrc = '';
+                    if (data.lyric != null) {
+                        var td = new TextDecoder('utf8');
+                        lrc = td.decode(str2ab(atob(data.lyric)));
+                    }
+                    return fn({"lyric":lrc});
                 });
-                return fn({"tracks":tracks, "info":info});
-            });
-        }
-    };
-}
+            }
+        };
+    }
 
-function qq_artist(url, hm) {
-    var artist_id = getParameterByName('artist_id', url).split('_').pop();
+return {
+    show_playlist: qq_show_playlist,
+    get_playlist: qq_get_playlist,
+    bootstrap_track: qq_bootstrap_track,
+    search: qq_search,
+    album: qq_album,
+    artist: qq_artist,
+    lyric: qq_lyric,
+};
 
-    return {
-        success: function(fn) {
-            var 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&notice=0&platform=' + 
-                'h5&needNewCode=1&from=h5&_=1459960621777&' + 
-                'jsonpCallback=ssonglist1459960621772';
-            hm({
-                url: target_url,
-                method: 'GET',
-                transformResponse: undefined
-            })
-            .success(function(data) {
-                data = data.slice(' ssonglist1459960621772('.length, -')'.length);
-                data = JSON.parse(data);
-
-                var 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
-                };
-
-                var tracks = [];
-                $.each(data.data.list, function(index, item){
-                    var track = qq_convert_song(item.musicData);
-                    tracks.push(track);
-                });
-                return fn({"tracks":tracks, "info":info});
-            });
-        }
-    };
-}
-
-function qq_search(url, hm, se) {
-    return {
-        success: function(fn) {
-            var keyword = getParameterByName('keywords', url);
-            var 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&notice=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=1' + 
-            '&remoteplace=txt.mqq.all&_=1459991037831&jsonpCallback=jsonp4';
-            hm({
-                url:target_url,
-                method: 'GET',
-                transformResponse: undefined
-            })
-            .success(function(data) {
-                data = data.slice('jsonp4('.length, -')'.length);
-                data = JSON.parse(data);
-                var tracks = [];
-                $.each(data.data.song.list, function(index, item){
-                    var track = qq_convert_song(item);
-                    tracks.push(track);
-                });
-                return fn({"result":tracks});
-            });
-        }
-    };
-}
-
-function qq_bootstrap_track(sound, track, callback, hm, se) {
-    var target_url ='http://base.music.qq.com/fcgi-bin/fcg_musicexpress.fcg?' + 
-        'json=3&guid=780782017&g_tk=938407465&loginUin=0&hostUin=0&' + 
-        'format=jsonp&inCharset=GB2312&outCharset=GB2312&notice=0&' + 
-        'platform=yqq&jsonpCallback=jsonCallback&needNewCode=0';
-
-    hm({
-        url:target_url,
-        method: 'GET',
-        transformResponse: undefined
-    })
-    .success(function(data) {
-        data = data.slice('jsonCallback('.length, -');'.length);
-        data = JSON.parse(data);
-        var token = data.key;
-        var url = 'http://cc.stream.qqmusic.qq.com/C200' +  track.id.slice('qqtrack_'.length)  + '.m4a?vkey=' +
-            token + '&fromtag=0&guid=780782017';
-        sound.url = url;
-        callback();
-    });
-}
-
-function str2ab(str) {
-  // string to array buffer.
-  var buf = new ArrayBuffer(str.length);
-  var bufView = new Uint8Array(buf);
-  for (var i=0, strLen=str.length; i<strLen; i++) {
-    bufView[i] = str.charCodeAt(i);
-  }
-  return buf;
-}
-
-function qq_lyric(url, hm, se) {
-    var track_id = getParameterByName('track_id', url).split('_').pop();
-    // use chrome extension to modify referer.
-    var 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&notice=0&platform=yqq&jsonpCallback=MusicJsonCallback&needNewCode=0';
-    return {
-        success: function(fn) {
-            hm({
-                url:target_url,
-                method: 'GET',
-                transformResponse: undefined
-            }).success(function(data) {
-                data = data.slice('MusicJsonCallback('.length, -')'.length);
-                data = JSON.parse(data);
-                var lrc = '';
-                if (data.lyric != null) {
-                    var td = new TextDecoder('utf8');
-                    lrc = td.decode(str2ab(atob(data.lyric)));
-                }
-                return fn({"lyric":lrc});
-            });
-        }
-    };
-}
+})();
 
diff --git a/js/xiami.js b/js/xiami.js
index c1b59f2..a0be678 100644
--- a/js/xiami.js
+++ b/js/xiami.js
@@ -1,215 +1,163 @@
-function caesar(location){
-    var num = location[0];
-    var avg_len = Math.floor(location.slice(1).length / num);
-    var remainder = location.slice(1).length % num;
+var xiami = (function() {
+    'use strict';
+    function caesar(location){
+        var num = location[0];
+        var avg_len = Math.floor(location.slice(1).length / num);
+        var remainder = location.slice(1).length % num;
 
-    var result = [];
-    for (var i=0; i<remainder; i++) {
-        var line = location.slice(i * (avg_len + 1) + 1, (i + 1) * (avg_len + 1) + 1);
-        result.push(line);
-    }
-
-    for (var i=0; i<num-remainder; i++) {
-        var line = location.slice((avg_len + 1) * remainder).slice(i * avg_len + 1, (i + 1) * avg_len + 1);
-        result.push(line);
-    }
-
-    var s = [];
-    for (var i=0; i< avg_len; i++) {
-        for (var j=0; j<num; j++){
-            s.push(result[j][i]);
+        var result = [];
+        for (var i=0; i<remainder; i++) {
+            var line = location.slice(i * (avg_len + 1) + 1, (i + 1) * (avg_len + 1) + 1);
+            result.push(line);
         }
+
+        for (var i=0; i<num-remainder; i++) {
+            var line = location.slice((avg_len + 1) * remainder).slice(i * avg_len + 1, (i + 1) * avg_len + 1);
+            result.push(line);
+        }
+
+        var s = [];
+        for (var i=0; i< avg_len; i++) {
+            for (var j=0; j<num; j++){
+                s.push(result[j][i]);
+            }
+        }
+
+        for (var i=0; i<remainder; i++) {
+            s.push(result[i].slice(-1));
+        }
+        
+        return unescape(s.join('')).replace(/\^/g, '0');
     }
 
-    for (var i=0; i<remainder; i++) {
-        s.push(result[i].slice(-1));
+    function xm_retina_url(s){
+        return s.slice(0, -6) + s.slice(-4);
     }
-    
-    return unescape(s.join('')).replace(/\^/g, '0');
-}
 
-function xm_retina_url(s){
-    return s.slice(0, -6) + s.slice(-4);
-}
+    var xm_show_playlist = function(url, hm) {
+        var target_url = 'http://www.xiami.com/collect/recommend';
 
-function xm_show_playlist(url, hm) {
-    var target_url = 'http://www.xiami.com/collect/recommend';
+        return {
+            success: function(fn) {
+                var result = [];
+                hm.get(target_url).success(function(data) {
+                    data = $.parseHTML(data);
+                    $(data).find('.block_list ul li').each(function(){
+                        var default_playlist = {
+                            'cover_img_url' : '',
+                            'title': '',
+                            'list_id': '',
+                            'source_url': ''
+                        };
+                        default_playlist.cover_img_url = $(this).find('img')[0].src;
+                        default_playlist.title = $(this).find('h3 a')[0].title;
+                        var xiami_url = $(this).find('h3 a')[0].href;
+                        var list_id = xiami_url.split('?')[0].split('/').pop()
+                        default_playlist.list_id = 'xmplaylist_' + list_id;
+                        default_playlist.source_url = 'http://www.xiami.com/collect/' + list_id;
+                        result.push(default_playlist);
+                    });
+                    return fn({"result":result});
+                });
+            }
+        };
+    }
 
-    return {
-        success: function(fn) {
-            var result = [];
-            hm.get(target_url).success(function(data) {
-                data = $.parseHTML(data);
-                $(data).find('.block_list ul li').each(function(){
-                    var default_playlist = {
-                        'cover_img_url' : '',
-                        'title': '',
-                        'list_id': '',
-                        'source_url': ''
+    var xm_get_playlist = function(url, hm) {
+        var list_id = getParameterByName('list_id', url).split('_').pop();
+
+        return {
+            success: function(fn) {
+                var target_url = 'http://api.xiami.com/web?v=2.0&app_key=1&id=' + list_id +
+                    '&callback=jsonp122&r=collect/detail';
+                hm({
+                    url:target_url,
+                    method: 'GET',
+                    transformResponse: undefined
+                })
+                .success(function(data) {
+                    data = data.slice('jsonp122('.length, -')'.length);
+                    data = JSON.parse(data);
+
+                    var info = {
+                        'cover_img_url': xm_retina_url(data.data.logo),
+                        'title': data.data.collect_name,
+                        'id': 'xmplaylist_' + list_id,
+                        'source_url': 'http://www.xiami.com/collect/' + list_id
                     };
-                    default_playlist.cover_img_url = $(this).find('img')[0].src;
-                    default_playlist.title = $(this).find('h3 a')[0].title;
-                    var xiami_url = $(this).find('h3 a')[0].href;
-                    var list_id = xiami_url.split('?')[0].split('/').pop()
-                    default_playlist.list_id = 'xmplaylist_' + list_id;
-                    default_playlist.source_url = 'http://www.xiami.com/collect/' + list_id;
-                    result.push(default_playlist);
+
+                    var tracks = [];
+                    $.each(data.data.songs, function(index, item){
+                        var track = xm_convert_song(item, 'artist_name');
+                        tracks.push(track);
+                    });
+                    return fn({"tracks":tracks, "info":info});
                 });
-                return fn({"result":result});
-            });
-        }
-    };
-}
+            }
+        };
+    }
 
-function xm_get_playlist(url, hm) {
-    var list_id = getParameterByName('list_id', url).split('_').pop();
+    var xm_bootstrap_track = function(sound, track, success, failure, hm, se) {
+        var target_url = 'http://www.xiami.com/song/playlist/id/' + track.id.slice('xmtrack_'.length) +
+            '/object_name/default/object_id/0/cat/json';
+        hm.get(target_url).success(function(data) {
+            var location = data.data.trackList[0].location;
+            sound.url = caesar(location);
+            track.img_url = data.data.trackList[0].pic;
+            track.album = data.data.trackList[0].album_name;
+            track.album_id = 'xmalbum_' + data.data.trackList[0].album_id;
+            track.lyric_url = data.data.trackList[0].lyric_url;
+            success();
+        });
+    }
 
-    return {
-        success: function(fn) {
-            var target_url = 'http://api.xiami.com/web?v=2.0&app_key=1&id=' + list_id +
-                '&callback=jsonp122&r=collect/detail';
-            hm({
-                url:target_url,
-                method: 'GET',
-                transformResponse: undefined
-            })
-            .success(function(data) {
-                data = data.slice('jsonp122('.length, -')'.length);
-                data = JSON.parse(data);
+    function xm_convert_song(song_info, artist_field_name) {
+        var 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;
+    }
 
-                var info = {
-                    'cover_img_url': xm_retina_url(data.data.logo),
-                    'title': data.data.collect_name,
-                    'id': 'xmplaylist_' + list_id,
-                    'source_url': 'http://www.xiami.com/collect/' + list_id
-                };
-
-                var tracks = [];
-                $.each(data.data.songs, function(index, item){
-                    var track = xm_convert_song(item, 'artist_name');
-                    tracks.push(track);
+    var xm_search = function(url, hm, se) {
+        return {
+            success: function(fn) {
+                var keyword = getParameterByName('keywords', url);
+                var target_url = 'http://api.xiami.com/web?v=2.0&app_key=1&key=' + keyword + '&page=1&limit=50&callback=jsonp154&r=search/songs';
+                hm({
+                    url:target_url,
+                    method: 'GET',
+                    transformResponse: undefined
+                })
+                .success(function(data) {
+                    data = data.slice('jsonp154('.length, -')'.length);
+                    data = JSON.parse(data);
+                    var tracks = [];
+                    $.each(data.data.songs, function(index, item){
+                        var track = xm_convert_song(item, 'artist_name');
+                        tracks.push(track);
+                    });
+                    return fn({"result":tracks});
                 });
-                return fn({"tracks":tracks, "info":info});
-            });
-        }
-    };
-}
+            }
+        };
+    }
 
-function xm_bootstrap_track(sound, track, callback, hm, se) {
-    var target_url = 'http://www.xiami.com/song/playlist/id/' + track.id.slice('xmtrack_'.length) +
-        '/object_name/default/object_id/0/cat/json';
-    hm.get(target_url).success(function(data) {
-        var location = data.data.trackList[0].location;
-        sound.url = caesar(location);
-        track.img_url = data.data.trackList[0].pic;
-        track.album = data.data.trackList[0].album_name;
-        track.album_id = 'xmalbum_' + data.data.trackList[0].album_id;
-        track.lyric_url = data.data.trackList[0].lyric_url;
-        callback();
-    });
-}
-
-function xm_convert_song(song_info, artist_field_name) {
-    var 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_search(url, hm, se) {
-    return {
-        success: function(fn) {
-            var keyword = getParameterByName('keywords', url);
-            var target_url = 'http://api.xiami.com/web?v=2.0&app_key=1&key=' + keyword + '&page=1&limit=50&callback=jsonp154&r=search/songs';
-            hm({
-                url:target_url,
-                method: 'GET',
-                transformResponse: undefined
-            })
-            .success(function(data) {
-                data = data.slice('jsonp154('.length, -')'.length);
-                data = JSON.parse(data);
-                var tracks = [];
-                $.each(data.data.songs, function(index, item){
-                    var track = xm_convert_song(item, 'artist_name');
-                    tracks.push(track);
-                });
-                return fn({"result":tracks});
-            });
-        }
-    };
-}
-
-function xm_album(url, hm, se) {
-    return {
-        success: function(fn) {
-            var album_id = getParameterByName('album_id', url).split('_').pop();
-            var 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: undefined
-            })
-            .success(function(data) {
-                data = data.slice('jsonp217('.length, -')'.length);
-                data = JSON.parse(data);
-
-                var info = {
-                    'cover_img_url': xm_retina_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
-                };
-
-                var tracks = [];
-                $.each(data.data.songs, function(index, item){
-                    var track = xm_convert_song(item, 'singers');
-                    tracks.push(track);
-                });
-                return fn({"tracks":tracks,"info": info});
-            });
-        }
-    };
-}
-
-function xm_artist(url, hm, se) {
-    return {
-        success: function(fn) {
-            var artist_id = getParameterByName('artist_id', url).split('_').pop();
-
-            var 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: undefined
-            })
-            .success(function(data) {   
-                data = data.slice('jsonp217('.length, -')'.length);
-                data = JSON.parse(data);
-
-                var 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'
+    var xm_album = function(url, hm, se) {
+        return {
+            success: function(fn) {
+                var album_id = getParameterByName('album_id', url).split('_').pop();
+                var 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',
@@ -219,31 +167,96 @@ function xm_artist(url, hm, se) {
                     data = data.slice('jsonp217('.length, -')'.length);
                     data = JSON.parse(data);
 
+                    var info = {
+                        'cover_img_url': xm_retina_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
+                    };
+
                     var tracks = [];
-                    $.each(data.data, function(index, item){
+                    $.each(data.data.songs, function(index, item){
                         var track = xm_convert_song(item, 'singers');
                         tracks.push(track);
                     });
                     return fn({"tracks":tracks,"info": info});
                 });
-            });
-        }
-    };
-}
+            }
+        };
+    }
 
-function xm_lyric(url, hm, se) {
-    var track_id = getParameterByName('track_id', url).split('_').pop();
-    var lyric_url = getParameterByName('lyric_url', url);
-    return {
-        success: function(fn) {
-            hm({
-                url: lyric_url,
-                method: 'GET',
-                transformResponse: undefined
-            }).success(function(data) {
-                return fn({"lyric":data});
-            });
-        }
-    };
-}
+    var xm_artist = function (url, hm, se) {
+        return {
+            success: function(fn) {
+                var artist_id = getParameterByName('artist_id', url).split('_').pop();
 
+                var 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: undefined
+                })
+                .success(function(data) {   
+                    data = data.slice('jsonp217('.length, -')'.length);
+                    data = JSON.parse(data);
+
+                    var 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: undefined
+                    })
+                    .success(function(data) {
+                        data = data.slice('jsonp217('.length, -')'.length);
+                        data = JSON.parse(data);
+
+                        var tracks = [];
+                        $.each(data.data, function(index, item){
+                            var track = xm_convert_song(item, 'singers');
+                            tracks.push(track);
+                        });
+                        return fn({"tracks":tracks,"info": info});
+                    });
+                });
+            }
+        };
+    }
+
+    var xm_lyric = function (url, hm, se) {
+        var track_id = getParameterByName('track_id', url).split('_').pop();
+        var lyric_url = getParameterByName('lyric_url', url);
+        return {
+            success: function(fn) {
+                hm({
+                    url: lyric_url,
+                    method: 'GET',
+                    transformResponse: undefined
+                }).success(function(data) {
+                    return fn({"lyric":data});
+                });
+            }
+        };
+    }
+
+return {
+    show_playlist: xm_show_playlist,
+    get_playlist: xm_get_playlist,
+    bootstrap_track: xm_bootstrap_track,
+    search: xm_search,
+    album: xm_album,
+    artist: xm_artist,
+    lyric: xm_lyric,
+};
+
+})();