咪咕.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. function migu(packages) {
  2. const { axios, cheerio } = packages;
  3. const searchRows = 20;
  4. async function searchBase(query, page, type) {
  5. const headers = {
  6. 'Accept': 'application/json, text/javascript, */*; q=0.01',
  7. 'Accept-Encoding': 'gzip, deflate, br',
  8. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
  9. 'Connection': 'keep-alive',
  10. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  11. 'Host': 'm.music.migu.cn',
  12. 'Referer': `https://m.music.migu.cn/v3/search?keyword=${encodeURIComponent(query)}`,
  13. 'Sec-Fetch-Dest': 'empty',
  14. 'Sec-Fetch-Mode': 'cors',
  15. 'Sec-Fetch-Site': 'same-origin',
  16. 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
  17. 'X-Requested-With': 'XMLHttpRequest'
  18. };
  19. const params = {
  20. keyword: query,
  21. type,
  22. pgc: page,
  23. rows: searchRows
  24. }
  25. const data = await axios.get('https://m.music.migu.cn/migu/remoting/scr_search_tag', { headers, params });
  26. return data.data;
  27. }
  28. function musicCanPlayFilter(_) {
  29. return _.mp3 || _.listenUrl || _.lisQq || _.lisCr;
  30. }
  31. async function searchMusic(query, page) {
  32. const data = await searchBase(query, page, 2);
  33. const musics = data.musics.filter(musicCanPlayFilter).map(_ => ({
  34. id: _.id,
  35. artwork: _.cover,
  36. title: _.songName,
  37. artist: _.artist,
  38. album: _.albumName,
  39. url: musicCanPlayFilter(_),
  40. copyrightId: _.copyrightId,
  41. singerId: _.singerId,
  42. }))
  43. return {
  44. isEnd: (+data.pageNo) * searchRows >= data.pgt,
  45. data: musics
  46. };
  47. }
  48. async function searchAlbum(query, page) {
  49. const data = await searchBase(query, page, 4);
  50. const albums = data.albums.map(_ => ({
  51. id: _.id,
  52. artwork: _.albumPicM,
  53. title: _.title,
  54. date: _.publishDate,
  55. artist: (_.singer || []).map(s => s.name).join(','),
  56. singer: _.singer,
  57. fullSongTotal: _.fullSongTotal
  58. }));
  59. return {
  60. isEnd: (+data.pageNo) * searchRows >= data.pgt,
  61. data: albums
  62. }
  63. }
  64. async function searchArtist(query, page) {
  65. const data = await searchBase(query, page, 1);
  66. const artists = data.artists.map(result => ({
  67. name: result.title,
  68. id: result.id,
  69. avatar: result.artistPicM,
  70. worksNum: result.songNum
  71. }));
  72. return {
  73. isEnd: (+data.pageNo) * searchRows >= data.pgt,
  74. data: artists,
  75. }
  76. }
  77. async function getArtistAlbumWorks(artistItem, page) {
  78. const headers = {
  79. 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
  80. 'accept-encoding': 'gzip, deflate, br',
  81. 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
  82. 'connection': 'keep-alive',
  83. 'host': 'music.migu.cn',
  84. 'referer': 'http://music.migu.cn',
  85. 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
  86. 'Cache-Control': 'max-age=0',
  87. };
  88. const html = (await axios.get(`https://music.migu.cn/v3/music/artist/${artistItem.id}/album?page=${page}`, {
  89. headers
  90. })).data;
  91. const $ = cheerio.load(html);
  92. const rawAlbums = $('div.artist-album-list').find('li');
  93. const albums = [];
  94. for (let i = 0; i < rawAlbums.length; ++i) {
  95. const al = $(rawAlbums[i]);
  96. const artwork = al.find('.thumb-img').attr('data-original');
  97. albums.push({
  98. id: al.find('.album-play').attr('data-id'),
  99. title: al.find('.album-name').text(),
  100. artwork: artwork.startsWith('//') ? `https:${artwork}` : artwork,
  101. date: '',
  102. artist: artistItem.name
  103. })
  104. }
  105. return {
  106. isEnd: $('.pagination-next').hasClass('disabled'),
  107. data: albums
  108. }
  109. }
  110. async function getArtistWorks(artistItem, page, type) {
  111. if (type === 'music') {
  112. const headers = {
  113. 'Accept': 'application/json, text/javascript, */*; q=0.01',
  114. 'Accept-Encoding': 'gzip, deflate, br',
  115. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
  116. 'Connection': 'keep-alive',
  117. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  118. 'Host': 'm.music.migu.cn',
  119. 'Referer': `https://m.music.migu.cn/migu/l/?s=149&p=163&c=5123&j=l&id=${artistItem.id}`,
  120. 'Sec-Fetch-Dest': 'empty',
  121. 'Sec-Fetch-Mode': 'cors',
  122. 'Sec-Fetch-Site': 'same-origin',
  123. 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
  124. 'X-Requested-With': 'XMLHttpRequest'
  125. };
  126. // https://m.music.migu.cn/migu/remoting/cms_artist_detail_tag
  127. const musicList = (await axios.get('https://m.music.migu.cn/migu/remoting/cms_artist_song_list_tag', {
  128. headers,
  129. params: {
  130. artistId: artistItem.id,
  131. pageSize: 20,
  132. pageNo: page - 1
  133. }
  134. })).data || {};
  135. return {
  136. data: musicList.result.results.filter(musicCanPlayFilter).map(_ => ({
  137. id: _.songId,
  138. artwork: _.picM,
  139. title: _.songName,
  140. artist: (_.singerName || []).join(', '),
  141. album: _.albumName,
  142. url: musicCanPlayFilter(_),
  143. rawLrc: _.lyricLrc,
  144. copyrightId: _.copyrightId,
  145. singerId: _.singerId,
  146. }))
  147. }
  148. } else if (type === 'album') {
  149. return getArtistAlbumWorks(artistItem, page);
  150. }
  151. }
  152. async function getLyric(musicItem) {
  153. const headers = {
  154. 'Accept': 'application/json, text/javascript, */*; q=0.01',
  155. 'Accept-Encoding': 'gzip, deflate, br',
  156. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
  157. 'Connection': 'keep-alive',
  158. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  159. 'Host': 'm.music.migu.cn',
  160. 'Referer': `https://m.music.migu.cn/migu/l/?s=149&p=163&c=5200&j=l&id=${musicItem.copyrightId}`,
  161. 'Sec-Fetch-Dest': 'empty',
  162. 'Sec-Fetch-Mode': 'cors',
  163. 'Sec-Fetch-Site': 'same-origin',
  164. 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
  165. 'X-Requested-With': 'XMLHttpRequest'
  166. };
  167. const result = (await axios.get('https://m.music.migu.cn/migu/remoting/cms_detail_tag', {
  168. headers,
  169. params: {
  170. cpid: musicItem.copyrightId
  171. }
  172. })).data;
  173. return {
  174. rawLrc: result.data.lyricLrc
  175. }
  176. }
  177. async function importMusicSheet(urlLike) {
  178. let id;
  179. if (!id) {
  180. id = (urlLike.match(/https?:\/\/music\.migu\.cn\/v3\/my\/playlist\/([0-9]+)/) || [])[1];
  181. }
  182. if (!id) {
  183. id = (urlLike.match(/https?:\/\/h5\.nf\.migu\.cn\/app\/v4\/p\/share\/playlist\/index.html\?.*id=([0-9]+)/) || [])[1];
  184. }
  185. if (!id) {
  186. return;
  187. }
  188. const headers = {
  189. host: 'm.music.migu.cn',
  190. 'Sec-Fetch-Dest': 'empty',
  191. 'Sec-Fetch-Mode': 'cors',
  192. 'Sec-Fetch-Site': 'same-origin',
  193. 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
  194. 'X-Requested-With': 'XMLHttpRequest',
  195. 'Referer': 'https://m.music.migu.cn'
  196. }
  197. const res = (await (axios.get(`https://m.music.migu.cn/migu/remoting/query_playlist_by_id_tag?onLine=1&queryChannel=0&createUserId=migu&contentCountMin=5&playListId=${id}`, {
  198. headers
  199. }))).data;
  200. const contentCount = parseInt(res.rsp.playList[0].contentCount);
  201. const cids = [];
  202. let pageNo = 1;
  203. while ((pageNo-1) * 20 < contentCount) {
  204. const listPage = (await axios.get(`https://music.migu.cn/v3/music/playlist/${id}?page=${pageNo}`)).data;
  205. const $ = cheerio.load(listPage);
  206. $('.row.J_CopySong').each((i, v) => cids.push($(v).attr('data-cid')));
  207. pageNo += 1;
  208. }
  209. if(cids.length === 0) {
  210. return;
  211. }
  212. const songs = (await axios({
  213. url: `https://music.migu.cn/v3/api/music/audioPlayer/songs?type=1&copyrightId=${cids.join(',')}`,
  214. headers: {
  215. referer: 'http://m.music.migu.cn/v3'
  216. },
  217. xsrfCookieName: 'XSRF-TOKEN',
  218. withCredentials: true,
  219. })).data;
  220. return songs.items.filter(_ => _.vipFlag === 0).map(_ => ({
  221. id: _.songId,
  222. artwork: _.cover,
  223. title: _.songName,
  224. artist: (_.singers || []).map(_ => _.artistName).join(', '),
  225. album: ((_.albums || [])[0] || {}).albumName,
  226. copyrightId: _.copyrightId,
  227. singerId: ((_.singers || [])[0] || {}).artistId,
  228. }))
  229. }
  230. return {
  231. platform: '咪咕',
  232. version: '0.0.2',
  233. primaryKey: ['id', 'copyrightId'],
  234. cacheControl: 'no-store',
  235. srcUrl: 'https://gitee.com/maotoumao/MusicFreePlugins/raw/master/migu.js',
  236. async getMediaSource(musicItem) {
  237. if(musicItem.url) {
  238. return {
  239. url: musicItem.url
  240. }
  241. };
  242. const resource = (await axios({
  243. url: `https://app.c.nf.migu.cn/MIGUM2.0/strategy/listen-url/v2.2?netType=01&resourceType=E&songId=${musicItem.copyrightId}&toneFlag=HQ`,
  244. headers: {
  245. referer: 'http://m.music.migu.cn/v3',
  246. uid: 123,
  247. channel: '0146741'
  248. }
  249. })).data.data;
  250. return {
  251. artwork: musicItem.artwork || ((resource.songItem.albumImgs[0] || {}).img),
  252. url: resource.url
  253. };
  254. },
  255. async search(query, page, type) {
  256. if (type === 'music') {
  257. return await searchMusic(query, page);
  258. }
  259. if (type === 'album') {
  260. return await searchAlbum(query, page);
  261. }
  262. if (type === 'artist') {
  263. return await searchArtist(query, page);
  264. }
  265. },
  266. async getAlbumInfo(albumItem) {
  267. const headers = {
  268. 'Accept': 'application/json, text/javascript, */*; q=0.01',
  269. 'Accept-Encoding': 'gzip, deflate, br',
  270. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
  271. 'Connection': 'keep-alive',
  272. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  273. 'Host': 'm.music.migu.cn',
  274. 'Referer': `https://m.music.migu.cn/migu/l/?record=record&id=${albumItem.id}`,
  275. 'Sec-Fetch-Dest': 'empty',
  276. 'Sec-Fetch-Mode': 'cors',
  277. 'Sec-Fetch-Site': 'same-origin',
  278. 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
  279. 'X-Requested-With': 'XMLHttpRequest'
  280. };
  281. const musicList = (await axios.get('https://m.music.migu.cn/migu/remoting/cms_album_song_list_tag', {
  282. headers,
  283. params: {
  284. albumId: albumItem.id,
  285. pageSize: 30
  286. }
  287. })).data || {};
  288. const albumDesc = (await axios.get('https://m.music.migu.cn/migu/remoting/cms_album_detail_tag', {
  289. headers,
  290. params: {
  291. albumId: albumItem.id
  292. }
  293. })).data || {}; // 有个trackcount
  294. return {
  295. ...albumItem,
  296. description: albumDesc.albumIntro,
  297. musicList: musicList.result.results.filter(musicCanPlayFilter).map(_ => ({
  298. id: _.songId,
  299. artwork: _.picM,
  300. title: _.songName,
  301. artist: (_.singerName || []).join(', '),
  302. album: albumItem.title,
  303. url: musicCanPlayFilter(_),
  304. rawLrc: _.lyricLrc,
  305. copyrightId: _.copyrightId,
  306. singerId: _.singerId,
  307. }))
  308. }
  309. },
  310. getArtistWorks: getArtistWorks,
  311. getLyric: getLyric,
  312. importMusicSheet
  313. };
  314. }