server: add get album bio from last fm function

- co-written by Github Copilot
This commit is contained in:
geoffrey45 2022-01-17 12:32:27 +03:00
parent a720891c20
commit 2ee8d27bf0
20 changed files with 370 additions and 446 deletions

11
Pipfile Normal file
View File

@ -0,0 +1,11 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
[dev-packages]
[requires]
python_version = "3.8"

View File

@ -5,18 +5,19 @@ from app import functions, instances, helpers, cache
bp = Blueprint('api', __name__, url_prefix='') bp = Blueprint('api', __name__, url_prefix='')
all_the_f_music = []
home_dir = helpers.home_dir home_dir = helpers.home_dir
all_the_f_music = helpers.getAllSongs() all_the_f_music = helpers.getAllSongs()
def initialize() -> None: def initialize() -> None:
helpers.create_config_dir() helpers.create_config_dir()
helpers.check_for_new_songs() helpers.check_for_new_songs()
initialize() initialize()
@bp.route('/') @bp.route('/')
def adutsfsd(): def adutsfsd():
return "^ _ ^" return "^ _ ^"
@ -161,7 +162,7 @@ def getArtistData(artist: str):
@bp.route("/f/<folder>") @bp.route("/f/<folder>")
@cache.cached() @cache.cached()
def getFolderTree(folder: str = None): def getFolderTree(folder: str):
req_dir = folder.replace('|', '/') req_dir = folder.replace('|', '/')
if folder == "home": if folder == "home":
@ -180,21 +181,11 @@ def getFolderTree(folder: str = None):
dir = { dir = {
"name": entry.name, "name": entry.name,
"count": len(files_in_dir), "count": len(files_in_dir),
"path": entry.path.replace(home_dir, "") "path": entry.path.replace(home_dir, ""),
} }
folders.append(dir) folders.append(dir)
# if entry.is_file():
# if isValidFile(entry.name) == True:
# file = instances.songs_instance.find_song_by_path(entry.path)
# if not file:
# getTags(entry.path)
# songs_array = instances.songs_instance.find_songs_by_folder(
# req_dir)
songs = [] songs = []
for x in all_the_f_music: for x in all_the_f_music:
@ -203,6 +194,7 @@ def getFolderTree(folder: str = None):
return {"files": helpers.remove_duplicates(songs), "folders": sorted(folders, key=lambda i: i['name'])} return {"files": helpers.remove_duplicates(songs), "folders": sorted(folders, key=lambda i: i['name'])}
@bp.route('/qwerty') @bp.route('/qwerty')
def populateArtists(): def populateArtists():
all_songs = instances.songs_instance.get_all_songs() all_songs = instances.songs_instance.get_all_songs()
@ -222,6 +214,7 @@ def populateArtists():
return {'songs': artists} return {'songs': artists}
@bp.route('/albums') @bp.route('/albums')
def getAlbums(): def getAlbums():
s = instances.songs_instance.get_all_songs() s = instances.songs_instance.get_all_songs()
@ -239,6 +232,7 @@ def getAlbums():
return {'albums': albums} return {'albums': albums}
@bp.route('/albums/<query>') @bp.route('/albums/<query>')
@cache.cached() @cache.cached()
def getAlbumSongs(query: str): def getAlbumSongs(query: str):
@ -260,3 +254,8 @@ def getAlbumSongs(query: str):
} }
return {'songs': helpers.remove_duplicates(songs), 'info': album_obj} return {'songs': helpers.remove_duplicates(songs), 'info': album_obj}
@bp.route('/album/<title>/<artist>/bio')
def drop_db(title, artist):
return functions.getAlbumBio(title, artist)

View File

@ -3,14 +3,23 @@ This module contains larger functions for the server
""" """
import time import time
from progress.bar import Bar
import requests
import os import os
import requests
import random
from mutagen.flac import MutagenError from mutagen.flac import MutagenError
from mutagen.mp3 import MP3
from mutagen.id3 import ID3
from mutagen.flac import FLAC
from progress.bar import Bar
from PIL import Image
from io import BytesIO
from app import helpers from app import helpers
from app import instances from app import instances
from app import api from app import api
def populate(): def populate():
''' '''
Populate the database with all songs in the music directory Populate the database with all songs in the music directory
@ -23,21 +32,8 @@ def populate():
files = helpers.run_fast_scandir(helpers.home_dir, [".flac", ".mp3"])[1] files = helpers.run_fast_scandir(helpers.home_dir, [".flac", ".mp3"])[1]
for file in files: for file in files:
file_in_db_obj = instances.songs_instance.find_song_by_path(file) getTags(file)
try:
image = file_in_db_obj['image']
if not os.path.exists(os.path.join(helpers.app_dir, 'images', 'thumbnails', image)):
helpers.extract_thumb(file)
except:
image = None
if image is None:
try:
helpers.getTags(file)
except MutagenError:
pass
api.all_the_f_music = helpers.getAllSongs() api.all_the_f_music = helpers.getAllSongs()
print('\ncheck done') print('\ncheck done')
@ -82,3 +78,163 @@ def populate_images():
bar.next() bar.next()
bar.finish() bar.finish()
def extract_thumb(path: str) -> str:
"""
Extracts the thumbnail from an audio file. Returns the path to the thumbnail.
"""
def use_defaults() -> str:
"""
Returns a path to a random image in the defaults directory.
"""
path = "http://127.0.0.1:8900/images/defaults/" + \
str(random.randint(0, 10)) + '.webp'
return path
webp_path = path.split('/')[-1] + '.webp'
img_path = os.path.join(helpers.app_dir, "images", "thumbnails", webp_path)
if os.path.exists(img_path):
return "http://127.0.0.1:8900/images/thumbnails/" + webp_path
if path.endswith('.flac'):
try:
audio = FLAC(path)
album_art = audio.pictures[0].data
except:
album_art = None
elif path.endswith('.mp3'):
try:
audio = ID3(path)
album_art = audio.getall('APIC')[0].data
except:
album_art = None
if album_art is None:
return use_defaults()
else:
img = Image.open(BytesIO(album_art))
try:
small_img = img.resize((150, 150), Image.ANTIALIAS)
small_img.save(img_path, format="webp")
except OSError:
try:
png = img.convert('RGB')
small_img = png.resize((150, 150), Image.ANTIALIAS)
small_img.save(img_path, format="webp")
except:
return use_defaults()
final_path = "http://127.0.0.1:8900/images/thumbnails/" + webp_path
return final_path
def getTags(full_path: str) -> dict:
"""
Returns a dictionary of tags for a given file.
"""
if full_path.endswith('.flac'):
try:
audio = FLAC(full_path)
except:
return
elif full_path.endswith('.mp3'):
try:
audio = MP3(full_path)
except:
return
try:
artists = audio['artist'][0]
except KeyError:
try:
artists = audio['TPE1'][0]
except:
artists = 'Unknown'
except IndexError:
artists = 'Unknown'
try:
album_artist = audio['albumartist'][0]
except KeyError:
try:
album_artist = audio['TPE2'][0]
except:
album_artist = 'Unknown'
except IndexError:
album_artist = 'Unknown'
try:
title = audio['title'][0]
except KeyError:
try:
title = audio['TIT2'][0]
except:
title = full_path.split('/')[-1]
except:
title = full_path.split('/')[-1]
try:
album = audio['album'][0]
except KeyError:
try:
album = audio['TALB'][0]
except:
album = "Unknown"
except IndexError:
album = "Unknown"
try:
genre = audio['genre'][0]
except KeyError:
try:
genre = audio['TCON'][0]
except:
genre = "Unknown"
except IndexError:
genre = "Unknown"
img_path = extract_thumb(full_path)
tags = {
"filepath": full_path.replace(helpers.home_dir, ''),
"folder": os.path.dirname(full_path).replace(helpers.home_dir, ""),
"title": title,
"artists": artists,
"album_artist": album_artist,
"album": album,
"genre": genre,
"length": round(audio.info.length),
"bitrate": audio.info.bitrate,
"image": img_path,
"type": {
"name": None,
"id": None
}
}
instances.songs_instance.insert_song(tags)
return tags
def getAlbumBio(title: str, album_artist: str) -> dict:
last_fm_url = 'http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key={}&artist={}&album={}&format=json'.format(
helpers.last_fm_api_key, album_artist, title)
response = requests.get(last_fm_url)
data = response.json()
try:
bio = data['album']['wiki']['content']
except KeyError:
bio = None
if bio is None:
return "None"
return {'data': data}

View File

@ -7,28 +7,25 @@ import threading
import time import time
import requests import requests
from mutagen.mp3 import MP3
from mutagen.id3 import ID3
from mutagen.flac import FLAC
from io import BytesIO from io import BytesIO
from PIL import Image from PIL import Image
from app import instances from app import instances
from app import functions from app import functions
home_dir = os.path.expanduser('~') + "/" home_dir = os.path.expanduser('~') + '/'
app_dir = home_dir + '/.musicx' app_dir = os.path.join(home_dir, '.musicx')
last_fm_api_key = "762db7a44a9e6fb5585661f5f2bdf23a"
def background(f): def background(f):
''' '''
a threading decorator a threading decorator
use @background above the function you want to run in the background use @background above the function you want to run in the background
''' '''
def backgrnd_func(*a, **kw): def background_func(*a, **kw):
threading.Thread(target=f, args=a, kwargs=kw).start() threading.Thread(target=f, args=a, kwargs=kw).start()
return backgrnd_func return background_func
@background @background
def check_for_new_songs(): def check_for_new_songs():
@ -62,142 +59,6 @@ def run_fast_scandir(dir: str, ext: str) -> list:
return subfolders, files return subfolders, files
def extract_thumb(path: str) -> str:
"""
Extracts the thumbnail from an audio file. Returns the path to the thumbnail.
"""
webp_path = path.split('/')[-1] + '.webp'
img_path = app_dir + "/images/thumbnails/" + webp_path
if os.path.exists(img_path):
return webp_path
if path.endswith('.flac'):
try:
audio = FLAC(path)
album_art = audio.pictures[0].data
except:
album_art = None
elif path.endswith('.mp3'):
try:
audio = ID3(path)
album_art = audio.getall('APIC')[0].data
except:
album_art = None
if album_art is None:
return "null.webp"
else:
img = Image.open(BytesIO(album_art))
try:
small_img = img.resize((150, 150), Image.ANTIALIAS)
small_img.save(img_path, format="webp")
except OSError:
try:
png = img.convert('RGB')
small_img = png.resize((150, 150), Image.ANTIALIAS)
small_img.save(img_path, format="webp")
print('{} :: was png'.format(
img_path
))
except:
img_path = None
return webp_path
def getTags(full_path: str) -> dict:
"""
Returns a dictionary of tags for a given file.
"""
if full_path.endswith('.flac'):
try:
audio = FLAC(full_path)
except:
return
elif full_path.endswith('.mp3'):
try:
audio = MP3(full_path)
except:
return
try:
artists = audio['artist'][0]
except KeyError:
try:
artists = audio['TPE1'][0]
except:
artists = 'Unknown'
except IndexError:
artists = 'Unknown'
try:
album_artist = audio['albumartist'][0]
except KeyError:
try:
album_artist = audio['TPE2'][0]
except:
album_artist = 'Unknown'
except IndexError:
album_artist = 'Unknown'
try:
title = audio['title'][0]
except KeyError:
try:
title = audio['TIT2'][0]
except:
title = full_path.split('/')[-1]
except:
title = full_path.split('/')[-1]
try:
album = audio['album'][0]
except KeyError:
try:
album = audio['TALB'][0]
except:
album = "Unknown"
except IndexError:
album = "Unknown"
try:
genre = audio['genre'][0]
except KeyError:
try:
genre = audio['TCON'][0]
except:
genre = "Unknown"
except IndexError:
genre = "Unknown"
img_path = extract_thumb(full_path)
tags = {
"filepath": full_path.replace(home_dir, ''),
"folder": os.path.dirname(full_path).replace(home_dir, ""),
"title": title,
"artists": artists,
"album_artist": album_artist,
"album": album,
"genre": genre,
"length": round(audio.info.length),
"bitrate": audio.info.bitrate,
"image": img_path,
"type": {
"name": None,
"id": None
}
}
instances.songs_instance.insert_song(tags)
return tags
def remove_duplicates(array: list) -> list: def remove_duplicates(array: list) -> list:
""" """
Removes duplicates from a list. Returns a list without duplicates. Removes duplicates from a list. Returns a list without duplicates.
@ -245,13 +106,18 @@ def create_config_dir() -> None:
""" """
home_dir = os.path.expanduser('~') home_dir = os.path.expanduser('~')
config_folder = home_dir + app_dir config_folder = os.path.join(home_dir, app_dir)
dirs = ["", "/images", "/images/artists", "/images/thumbnails"] dirs = ["", "images", "images/defaults",
"images/artists", "images/thumbnails"]
for dir in dirs: for dir in dirs:
if not os.path.exists(config_folder + dir): path = os.path.join(config_folder, dir)
os.makedirs(config_folder + dir) os.chmod(path, 0o755)
try:
os.makedirs(path)
except FileExistsError:
pass
def getAllSongs() -> None: def getAllSongs() -> None:
@ -259,19 +125,14 @@ def getAllSongs() -> None:
Gets all songs under the ~/ directory. Gets all songs under the ~/ directory.
""" """
tracks = [] tracks = instances.songs_instance.get_all_songs()
tracks.extend(instances.songs_instance.get_all_songs())
for track in tracks: for track in tracks:
try: try:
os.chmod(os.path.join(home_dir, track['filepath']), 0o755) os.chmod(os.path.join(home_dir, track['filepath']), 0o755)
except FileNotFoundError: except FileNotFoundError:
instances.songs_instance.remove_song_by_filepath( instances.songs_instance.remove_song_by_filepath(track['filepath'])
os.path.join(home_dir, track['filepath']))
if track['image'] is not None:
track['image'] = "http://127.0.0.1:8900/images/thumbnails/" + \
track['image']
if track['artists'] is not None: if track['artists'] is not None:
track['artists'] = track['artists'].split(', ') track['artists'] = track['artists'].split(', ')

View File

@ -115,7 +115,7 @@ class AllSongs(Mongo):
def remove_song_by_filepath(self, filepath): def remove_song_by_filepath(self, filepath):
try: try:
self.collection.remove({'filepath': filepath}) self.collection.delete_one({'filepath': filepath})
return True return True
except: except:
return False return False

View File

@ -8,4 +8,3 @@ $separator: #ffffff46;
// sizes // sizes
$small: .5em; $small: .5em;
$smaller: .25em; $smaller: .25em;

View File

@ -74,18 +74,6 @@ button {
background-size: cover; background-size: cover;
background-position: center; background-position: center;
background: rgba(0, 0, 0, 0.575); background: rgba(0, 0, 0, 0.575);
backdrop-filter: blur(40px);
-webkit-backdrop-filter: blur(40px);
-moz-backdrop-filter: blur(40px);
}
#bg-blur {
position: absolute;
width: 100vw;
height: 100vh;
background-color: rgba(27, 27, 27, 0.548);
background-image: url(../images/dark-bg.jpg);
z-index: -1;
} }
.l-sidebar { .l-sidebar {

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,10 +1,17 @@
<template> <template>
<div class="al-bio rounded"> <div class="al-bio rounded">
<div class="heading">ALBUM BIOGRAPHY</div> <div class="heading">
The Very Best Of UB40: ALBUM BIOGRAPHY
<div class="tags">
<div class="item" v-for="tag in tags" :key="tag">
{{ tag }}
</div>
</div>
</div>
<div class="separator"></div> <div class="separator"></div>
<div class="content"> <div class="content">
Two years after he prematurely left us, the Juice WRLD story continues Two years after he prematurely left us, the Juice WRLD story continues
with Fighting Demons. The rappers second posthumous album dropped at with Fighting Demons. The rapper's second posthumous album dropped at
midnight, and is the followup to Legends Never Die, which arrived in July midnight, and is the followup to Legends Never Die, which arrived in July
2020 and hit No. 1 on the Billboard 200 chart. 2020 and hit No. 1 on the Billboard 200 chart.
<br /><br /> <br /><br />
@ -16,21 +23,28 @@
and codeine. Following his death on Dec. 9, 2019, his mother, Carmela and codeine. Following his death on Dec. 9, 2019, his mother, Carmela
Wallace, created the Live Free 999 Fund, to help youth struggling with Wallace, created the Live Free 999 Fund, to help youth struggling with
mental health and substance use issues. mental health and substance use issues.
<br><br> <br /><br />
Jarad was always searingly honest Jarad was always searingly honest about his struggles and through his
about his struggles and through his musical genius he articulated what was musical genius he articulated what was on his heart and mind vividly
on his heart and mind vividly through his art. He never gave up and his through his art. He never gave up and his friends and family never gave up
friends and family never gave up on offering their support to him, she on offering their support to him, she continued. We encourage all of you
continued. We encourage all of you who struggle with addiction and mental who struggle with addiction and mental health to never give up the fight."
health to never give up the fight. Juices fast rise in the hip-hop space Juice's fast rise in the hip-hop space and untimely passing is the focus
and untimely passing is the focus of Into the Abyss, a Tommy of Into the Abyss, a Tommy Oliver-directed documentary set to premiere
Oliver-directed documentary set to premiere Dec. 16 at 8PM on HBO Max. Dec. 16 at 8PM on HBO Max.
</div> </div>
</div> </div>
</template> </template>
<script> <script>
export default {}; export default {
setup() {
const tags = ["reggea", "ub40", "ali campbell", "astro"];
return {
tags,
};
},
};
</script> </script>
<style lang="scss"> <style lang="scss">
@ -40,6 +54,25 @@ export default {};
.heading { .heading {
margin: 0 0 0 $small; margin: 0 0 0 $small;
height: 2rem;
position: relative;
.tags {
position: absolute;
right: 0;
display: flex;
font-weight: normal;
.item {
padding: $small;
background-color: rgb(15, 74, 114);
margin-left: $small;
border-radius: $small;
}
.item::before {
content: "#"
}
}
} }
.content { .content {

View File

@ -1,30 +1,27 @@
<template> <template>
<div class="folder"> <div class="folder">
<div class="table rounded" ref="songtitle" v-if="songs.length"> <div class="table rounded" v-if="songs.length">
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Track</th> <th>Track</th>
<th>Artist</th> <th>Artist</th>
<th>Album</th> <th>Album</th>
<th v-if="songTitleWidth > minWidth">Duration</th> <th>Duration</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<SongItem <SongItem
:songTitleWidth="songTitleWidth"
:minWidth="minWidth"
v-for="song in songs" v-for="song in songs"
:key="song" :key="song"
:song="song" :song="song"
:current="current"
@updateQueue="updateQueue" @updateQueue="updateQueue"
@loadAlbum="loadAlbum" @loadAlbum="loadAlbum"
/> />
</tbody> </tbody>
</table> </table>
</div> </div>
<div ref="songtitle" v-else-if="songs.length === 0 && search_query"> <div v-else-if="songs.length === 0 && search_query">
<div class="no-results"> <div class="no-results">
<div class="icon"></div> <div class="icon"></div>
<div class="text"> Track not found!</div> <div class="text"> Track not found!</div>
@ -36,7 +33,7 @@
<script> <script>
import { ref } from "@vue/reactivity"; import { ref } from "@vue/reactivity";
import { onMounted, onUnmounted } from "@vue/runtime-core"; import { onMounted } from "@vue/runtime-core";
import SongItem from "../SongItem.vue"; import SongItem from "../SongItem.vue";
import album from "@/composables/album.js"; import album from "@/composables/album.js";
@ -50,40 +47,14 @@ export default {
SongItem, SongItem,
}, },
setup() { setup() {
const songtitle = ref(null);
const songTitleWidth = ref(null);
const minWidth = ref(300);
let routex; let routex;
const current = ref(perks.current); const current = ref(perks.current);
const search_query = ref(state.search_query); const search_query = ref(state.search_query);
const route = useRouter(); const route = useRouter();
const resizeSongTitleWidth = () => {
try {
let a = songtitle.value.clientWidth;
songTitleWidth.value = a > minWidth.value * 4 ? a / 4 : a / 3;
} catch (error) {
return;
}
};
onMounted(() => { onMounted(() => {
routex = useRoute().name; routex = useRoute().name;
resizeSongTitleWidth();
window.addEventListener("resize", () => {
resizeSongTitleWidth();
});
});
onUnmounted(() => {
window.removeEventListener("resize", () => {
resizeSongTitleWidth();
});
}); });
function updateQueue(song) { function updateQueue(song) {
@ -122,9 +93,6 @@ export default {
return { return {
updateQueue, updateQueue,
loadAlbum, loadAlbum,
songtitle,
songTitleWidth,
minWidth,
current, current,
search_query, search_query,
}; };
@ -161,49 +129,48 @@ export default {
} }
} }
.folder .table table td .album-art { .folder .table table {
border-collapse: collapse;
text-transform: capitalize;
width: 100%;
table-layout: fixed;
thead {
height: 2rem;
text-transform: uppercase;
th {
text-align: left;
padding-left: $small;
}
}
tbody tr {
cursor: pointer;
.flex {
position: relative;
padding-left: 4rem;
align-items: center;
.album-art {
position: absolute;
left: $small;
width: 3rem; width: 3rem;
height: 3rem; height: 3rem;
margin-right: 1rem; margin-right: 1rem;
background-image: url(../../assets/images/null.webp); background-image: url(../../assets/images/null.webp);
display: grid; display: grid;
place-items: center; place-items: center;
} }
}
.folder .table .flex { td {
position: relative; height: 4rem;
align-items: center; padding: $small;
}
.folder .table .flex > div > span { }
position: absolute;
bottom: 1.5rem;
width: calc(100% - 6rem);
}
td,
th {
padding: $small 0 $small $small;
text-align: left;
}
th {
text-transform: uppercase;
font-weight: normal;
// display: none;
}
td .artist {
margin-right: 0.2rem;
}
.folder .table table {
border-collapse: collapse;
text-transform: capitalize;
position: relative;
tbody tr {
cursor: pointer;
transition: all 0.1s ease;
&:hover { &:hover {
& { & {

View File

@ -73,16 +73,21 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
.collapsed .nav-button { .collapsed {
.nav-button {
font-size: smaller; font-size: smaller;
margin-top: 5px; margin-top: 5px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
span {
display: none;
}
.in { .in {
width: 100%; width: 100%;
flex-direction: column; flex-direction: column;
} }
}
} }
.side-nav-container { .side-nav-container {

View File

@ -5,7 +5,7 @@
<div class="next" @click="scrollRight"></div> <div class="next" @click="scrollRight"></div>
</div> </div>
<div class="artists" ref="artists_dom" v-on:mouseover="scrollArtists"> <div class="artists" ref="artists_dom" v-on:mouseover="scrollArtists">
<div class="artist c1"> <div class="artist c1 image">
<div class="blur"></div> <div class="blur"></div>
<div class="s2"></div> <div class="s2"></div>
<p>Featured Artists</p> <p>Featured Artists</p>
@ -74,7 +74,7 @@ export default {
<style lang="scss"> <style lang="scss">
.f-artists { .f-artists {
position: relative; position: relative;
height: 13em; height: 15em;
width: calc(100%); width: calc(100%);
background-color: $card-dark; background-color: $card-dark;
padding: $small; padding: $small;
@ -143,69 +143,69 @@ export default {
margin-left: $smaller; margin-left: $smaller;
margin-right: $smaller; margin-right: $smaller;
width: 9em; width: 9em;
height: 9em; height: 11em;
border-radius: $small; border-radius: $small;
background-color: #0f0e0e; background-color: #0f0e0e;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: all 0.5s ease-in-out;
cursor: pointer; cursor: pointer;
border: solid 1px rgba(165, 151, 151, 0.055);
.artist-image { .artist-image {
width: 7em; width: 7em;
height: 7em; height: 7em;
margin-left: 0.5em;
border-radius: 50%; border-radius: 50%;
margin-bottom: $small; margin-bottom: $small;
background: url(../../assets/images/girl1.jpg); background: url(../../assets/images/girl1.jpg);
background-size: cover; background-size: 7rem 7rem;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
transition: all 0.75s ease-in-out;
border: solid 1px rgba(165, 151, 151, 0.055);
box-shadow: 0px 0px 80px rgb(0, 0, 0);
}
&:hover {
.artist-image {
background-position: 50% 20%;
border-radius: 20%;
background-size: 10rem 10rem;
}
} }
.artist-name { .artist-name {
margin: 0; margin: 0;
text-align: center; text-align: center;
font-size: small; font-size: 0.9rem;
width: 10em; font-weight: 510;
} max-width: 7rem;
&:hover {
transform: translateY(-0.5em);
transition: all 0.5s ease-in-out;
} }
} }
.f-artists .c1 { .f-artists .c1 {
position: relative; position: relative;
background: rgb(16, 25, 51); background-size: 400px 11rem;
width: 15em; background-position: 100%;
&:hover > .s2 { background-image: linear-gradient(
background: rgba(53, 53, 146, 0.8); 320deg,
transition: all 0.5s ease; hsl(0deg 3% 6%) 13%,
width: 12em; hsl(211deg 81% 23%) 50%,
height: 12em; hsl(209deg 94% 30%) 87%
);
transition: all 0.75s ease-in-out;
&:hover {
background-position: 10%;
} }
p { p {
position: absolute; margin-left: 1rem;
bottom: -2rem; font-size: 1.5rem;
margin-left: 0.5rem;
font-size: 2rem;
font-weight: 700; font-weight: 700;
color: #ffffff; text-shadow: 0px 0px 80px rgb(0, 0, 0);
}
.s2 {
position: absolute;
left: -2em;
bottom: -4em;
width: 10em;
height: 10em;
background: rgba(53, 53, 146, 0.445);
border-radius: 50%;
transition: all 0.5s ease;
} }
} }
</style> </style>

View File

@ -1,94 +0,0 @@
<template>
<div class="folder" id="p-table">
<div class="table rounded" ref="songtitle">
<table>
<tr>
<th>Track</th>
<th>Artist</th>
<th>Album</th>
<th v-if="songTitleWidth > minWidth">Duration</th>
</tr>
<tr v-for="song in songs" :key="song">
<td :style="{ width: songTitleWidth + 'px' }" class="flex">
<div class="album-art rounded image"></div>
<div>
<span class="ellipsis">{{ song.title }}</span>
</div>
</td>
<td :style="{ width: songTitleWidth + 'px' }">
<span class="artist" v-for="artist in song.artists" :key="artist">{{
artist
}}</span>
</td>
<td :style="{ width: songTitleWidth + 'px' }">{{ song.album }}</td>
<td
:style="{ width: songTitleWidth + 'px' }"
v-if="songTitleWidth > minWidth"
>
{{ song.duration }}
</td>
</tr>
</table>
</div>
</div>
</template>
<script>
import { ref } from "@vue/reactivity";
import { onMounted, onUnmounted } from "@vue/runtime-core";
import Songs from "../../data/songs.js";
export default {
setup() {
const songtitle = ref(null);
const songTitleWidth = ref(null);
const minWidth = ref(300);
const songs = Songs.songs;
const resizeSongTitleWidth = () => {
let a = songtitle.value.clientWidth;
songTitleWidth.value = a > minWidth.value * 4 ? a / 4 : a / 3;
};
onMounted(() => {
resizeSongTitleWidth();
window.addEventListener("resize", () => {
resizeSongTitleWidth();
});
});
onUnmounted(() => {
window.removeEventListener("resize", () => {
resizeSongTitleWidth();
});
});
return { songtitle, songTitleWidth, songs, minWidth };
},
};
</script>
<style lang="scss">
#p-table {
height: calc(100% - 0rem) !important;
overflow: hidden;
padding-bottom: 0rem;
table {
&::-webkit-scrollbar {
display: none;
}
th {
position: sticky;
background-color: rgb(58, 57, 57);
top: 0;
z-index: 5;
}
}
}
</style>

View File

@ -276,8 +276,8 @@ export default {
border-radius: 0.5rem; border-radius: 0.5rem;
margin-right: 0.5rem; margin-right: 0.5rem;
margin-left: $small; margin-left: $small;
background-color: #ad1717a8; // background-color: #ad1717a8;
background-image: url(../../assets/images/null.webp); background-image: url("../../assets/images/null.webp");
} }
} }
} }

View File

@ -1,7 +1,6 @@
<template> <template>
<tr :class="{ current: current._id.$oid == song._id.$oid }"> <tr :class="{ current: current._id.$oid == song._id.$oid }">
<td <td
:style="{ width: songTitleWidth + 'px' }"
class="flex" class="flex"
@click="emitUpdate(song)" @click="emitUpdate(song)"
> >
@ -21,7 +20,7 @@
<span class="ellip">{{ song.title }}</span> <span class="ellip">{{ song.title }}</span>
</div> </div>
</td> </td>
<td :style="{ width: songTitleWidth + 'px' }"> <td>
<div class="ellip" v-if="song.artists[0] != ''"> <div class="ellip" v-if="song.artists[0] != ''">
<span <span
class="artist" class="artist"
@ -34,7 +33,7 @@
<span class="artist">{{ song.album_artist }}</span> <span class="artist">{{ song.album_artist }}</span>
</div> </div>
</td> </td>
<td :style="{ width: songTitleWidth + 'px' }"> <td>
<div <div
class="ellip" class="ellip"
@click="emitLoadAlbum(song.album, song.album_artist)" @click="emitLoadAlbum(song.album, song.album_artist)"
@ -42,8 +41,6 @@
> >
</td> </td>
<td <td
:style="{ width: songTitleWidth + 'px' }"
v-if="songTitleWidth > minWidth"
> >
{{ `${Math.trunc(song.length / 60)} min` }} {{ `${Math.trunc(song.length / 60)} min` }}
</td> </td>
@ -55,7 +52,7 @@ import perks from "@/composables/perks.js";
import state from "@/composables/state.js"; import state from "@/composables/state.js";
export default { export default {
props: ["song", "songTitleWidth", "minWidth"], props: ["song"],
setup(props, { emit }) { setup(props, { emit }) {
function emitUpdate(song) { function emitUpdate(song) {
emit("updateQueue", song); emit("updateQueue", song);

View File

@ -10,6 +10,7 @@ const current = ref(state.current);
const next = ref({ const next = ref({
title: "The next song", title: "The next song",
artists: ["... blah blah blah"], artists: ["... blah blah blah"],
image: "http://127.0.0.1:8900/images/defaults/4.webp",
_id: { _id: {
$oid: "", $oid: "",
}, },

View File

@ -6,18 +6,20 @@ const queue = ref([
{ {
title: "Nothing played yet", title: "Nothing played yet",
artists: ["... blah blah blah"], artists: ["... blah blah blah"],
image: "http://127.0.0.1:8900/images/defaults/5.webp",
_id: { _id: {
$oid: "", $oid: "",
}, },
}, },
]); ]);
const folder_song_list = ref([]) const folder_song_list = ref([]);
const folder_list = ref([]) const folder_list = ref([]);
const current = ref({ const current = ref({
title: "Nothing played yet", title: "Nothing played yet",
artists: ["... blah blah blah"], artists: ["... blah blah blah"],
image: "http://127.0.0.1:8900/images/defaults/1.webp",
_id: { _id: {
$oid: "", $oid: "",
}, },
@ -31,9 +33,9 @@ const prev = ref({
}, },
}); });
const album_song_list = ref([]) const album_song_list = ref([]);
const album_info = ref([]) const album_info = ref([]);
const album_artists = ref([]) const album_artists = ref([]);
const filters = ref([]); const filters = ref([]);
const magic_flag = ref(false); const magic_flag = ref(false);
@ -61,5 +63,5 @@ export default {
search_artists, search_artists,
album_song_list, album_song_list,
album_info, album_info,
album_artists album_artists,
}; };

View File

@ -57,7 +57,6 @@ export default {
if (state.album_artists.value.length == 0) { if (state.album_artists.value.length == 0) {
album.getAlbumArtists(title, album_artists).then((data) => { album.getAlbumArtists(title, album_artists).then((data) => {
state.album_artists.value = data; state.album_artists.value = data;
console.log(state.album_artists.value)
}); });
} }
}); });

View File

@ -14,7 +14,7 @@
<script> <script>
import Header from "@/components/PlaylistView/Header.vue"; import Header from "@/components/PlaylistView/Header.vue";
import SongList from "@/components/PlaylistView/SongList.vue"; import SongList from "@/components/FolderView/SongList.vue";
import FeaturedArtists from "@/components/PlaylistView/FeaturedArtists.vue"; import FeaturedArtists from "@/components/PlaylistView/FeaturedArtists.vue";
export default { export default {