mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-07 03:35:35 +00:00
server: add get album bio from last fm function
- co-written by Github Copilot
This commit is contained in:
parent
a720891c20
commit
2ee8d27bf0
11
Pipfile
Normal file
11
Pipfile
Normal 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"
|
@ -5,18 +5,19 @@ from app import functions, instances, helpers, cache
|
||||
|
||||
bp = Blueprint('api', __name__, url_prefix='')
|
||||
|
||||
all_the_f_music = []
|
||||
home_dir = helpers.home_dir
|
||||
|
||||
|
||||
all_the_f_music = helpers.getAllSongs()
|
||||
|
||||
|
||||
def initialize() -> None:
|
||||
helpers.create_config_dir()
|
||||
helpers.check_for_new_songs()
|
||||
|
||||
|
||||
initialize()
|
||||
|
||||
|
||||
@bp.route('/')
|
||||
def adutsfsd():
|
||||
return "^ _ ^"
|
||||
@ -79,7 +80,7 @@ def x():
|
||||
def get_album_artists(album, artist):
|
||||
album = album.replace('|', '/')
|
||||
artist = artist.replace('|', '/')
|
||||
|
||||
|
||||
tracks = []
|
||||
|
||||
for track in all_the_f_music:
|
||||
@ -161,7 +162,7 @@ def getArtistData(artist: str):
|
||||
|
||||
@bp.route("/f/<folder>")
|
||||
@cache.cached()
|
||||
def getFolderTree(folder: str = None):
|
||||
def getFolderTree(folder: str):
|
||||
req_dir = folder.replace('|', '/')
|
||||
|
||||
if folder == "home":
|
||||
@ -180,21 +181,11 @@ def getFolderTree(folder: str = None):
|
||||
dir = {
|
||||
"name": entry.name,
|
||||
"count": len(files_in_dir),
|
||||
"path": entry.path.replace(home_dir, "")
|
||||
"path": entry.path.replace(home_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 = []
|
||||
|
||||
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'])}
|
||||
|
||||
|
||||
@bp.route('/qwerty')
|
||||
def populateArtists():
|
||||
all_songs = instances.songs_instance.get_all_songs()
|
||||
@ -222,6 +214,7 @@ def populateArtists():
|
||||
|
||||
return {'songs': artists}
|
||||
|
||||
|
||||
@bp.route('/albums')
|
||||
def getAlbums():
|
||||
s = instances.songs_instance.get_all_songs()
|
||||
@ -239,6 +232,7 @@ def getAlbums():
|
||||
|
||||
return {'albums': albums}
|
||||
|
||||
|
||||
@bp.route('/albums/<query>')
|
||||
@cache.cached()
|
||||
def getAlbumSongs(query: str):
|
||||
@ -260,3 +254,8 @@ def getAlbumSongs(query: str):
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -3,14 +3,23 @@ This module contains larger functions for the server
|
||||
"""
|
||||
|
||||
import time
|
||||
from progress.bar import Bar
|
||||
import requests
|
||||
import os
|
||||
import requests
|
||||
import random
|
||||
|
||||
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 instances
|
||||
from app import api
|
||||
|
||||
|
||||
def populate():
|
||||
'''
|
||||
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]
|
||||
|
||||
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()
|
||||
print('\ncheck done')
|
||||
|
||||
@ -82,3 +78,163 @@ def populate_images():
|
||||
bar.next()
|
||||
|
||||
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}
|
||||
|
@ -7,28 +7,25 @@ import threading
|
||||
import time
|
||||
import requests
|
||||
|
||||
from mutagen.mp3 import MP3
|
||||
from mutagen.id3 import ID3
|
||||
from mutagen.flac import FLAC
|
||||
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
from app import instances
|
||||
from app import functions
|
||||
|
||||
home_dir = os.path.expanduser('~') + "/"
|
||||
app_dir = home_dir + '/.musicx'
|
||||
|
||||
home_dir = os.path.expanduser('~') + '/'
|
||||
app_dir = os.path.join(home_dir, '.musicx')
|
||||
last_fm_api_key = "762db7a44a9e6fb5585661f5f2bdf23a"
|
||||
|
||||
def background(f):
|
||||
'''
|
||||
a threading decorator
|
||||
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()
|
||||
return backgrnd_func
|
||||
return background_func
|
||||
|
||||
|
||||
@background
|
||||
def check_for_new_songs():
|
||||
@ -62,142 +59,6 @@ def run_fast_scandir(dir: str, ext: str) -> list:
|
||||
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:
|
||||
"""
|
||||
Removes duplicates from a list. Returns a list without duplicates.
|
||||
@ -245,33 +106,33 @@ def create_config_dir() -> None:
|
||||
"""
|
||||
|
||||
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:
|
||||
if not os.path.exists(config_folder + dir):
|
||||
os.makedirs(config_folder + dir)
|
||||
path = os.path.join(config_folder, dir)
|
||||
os.chmod(path, 0o755)
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
|
||||
def getAllSongs() -> None:
|
||||
"""
|
||||
Gets all songs under the ~/ directory.
|
||||
"""
|
||||
|
||||
tracks = []
|
||||
tracks.extend(instances.songs_instance.get_all_songs())
|
||||
|
||||
tracks = instances.songs_instance.get_all_songs()
|
||||
|
||||
for track in tracks:
|
||||
try:
|
||||
os.chmod(os.path.join(home_dir, track['filepath']), 0o755)
|
||||
except FileNotFoundError:
|
||||
instances.songs_instance.remove_song_by_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']
|
||||
instances.songs_instance.remove_song_by_filepath(track['filepath'])
|
||||
|
||||
if track['artists'] is not None:
|
||||
track['artists'] = track['artists'].split(', ')
|
||||
|
||||
|
@ -115,7 +115,7 @@ class AllSongs(Mongo):
|
||||
|
||||
def remove_song_by_filepath(self, filepath):
|
||||
try:
|
||||
self.collection.remove({'filepath': filepath})
|
||||
self.collection.delete_one({'filepath': filepath})
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
@ -7,5 +7,4 @@ $green: rgb(67, 148, 67);
|
||||
$separator: #ffffff46;
|
||||
// sizes
|
||||
$small: .5em;
|
||||
$smaller: .25em;
|
||||
|
||||
$smaller: .25em;
|
@ -74,18 +74,6 @@ button {
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
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 {
|
||||
|
BIN
src/assets/images/featured-artists.webp
Normal file
BIN
src/assets/images/featured-artists.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
@ -1,10 +1,17 @@
|
||||
<template>
|
||||
<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="content">
|
||||
Two years after he prematurely left us, the Juice WRLD story continues
|
||||
with Fighting Demons. The rapper’s 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
|
||||
2020 and hit No. 1 on the Billboard 200 chart.
|
||||
<br /><br />
|
||||
@ -15,22 +22,29 @@
|
||||
Higgins) was just 21 when he died of an accidental overdose of oxycodone
|
||||
and codeine. Following his death on Dec. 9, 2019, his mother, Carmela
|
||||
Wallace, created the Live Free 999 Fund, to help youth struggling with
|
||||
mental health and substance use issues.
|
||||
<br><br>
|
||||
“Jarad was always searingly honest
|
||||
about his struggles and through his musical genius he articulated what was
|
||||
on his heart and mind vividly through his art. He never gave up and his
|
||||
friends and family never gave up on offering their support to him,” she
|
||||
continued. “We encourage all of you who struggle with addiction and mental
|
||||
health to never give up the fight.” Juice’s fast rise in the hip-hop space
|
||||
and untimely passing is the focus of Into the Abyss, a Tommy
|
||||
Oliver-directed documentary set to premiere Dec. 16 at 8PM on HBO Max.
|
||||
mental health and substance use issues.
|
||||
<br /><br />
|
||||
“Jarad was always searingly honest about his struggles and through his
|
||||
musical genius he articulated what was on his heart and mind vividly
|
||||
through his art. He never gave up and his friends and family never gave up
|
||||
on offering their support to him,” she continued. “We encourage all of you
|
||||
who struggle with addiction and mental health to never give up the fight."
|
||||
Juice's fast rise in the hip-hop space and untimely passing is the focus
|
||||
of Into the Abyss, a Tommy Oliver-directed documentary set to premiere
|
||||
Dec. 16 at 8PM on HBO Max.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {};
|
||||
export default {
|
||||
setup() {
|
||||
const tags = ["reggea", "ub40", "ali campbell", "astro"];
|
||||
return {
|
||||
tags,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@ -40,6 +54,25 @@ export default {};
|
||||
|
||||
.heading {
|
||||
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 {
|
||||
|
@ -1,30 +1,27 @@
|
||||
<template>
|
||||
<div class="folder">
|
||||
<div class="table rounded" ref="songtitle" v-if="songs.length">
|
||||
<div class="table rounded" v-if="songs.length">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Track</th>
|
||||
<th>Artist</th>
|
||||
<th>Album</th>
|
||||
<th v-if="songTitleWidth > minWidth">Duration</th>
|
||||
<th>Duration</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<SongItem
|
||||
:songTitleWidth="songTitleWidth"
|
||||
:minWidth="minWidth"
|
||||
v-for="song in songs"
|
||||
:key="song"
|
||||
:song="song"
|
||||
:current="current"
|
||||
@updateQueue="updateQueue"
|
||||
@loadAlbum="loadAlbum"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</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="icon"></div>
|
||||
<div class="text">❗ Track not found!</div>
|
||||
@ -36,7 +33,7 @@
|
||||
|
||||
<script>
|
||||
import { ref } from "@vue/reactivity";
|
||||
import { onMounted, onUnmounted } from "@vue/runtime-core";
|
||||
import { onMounted } from "@vue/runtime-core";
|
||||
|
||||
import SongItem from "../SongItem.vue";
|
||||
import album from "@/composables/album.js";
|
||||
@ -50,40 +47,14 @@ export default {
|
||||
SongItem,
|
||||
},
|
||||
setup() {
|
||||
const songtitle = ref(null);
|
||||
const songTitleWidth = ref(null);
|
||||
|
||||
const minWidth = ref(300);
|
||||
let routex;
|
||||
|
||||
const current = ref(perks.current);
|
||||
const search_query = ref(state.search_query);
|
||||
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(() => {
|
||||
routex = useRoute().name;
|
||||
|
||||
resizeSongTitleWidth();
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
resizeSongTitleWidth();
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", () => {
|
||||
resizeSongTitleWidth();
|
||||
});
|
||||
});
|
||||
|
||||
function updateQueue(song) {
|
||||
@ -91,10 +62,10 @@ export default {
|
||||
|
||||
switch (routex) {
|
||||
case "FolderView":
|
||||
type = "folder";
|
||||
type = "folder";
|
||||
break;
|
||||
case "AlbumView":
|
||||
type = "album";
|
||||
type = "album";
|
||||
break;
|
||||
}
|
||||
|
||||
@ -122,9 +93,6 @@ export default {
|
||||
return {
|
||||
updateQueue,
|
||||
loadAlbum,
|
||||
songtitle,
|
||||
songTitleWidth,
|
||||
minWidth,
|
||||
current,
|
||||
search_query,
|
||||
};
|
||||
@ -161,49 +129,48 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.folder .table table td .album-art {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: 1rem;
|
||||
background-image: url(../../assets/images/null.webp);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.folder .table .flex {
|
||||
position: relative;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.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;
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
|
||||
thead {
|
||||
height: 2rem;
|
||||
text-transform: uppercase;
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding-left: $small;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tbody tr {
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease;
|
||||
|
||||
.flex {
|
||||
position: relative;
|
||||
padding-left: 4rem;
|
||||
align-items: center;
|
||||
|
||||
.album-art {
|
||||
position: absolute;
|
||||
left: $small;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin-right: 1rem;
|
||||
background-image: url(../../assets/images/null.webp);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
height: 4rem;
|
||||
padding: $small;
|
||||
|
||||
}
|
||||
|
||||
&:hover {
|
||||
& {
|
||||
|
@ -73,15 +73,20 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.collapsed .nav-button {
|
||||
font-size: smaller;
|
||||
margin-top: 5px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.collapsed {
|
||||
.nav-button {
|
||||
font-size: smaller;
|
||||
margin-top: 5px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.in {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
.in {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="next" @click="scrollRight"></div>
|
||||
</div>
|
||||
<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="s2"></div>
|
||||
<p>Featured Artists</p>
|
||||
@ -74,7 +74,7 @@ export default {
|
||||
<style lang="scss">
|
||||
.f-artists {
|
||||
position: relative;
|
||||
height: 13em;
|
||||
height: 15em;
|
||||
width: calc(100%);
|
||||
background-color: $card-dark;
|
||||
padding: $small;
|
||||
@ -143,69 +143,69 @@ export default {
|
||||
margin-left: $smaller;
|
||||
margin-right: $smaller;
|
||||
width: 9em;
|
||||
height: 9em;
|
||||
height: 11em;
|
||||
border-radius: $small;
|
||||
background-color: #0f0e0e;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.5s ease-in-out;
|
||||
cursor: pointer;
|
||||
border: solid 1px rgba(165, 151, 151, 0.055);
|
||||
|
||||
.artist-image {
|
||||
width: 7em;
|
||||
height: 7em;
|
||||
margin-left: 0.5em;
|
||||
border-radius: 50%;
|
||||
margin-bottom: $small;
|
||||
background: url(../../assets/images/girl1.jpg);
|
||||
background-size: cover;
|
||||
background-size: 7rem 7rem;
|
||||
background-repeat: no-repeat;
|
||||
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 {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
font-size: small;
|
||||
width: 10em;
|
||||
}
|
||||
&:hover {
|
||||
transform: translateY(-0.5em);
|
||||
transition: all 0.5s ease-in-out;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 510;
|
||||
max-width: 7rem;
|
||||
}
|
||||
}
|
||||
|
||||
.f-artists .c1 {
|
||||
position: relative;
|
||||
background: rgb(16, 25, 51);
|
||||
width: 15em;
|
||||
background-size: 400px 11rem;
|
||||
background-position: 100%;
|
||||
|
||||
&:hover > .s2 {
|
||||
background: rgba(53, 53, 146, 0.8);
|
||||
transition: all 0.5s ease;
|
||||
width: 12em;
|
||||
height: 12em;
|
||||
background-image: linear-gradient(
|
||||
320deg,
|
||||
hsl(0deg 3% 6%) 13%,
|
||||
hsl(211deg 81% 23%) 50%,
|
||||
hsl(209deg 94% 30%) 87%
|
||||
);
|
||||
|
||||
transition: all 0.75s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-position: 10%;
|
||||
}
|
||||
|
||||
p {
|
||||
position: absolute;
|
||||
bottom: -2rem;
|
||||
margin-left: 0.5rem;
|
||||
font-size: 2rem;
|
||||
margin-left: 1rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.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;
|
||||
text-shadow: 0px 0px 80px rgb(0, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -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>
|
@ -276,8 +276,8 @@ export default {
|
||||
border-radius: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
margin-left: $small;
|
||||
background-color: #ad1717a8;
|
||||
background-image: url(../../assets/images/null.webp);
|
||||
// background-color: #ad1717a8;
|
||||
background-image: url("../../assets/images/null.webp");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<tr :class="{ current: current._id.$oid == song._id.$oid }">
|
||||
<td
|
||||
:style="{ width: songTitleWidth + 'px' }"
|
||||
class="flex"
|
||||
@click="emitUpdate(song)"
|
||||
>
|
||||
@ -21,7 +20,7 @@
|
||||
<span class="ellip">{{ song.title }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td :style="{ width: songTitleWidth + 'px' }">
|
||||
<td>
|
||||
<div class="ellip" v-if="song.artists[0] != ''">
|
||||
<span
|
||||
class="artist"
|
||||
@ -34,7 +33,7 @@
|
||||
<span class="artist">{{ song.album_artist }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td :style="{ width: songTitleWidth + 'px' }">
|
||||
<td>
|
||||
<div
|
||||
class="ellip"
|
||||
@click="emitLoadAlbum(song.album, song.album_artist)"
|
||||
@ -42,8 +41,6 @@
|
||||
>
|
||||
</td>
|
||||
<td
|
||||
:style="{ width: songTitleWidth + 'px' }"
|
||||
v-if="songTitleWidth > minWidth"
|
||||
>
|
||||
{{ `${Math.trunc(song.length / 60)} min` }}
|
||||
</td>
|
||||
@ -55,7 +52,7 @@ import perks from "@/composables/perks.js";
|
||||
import state from "@/composables/state.js";
|
||||
|
||||
export default {
|
||||
props: ["song", "songTitleWidth", "minWidth"],
|
||||
props: ["song"],
|
||||
setup(props, { emit }) {
|
||||
function emitUpdate(song) {
|
||||
emit("updateQueue", song);
|
||||
|
@ -10,6 +10,7 @@ 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: "",
|
||||
},
|
||||
|
@ -6,18 +6,20 @@ const queue = ref([
|
||||
{
|
||||
title: "Nothing played yet",
|
||||
artists: ["... blah blah blah"],
|
||||
image: "http://127.0.0.1:8900/images/defaults/5.webp",
|
||||
_id: {
|
||||
$oid: "",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const folder_song_list = ref([])
|
||||
const folder_list = ref([])
|
||||
const folder_song_list = ref([]);
|
||||
const folder_list = ref([]);
|
||||
|
||||
const current = ref({
|
||||
title: "Nothing played yet",
|
||||
artists: ["... blah blah blah"],
|
||||
image: "http://127.0.0.1:8900/images/defaults/1.webp",
|
||||
_id: {
|
||||
$oid: "",
|
||||
},
|
||||
@ -31,9 +33,9 @@ const prev = ref({
|
||||
},
|
||||
});
|
||||
|
||||
const album_song_list = ref([])
|
||||
const album_info = ref([])
|
||||
const album_artists = ref([])
|
||||
const album_song_list = ref([]);
|
||||
const album_info = ref([]);
|
||||
const album_artists = ref([]);
|
||||
const filters = ref([]);
|
||||
|
||||
const magic_flag = ref(false);
|
||||
@ -61,5 +63,5 @@ export default {
|
||||
search_artists,
|
||||
album_song_list,
|
||||
album_info,
|
||||
album_artists
|
||||
album_artists,
|
||||
};
|
||||
|
@ -13,4 +13,4 @@ const emitter = mitt();
|
||||
const app = createApp(App);
|
||||
app.use(router);
|
||||
app.provide('emitter', emitter);
|
||||
app.mount('#app');
|
||||
app.mount('#app');
|
||||
|
@ -57,7 +57,6 @@ export default {
|
||||
if (state.album_artists.value.length == 0) {
|
||||
album.getAlbumArtists(title, album_artists).then((data) => {
|
||||
state.album_artists.value = data;
|
||||
console.log(state.album_artists.value)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
<script>
|
||||
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";
|
||||
|
||||
export default {
|
||||
|
Loading…
x
Reference in New Issue
Block a user