major refactors

This commit is contained in:
geoffrey45
2022-03-30 14:56:40 +03:00
parent 1a19fb61cd
commit 0c1e792839
21 changed files with 164 additions and 322 deletions
+8 -1
View File
@@ -30,7 +30,14 @@ def get_all_playlists():
@playlist_bp.route("/playlist/new", methods=["POST"])
def create_playlist():
data = request.get_json()
playlist = {"name": data["name"], "description": [], "tracks": []}
playlist = {
"name": data["name"],
"description": [],
"tracks": [],
"count": 0,
"lastUpdated": 0,
}
try:
p_in_db = instances.playlist_instance.get_playlist_by_name(playlist["name"])
+1 -1
View File
@@ -21,7 +21,7 @@ class AllSongs(db.Mongo):
# def drop_db(self):
# self.collection.drop()
def insert_song(self, song_obj: dict) -> None:
def insert_song(self, song_obj: dict) -> str:
"""
Inserts a new track object into the database.
"""
+5 -4
View File
@@ -69,11 +69,12 @@ def populate():
for file in files:
tags = get_tags(file)
if tags not in api.PRE_TRACKS:
api.PRE_TRACKS.append(tags)
if tags is not None:
instances.songs_instance.insert_song(tags)
upsert_id = instances.songs_instance.insert_song(tags)
if upsert_id is not None:
tags["_id"] = {"$oid": upsert_id}
api.PRE_TRACKS.append(tags)
_bar.next()
_bar.finish()
+7 -3
View File
@@ -2,6 +2,7 @@
This library contains all the functions related to albums.
"""
from pprint import pprint
import urllib
from typing import List
from app import models, functions, helpers
@@ -52,9 +53,12 @@ def get_album_tracks(album: str, artist: str) -> List:
tracks = []
for track in api.PRE_TRACKS:
if track["album"] == album and track["albumartist"] == artist:
tracks.append(track)
try:
if track["album"] == album and track["albumartist"] == artist:
tracks.append(track)
except TypeError:
pprint(track, indent=4)
print(album, artist)
return tracks
+6
View File
@@ -3,6 +3,7 @@ Contains all the models for objects generation and typing.
"""
from dataclasses import dataclass
from datetime import date
from typing import List
from app import api
from app import settings
@@ -95,6 +96,8 @@ class Playlist:
description: str
image: str
tracks: List[Track]
count: int
lastUpdated: int
"""A list of track objects in the playlist"""
def __init__(self, data):
@@ -103,6 +106,9 @@ class Playlist:
self.description = data["description"]
self.image = ""
self.tracks = create_playlist_tracks(data["tracks"])
self.count = len(data["tracks"])
self.lastUpdated = data["lastUpdated"]
@dataclass
+5 -1
View File
@@ -39,11 +39,15 @@ import useContextStore from "./stores/context";
import ContextMenu from "./components/contextMenu.vue";
import Modal from "./components/modal.vue";
import Notification from "./components/Notification.vue";
import useQStore from "./stores/queue";
const context_store = useContextStore();
const queue = useQStore();
queue.readQueueFromLocalStorage();
const RightSideBar = Main;
perks.readQueue();
const collapsed = ref(false);
const app_dom = document.getElementById("app");
+18 -19
View File
@@ -1,23 +1,22 @@
<template>
<div class="hotkeys">
<div class="image ctrl-btn" id="previous" @click="playPrev"></div>
<div class="image ctrl-btn" id="previous" @click="props.prev"></div>
<div
class="image ctrl-btn play-pause"
@click="playPause"
:class="{ isPlaying: isPlaying }"
:class="{ isPlaying: props.playing }"
></div>
<div class="image ctrl-btn" id="next" @click="playNext"></div>
<div class="image ctrl-btn" id="next" @click="props.next"></div>
</div>
</template>
<script setup>
import playAudio from '../../../composables/playAudio';
const playPause = playAudio.playPause;
const playNext = playAudio.playNext;
const playPrev = playAudio.playPrev;
const isPlaying = playAudio.playing;
<script setup lang="ts">
const props = defineProps<{
playing: boolean;
playPause: () => void;
next: () => void;
prev: () => void;
}>();
</script>
<style lang="scss">
@@ -31,16 +30,16 @@ const isPlaying = playAudio.playing;
place-content: flex-end;
.ctrl-btn {
height: 2.5rem;
width: 100%;
background-size: 1.5rem !important;
cursor: pointer;
border-radius: 0.5rem;
height: 2.5rem;
width: 100%;
background-size: 1.5rem !important;
cursor: pointer;
border-radius: 0.5rem;
&:hover {
background-color: $red;
}
&:hover {
background-color: $red;
}
}
#previous {
background-image: url(../../../assets/icons/previous.svg);
+9 -7
View File
@@ -2,7 +2,7 @@
<input
id="progress"
type="range"
:value="pos"
:value="props.pos"
min="0"
max="100"
step="0.1"
@@ -10,12 +10,14 @@
/>
</template>
<script setup>
import { ref } from "vue";
import playAudio from "../../../composables/playAudio";
const pos = ref(playAudio.pos);
<script setup lang="ts">
const seek = () => {
playAudio.seek(document.getElementById("progress").value);
const value = Number(document.getElementById("progress").value);
props.seek(value);
};
const props = defineProps<{
pos: number;
seek: (time: number) => void;
}>();
</script>
+15 -24
View File
@@ -1,39 +1,30 @@
<template>
<div class="info">
<div
v-if="props.collapsed"
class="image art"
:style="{
backgroundImage: `url(&quot;${track.image}&quot;)`,
}"
></div>
<div class="desc">
<div>
<div class="title ellip">{{ track.title }}</div>
<div class="title ellip">{{ props.track.title }}</div>
<div class="separator no-border"></div>
<div class="artists ellip" v-if="track.artists[0] !== ''">
<span v-for="artist in putCommas(track.artists)" :key="artist">{{
artist
}}</span>
<div class="artists ellip" v-if="props.track.artists[0] !== ''">
<span
v-for="artist in putCommas(props.track.artists)"
:key="artist"
>{{ artist }}</span
>
</div>
<div class="artists" v-else>
<span>{{ track.albumartist }}</span>
<span>{{ props.track.albumartist }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
<script setup lang="ts">
import perks from "../../composables/perks";
import state from "../../composables/state";
const track = state.current;
const props = defineProps({
collapsed: {
type: Boolean,
default: false,
},
});
import { Track } from "../../interfaces";
const putCommas = perks.putCommas;
</script>
const props = defineProps<{
track: Track;
}>();
</script>
+12 -14
View File
@@ -1,5 +1,5 @@
<template>
<div class="l_ rounded" v-if="!props.collapsed">
<div class="l_ rounded">
<div class="headin">Now Playing</div>
<div class="button menu image rounded"></div>
<div class="separator no-border"></div>
@@ -8,32 +8,30 @@
<div
class="l-image image rounded"
:style="{
backgroundImage: `url(&quot;${current.image}&quot;)`,
backgroundImage: `url(&quot;${queue.current.image}&quot;)`,
}"
></div>
</div>
<div class="separator no-border"></div>
<SongCard />
<Progress />
<HotKeys />
<SongCard :track="queue.current" />
<Progress :seek="queue.seek" :pos="queue.current_time" />
<HotKeys
:playing="queue.playing"
:playPause="queue.playPause"
:next="queue.playNext"
:prev="queue.playPrev"
/>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import state from "../../composables/state";
import SongCard from "./SongCard.vue";
import HotKeys from "./NP/HotKeys.vue";
import Progress from "./NP/Progress.vue";
import useQStore from "../../stores/queue";
const current = ref(state.current);
const props = defineProps({
collapsed: {
type: Boolean,
default: false,
},
});
const queue = useQStore();
</script>
<style lang="scss">
.l_ {
+1 -1
View File
@@ -28,7 +28,7 @@ const props = defineProps<{
.image {
min-width: 100%;
height: 8.5rem;
height: 10rem;
background-image: url("../../assets/images/eggs.jpg");
}
+1 -3
View File
@@ -12,10 +12,8 @@ const loading = state.loading
<style lang="scss">
.loaderx {
position: absolute;
top: 0.65rem;
width: 1.5rem;
height: 1.5rem;
height:1.5rem;
border-radius: 50%;
}
+1 -4
View File
@@ -79,10 +79,7 @@ const showContextMenu = (e: Event) => {
e.preventDefault();
e.stopPropagation();
contextStore.showContextMenu(
e,
trackContext(props.song, modalStore)
);
contextStore.showContextMenu(e, trackContext(props.song, modalStore));
context_on.value = true;
contextStore.$subscribe((mutation, state) => {
+24 -16
View File
@@ -4,7 +4,7 @@
@click="playThis(props.track)"
:class="[
{
currentInQueue: current.trackid === props.track.trackid,
currentInQueue: props.isCurrent,
},
{ 'context-on': context_on },
]"
@@ -18,8 +18,8 @@
>
<div
class="now-playing-track image"
v-if="current.trackid === props.track.trackid"
:class="{ active: is_playing, not_active: !is_playing }"
v-if="props.isCurrent"
:class="{ active: props.isPlaying, not_active: !props.isPlaying }"
></div>
</div>
<div class="tags">
@@ -34,21 +34,31 @@
</div>
</template>
<script setup>
<script setup lang="ts">
import { ref } from "vue";
import perks from "../../composables/perks";
import playAudio from "../../composables/playAudio";
import useContextStore from "@/stores/context";
import trackContext from "../../contexts/track_context";
import { Track } from "../../interfaces";
import useContextStore from "../../stores/context";
import useModalStore from "../../stores/modal";
const contextStore = useContextStore();
const modalStore = useModalStore();
const props = defineProps<{
track: Track;
isCurrent: boolean;
isPlaying: boolean;
}>();
const context_on = ref(false);
const showContextMenu = (e) => {
const showContextMenu = (e: Event) => {
e.preventDefault();
e.stopPropagation();
contextStore.showContextMenu(e, trackContext(props.track));
contextStore.showContextMenu(e, trackContext(props.track, modalStore));
context_on.value = true;
contextStore.$subscribe((mutation, state) => {
@@ -57,18 +67,16 @@ const showContextMenu = (e) => {
}
});
};
const props = defineProps({
track: Object,
default: () => ({}),
});
const emit = defineEmits<{
(e: "PlayThis", track: Track): void;
}>();
const current = ref(perks.current);
const putCommas = perks.putCommas;
const is_playing = ref(playAudio.playing);
const playThis = (song) => {
playAudio.playAudio(song.trackid);
perks.current.value = song;
const playThis = (track: Track) => {
emit("PlayThis", track);
};
</script>
@@ -1,41 +1,44 @@
import { Track } from "../interfaces.js";
import perks from "./perks.js";
import playAudio from "./playAudio.js";
let showMediaNotif = () => {
let current = perks.current.value;
export default (
track: Track,
playPause: () => void,
playNext: () => void,
playPrev: () => void
) => {
if ("mediaSession" in navigator) {
navigator.mediaSession.metadata = new window.MediaMetadata({
title: current.title,
artist: current.artists,
title: track.title,
artist: track.artists.join(", "),
artwork: [
{
src: current.image,
src: track.image,
sizes: "96x96",
type: "image/jpeg",
},
{
src: current.image,
src: track.image,
sizes: "128x128",
type: "image/webp",
},
{
src: current.image,
src: track.image,
sizes: "192x192",
type: "image/webp",
},
{
src: current.image,
src: track.image,
sizes: "256x256",
type: "image/webp",
},
{
src: current.image,
src: track.image,
sizes: "384x384",
type: "image/webp",
},
{
src: current.image,
src: track.image,
sizes: "512x512",
type: "image/webp",
},
@@ -43,22 +46,16 @@ let showMediaNotif = () => {
});
navigator.mediaSession.setActionHandler("play", function () {
playAudio.playPause();
playPause();
});
navigator.mediaSession.setActionHandler("pause", function () {
playAudio.playPause();
playPause();
});
navigator.mediaSession.setActionHandler("seekbackward", function () {});
navigator.mediaSession.setActionHandler("seekforward", function () {});
navigator.mediaSession.setActionHandler("previoustrack", function () {
playAudio.playPrev();
playPrev();
});
navigator.mediaSession.setActionHandler("nexttrack", function () {
playAudio.playNext();
playNext();
});
}
};
export default {
showMediaNotif,
};
+3 -114
View File
@@ -1,26 +1,3 @@
import { ref } from "@vue/reactivity";
import { watch } from "@vue/runtime-core";
import media from "./mediaNotification.js";
import playAudio from "./playAudio.js";
import state from "./state";
const current = ref(state.current);
const next = ref({
title: "The next song",
artists: ["... blah blah blah"],
image: "http://127.0.0.1:8900/images/defaults/4.webp",
_id: {
$oid: "",
},
});
const prev = ref(state.prev);
const queue = ref(state.queue);
const search = ref("");
const putCommas = (artists) => {
let result = [];
@@ -35,74 +12,6 @@ const putCommas = (artists) => {
return result;
};
function updateNext(song_) {
const index = state.queue.value.findIndex(
(item) => item.trackid === song_.trackid
);
if (index === queue.value.length - 1) {
next.value = queue.value[0];
state.prev.value = queue.value[queue.value.length - 2];
} else if (index === 0) {
next.value = queue.value[1];
} else {
next.value = queue.value[index + 1];
}
}
function updatePrev(song) {
const index = state.queue.value.findIndex(
(item) => item.trackid === song.trackid
);
if (index === 0) {
prev.value = queue.value[queue.value.length - 1];
} else if (index === queue.value.length - 1) {
prev.value = queue.value[index - 1];
} else {
prev.value = queue.value[index - 1];
}
}
const readQueue = () => {
const prev_queue = JSON.parse(localStorage.getItem("queue"));
const last_played = JSON.parse(localStorage.getItem("current"));
if (last_played) {
state.current.value = last_played;
}
if (prev_queue) {
state.queue.value = prev_queue;
updateNext(state.current.value);
updatePrev(state.current.value);
}
};
const updateQueue = async (song, type) => {
playAudio.playAudio(song.trackid);
let list;
switch (type) {
case "folder":
list = state.folder_song_list.value;
break;
case "album":
list = state.album.tracklist;
break;
}
if (state.queue.value[0].trackid !== list[0].trackid) {
const new_queue = list;
localStorage.setItem("queue", JSON.stringify(new_queue));
state.queue.value = new_queue;
}
state.current.value = song;
localStorage.setItem("current", JSON.stringify(song));
};
function focusCurrent() {
const elem = document.getElementsByClassName("currentInQueue")[0];
@@ -132,17 +41,6 @@ function focusSearchBox() {
elem.focus();
}
setTimeout(() => {
watch(current, (new_current) => {
media.showMediaNotif();
updateNext(new_current);
updatePrev(new_current);
localStorage.setItem("current", JSON.stringify(new_current));
});
}, 1000);
let key_down_fired = false;
window.addEventListener("keydown", (e) => {
@@ -161,7 +59,7 @@ window.addEventListener("keydown", (e) => {
key_down_fired = false;
}, 1000);
playAudio.playNext();
// playAudio.playNext();
}
}
break;
@@ -173,7 +71,7 @@ window.addEventListener("keydown", (e) => {
key_down_fired = true;
playAudio.playPrev();
// playAudio.playPrev();
setTimeout(() => {
key_down_fired = false;
@@ -190,7 +88,7 @@ window.addEventListener("keydown", (e) => {
e.preventDefault();
key_down_fired = true;
playAudio.playPause();
// playAudio.playPause();
}
}
@@ -212,8 +110,6 @@ window.addEventListener("keyup", () => {
key_down_fired = false;
});
function formatSeconds(seconds) {
// check if there are arguments
@@ -256,14 +152,7 @@ function formatSeconds(seconds) {
export default {
putCommas,
readQueue,
focusCurrent,
updateQueue,
formatSeconds,
getElem,
current,
queue,
next,
prev,
search,
};
-86
View File
@@ -1,86 +0,0 @@
import {ref} from "@vue/reactivity";
import perks from "./perks";
import media from "./mediaNotification.js";
import state from "./state";
const audio = ref(new Audio()).value;
const pos = ref(0);
const current_time = ref(0);
const playing = ref(state.is_playing);
const url = "http://127.0.0.1:9876//file/";
const playAudio = (trackid) => {
const elem = document.getElementById('progress');
const full_path = url + encodeURIComponent(trackid);
new Promise((resolve, reject) => {
audio.src = full_path;
audio.oncanplaythrough = resolve;
audio.onerror = reject;
})
.then(() => {
audio.play().then(() => {
perks.focusCurrent()
state.is_playing.value = true;
}
);
audio.ontimeupdate = () => {
current_time.value = audio.currentTime;
pos.value = (audio.currentTime / audio.duration) * 100;
let bg_size = ((audio.currentTime / audio.duration) * 100)
elem.style.backgroundSize = `${bg_size}% 100%`;
};
})
.catch((err) => console.log(err));
};
function playNext() {
playAudio(perks.next.value.trackid);
perks.current.value = perks.next.value;
media.showMediaNotif();
}
function playPrev() {
playAudio(state.prev.value.trackid);
perks.current.value = perks.prev.value;
}
function seek(position) {
audio.currentTime = (position / 100) * audio.duration;
}
function playPause() {
if (audio.src === "") {
playAudio(perks.current.value.trackid);
}
if (audio.paused) {
audio.play();
} else {
audio.pause();
}
}
audio.addEventListener("play", () => {
state.is_playing.value = true;
});
audio.addEventListener("pause", () => {
state.is_playing.value = false;
});
audio.addEventListener("ended", () => {
playNext();
});
export default {playAudio, playNext, playPrev, playPause, seek, pos, playing, current_time};
// TODO
// Try implementing classes to play audio .ie. Make the seek, playNext, playPrev, etc the methods of a class. etc
+5
View File
@@ -12,6 +12,7 @@ import SettingsView from "../views/SettingsView.vue";
import usePStore from "../stores/playlists";
import usePTrackStore from "../stores/p.ptracks";
import useFStore from "../stores/folder";
const routes = [
{
@@ -23,6 +24,10 @@ const routes = [
path: "/folder/:path",
name: "FolderView",
component: FolderView,
beforeEnter: async (to) => {
console.log("beforeEnter")
await useFStore().fetchAll(to.params.path);
},
},
{
path: "/folder/",
+21
View File
@@ -0,0 +1,21 @@
import { defineStore } from "pinia";
import { Folder, Track } from "../interfaces";
import fetchThem from "../composables/getFilesAndFolders";
export default defineStore("FolderDirs&Tracks", {
state: () => ({
path: <string>{},
dirs: <Folder[]>[],
tracks: <Track[]>[],
}),
actions: {
async fetchAll(path: string) {
const { tracks, folders } = await fetchThem(path);
this.path = path;
this.dirs = folders;
this.tracks = tracks;
},
},
});
+2 -2
View File
@@ -1,8 +1,8 @@
import { defineStore } from "pinia";
import { Track } from "../interfaces";
enum ModalOptions {
newPlaylist = "newPlaylist",
editPlaylist = "editPlaylist",
newPlaylist,
editPlaylist,
}
export default defineStore("newModal", {
+1
View File
@@ -22,6 +22,7 @@ const playlist = usePTrackStore().playlist;
const info = {
name: playlist.name,
count: playlist.tracks.length,
desc: playlist.description,
duration: "3 hours, 4 minutes",
lastUpdated: "yesterday",
};