Skip to content

Commit d5e0b27

Browse files
committed
UI/UX improvments
1 parent 1785fe8 commit d5e0b27

18 files changed

Lines changed: 207 additions & 166 deletions

File tree

CLAUDE.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
Discord Music Bot - A .NET 9 application that provides music playback functionality for Discord servers. Supports YouTube, SoundCloud, Spotify (for playlist fetching), and radio streams.
7+
Discord Music Bot - A .NET 10 application that provides music playback functionality for Discord servers. Supports various audio sources, Spotify (for playlist fetching), and radio streams.
88

99
## Build & Run Commands
1010

@@ -22,13 +22,13 @@ dotnet run --project src/Worker/Worker.csproj
2222
dotnet run --project src/Worker/Worker.csproj -c Release
2323

2424
# Frontend dev server (from src/UI/App)
25-
npm run dev
25+
bun run dev
2626

2727
# Frontend production build
28-
npm run build
28+
bun run build
2929

3030
# Frontend development build
31-
npm run build:dev
31+
bun run build:dev
3232
```
3333

3434
## Architecture
@@ -40,7 +40,7 @@ npm run build:dev
4040
- **Infrastructure** (`src/Infrastructure/`) - Discord bot implementation using NetCord, audio processing with FFmpeg/NAudio, database access via EF Core, YouTube/SoundCloud integration
4141
- **UI** (`src/UI/`) - Contains two sub-projects:
4242
- `Api/` - ASP.NET Core REST API with JWT authentication
43-
- `App/` - Lit-based TypeScript frontend (Vite build)
43+
- `App/` - React TypeScript frontend (Vite build, Bun package manager)
4444
- **Worker** (`src/Worker/`) - Main entry point that composes all layers, hosts the Discord bot and web API
4545

4646
### Key Patterns
@@ -86,4 +86,4 @@ Environment variables (via docker-compose or appsettings.json):
8686

8787
## Frontend Stack
8888

89-
TypeScript with Lit web components, Vaadin Router, Chart.js. Build output goes to `src/Worker/wwwroot/`.
89+
React 19 with TypeScript, TanStack Router, TanStack Query, Recharts. Uses Bun as the package manager and Vite 8 for bundling. Build output goes to `src/Worker/wwwroot/`.

README.md

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
1-
# Local Radio and Music Bot Player for Discord
1+
# Discord Music Bot
22

3-
**_Disclaimer_**: _This code is meant for educational purposes only. Please do not use this code for malicious purposes or to violate the [Discord](https://discord.com), [Youtube](https://www.youtube.com), [Radio Televisyen Malaysia (RTM)](https://www.rtm.gov.my) and [Astro](https://www.astro.com.my) Terms of Service._
3+
A .NET 10 Discord bot for music playback, supporting various audio sources and radio streams. Includes a React-based web dashboard.
44

5-
**Note:** This code has been tested on a Linux environment. If you are using a different operating system, you may need to modify the code accordingly. You also can run this application using Docker and WSL2. Dockerfile is provided in this repository.
5+
**Note:** This application is designed to run on Linux via Docker. A Dockerfile and docker-compose setup are provided.
66

77
## Installation
88

9-
To use this application, follow these steps:
10-
11-
1. You need to create a Discord bot and add it to your server. You can follow the instructions [here](https://discord.com/developers/docs/intro).
12-
2. Setup up neccessary bot access permissions. You can follow the instructions [here](https://discord.com/developers/docs/topics/permissions).
13-
3. Discord bot token is required to run this application. You can create a bot token by following the instructions [here](https://discord.com/developers/docs/topics/oauth2#bots).
14-
3. Clone this repository.
15-
4. Use docker-compose to build and run the application. It will automatically install the required dependencies.
9+
1. Create a Discord bot and add it to your server. Follow the [Discord Developer docs](https://discord.com/developers/docs/intro).
10+
2. Set up the necessary bot permissions. See [Permissions](https://discord.com/developers/docs/topics/permissions).
11+
3. Obtain a Discord bot token. See [OAuth2 Bots](https://discord.com/developers/docs/topics/oauth2#bots).
12+
4. Clone this repository.
13+
5. Configure `deployment/.env` with your values (Discord token, Spotify credentials, database, JWT, etc.).
14+
6. Build and run with Docker:
1615
```
17-
docker-compose up --build
16+
docker-compose -f deployment/docker-compose.yml up --build
1817
```
19-
5. Please refer to the [Discord.NET](https://discordnet.dev) for more information on how to create a Discord bot and how to use the Discord.NET library.
2018

2119
## Technologies Used
22-
1. [Discord.NET](https://discordnet.dev)
23-
2. [YoutubeExplode](https://github.com/Tyrrrz/YoutubeExplode)
24-
3. [FFmpeg](https://ffmpeg.org)
25-
4. [Opus](https://opus-codec.org)
26-
5. [Sodium](https://doc.libsodium.org)
20+
21+
### Backend
22+
- [.NET 10](https://dotnet.microsoft.com) / ASP.NET Core
23+
- [NetCord](https://github.com/NetCordDev/NetCord) - Discord bot framework
24+
- [Entity Framework Core](https://learn.microsoft.com/en-us/ef/core/) + [PostgreSQL](https://www.postgresql.org)
25+
- [YoutubeExplode](https://github.com/Tyrrrz/YoutubeExplode) / [YoutubeDLSharp](https://github.com/Bluegrams/YoutubeDLSharp) + [yt-dlp](https://github.com/yt-dlp/yt-dlp)
26+
- [SoundCloudExplode](https://github.com/jerry08/SoundCloudExplode)
27+
- [NAudio](https://github.com/naudio/NAudio) / [FFmpeg](https://ffmpeg.org) - audio processing
28+
- [libopus](https://opus-codec.org) / [libsodium](https://doc.libsodium.org) - voice encryption and encoding
29+
30+
### Frontend
31+
- [React 19](https://react.dev) with TypeScript
32+
- [TanStack Router](https://tanstack.com/router) / [TanStack Query](https://tanstack.com/query)
33+
- [Recharts](https://recharts.org)
34+
- [Vite](https://vite.dev) + [Bun](https://bun.sh)

src/Application/Interfaces/Services/IRadioSourceService.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ namespace Application.Interfaces.Services;
44

55
public interface IRadioSourceService
66
{
7-
Task<IReadOnlyCollection<RadioSource>> GetAllRadioSourcesAsync(CancellationToken cancellationToken = default);
8-
Task<RadioSource> GetRadioSourceByIdAsync(Guid id, CancellationToken cancellationToken = default);
9-
Task UpdateRadioSourceUrlAsync(Guid id, string newSourceUrl, bool isActive, CancellationToken cancellationToken = default);
10-
Task<Guid> AddRadioSourceAsync(string name, string sourceUrl, CancellationToken cancellationToken = default);
11-
Task<int> DeleteRadioSourceAsync(Guid id, CancellationToken cancellationToken = default);
7+
Task<IReadOnlyCollection<RadioSource>> GetAllRadioSourcesAsync(CancellationToken cancellationToken);
8+
Task<RadioSource> GetRadioSourceByIdAsync(Guid id, CancellationToken cancellationToken);
9+
Task UpdateRadioSourceUrlAsync(Guid id, string name, string newSourceUrl, bool isActive, CancellationToken cancellationToken);
10+
Task<Guid> AddRadioSourceAsync(string name, string sourceUrl, CancellationToken cancellationToken);
11+
Task<int> DeleteRadioSourceAsync(Guid id, CancellationToken cancellationToken);
1212
}

src/Domain/Entities/RadioSource.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace Domain.Entities;
55
public class RadioSource: EntityBase
66
{
77
public Guid Id { get; init; }
8-
public string Name { get; init; }
8+
public string Name { get; set; }
99
public string SourceUrl { get; private set; }
1010
public bool IsActive { get; set; } = true;
1111

@@ -15,14 +15,15 @@ private RadioSource(string name, string sourceUrl)
1515
SourceUrl = sourceUrl;
1616
}
1717

18-
public static RadioSource UpdateSourceUrl(RadioSource radioSource, string newSourceUrl, bool isActive)
18+
public static void Update(RadioSource radioSource, string name, string newSourceUrl, bool isActive)
1919
{
20-
ArgumentNullException.ThrowIfNull(radioSource, nameof(radioSource));
21-
ArgumentNullException.ThrowIfNull(newSourceUrl, nameof(newSourceUrl));
20+
ArgumentNullException.ThrowIfNull(radioSource);
21+
ArgumentNullException.ThrowIfNull(radioSource);
22+
ArgumentNullException.ThrowIfNull(newSourceUrl);
2223

24+
radioSource.Name = name;
2325
radioSource.SourceUrl = newSourceUrl;
2426
radioSource.IsActive = isActive;
25-
return radioSource;
2627
}
2728

2829
public static RadioSource Create(string name, string sourceUrl)

src/Infrastructure/Commands/MusicPlayCommands.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ await executor.ExecuteAsync(async serviceProvider =>
6262
var message =
6363
CommandUtils.CreateMessage<InteractionMessageProperties>("Select a radio station to play:");
6464

65-
var radiosSourceList = (await radioSourceService.GetAllRadioSourcesAsync()).Where(rs => rs.IsActive);
65+
var radiosSourceList = (await radioSourceService.GetAllRadioSourcesAsync(CancellationToken.None)).Where(rs => rs.IsActive);
6666
message.Components =
6767
CommandUtils.CreateComponent(radiosSourceList.Select(rs =>
6868
new CommandUtils.ComponentModel(rs.Name, rs.Id.ToString())));

src/Infrastructure/Services/AudioPlayerService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ private async Task<string> GetSourceUrl(string selectedValue, IRadioSourceServic
166166

167167
if (Guid.TryParse(selectedValue, out var radioId))
168168
{
169-
sourceUrl = (await radioSourceService.GetRadioSourceByIdAsync(radioId)).SourceUrl;
169+
sourceUrl = (await radioSourceService.GetRadioSourceByIdAsync(radioId, CancellationToken.None)).SourceUrl;
170170
}
171171
else
172172
{

src/Infrastructure/Services/RadioSourceService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public async Task<RadioSource> GetRadioSourceByIdAsync(Guid id, CancellationToke
1818
throw new KeyNotFoundException($"Radio source with ID {id} not found.");
1919
}
2020

21-
public async Task UpdateRadioSourceUrlAsync(Guid id, string newSourceUrl, bool isActive, CancellationToken cancellationToken = default)
21+
public async Task UpdateRadioSourceUrlAsync(Guid id, string name, string newSourceUrl, bool isActive, CancellationToken cancellationToken = default)
2222
{
2323
if (string.IsNullOrEmpty(newSourceUrl))
2424
{
@@ -31,7 +31,7 @@ public async Task UpdateRadioSourceUrlAsync(Guid id, string newSourceUrl, bool i
3131
throw new KeyNotFoundException($"Radio source with ID {id} not found.");
3232
}
3333

34-
RadioSource.UpdateSourceUrl(radioSource, newSourceUrl, isActive);
34+
RadioSource.Update(radioSource, name, newSourceUrl, isActive);
3535

3636
context.RadioSources.Update(radioSource);
3737

0 commit comments

Comments
 (0)