2020-12-26 15:03:12 +01:00
|
|
|
<template>
|
|
|
|
<Loading v-if="loading" />
|
|
|
|
|
|
|
|
<div class="playlist fade-in" v-else>
|
|
|
|
<div class="header-container">
|
2020-12-31 01:36:02 +01:00
|
|
|
<MusicHeader ref="header">
|
2024-01-07 16:19:59 +01:00
|
|
|
<div class="col-7 filter">
|
2020-12-26 15:03:12 +01:00
|
|
|
<label>
|
2020-12-31 01:36:02 +01:00
|
|
|
<input type="search" placeholder="Filter" v-model="filter">
|
2020-12-26 15:03:12 +01:00
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
|
2024-01-07 16:19:59 +01:00
|
|
|
<div class="col-5 buttons">
|
|
|
|
<button class="mobile" title="Menu" @click="$emit('toggle-nav')" v-if="showNavButton">
|
|
|
|
<i class="fas fa-bars" />
|
|
|
|
</button>
|
|
|
|
|
2020-12-26 15:03:12 +01:00
|
|
|
<Dropdown title="Actions" icon-class="fa fa-ellipsis-h">
|
2024-01-07 16:19:59 +01:00
|
|
|
<DropdownItem text="Add track" icon-class="fa fa-plus" @click="addTrack" />
|
|
|
|
<DropdownItem text="Refresh status" icon-class="fa fa-sync" @click="$emit('refresh-status')" v-if="devices != null" />
|
2020-12-26 15:03:12 +01:00
|
|
|
<DropdownItem text="Save as playlist" icon-class="fa fa-save" :disabled="!tracks?.length"
|
2020-12-31 01:36:02 +01:00
|
|
|
@click="playlistSave" />
|
|
|
|
<DropdownItem text="Swap tracks" icon-class="fa fa-retweet" v-if="selectedTracks?.length === 2"
|
|
|
|
@click="$emit('swap', selectedTracks)" />
|
2020-12-26 15:03:12 +01:00
|
|
|
<DropdownItem :text="selectionMode ? 'End selection' : 'Start selection'" icon-class="far fa-check-square"
|
|
|
|
:disabled="!tracks?.length" @click="selectionMode = !selectionMode" />
|
|
|
|
<DropdownItem :text="selectedTracks?.length === tracks?.length ? 'Unselect all' : 'Select all'"
|
|
|
|
icon-class="fa fa-check-double" :disabled="!tracks?.length"
|
2020-12-31 01:36:02 +01:00
|
|
|
@click="selectedTracks = selectedTracks.length === tracks.length ? [] : [...Array(tracks.length).keys()]" />
|
|
|
|
<DropdownItem :text="'Remove track' + (selectedTracks.length > 1 ? 's' : '')"
|
|
|
|
icon-class="fa fa-trash" v-if="selectedTracks.length > 0"
|
|
|
|
@click="$emit('remove', [...(new Set(selectedTracks))])" />
|
2020-12-26 15:03:12 +01:00
|
|
|
<DropdownItem text="Clear playlist" icon-class="fa fa-ban" :disabled="!tracks?.length" @click="$emit('clear')" />
|
|
|
|
</Dropdown>
|
2021-02-26 20:07:37 +01:00
|
|
|
|
2021-07-20 01:35:21 +02:00
|
|
|
<Dropdown title="Players" icon-class="fa fa-volume-up" v-if="Object.keys(devices || {}).length">
|
|
|
|
<DropdownItem v-for="(device, id) in devices" :key="id" v-text="device.name"
|
|
|
|
:item-class="{active: activeDevice === id, selected: selectedDevice === id}"
|
|
|
|
icon-class="fa fa-volume-up" @click="$emit('select-device', id)" />
|
|
|
|
</Dropdown>
|
2020-12-26 15:03:12 +01:00
|
|
|
</div>
|
|
|
|
</MusicHeader>
|
|
|
|
</div>
|
|
|
|
|
2024-01-05 02:19:38 +01:00
|
|
|
<div class="body" ref="body" @scroll="onScroll">
|
2020-12-26 15:03:12 +01:00
|
|
|
<div class="no-content" v-if="!tracks?.length">
|
|
|
|
No tracks are loaded
|
|
|
|
</div>
|
|
|
|
|
2023-12-25 13:47:13 +01:00
|
|
|
<div class="row track"
|
|
|
|
@dragstart="onTrackDragStart(i)"
|
|
|
|
@dragend="onTrackDragEnd(i)"
|
|
|
|
@dragover="onTrackDragOver(i)"
|
|
|
|
draggable="true"
|
|
|
|
v-for="i in displayedTrackIndices"
|
|
|
|
:set="track = tracks[i]"
|
|
|
|
:key="i"
|
2024-01-05 02:19:38 +01:00
|
|
|
:data-index="i"
|
2023-12-25 13:47:13 +01:00
|
|
|
:class="trackClass(i)"
|
|
|
|
@click="onTrackClick($event, i)"
|
|
|
|
@dblclick="$emit('play', {pos: i})">
|
2020-12-26 15:03:12 +01:00
|
|
|
<div class="col-10">
|
2020-12-31 01:36:02 +01:00
|
|
|
<div class="title">
|
|
|
|
{{ track.title || '[No Title]' }}
|
|
|
|
<div class="playing-icon" :class="{paused: status?.state === 'pause'}"
|
|
|
|
v-if="status?.playingPos === i && (status?.state === 'play' || status?.state === 'pause')">
|
|
|
|
<span v-for="i in [...Array(3).keys()]" :key="i" />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="artist" v-if="track.artist">
|
|
|
|
<a :href="$route.fullPath" v-text="track.artist"
|
2021-01-01 15:58:37 +01:00
|
|
|
@click.prevent="$emit('search', {artist: track.artist})" />
|
2020-12-31 01:36:02 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="album" v-if="track.album">
|
|
|
|
<a :href="$route.fullPath" v-text="track.album"
|
2021-01-01 15:58:37 +01:00
|
|
|
@click.prevent="$emit('search', {artist: track.artist, album: track.album})" />
|
2020-12-31 01:36:02 +01:00
|
|
|
</div>
|
2020-12-26 15:03:12 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="col-2 right-side">
|
2020-12-31 01:36:02 +01:00
|
|
|
<span class="duration" v-text="track.time ? convertTime(track.time) : '-:--'" />
|
2020-12-26 15:03:12 +01:00
|
|
|
|
|
|
|
<span class="actions">
|
|
|
|
<Dropdown title="Actions" icon-class="fa fa-ellipsis-h">
|
|
|
|
<DropdownItem text="Play" icon-class="fa fa-play" @click="$emit('play', {pos: i})" />
|
2020-12-31 01:36:02 +01:00
|
|
|
<DropdownItem text="Add to playlist" icon-class="fa fa-list-ul" @click="$emit('add-to-playlist', track)" />
|
|
|
|
<DropdownItem text="Remove" icon-class="fa fa-trash" @click="$emit('remove', [...(new Set([...selectedTracks, i]))])" />
|
2021-01-01 15:58:37 +01:00
|
|
|
<DropdownItem text="Info" icon-class="fa fa-info" @click="$emit('info', tracks[i])" />
|
2020-12-26 15:03:12 +01:00
|
|
|
</Dropdown>
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import MusicHeader from "@/components/panels/Music/Header";
|
|
|
|
import MediaUtils from "@/components/Media/Utils";
|
|
|
|
import Dropdown from "@/components/elements/Dropdown";
|
|
|
|
import DropdownItem from "@/components/elements/DropdownItem";
|
2021-02-26 20:07:37 +01:00
|
|
|
import Loading from "@/components/Loading";
|
2020-12-26 15:03:12 +01:00
|
|
|
|
|
|
|
export default {
|
|
|
|
name: "Playlist",
|
|
|
|
mixins: [MediaUtils],
|
2021-02-26 20:07:37 +01:00
|
|
|
components: {DropdownItem, Dropdown, MusicHeader, Loading},
|
2024-01-07 16:19:59 +01:00
|
|
|
emits: [
|
|
|
|
'add',
|
|
|
|
'clear',
|
|
|
|
'info',
|
|
|
|
'move',
|
|
|
|
'play',
|
|
|
|
'refresh-status',
|
|
|
|
'remove',
|
|
|
|
'save',
|
|
|
|
'search',
|
|
|
|
'select-device',
|
|
|
|
'swap',
|
|
|
|
'toggle-nav',
|
|
|
|
],
|
|
|
|
|
2020-12-26 15:03:12 +01:00
|
|
|
props: {
|
|
|
|
tracks: {
|
|
|
|
type: Array,
|
|
|
|
default: () => [],
|
|
|
|
},
|
|
|
|
|
|
|
|
loading: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
|
|
|
|
status: {
|
|
|
|
type: Object,
|
|
|
|
default: () => {},
|
2021-07-20 01:35:21 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
devices: {
|
|
|
|
type: Object,
|
|
|
|
},
|
|
|
|
|
|
|
|
selectedDevice: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
|
|
|
|
activeDevice: {
|
|
|
|
type: String,
|
|
|
|
},
|
2024-01-05 02:19:38 +01:00
|
|
|
|
|
|
|
maxVisibleTracks: {
|
|
|
|
type: Number,
|
|
|
|
default: 100,
|
|
|
|
},
|
2024-01-07 16:19:59 +01:00
|
|
|
|
|
|
|
showNavButton: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
2020-12-26 15:03:12 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
selectionMode: false,
|
|
|
|
selectedTracks: [],
|
2020-12-31 01:36:02 +01:00
|
|
|
filter: '',
|
|
|
|
infoTrack: null,
|
|
|
|
sourcePos: null,
|
|
|
|
targetPos: null,
|
2024-01-05 02:19:38 +01:00
|
|
|
centerPos: 0,
|
|
|
|
mounted: false,
|
2024-01-08 02:44:21 +01:00
|
|
|
scrollTimeout: null,
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
|
|
|
},
|
2020-12-31 01:36:02 +01:00
|
|
|
|
|
|
|
computed: {
|
|
|
|
selectedTracksSet() {
|
|
|
|
return new Set(this.selectedTracks)
|
|
|
|
},
|
|
|
|
|
2024-01-05 02:19:38 +01:00
|
|
|
trackIndicesByToken() {
|
|
|
|
const indices = {}
|
|
|
|
this.tracks.forEach((track, i) => {
|
|
|
|
const token = [track?.artist, track?.album, track?.title]
|
|
|
|
.filter((field) => field?.trim()?.length)
|
|
|
|
.map((field) => field.trim().toLowerCase())
|
|
|
|
.join(' ')
|
|
|
|
|
|
|
|
if (!indices[token])
|
|
|
|
indices[token] = new Set()
|
|
|
|
indices[token].add(i)
|
|
|
|
})
|
|
|
|
|
|
|
|
return indices
|
|
|
|
},
|
|
|
|
|
2023-12-25 13:47:13 +01:00
|
|
|
displayedTrackIndices() {
|
2024-01-05 02:19:38 +01:00
|
|
|
let positions = [...Array(this.tracks.length).keys()]
|
|
|
|
|
|
|
|
if (this.filter?.length) {
|
|
|
|
const filter = this.filter?.trim()?.replace(/\s+/g, ' ').toLowerCase()
|
|
|
|
const matchingPositions = new Set()
|
|
|
|
Object.entries(this.trackIndicesByToken).forEach(([key, positions]) => {
|
|
|
|
if (key.indexOf(filter) < 0)
|
|
|
|
return
|
|
|
|
|
|
|
|
matchingPositions.add(...positions)
|
2023-12-25 13:47:13 +01:00
|
|
|
})
|
2024-01-05 02:19:38 +01:00
|
|
|
|
|
|
|
positions = [...matchingPositions]
|
|
|
|
positions.sort()
|
|
|
|
}
|
|
|
|
|
|
|
|
if (positions.length > this.maxVisibleTracks) {
|
|
|
|
const offset = Math.max(0, this.centerPos - Math.floor(this.maxVisibleTracks / 2))
|
|
|
|
positions = positions.slice(offset, offset + this.maxVisibleTracks)
|
|
|
|
}
|
|
|
|
|
|
|
|
return positions
|
2020-12-31 01:36:02 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
onTrackClick(event, pos) {
|
|
|
|
if (event.shiftKey) {
|
|
|
|
const selectedTracks = this.selectedTracks.sort()
|
|
|
|
if (!selectedTracks.length) {
|
|
|
|
this.selectedTracks = [pos]
|
|
|
|
} else if (pos < selectedTracks[0]) {
|
|
|
|
this.selectedTracks = [
|
|
|
|
...this.selectedTracks,
|
|
|
|
...[...Array(selectedTracks[0] - pos).keys()].map((i) => i + pos)
|
|
|
|
]
|
|
|
|
} else if (pos > selectedTracks[selectedTracks.length - 1]) {
|
|
|
|
this.selectedTracks = [
|
|
|
|
...this.selectedTracks,
|
|
|
|
...[...Array(pos - selectedTracks[selectedTracks.length - 1] + 1).keys()].
|
|
|
|
map((i) => i + selectedTracks[selectedTracks.length - 1])
|
|
|
|
]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const idx = this.selectedTracks.indexOf(pos)
|
|
|
|
if (this.selectionMode || event.ctrlKey) {
|
|
|
|
if (idx >= 0)
|
|
|
|
this.selectedTracks.splice(idx, 1)
|
|
|
|
else
|
|
|
|
this.selectedTracks.push(pos)
|
|
|
|
} else {
|
|
|
|
if (idx >= 0)
|
|
|
|
this.selectedTracks = []
|
|
|
|
else
|
|
|
|
this.selectedTracks = [pos]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2023-12-25 13:47:13 +01:00
|
|
|
trackClass(i) {
|
|
|
|
return {
|
|
|
|
selected: this.selectedTracksSet.has(i),
|
|
|
|
active: this.status?.playingPos === i,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-12-31 01:36:02 +01:00
|
|
|
addTrack() {
|
|
|
|
const track = prompt('Item path or URL')
|
|
|
|
if (!track?.length)
|
|
|
|
return
|
|
|
|
|
|
|
|
this.$emit('add', track)
|
|
|
|
},
|
|
|
|
|
|
|
|
onTrackDragStart(track) {
|
|
|
|
this.sourcePos = track
|
|
|
|
},
|
|
|
|
|
|
|
|
onTrackDragEnd() {
|
|
|
|
this.$refs.body.querySelectorAll('.track').forEach((track) => track.classList.remove('dragover'));
|
|
|
|
if (this.sourcePos == null || this.targetPos == null || this.sourcePos === this.targetPos)
|
|
|
|
return
|
|
|
|
|
|
|
|
this.$emit('move', {from: this.sourcePos, to: this.targetPos})
|
|
|
|
this.sourcePos = null
|
|
|
|
this.targetPos = null
|
|
|
|
},
|
|
|
|
|
|
|
|
onTrackDragOver(track) {
|
|
|
|
this.targetPos = track
|
|
|
|
const tracks = this.$refs.body.querySelectorAll('.track')
|
|
|
|
tracks.forEach((track) => track.classList.remove('dragover'));
|
|
|
|
[...tracks][track].classList.add('dragover')
|
|
|
|
},
|
|
|
|
|
2024-01-05 02:19:38 +01:00
|
|
|
onScroll() {
|
|
|
|
const offset = this.$refs.body.scrollTop
|
|
|
|
const bodyHeight = parseFloat(getComputedStyle(this.$refs.body).height)
|
|
|
|
const scrollHeight = this.$refs.body.scrollHeight
|
|
|
|
|
2024-01-08 02:44:21 +01:00
|
|
|
if (offset < 5) {
|
|
|
|
if (this.scrollTimeout)
|
|
|
|
return
|
|
|
|
|
|
|
|
this.scrollTimeout = setTimeout(() => {
|
|
|
|
this.centerPos = Math.max(0, parseInt(this.centerPos - (this.maxVisibleTracks / 1.5)))
|
|
|
|
this.$refs.body.scrollTop = 6
|
|
|
|
this.scrollTimeout = null
|
|
|
|
}, 250)
|
|
|
|
} else if (offset >= (scrollHeight - bodyHeight - 5)) {
|
|
|
|
if (this.scrollTimeout)
|
|
|
|
return
|
|
|
|
|
|
|
|
this.scrollTimeout = setTimeout(() => {
|
|
|
|
this.centerPos = Math.min(this.tracks.length - 1, parseInt(this.centerPos + (this.maxVisibleTracks / 1.5)))
|
|
|
|
this.scrollTimeout = null
|
|
|
|
}, 250)
|
|
|
|
}
|
2024-01-05 02:19:38 +01:00
|
|
|
},
|
|
|
|
|
2020-12-31 01:36:02 +01:00
|
|
|
playlistSave() {
|
|
|
|
const name = prompt('Playlist name')
|
|
|
|
if (!name?.length)
|
|
|
|
return
|
|
|
|
|
|
|
|
this.$emit('save', name)
|
|
|
|
},
|
2024-01-05 02:19:38 +01:00
|
|
|
|
|
|
|
scrollToTrack(pos) {
|
|
|
|
this.centerPos = pos || this.status?.playingPos || 0
|
|
|
|
this.$nextTick(() => {
|
|
|
|
if (!this.$refs.body) {
|
|
|
|
this.$watch(() => this.$refs.body, () => {
|
|
|
|
if (!this.mounted)
|
|
|
|
this.scrollToTrack(pos)
|
|
|
|
})
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
[...this.$refs.body.querySelectorAll('.track')]
|
|
|
|
.filter((track) => track.classList.contains('active'))
|
|
|
|
.forEach((track) => track.scrollIntoView({block: 'center', behavior: 'smooth'}))
|
|
|
|
|
|
|
|
this.mounted = true
|
|
|
|
})
|
|
|
|
},
|
2020-12-31 01:36:02 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
mounted() {
|
2024-01-05 02:19:38 +01:00
|
|
|
this.scrollToTrack()
|
|
|
|
this.$watch(() => this.status, () => this.scrollToTrack())
|
|
|
|
this.$watch(() => this.filter, (filter) => {
|
|
|
|
if (!filter?.length)
|
|
|
|
this.scrollToTrack()
|
2020-12-31 01:36:02 +01:00
|
|
|
})
|
|
|
|
},
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
@import 'vars.scss';
|
2020-12-31 01:36:02 +01:00
|
|
|
@import 'track.scss';
|
2021-07-20 01:35:21 +02:00
|
|
|
@import '../../Media/vars.scss';
|
2020-12-26 15:03:12 +01:00
|
|
|
|
|
|
|
.playlist {
|
|
|
|
width: 100%;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
.header-container {
|
|
|
|
button {
|
|
|
|
border: 0;
|
|
|
|
background: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
.filter {
|
|
|
|
input {
|
|
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.buttons {
|
2021-02-26 20:07:37 +01:00
|
|
|
direction: rtl;
|
|
|
|
|
|
|
|
.dropdown-container {
|
|
|
|
direction: ltr;
|
|
|
|
}
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.body {
|
2021-07-20 01:35:21 +02:00
|
|
|
height: calc(100% - #{$music-header-height} - #{$media-ctrl-panel-height});
|
2020-12-26 15:03:12 +01:00
|
|
|
overflow: auto;
|
|
|
|
}
|
|
|
|
|
|
|
|
.no-content {
|
|
|
|
height: 100%;
|
|
|
|
}
|
2020-12-31 01:36:02 +01:00
|
|
|
}
|
2020-12-26 15:03:12 +01:00
|
|
|
|
2020-12-31 01:36:02 +01:00
|
|
|
.playing-icon {
|
|
|
|
display: inline-block;
|
|
|
|
position: relative;
|
|
|
|
margin-left: .75em;
|
|
|
|
width: 1.5em;
|
|
|
|
height: 1em;
|
2020-12-26 15:03:12 +01:00
|
|
|
|
2020-12-31 01:36:02 +01:00
|
|
|
@keyframes playing_bar {
|
|
|
|
0% {
|
|
|
|
height: 0
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
2020-12-31 01:36:02 +01:00
|
|
|
12.5% {
|
|
|
|
height: 75%
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
2020-12-31 01:36:02 +01:00
|
|
|
25% {
|
|
|
|
height: 100%
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
2020-12-31 01:36:02 +01:00
|
|
|
37.5% {
|
|
|
|
height: 10%
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
2020-12-31 01:36:02 +01:00
|
|
|
50% {
|
|
|
|
height: 40%
|
|
|
|
}
|
|
|
|
62.5% {
|
|
|
|
height: 50%
|
|
|
|
}
|
|
|
|
75% {
|
|
|
|
height: 30%
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
2020-12-31 01:36:02 +01:00
|
|
|
87.5% {
|
|
|
|
height: 55%
|
|
|
|
}
|
|
|
|
100% {
|
|
|
|
height: 0
|
|
|
|
}
|
|
|
|
}
|
2020-12-26 15:03:12 +01:00
|
|
|
|
2020-12-31 01:36:02 +01:00
|
|
|
span {
|
|
|
|
@include animation(0.2s);
|
|
|
|
display: block;
|
|
|
|
position: absolute;
|
|
|
|
bottom: 0;
|
|
|
|
width: .25em;
|
|
|
|
height: 100%;
|
|
|
|
background: $default-hover-fg-2;
|
|
|
|
animation-name: playing_bar;
|
|
|
|
animation-iteration-count: infinite;
|
|
|
|
|
|
|
|
&:nth-child(1){
|
|
|
|
left: 0;
|
|
|
|
animation-duration: 2s;
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
|
|
|
|
2020-12-31 01:36:02 +01:00
|
|
|
&:nth-child(2){
|
|
|
|
left: 6px;
|
|
|
|
animation-duration: 4s;
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
|
|
|
|
2020-12-31 01:36:02 +01:00
|
|
|
&:nth-child(3){
|
|
|
|
left: 12px;
|
|
|
|
animation-duration: 1s;
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
2020-12-31 01:36:02 +01:00
|
|
|
}
|
2020-12-26 15:03:12 +01:00
|
|
|
|
2020-12-31 01:36:02 +01:00
|
|
|
&.paused {
|
|
|
|
span {
|
|
|
|
animation-play-state: paused;
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-14 13:30:01 +02:00
|
|
|
:deep(.track-info-content) {
|
2020-12-31 01:36:02 +01:00
|
|
|
.attr {
|
|
|
|
opacity: 0.75;
|
|
|
|
}
|
|
|
|
|
|
|
|
.value {
|
|
|
|
text-align: right;
|
|
|
|
}
|
2020-12-26 15:03:12 +01:00
|
|
|
}
|
|
|
|
</style>
|