improved artist ui
This commit is contained in:
@@ -74,52 +74,6 @@ body {
|
|||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Playlist Header */
|
|
||||||
#playlist-header {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
align-items: center;
|
|
||||||
padding-bottom: 1.5rem;
|
|
||||||
border-bottom: 1px solid #2a2a2a;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#playlist-image {
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: 8px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
#playlist-image:hover {
|
|
||||||
transform: scale(1.02);
|
|
||||||
}
|
|
||||||
|
|
||||||
#playlist-info {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#playlist-name {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
background: linear-gradient(90deg, #1db954, #17a44b);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#playlist-owner,
|
|
||||||
#playlist-stats,
|
|
||||||
#playlist-description {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
color: #b3b3b3;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tracks Container */
|
/* Tracks Container */
|
||||||
#tracks-container {
|
#tracks-container {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
@@ -182,17 +136,6 @@ body {
|
|||||||
color: #b3b3b3;
|
color: #b3b3b3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-album {
|
|
||||||
max-width: 200px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: #b3b3b3;
|
|
||||||
margin-left: 1rem;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-duration {
|
.track-duration {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@@ -244,27 +187,37 @@ body {
|
|||||||
transform: scale(0.98);
|
transform: scale(0.98);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Circular Variant for Compact Areas (e.g., in a queue list) */
|
/* Circular Variant for Compact Areas */
|
||||||
.download-btn--circle {
|
.download-btn--circle {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
font-size: 0;
|
font-size: 0; /* Hide any text */
|
||||||
}
|
background-color: #1db954;
|
||||||
|
border: none;
|
||||||
.download-btn--circle::before {
|
|
||||||
content: "↓";
|
|
||||||
font-size: 16px;
|
|
||||||
color: #fff;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Icon next to text */
|
|
||||||
.download-btn .btn-icon {
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease, transform 0.2s ease;
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn--circle img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn--circle:hover {
|
||||||
|
background-color: #17a44b;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn--circle:active {
|
||||||
|
transform: scale(0.98);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Home Button Styling */
|
/* Home Button Styling */
|
||||||
@@ -279,7 +232,7 @@ body {
|
|||||||
.home-btn img {
|
.home-btn img {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
filter: invert(1); /* Makes the SVG icon appear white */
|
filter: invert(1);
|
||||||
transition: transform 0.2s ease;
|
transition: transform 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,11 +244,8 @@ body {
|
|||||||
transform: scale(0.98);
|
transform: scale(0.98);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Download Queue Toggle Button */
|
/* Queue Toggle Button */
|
||||||
.queue-toggle {
|
.queue-toggle {
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
right: 20px;
|
|
||||||
background: #1db954;
|
background: #1db954;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -308,125 +258,130 @@ body {
|
|||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||||
transition: background-color 0.3s ease, transform 0.2s ease;
|
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||||
z-index: 1002;
|
z-index: 1002;
|
||||||
|
/* Remove any fixed positioning by default for mobile; fixed positioning remains for larger screens */
|
||||||
}
|
}
|
||||||
|
|
||||||
.queue-toggle:hover {
|
/* Actions Container for Small Screens */
|
||||||
background: #1ed760;
|
#album-actions {
|
||||||
transform: scale(1.05);
|
display: flex;
|
||||||
}
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
.queue-toggle:active {
|
align-items: center;
|
||||||
transform: scale(1);
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Styles */
|
/* Responsive Styles */
|
||||||
|
|
||||||
/* Medium Devices (Tablets) */
|
/* Medium Devices (Tablets) */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
#album-header, #playlist-header {
|
#album-header {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#album-image, #playlist-image {
|
#album-image {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
height: 180px;
|
height: 180px;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#album-name, #playlist-name {
|
#album-name {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#album-artist,
|
#album-artist,
|
||||||
#album-stats,
|
#album-stats {
|
||||||
#playlist-owner,
|
font-size: 1rem;
|
||||||
#playlist-stats,
|
|
||||||
#playlist-description {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.track {
|
.track {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-album,
|
|
||||||
.track-duration {
|
.track-duration {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Small Devices (Mobile Phones) */
|
/* Small Devices (Mobile Phones) */
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
#app {
|
#app {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#album-name, #playlist-name {
|
#album-header {
|
||||||
font-size: 1.75rem;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Center the album cover */
|
||||||
|
#album-image {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#album-name {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
#album-artist,
|
#album-artist,
|
||||||
#album-stats,
|
#album-stats,
|
||||||
#album-copyright,
|
#album-copyright {
|
||||||
#playlist-owner,
|
font-size: 0.9rem;
|
||||||
#playlist-stats,
|
|
||||||
#playlist-description {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.track {
|
.track {
|
||||||
padding: 0.8rem;
|
padding: 0.8rem;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-number {
|
.track-number {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-info {
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-album,
|
|
||||||
.track-duration {
|
.track-duration {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure the actions container lays out buttons properly */
|
||||||
|
#album-actions {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove extra margins from the queue toggle */
|
||||||
|
.queue-toggle {
|
||||||
|
position: static;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prevent anchor links from appearing all blue */
|
/* Prevent anchor links from appearing all blue */
|
||||||
a {
|
a {
|
||||||
color: inherit; /* Inherit color from the parent */
|
color: inherit;
|
||||||
text-decoration: none; /* Remove default underline */
|
text-decoration: none;
|
||||||
transition: color 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover,
|
a:hover,
|
||||||
a:focus {
|
a:focus {
|
||||||
color: #1db954; /* Change to a themed green on hover/focus */
|
color: #1db954;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
/* Override the default pseudo-element for the circular download button */
|
|
||||||
|
/* (Optional) Override for circular download button pseudo-element */
|
||||||
.download-btn--circle::before {
|
.download-btn--circle::before {
|
||||||
content: none;
|
content: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style the icon inside the circular download button */
|
|
||||||
.download-btn--circle img {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
filter: brightness(0) invert(1); /* ensures the icon appears white */
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -98,7 +98,49 @@ body {
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Track Card (for Albums) */
|
/* Unified Album Card (Desktop & Mobile) */
|
||||||
|
.album-card {
|
||||||
|
background: #181818;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Album Cover Image */
|
||||||
|
.album-cover {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Album Info */
|
||||||
|
.album-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-artist {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #b3b3b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Track Card (for Albums or Songs) */
|
||||||
.track {
|
.track {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -221,7 +263,6 @@ body {
|
|||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Image inside circular download button */
|
|
||||||
.download-btn--circle img {
|
.download-btn--circle img {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@@ -302,7 +343,6 @@ body {
|
|||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust album card layout */
|
|
||||||
.track {
|
.track {
|
||||||
padding: 0.8rem;
|
padding: 0.8rem;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -327,6 +367,38 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mobile Album Grid Styles Inspired by Spotify */
|
||||||
|
.albums-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust album card for mobile grid */
|
||||||
|
.album-card {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-cover {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-info {
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-artist {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prevent anchor links from appearing blue */
|
/* Prevent anchor links from appearing blue */
|
||||||
|
|||||||
@@ -29,18 +29,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function renderAlbum(album) {
|
function renderAlbum(album) {
|
||||||
|
// Hide loading and error messages.
|
||||||
document.getElementById('loading').classList.add('hidden');
|
document.getElementById('loading').classList.add('hidden');
|
||||||
document.getElementById('error').classList.add('hidden');
|
document.getElementById('error').classList.add('hidden');
|
||||||
|
|
||||||
const baseUrl = window.location.origin;
|
const baseUrl = window.location.origin;
|
||||||
|
|
||||||
// Album header info with embedded links
|
// Set album header info.
|
||||||
|
|
||||||
// Album name becomes a link to the album page.
|
|
||||||
document.getElementById('album-name').innerHTML =
|
document.getElementById('album-name').innerHTML =
|
||||||
`<a href="${baseUrl}/album/${album.id}">${album.name}</a>`;
|
`<a href="${baseUrl}/album/${album.id}">${album.name}</a>`;
|
||||||
|
|
||||||
// Album artists become links to their artist pages.
|
|
||||||
document.getElementById('album-artist').innerHTML =
|
document.getElementById('album-artist').innerHTML =
|
||||||
`By ${album.artists.map(artist => `<a href="${baseUrl}/artist/${artist.id}">${artist.name}</a>`).join(', ')}`;
|
`By ${album.artists.map(artist => `<a href="${baseUrl}/artist/${artist.id}">${artist.name}</a>`).join(', ')}`;
|
||||||
|
|
||||||
@@ -54,19 +52,19 @@ function renderAlbum(album) {
|
|||||||
const image = album.images[0]?.url || 'placeholder.jpg';
|
const image = album.images[0]?.url || 'placeholder.jpg';
|
||||||
document.getElementById('album-image').src = image;
|
document.getElementById('album-image').src = image;
|
||||||
|
|
||||||
// Home Button using SVG icon
|
// Create (if needed) the Home Button.
|
||||||
let homeButton = document.getElementById('homeButton');
|
let homeButton = document.getElementById('homeButton');
|
||||||
if (!homeButton) {
|
if (!homeButton) {
|
||||||
homeButton = document.createElement('button');
|
homeButton = document.createElement('button');
|
||||||
homeButton.id = 'homeButton';
|
homeButton.id = 'homeButton';
|
||||||
homeButton.className = 'home-btn';
|
homeButton.className = 'home-btn';
|
||||||
|
|
||||||
// Create an image element for the home icon.
|
|
||||||
const homeIcon = document.createElement('img');
|
const homeIcon = document.createElement('img');
|
||||||
homeIcon.src = '/static/images/home.svg';
|
homeIcon.src = '/static/images/home.svg';
|
||||||
homeIcon.alt = 'Home';
|
homeIcon.alt = 'Home';
|
||||||
homeButton.appendChild(homeIcon);
|
homeButton.appendChild(homeIcon);
|
||||||
|
|
||||||
|
// Insert as first child of album-header.
|
||||||
const headerContainer = document.getElementById('album-header');
|
const headerContainer = document.getElementById('album-header');
|
||||||
headerContainer.insertBefore(homeButton, headerContainer.firstChild);
|
headerContainer.insertBefore(homeButton, headerContainer.firstChild);
|
||||||
}
|
}
|
||||||
@@ -74,7 +72,7 @@ function renderAlbum(album) {
|
|||||||
window.location.href = window.location.origin;
|
window.location.href = window.location.origin;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Download Album Button
|
// Create (if needed) the Download Album Button.
|
||||||
let downloadAlbumBtn = document.getElementById('downloadAlbumBtn');
|
let downloadAlbumBtn = document.getElementById('downloadAlbumBtn');
|
||||||
if (!downloadAlbumBtn) {
|
if (!downloadAlbumBtn) {
|
||||||
downloadAlbumBtn = document.createElement('button');
|
downloadAlbumBtn = document.createElement('button');
|
||||||
@@ -85,6 +83,7 @@ function renderAlbum(album) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
downloadAlbumBtn.addEventListener('click', () => {
|
downloadAlbumBtn.addEventListener('click', () => {
|
||||||
|
// Remove any other download buttons (keeping the full-album button in place).
|
||||||
document.querySelectorAll('.download-btn').forEach(btn => {
|
document.querySelectorAll('.download-btn').forEach(btn => {
|
||||||
if (btn.id !== 'downloadAlbumBtn') btn.remove();
|
if (btn.id !== 'downloadAlbumBtn') btn.remove();
|
||||||
});
|
});
|
||||||
@@ -92,15 +91,17 @@ function renderAlbum(album) {
|
|||||||
downloadAlbumBtn.disabled = true;
|
downloadAlbumBtn.disabled = true;
|
||||||
downloadAlbumBtn.textContent = 'Queueing...';
|
downloadAlbumBtn.textContent = 'Queueing...';
|
||||||
|
|
||||||
downloadWholeAlbum(album).then(() => {
|
downloadWholeAlbum(album)
|
||||||
downloadAlbumBtn.textContent = 'Queued!';
|
.then(() => {
|
||||||
}).catch(err => {
|
downloadAlbumBtn.textContent = 'Queued!';
|
||||||
showError('Failed to queue album download: ' + err.message);
|
})
|
||||||
downloadAlbumBtn.disabled = false;
|
.catch(err => {
|
||||||
});
|
showError('Failed to queue album download: ' + err.message);
|
||||||
|
downloadAlbumBtn.disabled = false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Render tracks
|
// Render each track.
|
||||||
const tracksList = document.getElementById('tracks-list');
|
const tracksList = document.getElementById('tracks-list');
|
||||||
tracksList.innerHTML = '';
|
tracksList.innerHTML = '';
|
||||||
|
|
||||||
@@ -108,36 +109,54 @@ function renderAlbum(album) {
|
|||||||
const trackElement = document.createElement('div');
|
const trackElement = document.createElement('div');
|
||||||
trackElement.className = 'track';
|
trackElement.className = 'track';
|
||||||
trackElement.innerHTML = `
|
trackElement.innerHTML = `
|
||||||
<div class="track-number">${index + 1}</div>
|
<div class="track-number">${index + 1}</div>
|
||||||
<div class="track-info">
|
<div class="track-info">
|
||||||
<div class="track-name">
|
<div class="track-name">
|
||||||
<a href="${baseUrl}/track/${track.id}">${track.name}</a>
|
<a href="${baseUrl}/track/${track.id}">${track.name}</a>
|
||||||
|
</div>
|
||||||
|
<div class="track-artist">
|
||||||
|
${track.artists.map(a => `<a href="${baseUrl}/artist/${a.id}">${a.name}</a>`).join(', ')}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="track-artist">
|
<div class="track-duration">${msToTime(track.duration_ms)}</div>
|
||||||
${track.artists.map(a => `<a href="${baseUrl}/artist/${a.id}">${a.name}</a>`).join(', ')}
|
<button class="download-btn download-btn--circle"
|
||||||
</div>
|
data-url="${track.external_urls.spotify}"
|
||||||
</div>
|
data-type="track"
|
||||||
<div class="track-duration">${msToTime(track.duration_ms)}</div>
|
data-name="${track.name}"
|
||||||
<button class="download-btn download-btn--circle"
|
title="Download">
|
||||||
data-url="${track.external_urls.spotify}"
|
<img src="/static/images/download.svg" alt="Download">
|
||||||
data-type="track"
|
</button>
|
||||||
data-name="${track.name}"
|
`;
|
||||||
title="Download">
|
|
||||||
<img src="/static/images/download.svg" alt="Download">
|
|
||||||
</button>
|
|
||||||
`;
|
|
||||||
|
|
||||||
tracksList.appendChild(trackElement);
|
tracksList.appendChild(trackElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reveal header and track list.
|
||||||
document.getElementById('album-header').classList.remove('hidden');
|
document.getElementById('album-header').classList.remove('hidden');
|
||||||
document.getElementById('tracks-container').classList.remove('hidden');
|
document.getElementById('tracks-container').classList.remove('hidden');
|
||||||
attachDownloadListeners();
|
attachDownloadListeners();
|
||||||
|
|
||||||
|
// If on a small screen, re-arrange the action buttons.
|
||||||
|
if (window.innerWidth <= 480) {
|
||||||
|
let actionsContainer = document.getElementById('album-actions');
|
||||||
|
if (!actionsContainer) {
|
||||||
|
actionsContainer = document.createElement('div');
|
||||||
|
actionsContainer.id = 'album-actions';
|
||||||
|
document.getElementById('album-header').appendChild(actionsContainer);
|
||||||
|
}
|
||||||
|
// Append in the desired order: Home, Download, then Queue Toggle (if exists).
|
||||||
|
actionsContainer.innerHTML = ''; // Clear any previous content
|
||||||
|
actionsContainer.appendChild(document.getElementById('homeButton'));
|
||||||
|
actionsContainer.appendChild(document.getElementById('downloadAlbumBtn'));
|
||||||
|
const queueToggle = document.querySelector('.queue-toggle');
|
||||||
|
if (queueToggle) {
|
||||||
|
actionsContainer.appendChild(queueToggle);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadWholeAlbum(album) {
|
async function downloadWholeAlbum(album) {
|
||||||
const url = album.external_urls.spotify;
|
const url = album.external_urls.spotify;
|
||||||
startDownload(url, 'album', { name: album.name });
|
return startDownload(url, 'album', { name: album.name });
|
||||||
}
|
}
|
||||||
|
|
||||||
function msToTime(duration) {
|
function msToTime(duration) {
|
||||||
@@ -160,19 +179,14 @@ function attachDownloadListeners() {
|
|||||||
const url = e.currentTarget.dataset.url;
|
const url = e.currentTarget.dataset.url;
|
||||||
const type = e.currentTarget.dataset.type;
|
const type = e.currentTarget.dataset.type;
|
||||||
const name = e.currentTarget.dataset.name || extractName(url);
|
const name = e.currentTarget.dataset.name || extractName(url);
|
||||||
const albumType = e.currentTarget.dataset.albumType;
|
// Remove the button immediately after click.
|
||||||
|
|
||||||
// Remove the button after click
|
|
||||||
e.currentTarget.remove();
|
e.currentTarget.remove();
|
||||||
|
startDownload(url, type, { name });
|
||||||
// Start the download for this track.
|
|
||||||
startDownload(url, type, { name }, albumType);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startDownload(url, type, item, albumType) {
|
async function startDownload(url, type, item, albumType) {
|
||||||
// Retrieve configuration (if any) from localStorage
|
|
||||||
const config = JSON.parse(localStorage.getItem('activeConfig')) || {};
|
const config = JSON.parse(localStorage.getItem('activeConfig')) || {};
|
||||||
const {
|
const {
|
||||||
fallback = false,
|
fallback = false,
|
||||||
@@ -186,17 +200,14 @@ async function startDownload(url, type, item, albumType) {
|
|||||||
const service = url.includes('open.spotify.com') ? 'spotify' : 'deezer';
|
const service = url.includes('open.spotify.com') ? 'spotify' : 'deezer';
|
||||||
let apiUrl = '';
|
let apiUrl = '';
|
||||||
|
|
||||||
// Build API URL based on the download type.
|
|
||||||
if (type === 'album') {
|
if (type === 'album') {
|
||||||
apiUrl = `/api/album/download?service=${service}&url=${encodeURIComponent(url)}`;
|
apiUrl = `/api/album/download?service=${service}&url=${encodeURIComponent(url)}`;
|
||||||
} else if (type === 'artist') {
|
} else if (type === 'artist') {
|
||||||
apiUrl = `/api/artist/download?service=${service}&artist_url=${encodeURIComponent(url)}&album_type=${encodeURIComponent(albumType || 'album,single,compilation')}`;
|
apiUrl = `/api/artist/download?service=${service}&artist_url=${encodeURIComponent(url)}&album_type=${encodeURIComponent(albumType || 'album,single,compilation')}`;
|
||||||
} else {
|
} else {
|
||||||
// Default is track download.
|
|
||||||
apiUrl = `/api/${type}/download?service=${service}&url=${encodeURIComponent(url)}`;
|
apiUrl = `/api/${type}/download?service=${service}&url=${encodeURIComponent(url)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append account and quality details.
|
|
||||||
if (fallback && service === 'spotify') {
|
if (fallback && service === 'spotify') {
|
||||||
apiUrl += `&main=${deezer}&fallback=${spotify}`;
|
apiUrl += `&main=${deezer}&fallback=${spotify}`;
|
||||||
apiUrl += `&quality=${deezerQuality}&fall_quality=${spotifyQuality}`;
|
apiUrl += `&quality=${deezerQuality}&fall_quality=${spotifyQuality}`;
|
||||||
@@ -212,9 +223,12 @@ async function startDownload(url, type, item, albumType) {
|
|||||||
try {
|
try {
|
||||||
const response = await fetch(apiUrl);
|
const response = await fetch(apiUrl);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
// Add the download to the queue using the working queue implementation.
|
|
||||||
downloadQueue.addDownload(item, type, data.prg_file);
|
downloadQueue.addDownload(item, type, data.prg_file);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('Download failed: ' + error.message);
|
showError('Download failed: ' + error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractName(url) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|||||||
@@ -85,12 +85,14 @@ function renderArtist(artistData, artistId) {
|
|||||||
downloadArtistBtn.disabled = true;
|
downloadArtistBtn.disabled = true;
|
||||||
downloadArtistBtn.textContent = 'Queueing...';
|
downloadArtistBtn.textContent = 'Queueing...';
|
||||||
|
|
||||||
downloadWholeArtist(artistData).then(() => {
|
downloadWholeArtist(artistData)
|
||||||
downloadArtistBtn.textContent = 'Queued!';
|
.then(() => {
|
||||||
}).catch(err => {
|
downloadArtistBtn.textContent = 'Queued!';
|
||||||
showError('Failed to queue artist download: ' + err.message);
|
})
|
||||||
downloadArtistBtn.disabled = false;
|
.catch(err => {
|
||||||
});
|
showError('Failed to queue artist download: ' + err.message);
|
||||||
|
downloadArtistBtn.disabled = false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Group albums by album type.
|
// Group albums by album type.
|
||||||
@@ -128,24 +130,20 @@ function renderArtist(artistData, artistId) {
|
|||||||
// Container for individual albums in this group.
|
// Container for individual albums in this group.
|
||||||
const albumsContainer = document.createElement('div');
|
const albumsContainer = document.createElement('div');
|
||||||
albumsContainer.className = 'albums-list';
|
albumsContainer.className = 'albums-list';
|
||||||
albums.forEach((album, index) => {
|
albums.forEach(album => {
|
||||||
const albumElement = document.createElement('div');
|
const albumElement = document.createElement('div');
|
||||||
albumElement.className = 'track'; // reusing styling from the playlist view
|
// Build a unified album card markup that works for both desktop and mobile.
|
||||||
|
albumElement.className = 'album-card';
|
||||||
// Use an <a> around the album image and name.
|
|
||||||
albumElement.innerHTML = `
|
albumElement.innerHTML = `
|
||||||
<div class="track-number">${index + 1}</div>
|
|
||||||
<a href="/album/${album.id}" class="album-link">
|
<a href="/album/${album.id}" class="album-link">
|
||||||
<img class="track-image" src="${album.images[1]?.url || album.images[0]?.url || 'placeholder.jpg'}"
|
<img src="${album.images[1]?.url || album.images[0]?.url || 'placeholder.jpg'}"
|
||||||
alt="Album cover"
|
alt="Album cover"
|
||||||
style="width: 64px; height: 64px; border-radius: 4px; margin-right: 1rem;">
|
class="album-cover">
|
||||||
</a>
|
</a>
|
||||||
<div class="track-info">
|
<div class="album-info">
|
||||||
<a href="/album/${album.id}" class="track-name">${album.name}</a>
|
<div class="album-title">${album.name}</div>
|
||||||
<div class="track-artist"></div>
|
<div class="album-artist">${album.artists.map(a => a.name).join(', ')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="track-album">${album.release_date}</div>
|
|
||||||
<div class="track-duration">${album.total_tracks} tracks</div>
|
|
||||||
<button class="download-btn download-btn--circle"
|
<button class="download-btn download-btn--circle"
|
||||||
data-url="${album.external_urls.spotify}"
|
data-url="${album.external_urls.spotify}"
|
||||||
data-type="album"
|
data-type="album"
|
||||||
|
|||||||
Reference in New Issue
Block a user