音乐是魔法。它有能力创造新的记忆、朋友和难忘的感觉。
我们都喜欢音乐,但对于一些人来说,它是一种热情。聆听和分享不同的曲调,这些曲调触动了我们的情感,是一种爱好,它加强了社会纽带,并将许多友谊联系在一起。
但是,鉴于我们生活在一个更加全球化的时代,我们的许多朋友都散布在全球各地,试图与他们组织任何社交活动都可能很困难。
Franco Chen 既是音乐爱好者又是创新者,他接受了这一挑战,创建了一个应用程序,允许用户无论身在何处,都可以与朋友一起收听、分享和发现新音乐。
为了最大程度地提高用户的体验,该应用程序需要使用能够实时传输、处理和检索数据的数据库。借助 Redis,Franco 能够创建一个低延迟应用程序,其中用户之间的命令和交互具有超高响应能力。
让我们看看 Franco 是如何将这个应用程序变为现实的。但在我们继续之前,我们想指出,我们还提供了一系列激动人心的应用程序,供您在 Redis Launchpad 上查看。
您将构建一个平台,允许您在线收听音乐并与朋友分享音乐。该应用程序通过创建私人或公共在线房间来工作,您可以在其中邀请不同品味的人加入体验。
从本质上讲,该应用程序是深入了解不同类型、品味和偏好的门户,通过与对音乐有共同欣赏的人互动来实现。下面我们将向您展示如何从头开始创建这个应用程序,重点介绍不同的组件以及引导您完成实现过程的每个阶段。
准备好开始了吗?
好的,让我们开始吧!
Socket.IO: 用作实时网络应用程序的 Javascript 库。
Javascript: 用作首选编程语言,允许您使网页具有交互性。
RedisJSON: Redis 的 JSON 数据类型,允许从 Redis 键存储、更新和获取 JSON 值
RediSearch: 用于 Redis 的查询、辅助索引和全文搜索。
Chakra UI: 为您提供基本构建块,可以帮助您构建应用程序的前端。
Redux: 用作管理和集中应用程序状态的开源 Javascript 库。
Axios: 用作 Node.js 和浏览器的基于 Promise 的 HTTP 客户端。
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}
套接字
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 上关注他。