@neteasecloudmusicapienhanced/api 4.31.0 → 4.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (439) hide show
  1. package/LICENSE +21 -21
  2. package/README.MD +248 -251
  3. package/app.js +18 -18
  4. package/data/china_ip_ranges.txt +4147 -4147
  5. package/data/deviceid.txt +24641 -24641
  6. package/generateConfig.js +24 -24
  7. package/interface.d.ts +1843 -1836
  8. package/main.js +63 -63
  9. package/module/activate_init_profile.js +9 -9
  10. package/module/aidj_content_rcmd.js +28 -28
  11. package/module/album.js +6 -6
  12. package/module/album_detail.js +12 -12
  13. package/module/album_detail_dynamic.js +12 -12
  14. package/module/album_list.js +16 -16
  15. package/module/album_list_style.js +15 -15
  16. package/module/album_new.js +11 -11
  17. package/module/album_newest.js +6 -6
  18. package/module/album_privilege.js +9 -9
  19. package/module/album_songsaleboard.js +19 -19
  20. package/module/album_sub.js +10 -10
  21. package/module/album_sublist.js +11 -11
  22. package/module/api.js +21 -21
  23. package/module/artist_album.js +15 -15
  24. package/module/artist_desc.js +9 -9
  25. package/module/artist_detail.js +10 -10
  26. package/module/artist_detail_dynamic.js +9 -9
  27. package/module/artist_fans.js +11 -11
  28. package/module/artist_follow_count.js +13 -13
  29. package/module/artist_list.js +33 -33
  30. package/module/artist_mv.js +12 -12
  31. package/module/artist_new_mv.js +12 -12
  32. package/module/artist_new_song.js +12 -12
  33. package/module/artist_songs.js +12 -12
  34. package/module/artist_sub.js +11 -11
  35. package/module/artist_sublist.js +11 -11
  36. package/module/artist_top_song.js +8 -8
  37. package/module/artist_video.js +15 -15
  38. package/module/artists.js +6 -6
  39. package/module/audio_match.js +19 -19
  40. package/module/avatar_upload.js +22 -22
  41. package/module/banner.js +16 -16
  42. package/module/batch.js +12 -12
  43. package/module/broadcast_category_region_get.js +11 -11
  44. package/module/broadcast_channel_collect_list.js +12 -12
  45. package/module/broadcast_channel_currentinfo.js +13 -13
  46. package/module/broadcast_channel_list.js +13 -13
  47. package/module/broadcast_sub.js +12 -12
  48. package/module/calendar.js +8 -8
  49. package/module/captcha_sent.js +11 -11
  50. package/module/captcha_verify.js +11 -11
  51. package/module/cellphone_existence_check.js +10 -10
  52. package/module/check_music.js +30 -30
  53. package/module/cloud.js +164 -164
  54. package/module/cloud_import.js +39 -39
  55. package/module/cloud_lyric_get.js +11 -11
  56. package/module/cloud_match.js +13 -13
  57. package/module/cloud_upload_complete.js +72 -72
  58. package/module/cloud_upload_token.js +111 -111
  59. package/module/cloudsearch.js +13 -13
  60. package/module/comment.js +30 -30
  61. package/module/comment_album.js +16 -16
  62. package/module/comment_dj.js +16 -16
  63. package/module/comment_event.js +15 -15
  64. package/module/comment_floor.js +16 -16
  65. package/module/comment_hot.js +18 -18
  66. package/module/comment_hug_list.js +20 -20
  67. package/module/comment_info_list.js +30 -30
  68. package/module/comment_like.js +20 -20
  69. package/module/comment_music.js +16 -16
  70. package/module/comment_mv.js +16 -16
  71. package/module/comment_new.js +37 -37
  72. package/module/comment_playlist.js +16 -16
  73. package/module/{comment_delete.js → comment_report.js} +11 -10
  74. package/module/comment_video.js +16 -16
  75. package/module/countries_code_list.js +6 -6
  76. package/module/creator_authinfo_get.js +6 -6
  77. package/module/daily_signin.js +16 -16
  78. package/module/digitalAlbum_detail.js +13 -13
  79. package/module/digitalAlbum_ordering.js +22 -22
  80. package/module/digitalAlbum_purchased.js +15 -15
  81. package/module/digitalAlbum_sales.js +13 -13
  82. package/module/djRadio_top.js +15 -15
  83. package/module/dj_banner.js +6 -6
  84. package/module/dj_category_excludehot.js +10 -10
  85. package/module/dj_category_recommend.js +10 -10
  86. package/module/dj_catelist.js +6 -6
  87. package/module/dj_detail.js +9 -9
  88. package/module/dj_difm_all_style_channel.js +9 -9
  89. package/module/dj_difm_channel_subscribe.js +9 -9
  90. package/module/dj_difm_channel_unsubscribe.js +9 -9
  91. package/module/dj_difm_playing_tracks_list.js +11 -11
  92. package/module/dj_difm_subscribe_channels_get.js +13 -13
  93. package/module/dj_hot.js +10 -10
  94. package/module/dj_paygift.js +15 -15
  95. package/module/dj_personalize_recommend.js +12 -12
  96. package/module/dj_program.js +12 -12
  97. package/module/dj_program_detail.js +9 -9
  98. package/module/dj_program_toplist.js +10 -10
  99. package/module/dj_program_toplist_hours.js +13 -13
  100. package/module/dj_radio_hot.js +11 -11
  101. package/module/dj_recommend.js +6 -6
  102. package/module/dj_recommend_type.js +32 -32
  103. package/module/dj_sub.js +10 -10
  104. package/module/dj_sublist.js +11 -11
  105. package/module/dj_subscriber.js +12 -12
  106. package/module/dj_today_perfered.js +13 -13
  107. package/module/dj_toplist.js +14 -14
  108. package/module/dj_toplist_hours.js +10 -10
  109. package/module/dj_toplist_newcomer.js +9 -9
  110. package/module/dj_toplist_pay.js +9 -9
  111. package/module/dj_toplist_popular.js +10 -10
  112. package/module/eapi_decrypt.js +27 -27
  113. package/module/event.js +10 -10
  114. package/module/event_del.js +9 -9
  115. package/module/event_forward.js +11 -11
  116. package/module/fanscenter_basicinfo_age_get.js +6 -6
  117. package/module/fanscenter_basicinfo_gender_get.js +10 -10
  118. package/module/fanscenter_basicinfo_province_get.js +10 -10
  119. package/module/fanscenter_overview_get.js +6 -6
  120. package/module/fanscenter_trend_list.js +10 -10
  121. package/module/fm_trash.js +11 -11
  122. package/module/follow.js +11 -11
  123. package/module/get_userids.js +7 -7
  124. package/module/history_recommend_songs.js +11 -11
  125. package/module/history_recommend_songs_detail.js +13 -13
  126. package/module/homepage_block_page.js +8 -8
  127. package/module/homepage_dragon_ball.js +10 -10
  128. package/module/hot_topic.js +10 -10
  129. package/module/hug_comment.js +16 -16
  130. package/module/inner_version.js +16 -16
  131. package/module/like.js +13 -13
  132. package/module/likelist.js +9 -9
  133. package/module/listen_data_realtime_report.js +11 -11
  134. package/module/listen_data_report.js +12 -12
  135. package/module/listen_data_today_song.js +9 -9
  136. package/module/listen_data_total.js +9 -9
  137. package/module/listen_data_year_report.js +9 -9
  138. package/module/listentogether_accept.js +13 -13
  139. package/module/listentogether_end.js +9 -9
  140. package/module/listentogether_heatbeat.js +12 -12
  141. package/module/listentogether_play_command.js +21 -21
  142. package/module/listentogether_room_check.js +9 -9
  143. package/module/listentogether_room_create.js +9 -9
  144. package/module/listentogether_status.js +10 -10
  145. package/module/listentogether_sync_list_command.js +26 -26
  146. package/module/listentogether_sync_playlist_get.js +13 -13
  147. package/module/login.js +41 -41
  148. package/module/login_cellphone.js +40 -40
  149. package/module/login_qr_check.js +29 -29
  150. package/module/login_qr_create.js +30 -30
  151. package/module/login_qr_key.js +19 -19
  152. package/module/login_refresh.js +21 -21
  153. package/module/login_status.js +21 -21
  154. package/module/logout.js +6 -6
  155. package/module/lyric.js +14 -14
  156. package/module/lyric_new.js +17 -17
  157. package/module/mlog_music_rcmd.js +13 -13
  158. package/module/mlog_to_video.js +13 -13
  159. package/module/mlog_url.js +11 -11
  160. package/module/msg_comments.js +17 -17
  161. package/module/msg_forwards.js +11 -11
  162. package/module/msg_notices.js +10 -10
  163. package/module/msg_private.js +11 -11
  164. package/module/msg_private_history.js +12 -12
  165. package/module/msg_recentcontact.js +11 -11
  166. package/module/music_first_listen_info.js +13 -13
  167. package/module/musician_cloudbean.js +7 -7
  168. package/module/musician_cloudbean_obtain.js +14 -14
  169. package/module/musician_data_overview.js +11 -11
  170. package/module/musician_play_trend.js +14 -14
  171. package/module/musician_sign.js +7 -7
  172. package/module/musician_tasks.js +11 -11
  173. package/module/musician_tasks_new.js +11 -11
  174. package/module/musician_vip_tasks.js +11 -11
  175. package/module/mv_all.js +16 -16
  176. package/module/mv_detail.js +9 -9
  177. package/module/mv_detail_info.js +14 -14
  178. package/module/mv_exclusive_rcmd.js +10 -10
  179. package/module/mv_first.js +12 -12
  180. package/module/mv_sub.js +11 -11
  181. package/module/mv_sublist.js +15 -15
  182. package/module/mv_url.js +14 -14
  183. package/module/nickname_check.js +7 -7
  184. package/module/personal_fm.js +6 -6
  185. package/module/personal_fm_mode.js +14 -14
  186. package/module/personalized.js +16 -16
  187. package/module/personalized_djprogram.js +10 -10
  188. package/module/personalized_mv.js +6 -6
  189. package/module/personalized_newsong.js +15 -15
  190. package/module/personalized_privatecontent.js +10 -10
  191. package/module/personalized_privatecontent_list.js +15 -15
  192. package/module/pl_count.js +6 -6
  193. package/module/playlist_category_list.js +11 -11
  194. package/module/playlist_catlist.js +6 -6
  195. package/module/playlist_cover_update.js +32 -32
  196. package/module/playlist_create.js +11 -11
  197. package/module/playlist_delete.js +9 -9
  198. package/module/playlist_desc_update.js +10 -10
  199. package/module/playlist_detail.js +11 -11
  200. package/module/playlist_detail_dynamic.js +11 -11
  201. package/module/playlist_detail_rcmd_get.js +11 -11
  202. package/module/playlist_highquality_tags.js +10 -10
  203. package/module/playlist_hot.js +6 -6
  204. package/module/playlist_import_name_task_create.js +62 -62
  205. package/module/playlist_import_task_status.js +11 -11
  206. package/module/playlist_mylike.js +12 -12
  207. package/module/playlist_name_update.js +10 -10
  208. package/module/playlist_order_update.js +13 -13
  209. package/module/playlist_privacy.js +10 -10
  210. package/module/playlist_subscribe.js +14 -14
  211. package/module/playlist_subscribers.js +11 -11
  212. package/module/playlist_tags_update.js +10 -10
  213. package/module/playlist_track_add.js +16 -16
  214. package/module/playlist_track_all.js +31 -31
  215. package/module/playlist_track_delete.js +20 -20
  216. package/module/playlist_tracks.js +45 -45
  217. package/module/playlist_update.js +13 -13
  218. package/module/playlist_update_playcount.js +9 -9
  219. package/module/playlist_video_recent.js +9 -9
  220. package/module/playmode_intelligence_list.js +13 -13
  221. package/module/playmode_song_vector.js +8 -8
  222. package/module/program_recommend.js +15 -15
  223. package/module/radio_sport_get.js +9 -0
  224. package/module/rebind.js +16 -16
  225. package/module/recent_listen_list.js +7 -7
  226. package/module/recommend_resource.js +10 -10
  227. package/module/recommend_songs.js +13 -11
  228. package/module/recommend_songs_dislike.js +14 -14
  229. package/module/record_recent_album.js +11 -11
  230. package/module/record_recent_dj.js +11 -11
  231. package/module/record_recent_playlist.js +11 -11
  232. package/module/record_recent_song.js +11 -11
  233. package/module/record_recent_video.js +11 -11
  234. package/module/record_recent_voice.js +11 -11
  235. package/module/register_anonimous.js +53 -52
  236. package/module/register_cellphone.js +15 -15
  237. package/module/related_allvideo.js +14 -14
  238. package/module/related_playlist.js +32 -32
  239. package/module/resource_like.js +14 -14
  240. package/module/sati_resource_list.js +11 -0
  241. package/module/sati_resource_list_more.js +13 -0
  242. package/module/sati_resource_sub.js +10 -0
  243. package/module/sati_resource_sub_list.js +7 -0
  244. package/module/sati_tag_list.js +7 -0
  245. package/module/sati_timescene_resources_get.js +13 -0
  246. package/module/scrobble.js +26 -26
  247. package/module/search.js +21 -21
  248. package/module/search_default.js +6 -6
  249. package/module/search_hot.js +9 -9
  250. package/module/search_hot_detail.js +6 -6
  251. package/module/search_match.js +18 -18
  252. package/module/search_multimatch.js +14 -14
  253. package/module/search_suggest.js +14 -14
  254. package/module/search_suggest_pc.js +13 -13
  255. package/module/send_album.js +12 -12
  256. package/module/send_playlist.js +12 -12
  257. package/module/send_song.js +12 -12
  258. package/module/send_text.js +11 -11
  259. package/module/setting.js +7 -7
  260. package/module/share_resource.js +11 -11
  261. package/module/sheet_list.js +9 -9
  262. package/module/sheet_preview.js +8 -8
  263. package/module/sign_happy_info.js +5 -5
  264. package/module/signin_progress.js +13 -13
  265. package/module/simi_artist.js +12 -12
  266. package/module/simi_mv.js +9 -9
  267. package/module/simi_playlist.js +15 -15
  268. package/module/simi_song.js +15 -15
  269. package/module/simi_user.js +11 -11
  270. package/module/song_chorus.js +11 -11
  271. package/module/song_copyright_rcmd.js +9 -0
  272. package/module/song_creators.js +9 -0
  273. package/module/song_detail.js +11 -11
  274. package/module/song_downlist.js +11 -11
  275. package/module/song_download_url.js +10 -10
  276. package/module/song_download_url_v1.js +13 -13
  277. package/module/song_dynamic_cover.js +9 -9
  278. package/module/song_like.js +12 -12
  279. package/module/song_like_check.js +9 -9
  280. package/module/song_lyrics_mark.js +9 -9
  281. package/module/song_lyrics_mark_add.js +12 -12
  282. package/module/song_lyrics_mark_del.js +9 -9
  283. package/module/song_lyrics_mark_user_page.js +14 -14
  284. package/module/song_monthdownlist.js +11 -11
  285. package/module/song_music_detail.js +9 -9
  286. package/module/song_order_update.js +12 -12
  287. package/module/song_purchased.js +14 -14
  288. package/module/song_red_count.js +9 -9
  289. package/module/song_singledownlist.js +11 -11
  290. package/module/song_url.js +26 -26
  291. package/module/song_url_match.js +38 -38
  292. package/module/song_url_ncmget.js +5 -5
  293. package/module/song_url_v1.js +57 -57
  294. package/module/song_url_v1_302.js +53 -53
  295. package/module/song_wiki_summary.js +8 -8
  296. package/module/starpick_comments_summary.js +12 -12
  297. package/module/style_album.js +16 -16
  298. package/module/style_artist.js +16 -16
  299. package/module/style_detail.js +9 -9
  300. package/module/style_list.js +7 -7
  301. package/module/style_playlist.js +16 -16
  302. package/module/style_preference.js +11 -11
  303. package/module/style_song.js +12 -12
  304. package/module/summary_annual.js +12 -12
  305. package/module/threshold_detail_get.js +10 -10
  306. package/module/top_album.js +22 -22
  307. package/module/top_artists.js +11 -11
  308. package/module/top_list.js +20 -20
  309. package/module/top_mv.js +12 -12
  310. package/module/top_playlist.js +22 -22
  311. package/module/top_playlist_highquality.js +16 -16
  312. package/module/top_song.js +16 -16
  313. package/module/topic_detail.js +7 -7
  314. package/module/topic_detail_event_hot.js +7 -7
  315. package/module/topic_sublist.js +11 -11
  316. package/module/toplist.js +6 -6
  317. package/module/toplist_artist.js +12 -12
  318. package/module/toplist_detail.js +6 -6
  319. package/module/toplist_detail_v2.js +6 -6
  320. package/module/ugc_album_get.js +8 -8
  321. package/module/ugc_artist_get.js +8 -8
  322. package/module/ugc_artist_search.js +10 -10
  323. package/module/ugc_detail.js +17 -17
  324. package/module/ugc_mv_get.js +8 -8
  325. package/module/ugc_song_get.js +8 -8
  326. package/module/ugc_user_devote.js +6 -6
  327. package/module/user_account.js +5 -5
  328. package/module/user_audio.js +9 -9
  329. package/module/user_binding.js +9 -9
  330. package/module/user_bindingcellphone.js +15 -15
  331. package/module/user_cloud.js +10 -10
  332. package/module/user_cloud_del.js +9 -9
  333. package/module/user_cloud_detail.js +10 -10
  334. package/module/user_comment_history.js +15 -15
  335. package/module/user_detail.js +15 -15
  336. package/module/user_detail_new.js +20 -20
  337. package/module/user_dj.js +14 -14
  338. package/module/user_event.js +12 -12
  339. package/module/user_follow_mixed.js +23 -23
  340. package/module/user_followeds.js +17 -17
  341. package/module/user_follows.js +15 -15
  342. package/module/user_level.js +7 -7
  343. package/module/user_medal.js +11 -11
  344. package/module/user_mutualfollow_get.js +9 -9
  345. package/module/user_playlist.js +12 -12
  346. package/module/user_playlist_collect.js +14 -14
  347. package/module/user_playlist_create.js +14 -14
  348. package/module/user_record.js +10 -10
  349. package/module/user_replacephone.js +14 -14
  350. package/module/user_social_status.js +11 -11
  351. package/module/user_social_status_edit.js +16 -16
  352. package/module/user_social_status_rcmd.js +5 -5
  353. package/module/user_social_status_support.js +5 -5
  354. package/module/user_subcount.js +6 -6
  355. package/module/user_update.js +15 -15
  356. package/module/verify_getQr.js +39 -39
  357. package/module/verify_qrcodestatus.js +12 -12
  358. package/module/video_category_list.js +15 -15
  359. package/module/video_detail.js +13 -13
  360. package/module/video_detail_info.js +14 -14
  361. package/module/video_group.js +16 -16
  362. package/module/video_group_list.js +11 -11
  363. package/module/video_sub.js +14 -14
  364. package/module/video_timeline_all.js +17 -17
  365. package/module/video_timeline_recommend.js +13 -13
  366. package/module/video_url.js +10 -10
  367. package/module/vip_growthpoint.js +11 -11
  368. package/module/vip_growthpoint_details.js +14 -14
  369. package/module/vip_growthpoint_get.js +13 -13
  370. package/module/vip_info.js +12 -12
  371. package/module/vip_info_v2.js +12 -12
  372. package/module/vip_sign.js +7 -11
  373. package/module/vip_sign_info.js +11 -11
  374. package/module/vip_tasks.js +11 -11
  375. package/module/vip_timemachine.js +17 -17
  376. package/module/voice_delete.js +7 -7
  377. package/module/voice_detail.js +7 -7
  378. package/module/voice_lyric.js +7 -7
  379. package/module/voice_upload.js +200 -200
  380. package/module/voicelist_detail.js +11 -11
  381. package/module/voicelist_list.js +13 -13
  382. package/module/voicelist_list_search.js +14 -14
  383. package/module/voicelist_my_created.js +13 -13
  384. package/module/voicelist_search.js +11 -11
  385. package/module/voicelist_trans.js +15 -15
  386. package/module/weblog.js +10 -10
  387. package/module/yunbei.js +6 -6
  388. package/module/yunbei_expense.js +8 -8
  389. package/module/yunbei_info.js +5 -5
  390. package/module/yunbei_rcmd_song.js +17 -17
  391. package/module/yunbei_rcmd_song_history.js +16 -16
  392. package/module/yunbei_receipt.js +8 -8
  393. package/module/yunbei_sign.js +5 -5
  394. package/module/yunbei_task_finish.js +12 -12
  395. package/module/yunbei_tasks.js +9 -9
  396. package/module/yunbei_tasks_todo.js +9 -9
  397. package/module/yunbei_today.js +5 -5
  398. package/package.json +8 -8
  399. package/plugins/songUpload.js +109 -109
  400. package/plugins/upload.js +35 -35
  401. package/public/api.html +222 -193
  402. package/public/audio_match_demo/afp.js +1626 -1626
  403. package/public/audio_match_demo/afp.wasm.js +5 -5
  404. package/public/audio_match_demo/index.html +367 -367
  405. package/public/audio_match_demo/rec.js +49 -49
  406. package/public/avatar_update.html +326 -326
  407. package/public/cloud.html +578 -578
  408. package/public/docs/_coverpage.md +13 -13
  409. package/public/docs/home.md +5318 -5218
  410. package/public/docs/index.html +129 -47
  411. package/public/docs/logo.svg +6 -6
  412. package/public/docs/netease.png +0 -0
  413. package/public/docs/sw.js +90 -90
  414. package/public/eapi_decrypt.html +264 -226
  415. package/public/home.html +40 -40
  416. package/public/index.html +124 -124
  417. package/public/listen_together_host.html +552 -552
  418. package/public/login.html +219 -219
  419. package/public/playlist_cover_update.html +323 -323
  420. package/public/playlist_import.html +416 -416
  421. package/public/qrlogin-nocookie.html +199 -199
  422. package/public/qrlogin.html +199 -199
  423. package/public/static/2169.png +0 -0
  424. package/public/static/neteaselogo.png +0 -0
  425. package/public/unblock_test.html +153 -153
  426. package/public/voice_upload.html +326 -326
  427. package/server.js +454 -435
  428. package/util/apicache.js +836 -836
  429. package/util/client-sign.js +169 -169
  430. package/util/config.json +20 -20
  431. package/util/crypto.js +153 -135
  432. package/util/fileHelper.js +88 -88
  433. package/util/index.js +233 -233
  434. package/util/logger.js +42 -42
  435. package/util/memory-cache.js +71 -71
  436. package/util/option.js +14 -14
  437. package/util/request.js +367 -368
  438. package/module/comment_add.js +0 -15
  439. package/module/comment_reply.js +0 -13
package/server.js CHANGED
@@ -1,435 +1,454 @@
1
- require('dotenv').config()
2
- const fs = require('fs')
3
- const path = require('path')
4
- const express = require('express')
5
- const request = require('./util/request')
6
- const packageJSON = require('./package.json')
7
- const exec = require('child_process').exec
8
- const cache = require('./util/apicache').middleware
9
- const { cookieToJson } = require('./util/index')
10
- const fileUpload = require('express-fileupload')
11
- const decode = require('safe-decode-uri-component')
12
- const logger = require('./util/logger.js')
13
-
14
- /**
15
- * The version check result.
16
- * @readonly
17
- * @enum {number}
18
- */
19
- const VERSION_CHECK_RESULT = {
20
- FAILED: -1,
21
- NOT_LATEST: 0,
22
- LATEST: 1,
23
- }
24
-
25
- /**
26
- * @typedef {{
27
- * identifier?: string,
28
- * route: string,
29
- * module: any
30
- * }} ModuleDefinition
31
- */
32
-
33
- /**
34
- * @typedef {{
35
- * port?: number,
36
- * host?: string,
37
- * checkVersion?: boolean,
38
- * moduleDefs?: ModuleDefinition[]
39
- * }} NcmApiOptions
40
- */
41
-
42
- /**
43
- * @typedef {{
44
- * status: VERSION_CHECK_RESULT,
45
- * ourVersion?: string,
46
- * npmVersion?: string,
47
- * }} VersionCheckResult
48
- */
49
-
50
- /**
51
- * @typedef {{
52
- * server?: import('http').Server,
53
- * }} ExpressExtension
54
- */
55
-
56
- /**
57
- * Get the module definitions dynamically.
58
- *
59
- * @param {string} modulesPath The path to modules (JS).
60
- * @param {Record<string, string>} [specificRoute] The specific route of specific modules.
61
- * @param {boolean} [doRequire] If true, require() the module directly.
62
- * Otherwise, print out the module path. Default to true.
63
- * @returns {Promise<ModuleDefinition[]>} The module definitions.
64
- *
65
- * @example getModuleDefinitions("./module", {"album_new.js": "/album/create"})
66
- */
67
- async function getModulesDefinitions(
68
- modulesPath,
69
- specificRoute,
70
- doRequire = true,
71
- ) {
72
- const files = await fs.promises.readdir(modulesPath)
73
- const parseRoute = (/** @type {string} */ fileName) =>
74
- specificRoute && fileName in specificRoute
75
- ? specificRoute[fileName]
76
- : `/${fileName.replace(/\.js$/i, '').replace(/_/g, '/')}`
77
-
78
- const modules = files
79
- .reverse()
80
- .filter((file) => file.endsWith('.js'))
81
- .map((file) => {
82
- const identifier = file.split('.').shift()
83
- const route = parseRoute(file)
84
- const modulePath = path.join(modulesPath, file)
85
- const module = doRequire ? require(modulePath) : modulePath
86
-
87
- return { identifier, route, module }
88
- })
89
-
90
- return modules
91
- }
92
-
93
- /**
94
- * Check if the version of this API is latest.
95
- *
96
- * @returns {Promise<VersionCheckResult>} If true, this API is up-to-date;
97
- * otherwise, this API should be upgraded and you would
98
- * need to notify users to upgrade it manually.
99
- */
100
- async function checkVersion() {
101
- return new Promise((resolve) => {
102
- exec('npm info NeteaseCloudMusicApiEnhanced version', (err, stdout) => {
103
- if (!err) {
104
- let version = stdout.trim()
105
-
106
- /**
107
- * @param {VERSION_CHECK_RESULT} status
108
- */
109
- const resolveStatus = (status) =>
110
- resolve({
111
- status,
112
- ourVersion: packageJSON.version,
113
- npmVersion: version,
114
- })
115
-
116
- resolveStatus(
117
- packageJSON.version < version
118
- ? VERSION_CHECK_RESULT.NOT_LATEST
119
- : VERSION_CHECK_RESULT.LATEST,
120
- )
121
- } else {
122
- resolve({
123
- status: VERSION_CHECK_RESULT.FAILED,
124
- })
125
- }
126
- })
127
- })
128
- }
129
-
130
- function parseCorsAllowOrigins(corsAllowOrigin) {
131
- if (!corsAllowOrigin) {
132
- return null
133
- }
134
-
135
- const origins = corsAllowOrigin
136
- .split(',')
137
- .map((origin) => origin.trim())
138
- .filter(Boolean)
139
-
140
- return origins.length > 0 ? origins : null
141
- }
142
-
143
- function getCorsAllowOrigin(allowOrigins, requestOrigin) {
144
- if (!allowOrigins) {
145
- return requestOrigin || '*'
146
- }
147
-
148
- if (allowOrigins.includes('*')) {
149
- return '*'
150
- }
151
-
152
- if (requestOrigin && allowOrigins.includes(requestOrigin)) {
153
- return requestOrigin
154
- }
155
-
156
- return null
157
- }
158
-
159
- /**
160
- * Construct the server of NCM API.
161
- *
162
- * @param {ModuleDefinition[]} [moduleDefs] Customized module definitions [advanced]
163
- * @returns {Promise<import("express").Express>} The server instance.
164
- */
165
- async function constructServer(moduleDefs) {
166
- const app = express()
167
- const { CORS_ALLOW_ORIGIN } = process.env
168
- const allowOrigins = parseCorsAllowOrigins(CORS_ALLOW_ORIGIN)
169
- app.set('trust proxy', true)
170
-
171
- /**
172
- * Serving static files
173
- */
174
- app.use(express.static(path.join(__dirname, 'public')))
175
- /**
176
- * CORS & Preflight request
177
- */
178
- app.use((req, res, next) => {
179
- if (req.path !== '/' && !req.path.includes('.')) {
180
- const corsAllowOrigin = getCorsAllowOrigin(
181
- allowOrigins,
182
- req.headers.origin,
183
- )
184
- const shouldSetVaryHeader = corsAllowOrigin && corsAllowOrigin !== '*'
185
- res.set({
186
- 'Access-Control-Allow-Credentials': true,
187
- ...(corsAllowOrigin
188
- ? { 'Access-Control-Allow-Origin': corsAllowOrigin }
189
- : {}),
190
- ...(shouldSetVaryHeader ? { Vary: 'Origin' } : {}),
191
- 'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
192
- 'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',
193
- 'Content-Type': 'application/json; charset=utf-8',
194
- })
195
- }
196
- req.method === 'OPTIONS' ? res.status(204).end() : next()
197
- })
198
-
199
- /**
200
- * Cookie Parser
201
- */
202
- app.use((req, _, next) => {
203
- req.cookies = {}
204
- //;(req.headers.cookie || '').split(/\s*;\s*/).forEach((pair) => { // Polynomial regular expression //
205
- ;(req.headers.cookie || '').split(/;\s+|(?<!\s)\s+$/g).forEach((pair) => {
206
- let crack = pair.indexOf('=')
207
- if (crack < 1 || crack == pair.length - 1) return
208
- req.cookies[decode(pair.slice(0, crack)).trim()] = decode(
209
- pair.slice(crack + 1),
210
- ).trim()
211
- })
212
- next()
213
- })
214
-
215
- /**
216
- * Body Parser and File Upload
217
- */
218
- const MAX_UPLOAD_SIZE_MB = 500
219
- const MAX_UPLOAD_SIZE_BYTES = MAX_UPLOAD_SIZE_MB * 1024 * 1024
220
-
221
- app.use(express.json({ limit: `${MAX_UPLOAD_SIZE_MB}mb` }))
222
- app.use(
223
- express.urlencoded({ extended: false, limit: `${MAX_UPLOAD_SIZE_MB}mb` }),
224
- )
225
-
226
- app.use(
227
- fileUpload({
228
- limits: {
229
- fileSize: MAX_UPLOAD_SIZE_BYTES,
230
- },
231
- useTempFiles: true,
232
- tempFileDir: require('os').tmpdir(),
233
- abortOnLimit: true,
234
- parseNested: true,
235
- }),
236
- )
237
-
238
- /**
239
- * Cache
240
- */
241
- app.use(cache('2 minutes', (_, res) => res.statusCode === 200))
242
-
243
- /**
244
- * Special Routers
245
- */
246
- const special = {
247
- 'daily_signin.js': '/daily_signin',
248
- 'fm_trash.js': '/fm_trash',
249
- 'personal_fm.js': '/personal_fm',
250
- }
251
-
252
- /**
253
- * Load every modules in this directory
254
- */
255
- const moduleDefinitions =
256
- moduleDefs ||
257
- (await getModulesDefinitions(path.join(__dirname, 'module'), special))
258
-
259
- for (const moduleDef of moduleDefinitions) {
260
- // Register the route.
261
- app.all(moduleDef.route, async (req, res) => {
262
- ;[req.query, req.body].forEach((item) => {
263
- // item may be undefined (some environments / middlewares).
264
- // Guard access to avoid "Cannot read properties of undefined (reading 'cookie')".
265
- if (item && typeof item.cookie === 'string') {
266
- item.cookie = cookieToJson(decode(item.cookie))
267
- }
268
- })
269
-
270
- let query = Object.assign(
271
- {},
272
- { cookie: req.cookies },
273
- req.query,
274
- req.body,
275
- req.files,
276
- )
277
-
278
- try {
279
- const moduleResponse = await moduleDef.module(query, (...params) => {
280
- // 参数注入客户端IP
281
- const obj = [...params]
282
- const options = obj[2] || {}
283
- if (!options.randomCNIP) {
284
- let ip = req.ip
285
-
286
- if (ip.substring(0, 7) == '::ffff:') {
287
- ip = ip.substring(7)
288
- }
289
- if (ip == '::1') {
290
- ip = global.cnIp
291
- }
292
- // logger.info('Requested from ip:', ip)
293
- obj[2] = {
294
- ...options,
295
- ip,
296
- }
297
- }
298
-
299
- return request(...obj)
300
- })
301
- logger.info(`Request Success: ${decode(req.originalUrl)}`)
302
-
303
- // 夹带私货部分:如果开启了通用解锁,并且是获取歌曲URL的接口,则尝试解锁(如果需要的话)ヾ(≧▽≦*)o
304
- if (
305
- req.baseUrl === '/song/url/v1' &&
306
- process.env.ENABLE_GENERAL_UNBLOCK === 'true'
307
- ) {
308
- const song = moduleResponse.body.data[0]
309
- if (
310
- song.freeTrialInfo !== null ||
311
- !song.url ||
312
- [1, 4].includes(song.fee)
313
- ) {
314
- const {
315
- matchID,
316
- } = require('@neteasecloudmusicapienhanced/unblockmusic-utils')
317
- logger.info('Starting unblock(uses general unblock):', req.query.id)
318
- const result = await matchID(req.query.id)
319
- song.url = result.data.url
320
- song.freeTrialInfo = null
321
- logger.info('Unblock success! url:', song.url)
322
- }
323
- if (song.url && song.url.includes('kuwo')) {
324
- const proxy = process.env.PROXY_URL
325
- const useProxy = process.env.ENABLE_PROXY || 'false'
326
- if (useProxy === 'true' && proxy) {
327
- song.proxyUrl = proxy + song.url
328
- }
329
- }
330
- }
331
-
332
- const cookies = moduleResponse.cookie
333
- if (!query.noCookie) {
334
- if (Array.isArray(cookies) && cookies.length > 0) {
335
- if (req.protocol === 'https') {
336
- // Try to fix CORS SameSite Problem
337
- res.append(
338
- 'Set-Cookie',
339
- cookies.map((cookie) => {
340
- return cookie + '; SameSite=None; Secure'
341
- }),
342
- )
343
- } else {
344
- res.append('Set-Cookie', cookies)
345
- }
346
- }
347
- }
348
- if (moduleResponse.redirectUrl) {
349
- res.redirect(moduleResponse.status || 302, moduleResponse.redirectUrl)
350
- return
351
- }
352
-
353
- res.status(moduleResponse.status).send(moduleResponse.body)
354
- } catch (/** @type {*} */ moduleResponse) {
355
- logger.error(`${decode(req.originalUrl)}`, {
356
- status: moduleResponse.status,
357
- body: moduleResponse.body,
358
- })
359
- if (!moduleResponse.body) {
360
- res.status(404).send({
361
- code: 404,
362
- data: null,
363
- msg: 'Not Found',
364
- })
365
- return
366
- }
367
- if (moduleResponse.body.code == '301')
368
- moduleResponse.body.msg = '需要登录'
369
- if (!query.noCookie) {
370
- res.append('Set-Cookie', moduleResponse.cookie)
371
- }
372
-
373
- res.status(moduleResponse.status).send(moduleResponse.body)
374
- }
375
- })
376
- }
377
-
378
- return app
379
- }
380
-
381
- /**
382
- * Serve the NCM API.
383
- * @param {NcmApiOptions} options
384
- * @returns {Promise<import('express').Express & ExpressExtension>}
385
- */
386
- async function serveNcmApi(options) {
387
- const port = Number(options.port || process.env.PORT || '3000')
388
- const host = options.host || process.env.HOST || ''
389
-
390
- const checkVersionSubmission =
391
- options.checkVersion &&
392
- checkVersion().then(({ npmVersion, ourVersion, status }) => {
393
- if (status == VERSION_CHECK_RESULT.NOT_LATEST) {
394
- logger.info(
395
- `最新版本: ${npmVersion}, 当前版本: ${ourVersion}, 请及时更新`,
396
- )
397
- }
398
- })
399
- const constructServerSubmission = constructServer(options.moduleDefs)
400
-
401
- const [_, app] = await Promise.all([
402
- checkVersionSubmission,
403
- constructServerSubmission,
404
- ])
405
-
406
- /** @type {import('express').Express & ExpressExtension} */
407
- const appExt = app
408
- appExt.server = app.listen(port, host, () => {
409
- console.log(`
410
- _ _ _____ __ __
411
- | \\ | |/ ____| \\/ |
412
- | \\| | | | \\ / |
413
- | . \` | | | |\\/| |
414
- | |\\ | |____| | | |
415
- |_| \\_|\\_____|_| |_|
416
- `)
417
- console.log(`
418
- ╔═╗╔═╗╦ ╔═╗╔╗╔╦ ╦╔═╗╔╗╔╔═╗╔═╗╔╦╗
419
- ╠═╣╠═╝║ ║╣ ║║║╠═╣╠═╣║║║║ ║╣ ║║
420
- ╩╩ ╩ ╚═╝╝╚╝╩ ╩╩ ╩╝╚╝╚═╝╚═╝═╩╝
421
- `)
422
- logger.info(`
423
- - Server started successfully @ http://${host ? host : 'localhost'}:${port}
424
- - Environment: ${process.env.NODE_ENV || 'development'}
425
- - Node Version: ${process.version}
426
- - Process ID: ${process.pid}`)
427
- })
428
-
429
- return appExt
430
- }
431
-
432
- module.exports = {
433
- serveNcmApi,
434
- getModulesDefinitions,
435
- }
1
+ require('dotenv').config()
2
+ const fs = require('fs')
3
+ const path = require('path')
4
+ const express = require('express')
5
+ const request = require('./util/request')
6
+ const packageJSON = require('./package.json')
7
+ const exec = require('child_process').exec
8
+ const cache = require('./util/apicache').middleware
9
+ const { cookieToJson } = require('./util/index')
10
+ const fileUpload = require('express-fileupload')
11
+ const decode = require('safe-decode-uri-component')
12
+ const logger = require('./util/logger.js')
13
+
14
+ /**
15
+ * The version check result.
16
+ * @readonly
17
+ * @enum {number}
18
+ */
19
+ const VERSION_CHECK_RESULT = {
20
+ FAILED: -1,
21
+ NOT_LATEST: 0,
22
+ LATEST: 1,
23
+ }
24
+
25
+ /**
26
+ * @typedef {{
27
+ * identifier?: string,
28
+ * route: string,
29
+ * module: any
30
+ * }} ModuleDefinition
31
+ */
32
+
33
+ /**
34
+ * @typedef {{
35
+ * port?: number,
36
+ * host?: string,
37
+ * checkVersion?: boolean,
38
+ * moduleDefs?: ModuleDefinition[]
39
+ * }} NcmApiOptions
40
+ */
41
+
42
+ /**
43
+ * @typedef {{
44
+ * status: VERSION_CHECK_RESULT,
45
+ * ourVersion?: string,
46
+ * npmVersion?: string,
47
+ * }} VersionCheckResult
48
+ */
49
+
50
+ /**
51
+ * @typedef {{
52
+ * server?: import('http').Server,
53
+ * }} ExpressExtension
54
+ */
55
+
56
+ /**
57
+ * Get the module definitions dynamically.
58
+ *
59
+ * @param {string} modulesPath The path to modules (JS).
60
+ * @param {Record<string, string>} [specificRoute] The specific route of specific modules.
61
+ * @param {boolean} [doRequire] If true, require() the module directly.
62
+ * Otherwise, print out the module path. Default to true.
63
+ * @returns {Promise<ModuleDefinition[]>} The module definitions.
64
+ *
65
+ * @example getModuleDefinitions("./module", {"album_new.js": "/album/create"})
66
+ */
67
+ async function getModulesDefinitions(
68
+ modulesPath,
69
+ specificRoute,
70
+ doRequire = true,
71
+ ) {
72
+ const files = await fs.promises.readdir(modulesPath)
73
+ const parseRoute = (/** @type {string} */ fileName) =>
74
+ specificRoute && fileName in specificRoute
75
+ ? specificRoute[fileName]
76
+ : `/${fileName.replace(/\.js$/i, '').replace(/_/g, '/')}`
77
+
78
+ const modules = files
79
+ .reverse()
80
+ .filter((file) => file.endsWith('.js'))
81
+ .map((file) => {
82
+ const identifier = file.split('.').shift()
83
+ const route = parseRoute(file)
84
+ const modulePath = path.join(modulesPath, file)
85
+ const module = doRequire ? require(modulePath) : modulePath
86
+
87
+ return { identifier, route, module }
88
+ })
89
+
90
+ return modules
91
+ }
92
+
93
+ /**
94
+ * Check if the version of this API is latest.
95
+ *
96
+ * @returns {Promise<VersionCheckResult>} If true, this API is up-to-date;
97
+ * otherwise, this API should be upgraded and you would
98
+ * need to notify users to upgrade it manually.
99
+ */
100
+ async function checkVersion() {
101
+ return new Promise((resolve) => {
102
+ exec('npm info NeteaseCloudMusicApiEnhanced version', (err, stdout) => {
103
+ if (!err) {
104
+ let version = stdout.trim()
105
+
106
+ /**
107
+ * @param {VERSION_CHECK_RESULT} status
108
+ */
109
+ const resolveStatus = (status) =>
110
+ resolve({
111
+ status,
112
+ ourVersion: packageJSON.version,
113
+ npmVersion: version,
114
+ })
115
+
116
+ resolveStatus(
117
+ packageJSON.version < version
118
+ ? VERSION_CHECK_RESULT.NOT_LATEST
119
+ : VERSION_CHECK_RESULT.LATEST,
120
+ )
121
+ } else {
122
+ resolve({
123
+ status: VERSION_CHECK_RESULT.FAILED,
124
+ })
125
+ }
126
+ })
127
+ })
128
+ }
129
+
130
+ function parseCorsAllowOrigins(corsAllowOrigin) {
131
+ if (!corsAllowOrigin) {
132
+ return null
133
+ }
134
+
135
+ const origins = corsAllowOrigin
136
+ .split(',')
137
+ .map((origin) => origin.trim())
138
+ .filter(Boolean)
139
+
140
+ return origins.length > 0 ? origins : null
141
+ }
142
+
143
+ function getCorsAllowOrigin(allowOrigins, requestOrigin) {
144
+ if (!allowOrigins) {
145
+ return requestOrigin || '*'
146
+ }
147
+
148
+ if (allowOrigins.includes('*')) {
149
+ return '*'
150
+ }
151
+
152
+ if (requestOrigin && allowOrigins.includes(requestOrigin)) {
153
+ return requestOrigin
154
+ }
155
+
156
+ return null
157
+ }
158
+
159
+ function createConsoleSpinner(message = '启动中') {
160
+ if (!process.stdout.isTTY) {
161
+ return {
162
+ stop() {},
163
+ }
164
+ }
165
+
166
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
167
+ let index = 0
168
+ process.stdout.write(`${frames[index]} ${message}...`)
169
+ const timer = setInterval(() => {
170
+ index = (index + 1) % frames.length
171
+ process.stdout.write(`\r${frames[index]} ${message}...`)
172
+ }, 80)
173
+
174
+ return {
175
+ stop() {
176
+ clearInterval(timer)
177
+ process.stdout.write(`\r✔ ${message} 完成。\n`)
178
+ },
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Construct the server of NCM API.
184
+ *
185
+ * @param {ModuleDefinition[]} [moduleDefs] Customized module definitions [advanced]
186
+ * @returns {Promise<import("express").Express>} The server instance.
187
+ */
188
+ async function constructServer(moduleDefs) {
189
+ const app = express()
190
+ const { CORS_ALLOW_ORIGIN } = process.env
191
+ const allowOrigins = parseCorsAllowOrigins(CORS_ALLOW_ORIGIN)
192
+ app.set('trust proxy', true)
193
+
194
+ /**
195
+ * Serving static files
196
+ */
197
+ app.use(express.static(path.join(__dirname, 'public')))
198
+ /**
199
+ * CORS & Preflight request
200
+ */
201
+ app.use((req, res, next) => {
202
+ if (req.path !== '/' && !req.path.includes('.')) {
203
+ const corsAllowOrigin = getCorsAllowOrigin(
204
+ allowOrigins,
205
+ req.headers.origin,
206
+ )
207
+ const shouldSetVaryHeader = corsAllowOrigin && corsAllowOrigin !== '*'
208
+ res.set({
209
+ 'Access-Control-Allow-Credentials': true,
210
+ ...(corsAllowOrigin
211
+ ? { 'Access-Control-Allow-Origin': corsAllowOrigin }
212
+ : {}),
213
+ ...(shouldSetVaryHeader ? { Vary: 'Origin' } : {}),
214
+ 'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
215
+ 'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',
216
+ 'Content-Type': 'application/json; charset=utf-8',
217
+ })
218
+ }
219
+ req.method === 'OPTIONS' ? res.status(204).end() : next()
220
+ })
221
+
222
+ /**
223
+ * Cookie Parser
224
+ */
225
+ app.use((req, _, next) => {
226
+ req.cookies = {}
227
+ //;(req.headers.cookie || '').split(/\s*;\s*/).forEach((pair) => { // Polynomial regular expression //
228
+ ;(req.headers.cookie || '').split(/;\s+|(?<!\s)\s+$/g).forEach((pair) => {
229
+ let crack = pair.indexOf('=')
230
+ if (crack < 1 || crack == pair.length - 1) return
231
+ req.cookies[decode(pair.slice(0, crack)).trim()] = decode(
232
+ pair.slice(crack + 1),
233
+ ).trim()
234
+ })
235
+ next()
236
+ })
237
+
238
+ /**
239
+ * Body Parser and File Upload
240
+ */
241
+ const MAX_UPLOAD_SIZE_MB = 500
242
+ const MAX_UPLOAD_SIZE_BYTES = MAX_UPLOAD_SIZE_MB * 1024 * 1024
243
+
244
+ app.use(express.json({ limit: `${MAX_UPLOAD_SIZE_MB}mb` }))
245
+ app.use(
246
+ express.urlencoded({ extended: false, limit: `${MAX_UPLOAD_SIZE_MB}mb` }),
247
+ )
248
+
249
+ app.use(
250
+ fileUpload({
251
+ limits: {
252
+ fileSize: MAX_UPLOAD_SIZE_BYTES,
253
+ },
254
+ useTempFiles: true,
255
+ tempFileDir: require('os').tmpdir(),
256
+ abortOnLimit: true,
257
+ parseNested: true,
258
+ }),
259
+ )
260
+
261
+ /**
262
+ * Cache
263
+ */
264
+ app.use(cache('2 minutes', (_, res) => res.statusCode === 200))
265
+
266
+ /**
267
+ * Special Routers
268
+ */
269
+ const special = {
270
+ 'daily_signin.js': '/daily_signin',
271
+ 'fm_trash.js': '/fm_trash',
272
+ 'personal_fm.js': '/personal_fm',
273
+ }
274
+
275
+ /**
276
+ * Load every modules in this directory
277
+ */
278
+ const moduleDefinitions =
279
+ moduleDefs ||
280
+ (await getModulesDefinitions(path.join(__dirname, 'module'), special))
281
+
282
+ for (const moduleDef of moduleDefinitions) {
283
+ // Register the route.
284
+ app.all(moduleDef.route, async (req, res) => {
285
+ ;[req.query, req.body].forEach((item) => {
286
+ // item may be undefined (some environments / middlewares).
287
+ // Guard access to avoid "Cannot read properties of undefined (reading 'cookie')".
288
+ if (item && typeof item.cookie === 'string') {
289
+ item.cookie = cookieToJson(decode(item.cookie))
290
+ }
291
+ })
292
+
293
+ let query = Object.assign(
294
+ {},
295
+ { cookie: req.cookies },
296
+ req.query,
297
+ req.body,
298
+ req.files,
299
+ )
300
+
301
+ try {
302
+ const moduleResponse = await moduleDef.module(query, (...params) => {
303
+ // 参数注入客户端IP
304
+ const obj = [...params]
305
+ const options = obj[2] || {}
306
+ if (!options.randomCNIP) {
307
+ let ip = req.ip
308
+
309
+ if (ip.substring(0, 7) == '::ffff:') {
310
+ ip = ip.substring(7)
311
+ }
312
+ if (ip == '::1') {
313
+ ip = global.cnIp
314
+ }
315
+ // logger.info('Requested from ip:', ip)
316
+ obj[2] = {
317
+ ...options,
318
+ ip,
319
+ }
320
+ }
321
+
322
+ return request(...obj)
323
+ })
324
+ logger.info(`Request Success: ${decode(req.originalUrl)}`)
325
+
326
+ // 夹带私货部分:如果开启了通用解锁,并且是获取歌曲URL的接口,则尝试解锁(如果需要的话)ヾ(≧▽≦*)o
327
+ if (
328
+ req.baseUrl === '/song/url/v1' &&
329
+ process.env.ENABLE_GENERAL_UNBLOCK === 'true'
330
+ ) {
331
+ const song = moduleResponse.body.data[0]
332
+ if (
333
+ song.freeTrialInfo !== null ||
334
+ !song.url ||
335
+ [1, 4].includes(song.fee)
336
+ ) {
337
+ const {
338
+ matchID,
339
+ } = require('@neteasecloudmusicapienhanced/unblockmusic-utils')
340
+ logger.info('Starting unblock(uses general unblock):', req.query.id)
341
+ const result = await matchID(req.query.id)
342
+ song.url = result.data.url
343
+ song.freeTrialInfo = null
344
+ logger.info('Unblock success! url:', song.url)
345
+ }
346
+ if (song.url && song.url.includes('kuwo')) {
347
+ const proxy = process.env.PROXY_URL
348
+ const useProxy = process.env.ENABLE_PROXY || 'false'
349
+ if (useProxy === 'true' && proxy) {
350
+ song.proxyUrl = proxy + song.url
351
+ }
352
+ }
353
+ }
354
+
355
+ const cookies = moduleResponse.cookie
356
+ if (!query.noCookie) {
357
+ if (Array.isArray(cookies) && cookies.length > 0) {
358
+ if (req.protocol === 'https') {
359
+ // Try to fix CORS SameSite Problem
360
+ res.append(
361
+ 'Set-Cookie',
362
+ cookies.map((cookie) => {
363
+ return cookie + '; SameSite=None; Secure'
364
+ }),
365
+ )
366
+ } else {
367
+ res.append('Set-Cookie', cookies)
368
+ }
369
+ }
370
+ }
371
+ if (moduleResponse.redirectUrl) {
372
+ res.redirect(moduleResponse.status || 302, moduleResponse.redirectUrl)
373
+ return
374
+ }
375
+
376
+ res.status(moduleResponse.status).send(moduleResponse.body)
377
+ } catch (/** @type {*} */ moduleResponse) {
378
+ logger.error(`${decode(req.originalUrl)}`, {
379
+ status: moduleResponse.status,
380
+ body: moduleResponse.body,
381
+ })
382
+ if (!moduleResponse.body) {
383
+ res.status(404).send({
384
+ code: 404,
385
+ data: null,
386
+ msg: 'Not Found',
387
+ })
388
+ return
389
+ }
390
+ if (moduleResponse.body.code == '301')
391
+ moduleResponse.body.msg = '需要登录'
392
+ if (!query.noCookie) {
393
+ res.append('Set-Cookie', moduleResponse.cookie)
394
+ }
395
+
396
+ res.status(moduleResponse.status).send(moduleResponse.body)
397
+ }
398
+ })
399
+ }
400
+
401
+ return app
402
+ }
403
+
404
+ /**
405
+ * Serve the NCM API.
406
+ * @param {NcmApiOptions} options
407
+ * @returns {Promise<import('express').Express & ExpressExtension>}
408
+ */
409
+ async function serveNcmApi(options) {
410
+ const port = Number(options.port || process.env.PORT || '3000')
411
+ const host = options.host || process.env.HOST || ''
412
+
413
+ const spinner = createConsoleSpinner('服务启动中')
414
+
415
+ const checkVersionSubmission =
416
+ options.checkVersion &&
417
+ checkVersion().then(({ npmVersion, ourVersion, status }) => {
418
+ if (status == VERSION_CHECK_RESULT.NOT_LATEST) {
419
+ logger.info(
420
+ `最新版本: ${npmVersion}, 当前版本: ${ourVersion}, 请及时更新`,
421
+ )
422
+ }
423
+ })
424
+ const constructServerSubmission = constructServer(options.moduleDefs)
425
+
426
+ const [_, app] = await Promise.all([
427
+ checkVersionSubmission,
428
+ constructServerSubmission,
429
+ ])
430
+
431
+ spinner.stop()
432
+
433
+ /** @type {import('express').Express & ExpressExtension} */
434
+ const appExt = app
435
+ appExt.server = app.listen(port, host, () => {
436
+ console.log(`
437
+ ╔═╗╔═╗╦ ╔═╗╔╗╔╦ ╦╔═╗╔╗╔╔═╗╔═╗╔╦╗
438
+ ╠═╣╠═╝║ ║╣ ║║║╠═╣╠═╣║║║║ ║╣ ║║
439
+ ╩ ╩╩ ╩ ╚═╝╝╚╝╩ ╩╩ ╩╝╚╝╚═╝╚═╝═╩╝
440
+ `)
441
+ logger.info(`
442
+ - Server started successfully @ http://${host ? host : 'localhost'}:${port}
443
+ - Environment: ${process.env.NODE_ENV || 'development'}
444
+ - Node Version: ${process.version}
445
+ - Process ID: ${process.pid}`)
446
+ })
447
+
448
+ return appExt
449
+ }
450
+
451
+ module.exports = {
452
+ serveNcmApi,
453
+ getModulesDefinitions,
454
+ }