diff --git a/content/music.json b/content/music.json
new file mode 100644
index 0000000..d601ded
--- /dev/null
+++ b/content/music.json
@@ -0,0 +1,13 @@
+{
+ "videos": [
+ "https://music.youtube.com/watch?v=NqCuW5bupyE&si=D0oy80gIACjXpHM1",
+ "https://music.youtube.com/watch?v=MXp3lZQgwes&si=fPOA9Rwkx6PLm9bv",
+ "https://music.youtube.com/watch?v=noiy7ajORtA&si=BeHiGpw-4eyKa76O",
+ "https://music.youtube.com/watch?v=aEPaTYwYlF8&si=UdCgBnMJTnORpXIX",
+ "https://music.youtube.com/watch?v=D1JpkgXxMR8&si=9u-Z4CAHbGv1Qopl",
+ "https://music.youtube.com/watch?v=KfEEm4Zx-EU&si=V0b6oiP4P-fcE6VI",
+ "https://music.youtube.com/watch?v=fixh0ixT-9U&si=dWLHv9bylwNAu-DD",
+ "https://music.youtube.com/watch?v=En_6WWOWh8E&si=KSUbzK7M3NFmbAUO",
+ "https://music.youtube.com/watch?v=Kf5pXDhx5Vc&si=yUIlI-2md9uYjGu5"
+ ]
+}
diff --git a/package.json b/package.json
index 057b5fe..af44e7b 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"vite": "^8.3.5"
},
"dependencies": {
+ "@distube/ytdl-core": "^5.2.0",
"@iconify/svelte": "^5.2.3",
"discord.js": "^14.28.0",
"svelte-qrcode": "^1.0.1"
diff --git a/src/lib/components/music/MusicBars.svelte b/src/lib/components/music/MusicBars.svelte
new file mode 100644
index 0000000..fc9929c
--- /dev/null
+++ b/src/lib/components/music/MusicBars.svelte
@@ -0,0 +1,61 @@
+
+
+
+ {#each { length: barCount }, index (index)}
+
+ {/each}
+
+
+
diff --git a/src/lib/components/music/MusicControls.svelte b/src/lib/components/music/MusicControls.svelte
new file mode 100644
index 0000000..d6962ec
--- /dev/null
+++ b/src/lib/components/music/MusicControls.svelte
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/music/MusicNowPlaying.svelte b/src/lib/components/music/MusicNowPlaying.svelte
new file mode 100644
index 0000000..fc7f845
--- /dev/null
+++ b/src/lib/components/music/MusicNowPlaying.svelte
@@ -0,0 +1,146 @@
+
+
+
+
+
diff --git a/src/lib/components/music/MusicPopover.svelte b/src/lib/components/music/MusicPopover.svelte
new file mode 100644
index 0000000..d19bd2e
--- /dev/null
+++ b/src/lib/components/music/MusicPopover.svelte
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+ {#if track}
+
+
+
+
+
+ {:else}
+
+
+ No music playing right now
+
+ {/if}
+
+
+
+
diff --git a/src/lib/components/music/MusicProgress.svelte b/src/lib/components/music/MusicProgress.svelte
new file mode 100644
index 0000000..2efab06
--- /dev/null
+++ b/src/lib/components/music/MusicProgress.svelte
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
(draggedPosition = Number(event.currentTarget.value))}
+ onchange={finishSeek}
+ />
+
+
+ {formatTrackTime(shownPosition)}
+ -{formatTrackTime(remainingSeconds)}
+
+
+
+
diff --git a/src/lib/components/music/MusicTrackList.svelte b/src/lib/components/music/MusicTrackList.svelte
new file mode 100644
index 0000000..7d4bac2
--- /dev/null
+++ b/src/lib/components/music/MusicTrackList.svelte
@@ -0,0 +1,142 @@
+
+
+
+
+
+ {#if expanded}
+
+ {#each musicPlayer.tracks as track, index (track.id)}
+ {@const isCurrent = index === musicPlayer.currentIndex}
+ -
+
+
+ {/each}
+
+ {/if}
+
+
+
diff --git a/src/lib/components/music/MusicVolume.svelte b/src/lib/components/music/MusicVolume.svelte
new file mode 100644
index 0000000..b13c867
--- /dev/null
+++ b/src/lib/components/music/MusicVolume.svelte
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
musicPlayer.setVolume(Number(event.currentTarget.value))}
+ />
+
+
{volumePercent}%
+
+
+
diff --git a/src/lib/components/shell/Bar.svelte b/src/lib/components/shell/Bar.svelte
index c53951f..6e8cdfd 100644
--- a/src/lib/components/shell/Bar.svelte
+++ b/src/lib/components/shell/Bar.svelte
@@ -6,6 +6,7 @@
import ThemeMenu from './ThemeMenu.svelte';
import DiscordStatus from './DiscordStatus.svelte';
import Battery from './Battery.svelte';
+ import Music from './Music.svelte';
@@ -21,6 +22,7 @@
+
@@ -86,4 +88,21 @@
width: 36px;
}
}
+
+ @media (max-width: 420px) {
+ .bar {
+ grid-template-columns: auto 1fr auto;
+ gap: 2px;
+ padding: 0 2px;
+ }
+
+ .clock-button {
+ justify-self: center;
+ padding: 0 0.25rem;
+ }
+
+ .bar :global(.tray-button) {
+ width: 30px;
+ }
+ }
diff --git a/src/lib/components/shell/Music.svelte b/src/lib/components/shell/Music.svelte
new file mode 100644
index 0000000..406ba06
--- /dev/null
+++ b/src/lib/components/shell/Music.svelte
@@ -0,0 +1,123 @@
+
+
+{#if track}
+ {#key musicPlayer.streamKey}
+
+ {/key}
+{/if}
+
+
+ {#if track}
+
+ {/if}
+
+
+ {#if open}
+ (open = false)} />
+ {/if}
+
+
+
diff --git a/src/lib/music/player.svelte.ts b/src/lib/music/player.svelte.ts
new file mode 100644
index 0000000..737ee1a
--- /dev/null
+++ b/src/lib/music/player.svelte.ts
@@ -0,0 +1,137 @@
+import type { Track } from './types';
+import { pickRandomIndex } from './playlist';
+
+class MusicPlayer {
+ tracks = $state