给自己的网站添加网易云音乐歌单吧^ ^

简介: 给自己的网站添加网易云音乐歌单吧^ ^

最近应该发现,我的博客https://blog.codelabo.cn左下角多了一个音乐播放器

image.png

这个是怎么实现的?一起来看看吧


APlayer


首先我们需要一个音频播放器,这里我用到了APlayer,这是由bilibili前端大神DIYgod开源的播放器,有兴趣的可以去TA的主页看看,非常惊艳,这里我就不多说了

我们看一下APlayer的官方文档,方法很简单

<link rel="stylesheet" href="APlayer.min.css">
<div id="aplayer"></div>
<script src="APlayer.min.js"></script>
const ap = new APlayer({
    container: document.getElementById('aplayer'),
    audio: [{
        name: 'name',
        artist: 'artist',
        url: 'url.mp3',
        cover: 'cover.jpg'
    }]
});

这里的audio是一个音频列表,可以是一个对象或对象数组


对象具体的参数如下


名称 描述
name 音频名称
artist 音频艺术家
url 音频链接
cover 音频封面
lrc 音频歌词


LRC一共有三种方式来给 APlayer 传递歌词,详情可参考


https://aplayer.js.org/#/home?id=lrc


这里我们选择最方便的一种,直接给LRC链接


网易云音乐API


这部分我找到了网上有人分享的API,这种官方不可能给公开API,所以还是要小心使用,说不定哪天就被修改了


http://moonlib.com/606.html


我们现在其实想要两个,一个是歌单的列表,还有一个是歌词。

# 歌单
https://music.163.com/api/playlist/detail?id=37880978


id为歌单ID
# 歌词
https://music.163.com/api/song/lyric?os=pc&id=93920&lv=-1&kv=-1&tv=-1


id为歌曲IDlv:值为-1,我猜测应该是判断是否搜索lyric格式kv:值为-1,这个值貌似并不影响结果,意义不明tv:值为-1,是否搜索tlyric格式


接口实现


虽然我们已经找到了网易云音乐API,但是返回的数据不是我们所需要的呀

比如这个歌单的接口

# Request
https://music.163.com/api/playlist/detail?id=2119983629
# Response
{
  "result":{
      "subscribers":[],
      "subscribed": false,
      "creator":{...},
      "artists": null,
      "tracks":[
        {
          album: {
              name: "メトロノーム", 
              id: 36787278, type: "专辑", 
              size: 12, picId:18419018788768520,
          }
          alias: [],
          artists: [{name: "MACO", id: 901025, picId: 0, img1v1Id: 0, briefDesc: "",…}],
          audition: null,
          bMusic: {...},
          commentThreadId: "R_SO_4_515573221",
          copyFrom: "",
          copyright: 1,
          copyrightId: 7003,
          crbt: null,
          ...
        }
      ]
  }
}


里面字段很多,我上面只列举了一部分,tracks就是歌单列表,但是很显然,和我们需要的格式还差很多


那么怎么来转换一下,变成我们需要的数据格式呢?

[{
    name: 'name',
    artist: 'artist',
    url: 'url.mp3',
    cover: 'cover.jpg',
    lrc: 'a.lrc'
}]


这里我们就需要在服务端来完成了,思路很简单,在服务器上请求

https://music.163.com/api/playlist/detail?id=2119983629这个接口,然后拿到结果后手动处理一下,最后再返给客户端,相当于做了一次中转


我这里服务端是用 koa实现的,其他框架应该差不多


服务端发起请求


在服务端发起请求也可以用我们熟悉的fetch,不过你需要先安装node-fetch这个库

yarn add node-fetch


然后你就可以像前端一样发起请求了

const fetch = require('node-fetch');
//...
const getPlayList = (id) => {
    return fetch(`http://music.163.com/api/playlist/detail?id=${id}`)
    .then((response) => {
        if (response.ok) {
            return response.json();
        }
    })
    .catch((err) => {
        console.warn(err);
    })
}


接口定义

现在我们需要新增一个接口用来处理歌单,返回出我们需要的格式

//获取音乐列表
router.get('/playlist/:id', async (ctx, next) => {
    const responseData = {
        "success": false,
        "data":[],
        "message": "",
    }
    const { id } = ctx.params;
    try {
        const data = await getPlayList(id);
        if(data.code===200){
            const playList = data.result.tracks.map(item=>({
                id: item.id,
                name: item.name,
                artist: item.artists.map(el=>el.name).join(','),//由于歌手是一个数组,这里我们把它转换成字符串拼接
                url: `https://music.163.com/song/media/outer/url?id=${item.id}.mp3`,//歌曲地址
                cover: item.album.picUrl.replace(/http:/,'https:'),
                lrc:null
            }))
            responseData.success = true;
            responseData.message = '操作成功';
            responseData.data = playList;
            ctx.body = responseData;
        }
    } catch (error) {
        responseData.success = false;
        responseData.message = error.message;
        responseData.data = [];
        ctx.body = responseData;
    }
});


注意这里的歌曲链接url,本来返回信息里面是不包含的,只有歌曲ID,不过我们发现通过https://music.163.com/song/media/outer/url?id=ID可以直接在线播放指定ID的歌曲,所以我们这里直接写在返回结果上。


歌词处理


还有一个问题就是歌词,上面的接口中,歌词返回结果也不是我们需要的格式

# Request
https://music.163.com/api/song/lyric?os=pc&id=93920&lv=-1&kv=-1&tv=-1
# Response
{
  "sgc": true,
  "sfy": false,
  "qfy": false,
  "lrc": {
    "version": 7,
    "lyric": "[00:29.620]细雨带风湿透黄昏的街道\n[00:35.050]抹去雨水双眼无帮地仰望\n[00:40.240]望向孤单的晚灯是那伤感的记忆\n[00:48.630]再次泛起心里无数的思念\n[00:54.000]以往片刻欢笑仍挂在脸上\n[00:58.770]愿你此刻可会知是我衷心的说声\n[01:06.310]喜欢你\n[01:08.940]那双眼动人笑声更迷人\n[01:14.330]愿再可轻抚你那可爱面容\n[01:22.490]挽手说梦话象昨天你共我\n[01:42.970]满带理想的我曾经多冲动\n[01:48.340]埋怨与她相爱难有自由\n[01:53.040]愿你此刻可会知是我衷心的说声\n[02:00.420]喜欢你\n[02:03.230]那双眼动人笑声更迷人\n[02:08.540]愿再可轻抚你那可爱面容\n[02:16.750]挽手说梦话象昨天你共我\n[02:24.740]每晚夜里自我独行\n[02:27.670]随处荡 多冰冷\n[02:35.070]以往为了自我挣扎从不知她的痛苦\n[02:49.380]喜欢你\n[02:52.020]那双眼动人笑声更迷人\n[02:57.420]愿再可轻抚你那可爱面容\n[03:05.590]挽手说梦话象昨天你共我\n[03:13.870]挽手说梦话象昨天你共我\n"
  },
  "klyric": {...},
  "code": 200
}


反正就是很全面,但是我们需要的仅仅是里面的内容部分,比如上面我就只需要这一段

[00:29.620]细雨带风湿透黄昏的街道
[00:35.050]抹去雨水双眼无帮地仰望
[00:40.240]望向孤单的晚灯是那伤感的记忆
[00:48.630]再次泛起心里无数的思念
[00:54.000]以往片刻欢笑仍挂在脸上
[00:58.770]愿你此刻可会知是我衷心的说声
...


所以我们需要再次做一个中介处理

const getLyric = (id) => {
    return fetch(`http://music.163.com/api/song/lyric?os=pc&id=${id}&lv=-1&kv=-1&tv=-1`)
    .then((response) => {
        if (response.ok) {
            return response.json();
        }
    })
    .catch((err) => {
        console.warn(err);
    })
}
//获取音乐歌词
router.get('/lyric/:id', async (ctx, next) => {
    const { id } = ctx.params;
    try {
        const lyric = await getLyric(id);
        ctx.body = lyric.lrc.lyric;//返回指定部分
    } catch (error) {
        ctx.body = '';
    }
});


这样在上面歌词列表中就可以直接用/api/lyric/:ID来获取歌词了

//...
{
  id: item.id,
  name: item.name,
  artist: item.artists.map(el=>el.name).join(','),
  url: `https://music.163.com/song/media/outer/url?id=${item.id}.mp3`,
  cover: item.album.picUrl.replace(/http:/,'https:'),
  lrc:`/api/lyric/${item.id}`//这里歌词写上我们定义的接口地址
}
//...


测试一下吧


通过以上处理,我们接口就返回我们自定义的数据格式了

# Request
https://localhost:3000/api/playlist/2119983629
# Response
{
  "success": true,
  "data": [
     {
      "id": 515573221,
      "name": "Sweet Memory",
      "artist": "MACO",
      "url": "https://music.163.com/song/media/outer/url?id=515573221.mp3",
      "cover": "https://p1.music.126.net/-U7mfaIjENUu8G_O0Dhv8g==/18419018788768520.jpg",
      "lrc": "/api/lyric/515573221"
    },
    {
      "id": 488388942,
      "name": "願い~あの頃のキミへ~",
      "artist": "當山みれい",
      "url": "https://music.163.com/song/media/outer/url?id=488388942.mp3",
      "cover": "https://p1.music.126.net/kbLlBkGfEcA3RJyC5JhkDA==/18346451021830743.jpg",
      "lrc": "/api/lyric/488388942"
    },
    ...
  ],
  "message": "操作成功"
}


添加到博客


其实上面对接口的数据改造才是关键,下面添加到自己的页面就很简单了。

如果你是传统HTML页面,可以直接文章开头的方式引用

<link rel="stylesheet" href="APlayer.min.css">
<div id="aplayer"></div>
<script src="APlayer.min.js"></script>
const ap = new APlayer({
    container: document.getElementById('aplayer'),
    audio: [{
        name: 'name',
        artist: 'artist',
        url: 'url.mp3',
        cover: 'cover.jpg'
    }]
});


如果使用了使用模块管理器:

import 'APlayer/dist/APlayer.min.css';
import APlayer from 'APlayer';
const ap = new APlayer(options);


如果是react项目,那么可以用封装好的react-aplayer

import React from 'react';
import ReactAplayer from 'react-aplayer';
export default class App extends React.Component {
  // event binding example
  onPlay = () => {
    console.log('on play');
  };
  onPause = () => {
    console.log('on pause');
  };
  // example of access aplayer instance
  onInit = ap => {
    this.ap = ap;
  };
  render() {
    const props = {
      theme: '#F57F17',
      lrcType: 3,
      audio: [
        {
          name: '光るなら',
          artist: 'Goose house',
          url: 'https://moeplayer.b0.upaiyun.com/aplayer/hikarunara.mp3',
          cover: 'https://moeplayer.b0.upaiyun.com/aplayer/hikarunara.jpg',
          lrc: 'https://moeplayer.b0.upaiyun.com/aplayer/hikarunara.lrc',
          theme: '#ebd0c2'
        }
      ]
    };
    return (
      <div>
        <ReactAplayer
          {...props}
          onInit={this.onInit}
          onPlay={this.onPlay}
          onPause={this.onPause}
        />
        {/* example of access aplayer instance API */}
        <button onClick={() => this.ap.toggle()}>toggle</button>
      <div>
    );
  }
}


如果是vue项目,可以使用vue-aplayer

<aplayer autoplay
  :music="{
    title: 'secret base~君がくれたもの~',
    artist: 'Silent Siren',
    src: 'https://moeplayer.b0.upaiyun.com/aplayer/secretbase.mp3',
    pic: 'https://moeplayer.b0.upaiyun.com/aplayer/secretbase.jpg'
  }"
/>


其他更多可以参考Aplayer生态


小节


这里的歌单,我选择了自己收藏的歌曲。每次用网易云音乐客户端播放听歌的时候,收藏的歌曲,在我的博客上也可以同步进行更新。


差不多就这些了,可能对于专业后端开发来说,这些完全就是小学生操作,但是对于一个前端来说,做这些事就感觉闯入了一片新天地,还是有很多感悟的。很多以前前端做不了的事,现在nodeJS也能帮我们解决,进一步打通了前后端的天然屏障,离全栈也越来越近了 ^ ^

相关文章
|
算法 前端开发 JavaScript
【五子棋实战】第1章 项目架构与开发思路
五子棋是一种古老而受欢迎的棋类游戏,本博客将介绍如何使用前端和算法接口来制作一个五子棋游戏。 此次实战项目包含项目的所有代码、教学的系列博客,分别放在公众号、CSDN,以便广大读者能够更好地学习、实践与二开。
543 0
|
2月前
|
监控 网络协议 Linux
Gobuster目录/文件扫描工具
Gobuster 是用 Go 语言编写的高效命令行扫描工具,支持目录/文件暴力扫描、子域名枚举和虚拟主机发现。其多线程机制使扫描速度快,资源占用低,适合远程或云服务器使用。它具备灵活性,支持自定义字典、代理、HTTP 认证等功能,适用于敏感目录查找、资产管理、渗透测试等场景。此外,Gobuster 可通过安装在 Kali 或 Linux 系统上,快速执行各类扫描任务,是安全测试和自动化脚本的理想选择。
256 0
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
自适应Prompt技术:让LLM精准理解用户意图的进阶策略
自适应Prompt技术通过动态意图解析与反馈驱动优化,将LLM从“机械执行者”进化为“认知协作者”。企业落地时需聚焦垂直场景,结合自动化工具链快速验证价值。
482 9
|
Android开发
基于android-11.0.0_r39,系统应用的手动签名方法和过程
本文介绍了基于Android 11.0.0_r39版本进行系统应用手动签名的方法和解决签名过程中遇到的错误,包括处理`no conscrypt_openjdk_jni-linux-x86_64`和`RegisterNatives failed`的问题。
631 2
|
11月前
|
存储 NoSQL Redis
redis主从集群与分片集群的区别
主从集群通过主节点处理写操作并向从节点广播读操作,从节点处理读操作并复制主节点数据,优点在于提高读取性能、数据冗余及故障转移。分片集群则将数据分散存储于多节点,根据规则路由请求,优势在于横向扩展能力强,提升读写性能与存储容量,增强系统可用性和容错性。主从适用于简单场景,分片适合大规模高性能需求。
467 5
|
12月前
|
Linux Python Windows
Matplotlib 中设置自定义中文字体的正确姿势
【11月更文挑战第16天】Matplotlib 默认不支持中文字体显示,需手动配置。方法包括:1) 修改全局字体设置,适用于整个脚本;2) 局部设置特定元素的字体;3) 使用系统字体名称,但可能因系统而异。通过这些方法可以有效解决中文乱码问题,确保图表中文本的正确显示。
1050 3
|
存储 数据库
|
SQL 数据库 C语言
【sqlite的C语言访问接口】执行SQL语句的接口------sqlite3_exec回调函数的使用
【sqlite的C语言访问接口】执行SQL语句的接口------sqlite3_exec回调函数的使用
|
关系型数据库 MySQL 数据安全/隐私保护
使用docker快速搭建wordpress服务,并指定域名访问
通过以上步骤,你可以使用Docker快速搭建WordPress服务,并通过指定的域名进行访问。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
449 1
|
SQL NoSQL Java
Java 进阶:使用 Lambda 表达式实现超强的排序功能
我们在系统开发过程中,对数据排序是很常见的场景。一般来说,我们可以采用两种方式: 借助存储系统(SQL、NoSQL、NewSQL 都支持)的排序功能,查询的结果即是排好序的结果 查询结果为无序数据,在内存中排序。
1697 0
Java 进阶:使用 Lambda 表达式实现超强的排序功能