音乐是神奇的。 它具有创造新的回忆、朋友和难忘的感觉的力量。
我们所有人都喜欢音乐,但对某些人来说,这是一种激情。 聆听和分享不同的曲调,触动我们的情感,这是一种爱好,可以加强社会联系,并将许多友谊凝聚在一起。
但鉴于我们生活在一个更加全球化的时代,我们的许多朋友遍布全球,因此尝试与他们组织任何社交活动都可能很困难。
作为音乐爱好者和创新者,Franco Chen 接受了这一挑战,创建了一个应用程序,允许用户与朋友一起收听、分享和发现新音乐,无论他们身在何处。
为了最大限度地提高用户体验,该应用程序需要使用一个数据库来运行,该数据库能够实时传输、处理和检索数据。 借助 Redis,Franco 能够创建一个低延迟的应用程序,其中用户之间的命令和交互具有超快的响应速度。
让我们来看看 Franco 是如何将这个应用程序变为现实的。 但在我们继续之前,我们想指出的是,我们还有一系列令人兴奋的应用程序供您在 Redis Launchpad 上查看。
您将构建一个平台,允许您在线收听音乐并与朋友分享。 该应用程序的工作方式是创建私人或公共在线房间,您可以在其中邀请具有不同品味的人加入体验。
从最纯粹的意义上讲,该应用程序是通过与对音乐有共同爱好的人互动,从而更深入地探索不同流派、品味和偏好的门户。 下面,我们将向您展示如何从头开始创建此应用程序,突出显示不同的组件,并引导您完成实施过程的每个阶段。
准备好开始了?
好的,让我们开始吧!
Socket.IO:用作实时 Web 应用程序的 Javascript 库。
Javascript:用作首选编程语言,允许您使网页具有交互性。
RedisJSON:用于 Redis 的 JSON 数据类型,允许从 Redis 键存储、更新和获取 JSON 值
RediSearch:用于 Redis 的查询、二级索引和全文搜索。
Chakra UI:为您提供基本构建块,可以帮助您构建应用程序的前端。
Redux:用作开源 Javascript 库,用于管理和集中应用程序状态。
Axios:用作基于 Promise 的 HTTP 客户端,用于 Node.js 和浏览器。
Node.js:用作开源、跨平台的工具,可在 Web 浏览器之外执行 JavaScript 代码。
Express:用作灵活的 Node.js Web 应用程序框架,为 Web 和移动应用程序提供了一组强大的功能。
先决条件
git clone https://github.com/spatialdj/frontend
使用 RedisMod docker 镜像来设置 Redis 模块。 在前端的根目录中,键入以下代码来安装前端依赖项:
npm install
转到后端的根目录并创建一个名为 config.js 的文件,其中包含以下内容
export default {
redisHost: 'localhost',
redisPassword: 'your_password_for_redis_here',
sessionSecret: 'somesessionsecret',
passwordSaltRounds: 10,
youtube_key: 'youtube_api_key'
}
键入以下命令以安装后端依赖项:
npm install
在后端的根目录中运行以下命令:
npm start
您的应用程序应该在 localhost:3000 上运行
房间
messages:Redis 键到此房间的消息
id:此房间的 ID
json:此房间的 JSON 表示形式(因为 RediSearch 尚不支持 JSON)
numMembers:当前房间中的成员数
description:房间的描述
name:房间的名称
private:(True | False)房间是否可搜索
genres: 房间面向的流派
索引(RediSearch)
name: TEXT
description: TEXT
genres: TAG
numMembers: NUMERIC
private: TAG
private: TAG
命令
获取此房间的 JSON 表示形式
HGET ${roomId} json
创建新房间或更新房间(并非所有字段都是必需的)
HSET room:${roomId} id ${roomId} name ${name} description ${description} private ${private} genres ${genres} numMembers ${numMembers} json ${roomJson}
检查房间是否存在(用于尝试加入房间时):
EXISTS ${roomId}
用于搜索的房间页面
FT.SEARCH @name|description:(${searchQuery}) @private:{false} @genres:{${genres}} SORTBY numMembers DESC LIMIT ${offset} ${limit}:
用户队列
会话
用户
示例播放列表
"db168000-fa58-491b-81d0-1287d866fcf7": {
"id": "db168000-fa58-491b-81d0-1287d866fcf7",
"name": "Text playlist",
"user": "exampleuser",
"queue": [
{
"videoId": "TT4PHY0_hwE",
"title": "Ekcle - Pearl Jigsaw",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/TT4PHY0_hwE/default.jpg",
"width": 120,
"height": 90
}
},
"channelTitle": "Ekcle",
"id": "2f7473db-2aec-4ec1-a2bd-623ed6b3ce48",
"duration": 348000
}
]
}
示例用户
{
"username": ...,
"password": ...,
"profilePicture": ...,
"playlist": { ... },
"selectedPlaylist": …
}
JSON.SET user:${username} ${path} ${userJson}
JSON.GET user:${username} ${path}
消息
LPUSH message:${messagesId} ${data}
LRANGE message:${messagesId} ${start} ${end}
套接字 (Socket)
SET socket:${socketId} ${username}
GET socket:${socketId}
DEL socket:${socketId}
JSON.SET user:${username} . ${userJson}
SET sess:${sessionId} ${data}
JSON.GET user:${username} ${path}
SET socket:${socketId} ${username}
DEL socket:${socketId}
FT.SEARCH @name|description:(${searchQuery}) @private:{false} @genres:{${genres}} SORTBY numMembers DESC LIMIT ${offset} ${limit}
HSET room:${roomId} id ${roomId} name ${name} description ${description} private ${private} genres ${genres} numMembers ${numMembers} json ${roomJson}
HSET room:${roomId} id ${roomId} name ${name} description ${description} private ${private} genres ${genres} numMembers ${numMembers} json ${roomJson}
RPUSH queue:${roomId} ${username}
LMOVE queue:${roomId} queue:${roomId} LEFT RIGHT
song = JSON.ARRPOP user:${username} .playlist.${playlistId}.queue 0
JSON.ARRAPPEND user:${username} .playlist.${playlistId}.queue song
JSON.SET user:${username} .playlist.${playlistId} ${playlistJson}
JSON.DEL user:${username} .playlist.${playlistId}
JSON.SET user:${username} .selectedPlaylist ${playlistId})
JSON.ARRAPPEND user:${username} .playlist.${playlistId}.queue ${song}
JSON.GET user:${username} .playlist.${playlistId}.queue
const songs = JSON.parse(await jsonGetAsync(getUserKey(username), `.playlist.${playlistId}.queue`))
const songIndex = songs.findIndex(song => song.id === songId)
const success = songIndex !== -1
if (success) {
songs.splice(songIndex, 1)
}
try {
await jsonSetAsync(getUserKey(username), `.playlist.${playlistId}.queue`, JSON.stringify(songs))
} catch (error) {
return res.status(400).json(error)
}
在主页上,您可以通过单击屏幕右上角导航栏上的这些图标之一来登录或创建新帐户。
房间是线上社区,根据主持人概述的标准托管各种音乐。 在这里,您将能够与不同的人联系和互动,以根据您喜欢的品味和流派发现新的音乐。
单击主页上的“创建房间”图标后,您必须提供有关要托管的房间的描述。 在这里,您可以在描述中添加一些个性来交流您希望加入房间的氛围和人员类型,以及应该在此处分享的音乐类型(见下文)。
加入房间的朋友将显示他们的角色图标(见下文)。
每个人都加入后,您需要创建一个播放列表来分享音乐。 单击屏幕左下角的箭头开始此过程。 接下来,将出现一个连接到 YouTube 的搜索栏。
从这里,您可以搜索要包含的不同歌曲,然后单击加号将其添加到播放列表中。 RediSearch 将根据输入到搜索引擎中的短语识别歌曲并将其添加到您的播放列表中。
当每首歌曲播放时,音乐视频将出现在屏幕的中央。 您还会在屏幕右侧看到一个聊天窗口,房间里的每个人都可以在其中分享他们对每首歌曲的看法。
在屏幕的左下角,您可以喜欢或不喜欢一首歌曲。 如果房间里超过一半的人不喜欢这首歌,那么应用程序将跳到播放列表中的下一首。
在不同组件之间以超高效率传输大量数据一直是 Franco 需要克服的最大障碍之一。 在今天的数字世界中,用户期望命令和响应是实时的,而且他们期望的不会更低。
对于此应用程序来说,这一点尤其重要,因为它需要在服务器和客户端之间传输大量数据。 利用 Redis 的高级数据功能使应用程序能够以低延迟检索数据,从而创建一个完全流畅且响应迅速的应用程序,让用户可以实时收听、分享和评论彼此的音乐。
要更直观地了解此应用程序的创建过程,请务必观看 Franco 的 YouTube 视频。
如果您喜欢这篇文章,我们在 Redis Launchpad 上为您准备了更多文章供您深入了解。 在这里,您将可以访问一系列激动人心的应用程序,这些应用程序正在对世界各地的日常生活产生影响。
其中包括实时车辆跟踪系统、加速献血过程的应用程序、拆分测试软件等等。
快去看看吧。 受到启发。 加入 Redis 的乐趣。
Franco Chen
Franco 拥有超过九年的软件工程经验,目前在滑铁卢大学学习。 如果您想及时了解他所有的项目,请务必在 GitHub 上关注他。