try adding playlists list to context menu - unsuccsessfully

This commit is contained in:
geoffrey45
2022-03-25 20:51:22 +03:00
parent 642c524a08
commit e2544dbbdc
21 changed files with 394 additions and 75 deletions
+2 -1
View File
@@ -19,13 +19,14 @@ def create_app():
cache.init_app(app)
with app.app_context():
from app.api import artist, track, search, folder, album
from app.api import artist, track, search, folder, album, playlist
app.register_blueprint(album.album_bp, url_prefix="/")
app.register_blueprint(artist.artist_bp, url_prefix="/")
app.register_blueprint(track.track_bp, url_prefix="/")
app.register_blueprint(search.search_bp, url_prefix="/")
app.register_blueprint(folder.folder_bp, url_prefix="/")
app.register_blueprint(playlist.playlist_bp, url_prefix="/")
return app
+3 -1
View File
@@ -10,6 +10,7 @@ from app import models, instances
from app import functions, helpers, prep
from app.lib import albumslib
from app.lib import folderslib
from app.lib import playlistlib
DB_TRACKS = instances.songs_instance.get_all_songs()
@@ -20,6 +21,7 @@ TRACKS: List[models.Track] = []
PLAYLISTS: List[models.Playlist] = []
FOLDERS: List[models.Folder] = []
@helpers.background
def initialize() -> None:
"""
@@ -29,8 +31,8 @@ def initialize() -> None:
prep.create_config_dir()
albumslib.create_everything()
folderslib.run_scandir()
playlistlib.create_all_playlists()
functions.reindex_tracks()
initialize()
+1 -1
View File
@@ -30,7 +30,7 @@ def get_folder_tree():
songs = []
for track in api.TRACKS:
if track.folder + "/" == req_dir:
if track.folder == req_dir:
songs.append(track)
final_tracks = helpers.remove_duplicates(songs)
+7 -5
View File
@@ -11,22 +11,24 @@ playlist_bp = Blueprint("playlist", __name__, url_prefix="/")
@playlist_bp.route("/playlists", methods=["GET"])
def get_all_playlists():
print(api.PLAYLISTS)
playlists = []
for playlist in api.PLAYLISTS:
del playlist.tracks
playlist.tracks = []
playlists.append(playlist)
return playlists
return {"data": playlists}
@playlist_bp.route("/playlist/new")
@playlist_bp.route("/playlist/new", methods=["POST"])
def create_playlist():
data = request.get_json()
playlist = {"name": data["name"], "description": data["description"], "tracks": []}
playlist = {"name": data["name"], "description": [], "tracks": []}
instances.playlist_instance.insert_playlist(playlist)
return 200
return {"msg": "success"}
@playlist_bp.route("/playlist/<playlist_id>/add", methods=["POST"])
+4 -3
View File
@@ -26,6 +26,7 @@ from app import settings, models
from app.lib import albumslib
from app import api
from app.lib import watchdoge
from app.lib import folderslib
@helpers.background
@@ -61,7 +62,6 @@ def populate():
start = time.time()
s, files = helpers.run_fast_scandir(settings.HOME_DIR, [".flac", ".mp3"], full=True)
# pprint(s)
_bar = Bar("Processing files", max=len(files))
for file in files:
@@ -74,7 +74,8 @@ def populate():
_bar.finish()
albumslib.create_everything()
folderslib.run_scandir()
end = time.time()
print(
@@ -340,7 +341,7 @@ def get_tags(fullpath: str) -> dict:
"length": round(audio.info.length),
"bitrate": round(int(audio.info.bitrate) / 1000),
"filepath": fullpath,
"folder": os.path.dirname(fullpath),
"folder": os.path.dirname(fullpath) + "/",
}
return tags
+10 -4
View File
@@ -29,11 +29,11 @@ def create_folder(foldername: str) -> models.Folder:
return models.Folder(folder)
def create_all_folders(foldernames: List[str]) -> List[models.Folder]:
def create_all_folders() -> List[models.Folder]:
folders_: List[models.Folder] = []
_bar = Bar("Creating folders", max=len(foldernames))
_bar = Bar("Creating folders", max=len(api.VALID_FOLDERS))
for foldername in foldernames:
for foldername in api.VALID_FOLDERS:
folder = create_folder(foldername)
folders_.append(folder)
_bar.next()
@@ -61,11 +61,17 @@ def get_subdirs(foldername: str) -> List[models.Folder]:
@helpers.background
def run_scandir():
"""
Initiates the creation of all folder objects for each folder with a track in it.
Runs in a background thread after every 5 minutes.
It calls the
"""
flag = False
while flag is False:
get_valid_folders()
folders_ = create_all_folders(api.VALID_FOLDERS)
folders_ = create_all_folders()
"""Create all the folder objects before clearing api.FOLDERS"""
api.FOLDERS.clear()
+4 -1
View File
@@ -1,5 +1,7 @@
<template>
<ContextMenu />
<Modal />
<Notification />
<div class="l-container" :class="{ collapsed: collapsed }">
<div class="l-sidebar">
<div id="logo-container">
@@ -25,7 +27,6 @@
<script setup>
import { ref } from "vue";
import Navigation from "./components/LeftSidebar/Navigation.vue";
import BottomBar from "@/components/BottomBar/BottomBar.vue";
import perks from "@/composables/perks.js";
@@ -36,6 +37,8 @@ import Tabs from "./components/RightSideBar/Tabs.vue";
import SearchInput from "./components/RightSideBar/SearchInput.vue";
import useContextStore from "./stores/context";
import ContextMenu from "./components/contextMenu.vue";
import Modal from "./components/modal.vue";
import Notification from "./components/Notification.vue";
const context_store = useContextStore();
+1 -3
View File
@@ -20,10 +20,8 @@ body {
}
.heading {
font-size: small;
font-size: 2rem;
font-weight: bold;
display: flex;
align-items: center;
}
.t-center {
+1 -1
View File
@@ -8,7 +8,7 @@
<div class="text">
<div class="icon image"></div>
<div class="ellip">
{{ path.split("/").splice(-1) + "" }}
{{ path.split("/").splice(-2).join("") }}
</div>
</div>
</div>
+29
View File
@@ -0,0 +1,29 @@
<template>
<div class="new-notification rounded" v-if="store.visible">
<div>{{ store.text }}</div>
</div>
</template>
<script setup lang="ts">
import { useNotificationStore } from "../stores/notification";
const store = useNotificationStore();
</script>
<style lang="scss">
.new-notification {
position: fixed;
z-index: 2000;
width: 25rem;
bottom: 2rem;
padding: $small;
left: 50%;
translate: -50%;
background-color: rgb(5, 62, 168);
display: grid;
place-items: center;
.link {
font-weight: bold;
text-decoration: underline;
}
}
</style>
+80
View File
@@ -0,0 +1,80 @@
<template>
<div class="new-playlist-modal" v-if="modal.visible">
<div class="bg" @click="modal.hideModal"></div>
<div class="m-content rounded">
<div class="heading">{{ modal.title }}</div>
<div class="cancel image" @click="modal.hideModal"></div>
<NewPlaylist
v-if="modal.component == modal.options.newPlaylist"
@hideModal="hideModal"
@title="title"
/>
</div>
</div>
</template>
<script setup lang="ts">
import useModalStore from "../stores/modal";
import NewPlaylist from "./modals/NewPlaylist.vue";
const modal = useModalStore();
/**
* Sets the modal title
* @param title
*/
function title(title: string) {
modal.setTitle(title);
}
/**
* Handle the emit to hide the modal
*/
function hideModal() {
modal.hideModal();
}
</script>
<style lang="scss">
.new-playlist-modal {
position: fixed;
z-index: 2000;
height: 100vh;
width: 100vw;
display: grid;
place-items: center;
.bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(12, 12, 12, 0.767);
}
.m-content {
width: 30rem;
background-color: $black;
padding: 1rem;
position: relative;
.cancel {
width: 2rem;
height: 2rem;
position: absolute;
top: 1rem;
right: 1rem;
background-image: url("../assets/icons/plus.svg");
transform: rotate(45deg);
&:hover {
cursor: pointer;
transform: rotate(135deg);
}
}
}
}
</style>
+69
View File
@@ -0,0 +1,69 @@
<template>
<form @submit="create" class="new-p-form">
<label for="name">Playlist name</label>
<br />
<input
type="text"
class="rounded"
name="name"
id="modal-playlist-name-input"
/>
<br />
<input type="submit" class="rounded" value="Create" />
</form>
</template>
<script setup lang="ts">
import { createNewPlaylist } from "../../composables/playlists";
const emit = defineEmits<{
(e: "title", title: string): void;
(e: "hideModal"): void;
}>();
emit("title", "New Playlist");
function create(e: Event) {
e.preventDefault();
const name = (e.target as HTMLFormElement).elements["name"].value;
if (name.trim()) {
createNewPlaylist(name).then(() => emit("hideModal"));
}
}
</script>
<style lang="scss">
.new-p-form {
grid-gap: 1rem;
margin-top: 1rem;
label {
font-size: 0.9rem;
color: $gray1;
}
input[type="text"] {
margin: $small 0;
border: 2px solid $gray3;
background-color: transparent;
color: #fff;
width: 100%;
padding: 0.5rem;
font-size: 1rem;
outline: none;
}
input[type="submit"] {
margin: $small 0;
background-color: $accent;
color: #fff;
width: 7rem;
padding: 0.75rem;
font-size: 1rem;
border: none;
outline: none;
cursor: pointer;
}
}
</style>
+10 -1
View File
@@ -64,18 +64,27 @@
import perks from "../../composables/perks.js";
import state from "../../composables/state";
import useContextStore from "../../stores/context";
import useModalStore from "../../stores/modal";
import usePlaylistStore from "../../stores/playlists";
import { ref } from "vue";
import trackContext from "../../contexts/track_context";
import { Track } from "../../interfaces.js";
const contextStore = useContextStore();
const modalStore = useModalStore();
const playlistStore = usePlaylistStore();
const context_on = ref(false);
const showContextMenu = (e: Event) => {
e.preventDefault();
e.stopPropagation();
contextStore.showContextMenu(e, trackContext(props.song));
contextStore.showContextMenu(
e,
trackContext(props.song, modalStore)
);
context_on.value = true;
contextStore.$subscribe((mutation, state) => {
+3 -29
View File
@@ -1,42 +1,18 @@
import axios from "axios";
import { Folder, Track } from "../interfaces";
import state from "./state";
import { Track, Folder } from "../interfaces";
let base_uri = "http://127.0.0.1:9876/";
const getTracksAndDirs = async (path) => {
let url;
const encoded_path = encodeURIComponent(path.replaceAll("/", "|"));
url = url = `${base_uri}/f/${encoded_path}`;
const res = await fetch(url);
if (!res.ok) {
const message = `An error has occurred: ${res.status}`;
throw new Error(message);
}
const data = await res.json();
const songs = data.files;
const folders = data.folders;
return { songs, folders };
};
async function fetchThat(path: string) {
export default async function (path: string) {
let tracks = Array<Track>();
let folders = Array<Folder>();
await axios
.post(state.settings.uri + "/folder", {
.post(`${state.settings.uri}/folder`, {
folder: path,
})
.then((res) => {
tracks = res.data.tracks;
folders = res.data.folders;
console.log(tracks)
})
.catch((err) => {
console.error(err);
@@ -44,5 +20,3 @@ async function fetchThat(path: string) {
return { tracks, folders };
}
export default fetchThat;
+45
View File
@@ -0,0 +1,45 @@
import axios from "axios";
import { Playlist } from "../interfaces";
import { Notification } from "../stores/notification";
import state from "./state";
/**
* Creates a new playlist on the server.
* @param playlist_name The name of the playlist to create.
*/
async function createNewPlaylist(playlist_name: string) {
await axios
.post(state.settings.uri + "/playlist/new", {
name: playlist_name,
})
.then((res) => {
console.log(res.data);
new Notification("Playlist created!");
})
.catch((err) => {
console.error(err);
});
}
/**
* Fetches all playlists from the server.
* @returns {Promise<Playlist[]>} A promise that resolves to an array of playlists.
*/
async function getAllPlaylists(): Promise<Playlist[]> {
let playlists = <Playlist[]>[];
const newLocal = `${state.settings.uri}/playlists`;
await axios
.get(newLocal)
.then((res) => {
playlists = res.data.data;
})
.catch((err) => {
console.error(err);
});
return playlists;
}
export { createNewPlaylist, getAllPlaylists };
+46 -21
View File
@@ -1,17 +1,19 @@
import { Track } from "../interfaces";
import Router from "../router";
import { Option } from "../interfaces";
import { getAllPlaylists } from "../composables/playlists";
/**
* Returns a list of context menu items for a track.
* @param {any} track a track object.
* @param {any} modalStore a pinia store.
* @return {Array<Option>()} a list of context menu items.
*/
export default (track: Track): Array<Option> => {
export default (track: Track, modalStore: any) => {
const single_artist = track.artists.length === 1;
const children = () => {
const goToArtist = () => {
if (single_artist) {
return false;
}
@@ -24,19 +26,42 @@ export default (track: Track): Array<Option> => {
});
};
const option1: Option = {
async function addToPlaylist() {
const p = await getAllPlaylists();
const playlists = p.map((playlist) => {
return <Option>{
label: playlist.name,
action: () => {
console.log("playlist");
},
};
});
const new_playlist = <Option>{
label: "New playlist",
action: () => {
modalStore.showModal(modalStore.options.newPlaylist);
},
};
console.log([new_playlist, ...playlists]);
return [new_playlist, ...playlists];
}
const add_to_playlist: Option = {
label: "Add to Playlist",
action: () => console.log("Add to Playlist"),
children: addToPlaylist(),
icon: "plus",
};
const option2: Option = {
const add_to_q: Option = {
label: "Add to Queue",
action: () => console.log("Add to Queue"),
icon: "add_to_queue",
};
const option3: Option = {
const go_to_folder: Option = {
label: "Go to Folder",
action: () => {
Router.push({
@@ -47,7 +72,7 @@ export default (track: Track): Array<Option> => {
icon: "folder",
};
const option4: Option = {
const go_to_artist: Option = {
label: single_artist ? "Go to Artist" : "Go to Artists",
icon: "artist",
action: () => {
@@ -55,16 +80,16 @@ export default (track: Track): Array<Option> => {
console.log("Go to Artist");
}
},
children: children(),
children: goToArtist(),
};
const option7: Option = {
const go_to_alb_artist: Option = {
label: "Go to Album Artist",
action: () => console.log("Go to Album Artist"),
icon: "artist",
};
const option5: Option = {
const go_to_album: Option = {
label: "Go to Album",
action: () => {
Router.push({
@@ -75,34 +100,34 @@ export default (track: Track): Array<Option> => {
icon: "album",
};
const option6: Option = {
const del_track: Option = {
label: "Delete Track",
action: () => console.log("Delete Track"),
icon: "delete",
critical: true,
};
const addToFav:Option = {
const add_to_fav: Option = {
label: "I love this",
action: () => console.log("I love this"),
icon: "heart",
}
};
const separator: Option = {
type: "separator",
};
const options: Option[] = [
option1,
option2,
addToFav,
add_to_playlist,
add_to_q,
add_to_fav,
separator,
option3,
option4,
option7,
option5,
go_to_folder,
go_to_artist,
go_to_alb_artist,
go_to_album,
separator,
option6,
del_track,
];
return options;
+9 -2
View File
@@ -40,9 +40,16 @@ interface Option {
type?: string;
label?: string;
action?: Function;
children?: Option[] | false;
children?: Option[] |Promise<Option[]>| false;
icon?: string;
critical?: Boolean;
}
export { Track, Folder, AlbumInfo, Artist, Option };
interface Playlist {
playlistid: string;
name: string;
description?: string;
image?: string;
}
export { Track, Folder, AlbumInfo, Artist, Option, Playlist };
+27
View File
@@ -0,0 +1,27 @@
import { defineStore } from "pinia";
enum ModalOptions {
newPlaylist = "newPlaylist",
editPlaylist = "editPlaylist",
}
export default defineStore("newModal", {
state: () => ({
title: "",
options: ModalOptions,
component: "",
visible: false,
}),
actions: {
showModal(modalOption: string) {
this.component = modalOption;
this.visible = true;
},
hideModal() {
this.visible = false;
},
setTitle(new_title: string) {
this.title = new_title;
},
},
});
+26
View File
@@ -0,0 +1,26 @@
import { defineStore } from "pinia";
const useNotificationStore = defineStore("notification", {
state: () => ({
text: "",
visible: false,
}),
actions: {
showNotification(new_text: string) {
this.text = new_text;
this.visible = true;
setTimeout(() => {
this.visible = false;
}, 2000);
},
},
});
class Notification {
constructor(text: string) {
useNotificationStore().showNotification(text);
}
}
export { useNotificationStore, Notification };
+14
View File
@@ -0,0 +1,14 @@
import { defineStore } from "pinia";
import { Playlist } from "../interfaces";
import { getAllPlaylists } from "../composables/playlists";
export default defineStore("playlists", {
state: () => ({
playlists: <Playlist[]>[],
}),
actions: {
fetchAll() {
},
},
});
+3 -2
View File
@@ -43,7 +43,7 @@ export default {
const songs = computed(() => {
const songs_ = [];
if (query.value.length > 1) {
if (query.value.length) {
for (let i = 0; i < song_list.value.length; i++) {
if (
song_list.value[i].title
@@ -63,7 +63,7 @@ export default {
const folders = computed(() => {
const folders_ = [];
if (query.value.length > 1) {
if (query.value.length) {
for (let i = 0; i < folders_list.value.length; i++) {
if (
folders_list.value[i].name
@@ -110,6 +110,7 @@ export default {
if (!path.value) return;
getDirData(path.value);
console.log(path.value);
}
);
});