ListenOne / js /loweb.js
CatPtain's picture
Upload 83 files
765bc42 verified
/* global async LRUCache setPrototypeOfLocalStorage getLocalStorageValue */
/* global netease xiami qq kugou kuwo bilibili migu taihe localmusic myplaylist */
const PROVIDERS = [
{
name: 'netease',
instance: netease,
searchable: true,
support_login: true,
id: 'ne',
},
{
name: 'xiami',
instance: xiami,
searchable: false,
hidden: true,
support_login: false,
id: 'xm',
},
{
name: 'qq',
instance: qq,
searchable: true,
support_login: true,
id: 'qq',
},
{
name: 'kugou',
instance: kugou,
searchable: true,
support_login: false,
id: 'kg',
},
{
name: 'kuwo',
instance: kuwo,
searchable: true,
support_login: false,
id: 'kw',
},
{
name: 'bilibili',
instance: bilibili,
searchable: false,
support_login: false,
id: 'bi',
},
{
name: 'migu',
instance: migu,
searchable: true,
support_login: true,
id: 'mg',
},
{
name: 'taihe',
instance: taihe,
searchable: true,
support_login: false,
id: 'th',
},
{
name: 'localmusic',
instance: localmusic,
searchable: false,
hidden: true,
support_login: false,
id: 'lm',
},
{
name: 'myplaylist',
instance: myplaylist,
searchable: false,
hidden: true,
support_login: false,
id: 'my',
},
];
function getProviderByName(sourceName) {
return (PROVIDERS.find((i) => i.name === sourceName) || {}).instance;
}
function getAllProviders() {
return PROVIDERS.filter((i) => !i.hidden).map((i) => i.instance);
}
function getAllSearchProviders() {
return PROVIDERS.filter((i) => i.searchable).map((i) => i.instance);
}
function getProviderNameByItemId(itemId) {
const prefix = itemId.slice(0, 2);
return (PROVIDERS.find((i) => i.id === prefix) || {}).name;
}
function getProviderByItemId(itemId) {
const prefix = itemId.slice(0, 2);
return (PROVIDERS.find((i) => i.id === prefix) || {}).instance;
}
/* cache for all playlist request except myplaylist and localmusic */
const playlistCache = new LRUCache({
max: 100,
maxAge: 60 * 60 * 1000, // 1 hour cache expire
});
function queryStringify(options) {
const query = JSON.parse(JSON.stringify(options));
return new URLSearchParams(query).toString();
}
setPrototypeOfLocalStorage();
// eslint-disable-next-line no-unused-vars
const MediaService = {
getLoginProviders() {
return PROVIDERS.filter((i) => !i.hidden && i.support_login);
},
search(source, options) {
const url = `/search?${queryStringify(options)}`;
if (source === 'allmusic') {
// search all platform and merge result
const callbackArray = getAllSearchProviders().map((p) => (fn) => {
p.search(url).success((r) => {
fn(null, r);
});
});
return {
success: (fn) =>
async.parallel(callbackArray, (err, platformResultArray) => {
// TODO: nicer pager, playlist support
const result = {
result: [],
total: 1000,
type: platformResultArray[0].type,
};
const maxLength = Math.max(
...platformResultArray.map((elem) => elem.result.length)
);
for (let i = 0; i < maxLength; i += 1) {
platformResultArray.forEach((elem) => {
if (i < elem.result.length) {
result.result.push(elem.result[i]);
}
});
}
return fn(result);
}),
};
}
const provider = getProviderByName(source);
return provider.search(url);
},
showMyPlaylist() {
return myplaylist.show_myplaylist('my');
},
showPlaylistArray(source, offset, filter_id) {
const provider = getProviderByName(source);
const url = `/show_playlist?${queryStringify({ offset, filter_id })}`;
return provider.show_playlist(url);
},
getPlaylistFilters(source) {
const provider = getProviderByName(source);
return provider.get_playlist_filters();
},
getLyric(track_id, album_id, lyric_url, tlyric_url) {
const provider = getProviderByItemId(track_id);
const url = `/lyric?${queryStringify({
track_id,
album_id,
lyric_url,
tlyric_url,
})}`;
return provider.lyric(url);
},
showFavPlaylist() {
return myplaylist.show_myplaylist('favorite');
},
queryPlaylist(listId, type) {
const result = myplaylist.myplaylist_containers(type, listId);
return {
success: (fn) => fn({ result }),
};
},
getPlaylist(listId, useCache = true) {
const provider = getProviderByItemId(listId);
const url = `/playlist?list_id=${listId}`;
let hit = null;
if (useCache) {
hit = playlistCache.get(listId);
}
if (hit) {
return {
success: (fn) => fn(hit),
};
}
return {
success: (fn) =>
provider.get_playlist(url).success((playlist) => {
if (provider !== myplaylist && provider !== localmusic) {
playlistCache.set(listId, playlist);
}
fn(playlist);
}),
};
},
clonePlaylist(id, type) {
const provider = getProviderByItemId(id);
const url = `/playlist?list_id=${id}`;
return {
success: (fn) => {
provider.get_playlist(url).success((data) => {
myplaylist.save_myplaylist(type, data);
fn();
});
},
};
},
removeMyPlaylist(id, type) {
myplaylist.remove_myplaylist(type, id);
return {
success: (fn) => fn(),
};
},
addMyPlaylist(id, track) {
const newPlaylist = myplaylist.add_track_to_myplaylist(id, track);
return {
success: (fn) => fn(newPlaylist),
};
},
insertTrackToMyPlaylist(id, track, to_track, direction) {
const newPlaylist = myplaylist.insert_track_to_myplaylist(
id,
track,
to_track,
direction
);
return {
success: (fn) => fn(newPlaylist),
};
},
addPlaylist(id, tracks) {
const provider = getProviderByItemId(id);
return provider.add_playlist(id, tracks);
},
removeTrackFromMyPlaylist(id, track) {
myplaylist.remove_track_from_myplaylist(id, track);
return {
success: (fn) => fn(),
};
},
removeTrackFromPlaylist(id, track) {
const provider = getProviderByItemId(id);
return provider.remove_from_playlist(id, track);
},
createMyPlaylist(title, track) {
myplaylist.create_myplaylist(title, track);
return {
success: (fn) => {
fn();
},
};
},
insertMyplaylistToMyplaylists(
playlistType,
playlistId,
toPlaylistId,
direction
) {
const newPlaylists = myplaylist.insert_myplaylist_to_myplaylists(
playlistType,
playlistId,
toPlaylistId,
direction
);
return {
success: (fn) => fn(newPlaylists),
};
},
editMyPlaylist(id, title, coverImgUrl) {
myplaylist.edit_myplaylist(id, title, coverImgUrl);
return {
success: (fn) => fn(),
};
},
parseURL(url) {
return {
success: (fn) => {
const providers = getAllProviders();
Promise.all(
providers.map(
(provider) =>
new Promise((res, rej) =>
provider.parse_url(url).success((r) => {
if (r !== undefined) {
return rej(r);
}
return res(r);
})
)
)
)
.then(() => fn({}))
.catch((result) => fn({ result }));
},
};
},
mergePlaylist(source, target) {
const tarData = localStorage.getObject(target).tracks;
const srcData = localStorage.getObject(source).tracks;
tarData.forEach((tarTrack) => {
if (!srcData.find((srcTrack) => srcTrack.id === tarTrack.id)) {
myplaylist.add_track_to_myplaylist(source, tarTrack);
}
});
return {
success: (fn) => fn(),
};
},
bootstrapTrack(track, playerSuccessCallback, playerFailCallback) {
const successCallback = playerSuccessCallback;
const sound = {};
function failureCallback() {
if (localStorage.getObject('enable_auto_choose_source') === false) {
playerFailCallback();
return;
}
const trackPlatform = getProviderNameByItemId(track.id);
const failover_source_list = getLocalStorageValue(
'auto_choose_source_list',
['kuwo', 'qq', 'migu']
).filter((i) => i !== trackPlatform);
const getUrlPromises = failover_source_list.map(
(source) =>
new Promise((resolve, reject) => {
if (track.source === source) {
// come from same source, no need to check
resolve();
return;
}
// TODO: better query method
const keyword = `${track.title} ${track.artist}`;
const curpage = 1;
const url = `/search?keywords=${keyword}&curpage=${curpage}&type=0`;
const provider = getProviderByName(source);
provider.search(url).success((data) => {
for (let i = 0; i < data.result.length; i += 1) {
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 &&
searchTrack.title === track.title &&
searchTrack.artist === track.artist
) {
provider.bootstrap_track(
searchTrack,
(response) => {
sound.url = response.url;
sound.bitrate = response.bitrate;
sound.platform = response.platform;
reject(sound); // Use Reject to return immediately
},
resolve
);
return;
}
}
resolve(sound);
});
})
);
// TODO: Use Promise.any() in ES2021 replace the tricky workaround
Promise.all(getUrlPromises)
.then(playerFailCallback)
.catch((response) => {
playerSuccessCallback(response);
});
}
const provider = getProviderByName(track.source);
provider.bootstrap_track(track, successCallback, failureCallback);
},
login(source, options) {
const url = `/login?${queryStringify(options)}`;
const provider = getProviderByName(source);
return provider.login(url);
},
getUser(source) {
const provider = getProviderByName(source);
return provider.get_user();
},
getLoginUrl(source) {
const provider = getProviderByName(source);
return provider.get_login_url();
},
getUserCreatedPlaylist(source, options) {
const provider = getProviderByName(source);
const url = `/get_user_create_playlist?${queryStringify(options)}`;
return provider.get_user_created_playlist(url);
},
getUserFavoritePlaylist(source, options) {
const provider = getProviderByName(source);
const url = `/get_user_favorite_playlist?${queryStringify(options)}`;
return provider.get_user_favorite_playlist(url);
},
getRecommendPlaylist(source) {
const provider = getProviderByName(source);
return provider.get_recommend_playlist();
},
logout(source) {
const provider = getProviderByName(source);
return provider.logout();
},
};
// eslint-disable-next-line no-unused-vars
const loWeb = MediaService;