Skip to content

Commit

Permalink
Play failover (listen1#320)
Browse files Browse the repository at this point in the history
* add play failover feature

* instant return when any source available
  • Loading branch information
listen1 authored Jun 29, 2020
1 parent bee03d8 commit 49056e4
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 16 deletions.
4 changes: 3 additions & 1 deletion i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,7 @@
"_COMFIRM": "Confirm",
"_THEME": "Theme",
"_THEME_WHITE": "White Theme",
"_THEME_BLACK": "Black Theme"
"_THEME_BLACK": "Black Theme",
"_AUTO_CHOOSE_SOURCE": "Auto Choose Source",
"_AUTO_CHOOSE_SOURCE_NOTICE": "If play fail, auto choose source from other music platform."
}
4 changes: 3 additions & 1 deletion i18n/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,7 @@
"_COMFIRM": "Confirmer",
"_THEME": "Thème",
"_THEME_WHITE": "Thème blanc",
"_THEME_BLACK": "Thème foncé"
"_THEME_BLACK": "Thème foncé",
"_AUTO_CHOOSE_SOURCE": "Auto Choose Source",
"_AUTO_CHOOSE_SOURCE_NOTICE": "If play fail, auto choose source from other music platform."
}
4 changes: 3 additions & 1 deletion i18n/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,7 @@
"_COMFIRM": "确认",
"_THEME": "主题",
"_THEME_WHITE": "简约白",
"_THEME_BLACK": "深空灰"
"_THEME_BLACK": "深空灰",
"_AUTO_CHOOSE_SOURCE": "自动切换源",
"_AUTO_CHOOSE_SOURCE_NOTICE": "如果播放音乐失败,是否自动切换播放源(默认从酷我,QQ和咪咕音乐搜索)"
}
21 changes: 21 additions & 0 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ const main = () => {

app.run(['angularPlayer', 'Notification', 'loWeb', '$translate',
(angularPlayer, Notification, loWeb, $translate) => {
function getAutoChooseSource(){
var enableAutoChooseSource = localStorage.getObject('enable_auto_choose_source');
if(enableAutoChooseSource === null) {
// default on
enableAutoChooseSource = true;
}
return enableAutoChooseSource;
}
angularPlayer.setBootstrapTrack(
loWeb.bootstrapTrack(
() => {},
Expand All @@ -70,6 +78,7 @@ const main = () => {
};
Notification.info(d);
},
getAutoChooseSource
),
);
},
Expand Down Expand Up @@ -894,6 +903,11 @@ const main = () => {
}
$scope.enableGlobalShortCut = localStorage.getObject('enable_global_shortcut');
$scope.enableLyricFloatingWindow = localStorage.getObject('enable_lyric_floating_window');
$scope.enableAutoChooseSource = localStorage.getObject('enable_auto_choose_source');
if($scope.enableAutoChooseSource === null) {
// default on
$scope.enableAutoChooseSource = true;
}
$scope.applyGlobalShortcut();
$scope.openLyricFloatingWindow();
};
Expand Down Expand Up @@ -1300,6 +1314,13 @@ const main = () => {
}
});
}

$scope.setAutoChooseSource = (toggle) => {
if (toggle === true) {
$scope.enableAutoChooseSource = !$scope.enableAutoChooseSource;
}
localStorage.setObject('enable_auto_choose_source', $scope.enableAutoChooseSource);
};
},
]);

Expand Down
90 changes: 82 additions & 8 deletions js/loweb.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,22 +184,96 @@ ngloWebManager.factory('loWeb', ['$rootScope', '$log', '$http', '$httpParamSeria
}
return null;
},
bootstrapTrack(success, failure) {
bootstrapTrack(success, failure, getAutoChooseSource) {
function getTrackFromSame(track, source, callback){
if (track.source === source){
// come from same source, no need to check
return callback(null);
}
//TODO: better query method
const keyword = track.title + ' ' + track.artist;
const curpage = 1;
const url = `/search?source=${source}&keywords=${keyword}&curpage=${curpage}`;
const provider = getProviderByName(source);
provider.search(url, $http, $httpParamSerializerJQLike).success((data)=>{
for(var i = 0; i < data.result.length; i++) {
const searchTrack = data.result[i];
// compare search track and track to check if they are same
// TODO: better similar compare method (duration, md5)
if (!searchTrack.disable){
if ((searchTrack.title === track.title) && (searchTrack.artist === track.artist)){
return callback(searchTrack);
}
}
};
return callback(null);
});
}

function getUrlFromTrack(track, source, callback) {
const provider = getProviderByName(source);
const soundInfo = {}
provider.bootstrap_track(soundInfo, track, () => {
callback(soundInfo.url);
},
()=>{
callback(null);
}, $http, $httpParamSerializerJQLike);
}

function getUrlFromSame(track, source, callback){
getTrackFromSame(track, source, (sameTrack)=>{
if (sameTrack === null) {
return callback(null);
}
return getUrlFromTrack(sameTrack, source, callback);
});
}

return (sound, track, playerSuccessCallback, playerFailCallback) => {
// always refresh url, becaues url will expires
// if (sound.url.search('http') !== -1){
// callback();
// return;
// }

function successCallback() {
playerSuccessCallback();
success();
}

function failureCallback() {
playerFailCallback();
failure();
if(!getAutoChooseSource()){
playerFailCallback();
failure();
return
}
const failover_source_list = ['kuwo', 'qq', 'migu'];

function makeFn(track, source){
return function(cb){
getUrlFromSame(track, source, function(url){
// pass url as error to return instant when any of source available
return cb(url);
});
}
}
const getUrlFnList = [];
for (var i=0; i<failover_source_list.length; i++){
getUrlFnList.push(makeFn(track, failover_source_list[i]));
}

async.parallel(
getUrlFnList,
function(err) {
if(err){
// use error to make instant return, error contains url
sound.url = err;
playerSuccessCallback();
success();
return;
}

playerFailCallback();
failure();
});
}

const { source } = track;
const provider = getProviderByName(source);

Expand Down
2 changes: 1 addition & 1 deletion js/provider/netease.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ function build_netease() {
const hm = params[0];
const se = params[1];
const target_url = 'https://music.163.com/weapi/v3/song/detail';
const queryIds = [item.id, item.id];
const queryIds = [item.id];
const d = {
c: '[' + queryIds.map(id => ('{"id":' + id + '}')).join(',') + ']',
ids: '[' + queryIds.join(',') + ']'
Expand Down
4 changes: 4 additions & 0 deletions js/provider/qq.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ function build_qq() {
.then((response) => {
let { data } = response;
data = JSON.parse(data);
if(data.req_0.data.midurlinfo[0].purl==''){
// vip
return failure();
}
const url = data.req_0.data.sip[0] + data.req_0.data.midurlinfo[0].purl;
sound.url = url; // eslint-disable-line no-param-reassign
success();
Expand Down
19 changes: 15 additions & 4 deletions listen1.html
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ <h2> {{ backup.id }} {{backup.description}}</h2>
</li>
<li ng-repeat="song in result" ng-class-odd="'odd'" ng-class-even="'even'" ng-mouseenter="options=true" ng-mouseleave="options=false">
<div class="title">
<a ng-if="song.disabled" class="disabled" ng-click="copyrightNotice()">{{ song.title |limitTo:30}}</a>
<a ng-if="!song.disabled" add-and-play="song">{{ song.title |limitTo:30}}</a>
<!-- <a ng-if="song.disabled" class="disabled" ng-click="copyrightNotice()">{{ song.title |limitTo:30}}</a> -->
<a add-and-play="song">{{ song.title |limitTo:30}}</a>
</div>
<div class="artist"><a ng-click="showPlaylist(song.artist_id)">{{ song.artist |limitTo:20}}</a></div>
<div class="album"><a ng-click="showPlaylist(song.album_id)">{{ song.album |limitTo:30}}</a></div>
Expand Down Expand Up @@ -288,6 +288,14 @@ <h2> {{ backup.id }} {{backup.description}}</h2>
<button class="theme-button" ng-click="setTheme('black')">{{_THEME_BLACK}}</button>
</div>
</div>
<div class="settings-title"><span>{{_AUTO_CHOOSE_SOURCE}} (Beta)<span></div>
<div class="settings-content">
<div class="shortcut" class="btn btn-primary confirm-button" >
<svg class="feather" ng-show="!enableAutoChooseSource" ng-click="setAutoChooseSource(true)"><use xlink:href="images/feather-sprite.svg#square"></use></svg>
<svg class="feather" ng-show="enableAutoChooseSource" ng-click="setAutoChooseSource(true)"><use xlink:href="images/feather-sprite.svg#check-square"></use></svg>
{{_AUTO_CHOOSE_SOURCE_NOTICE}}
</div>
</div>
<div class="settings-title"><span>{{_BACKUP_PLAYLIST}}<span></div>
<div class="settings-content">
<p>{{_BACKUP_WARNING}}</p>
Expand Down Expand Up @@ -321,7 +329,7 @@ <h2> {{ backup.id }} {{backup.description}}</h2>
<div class="shortcut" ng-if="!isChrome" class="btn btn-primary confirm-button" >
<svg class="feather" ng-show="!enableGlobalShortCut" ng-click="applyGlobalShortcut(true)"><use xlink:href="images/feather-sprite.svg#square"></use></svg>
<svg class="feather" ng-show="enableGlobalShortCut" ng-click="applyGlobalShortcut(true)"><use xlink:href="images/feather-sprite.svg#check-square"></use></svg>
<span ng-show="enableGlobalShortCut"></span>{{_GLOBAL_SHORTCUTS_NOTICE}}
{{_GLOBAL_SHORTCUTS_NOTICE}}
</div>
</div>
<div class="settings-title"><span>{{_CONNECT_TO_LASTFM}}<span></div>
Expand Down Expand Up @@ -396,7 +404,10 @@ <h2>{{ playlist_title }}</h2>
<div class="tools">{{_OPERATION}}</div>
</li>
<li ng-repeat="song in songs track by $index" ng-class-odd="'odd'" ng-class-even="'even'" ng-mouseenter="options=true" ng-mouseleave="options=false">
<div class="title"><a class="disabled" ng-if="song.disabled" ng-click="copyrightNotice()">{{ song.title }}</a><a ng-if="!song.disabled" add-and-play="song">{{ song.title }}</a></div>
<div class="title">
<!-- <a class="disabled" ng-if="song.disabled" ng-click="copyrightNotice()">{{ song.title }}</a> -->
<a add-and-play="song">{{ song.title }}</a>
</div>
<div class="artist"><a ng-click="showPlaylist(song.artist_id)">{{ song.artist }}</a></div>
<div class="album"><a ng-click="showPlaylist(song.album_id)">{{ song.album }}</a></div>
<div class="tools">
Expand Down

0 comments on commit 49056e4

Please sign in to comment.