Skip to content

Commit

Permalink
added all songs to library and recently played to home page
Browse files Browse the repository at this point in the history
  • Loading branch information
0PandaDEV committed Apr 15, 2024
1 parent bd97bb7 commit 9c65940
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 85 deletions.
20 changes: 19 additions & 1 deletion assets/styles/components/library.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
width: v.$sidebar-width;
height: 100%;
padding: 16px;
position: relative;

.link{
display: flex;
Expand All @@ -34,7 +35,7 @@
.search-container {
width: 268px;
height: 32px;
background-color: #222222;
background-color: v.$element;
outline: none;
border: none;
display: flex;
Expand All @@ -54,4 +55,21 @@
height: 32px;
}
}
}

.items{
display: flex;
flex-direction: column;
gap: 8px;
color: v.$text-bright;

.song{
display: flex;
align-items: center;
gap: 8px;
}

.cover{
width: 32px;
}
}
47 changes: 46 additions & 1 deletion assets/styles/pages/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,52 @@
padding: 0 !important;
}

.index{
.index {
padding: 24px;
padding-top: 48px;
padding-bottom: 0;
font-size: 14px;
}

.ascii {
padding-bottom: 48px;
color: v.$accent;
}

.playlists,
.recently-played {
display: flex;
flex-direction: column;
gap: 14px;
}

.recently-played .cards {
display: flex;
flex-direction: row;
gap: 16px;

.song {
display: flex;
flex-direction: column;
gap: 8px;

.info {
p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}

.artist{
color: v.$text;
}
}

.cover {
flex: 1;
max-width: 240px;
min-width: 150px;
}
}
}
83 changes: 61 additions & 22 deletions components/Library.vue
Original file line number Diff line number Diff line change
@@ -1,32 +1,71 @@
<template>
<div class="library element">
<p class="element-title">Library</p>
<!-- <p class="link">
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg">
<g id="Icon">
<path d="M0 0L16 0L16 16L0 16L0 0Z" id="Rectangle" fill="none" fill-rule="evenodd" stroke="none" />
<path d="M2 14L2 2L3 2L3 14L2 14ZM5.5 14L5.5 2L6.5 2L6.5 14L5.5 14ZM8.8 14L8.8 2L13.8 4.5L13.8 14L8.8 14Z"
id="Rectangle-3-Union" fill-rule="evenodd" stroke="none" />
</g>
</svg>
Your Library
</p> -->
<div class="search-container">
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg">
<g id="Search-icon">
<path d="M0 0L20 0L20 20L0 20L0 0Z" id="Shape-2" fill="none" fill-rule="evenodd" stroke="none" />
<path
d="M8.74998 1.66687C5.64729 1.66713 2.9059 3.68661 1.98584 6.64974C1.06578 9.61287 2.18123 12.8299 4.73811 14.5875C7.29498 16.345 10.6981 16.234 13.135 14.3135L16.1783 17.3569C16.5053 17.6727 17.025 17.6681 17.3465 17.3467C17.6679 17.0253 17.6724 16.5055 17.3566 16.1785L14.3133 13.1352C15.9933 11.004 16.3079 8.10025 15.1235 5.65861C13.9391 3.21698 11.4637 1.66669 8.74998 1.66687M3.33328 8.75041C3.33328 5.75886 5.75841 3.33374 8.74995 3.33374C11.7415 3.33374 14.1666 5.75887 14.1666 8.75041C14.1666 11.742 11.7415 14.1671 8.74995 14.1671C5.75841 14.1671 3.33328 11.742 3.33328 8.75041"
id="Shape" fill="#8B8B8B" fill-rule="evenodd" stroke="none" />
</g>
</svg>
<input class="input" spellcheck="false">
<IconsSearch/>
<input class="input" spellcheck="false" v-model="searchQuery" />
</div>
<div class="items">
<div
v-for="song in filteredSongs"
:key="song.id"
@click="play(song.id)"
class="song"
>
<img :src="song.coverURL" :alt="song.title" class="cover" />
<div class="info">
<p class="title">{{ truncate(song.title) }}</p>
</div>
</div>
</div>
</div>
</template>

<script lang="ts" setup>
import { type Song } from "~/types/types";
import { computed, ref, onMounted } from "vue";
const { $music } = useNuxtApp();
const songs = ref<Song[]>([]);
const searchQuery = ref("");
onMounted(async () => {
const loadedSongs = await $music.getSongs();
const songArray = Object.values(loadedSongs.songs);
await Promise.all(
songArray.map(async (song) => {
song.coverURL = await $music.getCoverURLFromID(song.id);
})
);
songs.value = songArray;
});
const filteredSongs = computed(() => {
return songs.value
.filter((song) =>
song.title.toLowerCase().includes(searchQuery.value.toLowerCase())
)
.sort((a, b) => {
// Sort by date added if search query is empty, otherwise sort by search relevance
if (searchQuery.value) {
return a.title.toLowerCase().indexOf(searchQuery.value.toLowerCase()) - b.title.toLowerCase().indexOf(searchQuery.value.toLowerCase());
} else {
return new Date(b.date_added).getTime() - new Date(a.date_added).getTime();
}
});
});
async function play(id: string) {
await $music.setSong(id);
$music.play();
}
function truncate(text: string, length: number = 40) {
return text.length > length ? text.substring(0, length - 3) + "..." : text;
}
</script>

<style lang="scss">
@import '~/assets/styles/components/library.scss';
@import "~/assets/styles/components/library.scss";
</style>
27 changes: 27 additions & 0 deletions components/icons/Search.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<svg
width="20px"
height="20px"
viewBox="0 0 20 20"
version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
>
<g id="Search-icon">
<path
d="M0 0L20 0L20 20L0 20L0 0Z"
id="Shape-2"
fill="none"
fill-rule="evenodd"
stroke="none"
/>
<path
d="M8.74998 1.66687C5.64729 1.66713 2.9059 3.68661 1.98584 6.64974C1.06578 9.61287 2.18123 12.8299 4.73811 14.5875C7.29498 16.345 10.6981 16.234 13.135 14.3135L16.1783 17.3569C16.5053 17.6727 17.025 17.6681 17.3465 17.3467C17.6679 17.0253 17.6724 16.5055 17.3566 16.1785L14.3133 13.1352C15.9933 11.004 16.3079 8.10025 15.1235 5.65861C13.9391 3.21698 11.4637 1.66669 8.74998 1.66687M3.33328 8.75041C3.33328 5.75886 5.75841 3.33374 8.74995 3.33374C11.7415 3.33374 14.1666 5.75887 14.1666 8.75041C14.1666 11.742 11.7415 14.1671 8.74995 14.1671C5.75841 14.1671 3.33328 11.742 3.33328 8.75041"
id="Shape"
fill="#8B8B8B"
fill-rule="evenodd"
stroke="none"
/>
</g>
</svg>
</template>
76 changes: 28 additions & 48 deletions pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,38 @@
<div class="main element">
<p class="element-title">Home</p>
<div class="index">
<pre class="ascii-art">
<pre class="ascii">
__
_ _____ / /________ ____ ___ ___
| | /| / / _ \/ / ___/ __ \/ __ `__ \/ _ \
| |/ |/ / __/ / /__/ /_/ / / / / / / __/
|__/|__/\___/_/\___/\____/_/ /_/ /_/\___/
</pre>
<div v-for="song in songs" :key="song.id" class="song-item">
<img :src="song.coverURL" :alt="song.title" class="song-cover" />
<p v-if="!song.id" class="error">Song ID is missing</p>
<div class="song-info">
<h2>{{ song.title }}</h2>
<p>{{ song.artist }}</p>
<p>{{ formatDuration(song.length) }}</p>
<p>{{ formatDate(song.date_added) }}</p>

<div class="playlists">
<div class="title">Playlists</div>
<div class="cards"></div>
</div>

<div class="recently-played">
<div class="title">Recently played</div>
<div class="cards">
<div v-for="song in sortedRecentlyPlayed" :key="song.id" @click="play(song.id)" class="song">
<img :src="song.coverURL" :alt="song.title" class="cover" />
<div class="info">
<p class="title">{{ truncate(song.title) }}</p>
<p class="artist">{{ truncate(song.artist) }}</p>
</div>
</div>
</div>
<button @click="play(song.id)">Play</button>
</div>
</div>
</div>
</template>

<script lang="ts" setup>
import { type Song } from "~/types/types";
import { computed, ref, onMounted } from 'vue';
const { $music } = useNuxtApp();
Expand All @@ -42,51 +50,23 @@ onMounted(async () => {
songs.value = songArray;
});
const sortedRecentlyPlayed = computed(() => {
return [...songs.value]
.filter(song => song.lastPlayed)
.sort((a, b) => b.lastPlayed - a.lastPlayed)
.slice(0, 7);
});
async function play(id: string) {
await $music.setSong(id);
$music.play();
}
function formatDuration(duration: number) {
const minutes = Math.floor(duration / 60);
const seconds = duration % 60;
return `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`;
}
function formatDate(dateString: string) {
const date = new Date(dateString);
const day = date.getDate();
const month = date.getMonth() + 1;
const year = date.getFullYear();
return `${day < 10 ? "0" : ""}${day}.${
month < 10 ? "0" : ""
}${month}.${year}`;
function truncate(text: string, length: number = 40) {
return text.length > length ? text.substring(0, length - 3) + '...' : text;
}
</script>

<style scoped lang="scss">
@import "~/assets/styles/pages/index.scss";
.song-item {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.song-cover {
width: 100px;
height: 100px;
object-fit: cover;
margin-right: 20px;
}
.song-info h2 {
margin: 0;
font-size: 20px;
}
.song-info p {
margin: 5px 0;
font-size: 16px;
}
</style>
</style>
2 changes: 2 additions & 0 deletions plugins/music.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ export default defineNuxtPlugin((nuxtApp) => {
musicStore.player.currentSongId = id;
await musicStore.setSongFromBuffer(contents);
await this.ensureAudioContextAndFilters();
const currentTime = new Date().toISOString();
musicStore.updateLastPlayed(id, currentTime);
} else {
settingsStore.settings.playerSettings.currentSong = "";
await settingsStore.saveSettings();
Expand Down
38 changes: 25 additions & 13 deletions stores/music.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import {
BaseDirectory,
writeTextFile,
} from "@tauri-apps/plugin-fs";
import type { MusicStore, SongsConfig, Song } from "~/types/types";

export const useMusicStore = defineStore("musicStore", {
state: () =>
({
songsConfig: {
songs: {},
},
player: {
audio: new Audio(),
currentSongId: "",
audioContext: null,
sourceNode: null,
eqFilters: []
},
} as MusicStore),
({
songsConfig: {
songs: {},
},
player: {
audio: new Audio(),
currentSongId: "",
audioContext: null,
sourceNode: null,
eqFilters: []
},
} as MusicStore),

actions: {
init(songs: SongsConfig) {
Expand Down Expand Up @@ -45,6 +49,14 @@ export const useMusicStore = defineStore("musicStore", {
},
getSongByID(id: string): Song | null {
return this.songsConfig?.songs?.[id] ?? null;
}
},
updateLastPlayed(songId: string, lastPlayed: string) {
if (this.songsConfig.songs[songId]) {
this.songsConfig.songs[songId].lastPlayed = lastPlayed;
writeTextFile("Vleer/songs.json", JSON.stringify(this.songsConfig, null, 2), {
baseDir: BaseDirectory.Audio,
});
}
},
},
});
1 change: 1 addition & 0 deletions types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface Song {
cover: string;
coverURL?: string;
date_added: string;
lastPlayed?: string;
}

export interface SongsConfig {
Expand Down

0 comments on commit 9c65940

Please sign in to comment.