mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2024-11-25 11:43:44 +03:00
updated music player, added native controls
This commit is contained in:
parent
b02a77ec5e
commit
3405b76897
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 4.2.5 on 2023-09-30 11:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("music", "0008_song_meta"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="songinque",
|
||||||
|
name="name",
|
||||||
|
field=models.CharField(blank=True, max_length=500),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="songinque",
|
||||||
|
name="status",
|
||||||
|
field=models.CharField(blank=True, max_length=500, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -4,6 +4,7 @@
|
||||||
from akarpov.common.models import BaseImageModel
|
from akarpov.common.models import BaseImageModel
|
||||||
from akarpov.tools.shortener.models import ShortLinkModel
|
from akarpov.tools.shortener.models import ShortLinkModel
|
||||||
from akarpov.users.services.history import UserHistoryModel
|
from akarpov.users.services.history import UserHistoryModel
|
||||||
|
from akarpov.utils.cache import cache_model_property
|
||||||
|
|
||||||
|
|
||||||
class Author(BaseImageModel, ShortLinkModel):
|
class Author(BaseImageModel, ShortLinkModel):
|
||||||
|
@ -47,17 +48,35 @@ def get_absolute_url(self):
|
||||||
return reverse("music:song", kwargs={"slug": self.slug})
|
return reverse("music:song", kwargs={"slug": self.slug})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_props(self) -> str:
|
def full_props(self):
|
||||||
if self.album and self.authors:
|
if self.album_name and self.artists_names:
|
||||||
return f"{self.album.name} - " + ", ".join(
|
return f"{self.album_name} - {self.artists_names}"
|
||||||
self.authors.values_list("name", flat=True)
|
elif self.album_name:
|
||||||
)
|
return self.album_name
|
||||||
elif self.album:
|
elif self.artists_names:
|
||||||
return f"{self.album.name}"
|
return self.artists_names
|
||||||
elif self.album:
|
return ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _album_name(self):
|
||||||
|
if self.album and self.album.name:
|
||||||
|
return self.album.name
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _authors_names(self):
|
||||||
|
if self.authors:
|
||||||
return ", ".join(self.authors.values_list("name", flat=True))
|
return ", ".join(self.authors.values_list("name", flat=True))
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def album_name(self):
|
||||||
|
return cache_model_property(self, "_album_name")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def artists_names(self):
|
||||||
|
return cache_model_property(self, "_authors_names")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
function addEventListener_multi(element, eventNames, handler) {
|
function addEventListener_multi(element, eventNames, handler) {
|
||||||
var events = eventNames.split(' ');
|
const events = eventNames.split(' ');
|
||||||
events.forEach(e => element.addEventListener(e, handler, false));
|
events.forEach(e => element.addEventListener(e, handler, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ function getRandom(min, max) {
|
||||||
|
|
||||||
// Position element inside element
|
// Position element inside element
|
||||||
function getRelativePos(elm) {
|
function getRelativePos(elm) {
|
||||||
var pPos = elm.parentNode.getBoundingClientRect(); // parent pos
|
const pPos = elm.parentNode.getBoundingClientRect(); // parent pos
|
||||||
var cPos = elm.getBoundingClientRect(); // target pos
|
const cPos = elm.getBoundingClientRect(); // target pos
|
||||||
var pos = {};
|
const pos = {};
|
||||||
|
|
||||||
pos.top = cPos.top - pPos.top + elm.parentNode.scrollTop,
|
pos.top = cPos.top - pPos.top + elm.parentNode.scrollTop,
|
||||||
pos.right = cPos.right - pPos.right,
|
pos.right = cPos.right - pPos.right,
|
||||||
|
@ -25,7 +25,7 @@ function getRelativePos(elm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(val) {
|
function formatTime(val) {
|
||||||
var h = 0, m = 0, s;
|
let h = 0, m = 0, s;
|
||||||
val = parseInt(val, 10);
|
val = parseInt(val, 10);
|
||||||
if (val > 60 * 60) {
|
if (val > 60 * 60) {
|
||||||
h = parseInt(val / (60 * 60), 10);
|
h = parseInt(val / (60 * 60), 10);
|
||||||
|
@ -56,7 +56,7 @@ function simp_initTime() {
|
||||||
simp_audio.removeEventListener('timeupdate', simp_initTime);
|
simp_audio.removeEventListener('timeupdate', simp_initTime);
|
||||||
|
|
||||||
if (simp_isNext) { //auto load next audio
|
if (simp_isNext) { //auto load next audio
|
||||||
var elem;
|
let elem;
|
||||||
simp_a_index++;
|
simp_a_index++;
|
||||||
if (simp_a_index == simp_a_url.length) { //repeat all audio
|
if (simp_a_index == simp_a_url.length) { //repeat all audio
|
||||||
simp_a_index = 0;
|
simp_a_index = 0;
|
||||||
|
@ -144,7 +144,7 @@ function simp_changeAudio(elem) {
|
||||||
elem = simp_isRandom && simp_isNext ? simp_a_url[getRandom(0, simp_a_url.length-1)] : elem;
|
elem = simp_isRandom && simp_isNext ? simp_a_url[getRandom(0, simp_a_url.length-1)] : elem;
|
||||||
|
|
||||||
// playlist, audio is running
|
// playlist, audio is running
|
||||||
for (var i = 0; i < simp_a_url.length; i++) {
|
for (let i = 0; i < simp_a_url.length; i++) {
|
||||||
simp_a_url[i].parentNode.classList.remove('simp-active');
|
simp_a_url[i].parentNode.classList.remove('simp-active');
|
||||||
if (simp_a_url[i] == elem) {
|
if (simp_a_url[i] == elem) {
|
||||||
simp_a_index = i;
|
simp_a_index = i;
|
||||||
|
@ -153,7 +153,7 @@ function simp_changeAudio(elem) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// scrolling to element inside element
|
// scrolling to element inside element
|
||||||
var simp_active = getRelativePos(simp_source[simp_a_index]);
|
const simp_active = getRelativePos(simp_source[simp_a_index]);
|
||||||
simp_source[simp_a_index].parentNode.scrollTop = simp_active.top;
|
simp_source[simp_a_index].parentNode.scrollTop = simp_active.top;
|
||||||
|
|
||||||
if (simp_auto_load || simp_isPlaying) simp_loadAudio(elem);
|
if (simp_auto_load || simp_isPlaying) simp_loadAudio(elem);
|
||||||
|
@ -162,6 +162,95 @@ function simp_changeAudio(elem) {
|
||||||
simp_controls.querySelector('.simp-plause').classList.remove('fa-play');
|
simp_controls.querySelector('.simp-plause').classList.remove('fa-play');
|
||||||
simp_controls.querySelector('.simp-plause').classList.add('fa-pause');
|
simp_controls.querySelector('.simp-plause').classList.add('fa-pause');
|
||||||
}
|
}
|
||||||
|
// set native audio properties
|
||||||
|
if('mediaSession' in navigator) {
|
||||||
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
|
title: elem.textContent,
|
||||||
|
artist: elem.dataset.artists,
|
||||||
|
album: elem.dataset.album,
|
||||||
|
artwork: [
|
||||||
|
{ src: elem.dataset.cover, sizes: '96x96', type: 'image/png' },
|
||||||
|
{ src: elem.dataset.cover, sizes: '128x128', type: 'image/png' },
|
||||||
|
{ src: elem.dataset.cover, sizes: '192x192', type: 'image/png' },
|
||||||
|
{ src: elem.dataset.cover, sizes: '256x256', type: 'image/png' },
|
||||||
|
{ src: elem.dataset.cover, sizes: '384x384', type: 'image/png' },
|
||||||
|
{ src: elem.dataset.cover, sizes: '512x512', type: 'image/png' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
navigator.mediaSession.setActionHandler('play', () => {
|
||||||
|
let eles = document.getElementById("simp-plause").classList
|
||||||
|
if (simp_audio.paused) {
|
||||||
|
if (!simp_isLoaded) simp_loadAudio(simp_a_url[simp_a_index]);
|
||||||
|
simp_audio.play();
|
||||||
|
simp_isPlaying = true;
|
||||||
|
eles.remove('fa-play');
|
||||||
|
eles.add('fa-pause');
|
||||||
|
} else {
|
||||||
|
simp_audio.pause();
|
||||||
|
simp_isPlaying = false;
|
||||||
|
eles.remove('fa-pause');
|
||||||
|
eles.add('fa-play');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
navigator.mediaSession.setActionHandler('pause', () => {
|
||||||
|
let eles = document.getElementById("simp-plause").classList
|
||||||
|
if (simp_audio.paused) {
|
||||||
|
if (!simp_isLoaded) simp_loadAudio(simp_a_url[simp_a_index]);
|
||||||
|
simp_audio.play();
|
||||||
|
simp_isPlaying = true;
|
||||||
|
eles.remove('fa-play');
|
||||||
|
eles.add('fa-pause');
|
||||||
|
} else {
|
||||||
|
simp_audio.pause();
|
||||||
|
simp_isPlaying = false;
|
||||||
|
eles.remove('fa-pause');
|
||||||
|
eles.add('fa-play');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
navigator.mediaSession.setActionHandler("previoustrack", () => {
|
||||||
|
let eles = document.getElementById("simp-previoustrack")
|
||||||
|
if (simp_a_index !== 0) {
|
||||||
|
simp_a_index = simp_a_index-1;
|
||||||
|
eles.disabled = simp_a_index == 0 ? true : false;
|
||||||
|
}
|
||||||
|
simp_audio.removeEventListener('timeupdate', simp_initTime);
|
||||||
|
simp_changeAudio(simp_a_url[simp_a_index]);
|
||||||
|
simp_setAlbum(simp_a_index);
|
||||||
|
});
|
||||||
|
navigator.mediaSession.setActionHandler("nexttrack", () => {
|
||||||
|
let eles = document.getElementById("simp-nexttrack")
|
||||||
|
if (simp_a_index !== simp_a_url.length-1) {
|
||||||
|
simp_a_index = simp_a_index+1;
|
||||||
|
eles.disabled = simp_a_index == simp_a_url.length-1 ? true : false;
|
||||||
|
}
|
||||||
|
simp_audio.removeEventListener('timeupdate', simp_initTime);
|
||||||
|
simp_changeAudio(simp_a_url[simp_a_index]);
|
||||||
|
simp_setAlbum(simp_a_index);
|
||||||
|
});
|
||||||
|
navigator.mediaSession.setActionHandler('seekbackward', (details) => {
|
||||||
|
simp_audio.currentTime = simp_audio.currentTime - (details.seekOffset || 10);
|
||||||
|
});
|
||||||
|
navigator.mediaSession.setActionHandler('seekforward', (details) => {
|
||||||
|
simp_audio.currentTime = simp_audio.currentTime + (details.seekOffset || 10);
|
||||||
|
});
|
||||||
|
navigator.mediaSession.setActionHandler('seekto', (details) => {
|
||||||
|
if (details.fastSeek && 'fastSeek' in simp_audio) {
|
||||||
|
simp_audio.fastSeek(details.seekTime);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
simp_audio.currentTime = details.seekTime;
|
||||||
|
});
|
||||||
|
navigator.mediaSession.setActionHandler('stop', () => {
|
||||||
|
let eles = document.getElementById("simp-plause").classList
|
||||||
|
simp_audio.currentTime = 0;
|
||||||
|
simp_controls.querySelector('.start-time').innerHTML = '00:00';
|
||||||
|
if (!simp_isLoaded) simp_loadAudio(simp_a_url[simp_a_index]);
|
||||||
|
simp_audio.play();
|
||||||
|
simp_isPlaying = true;
|
||||||
|
eles.remove('fa-play');
|
||||||
|
eles.add('fa-pause');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function simp_startScript() {
|
function simp_startScript() {
|
||||||
|
@ -210,7 +299,7 @@ function simp_startScript() {
|
||||||
|
|
||||||
// Controls listeners
|
// Controls listeners
|
||||||
simp_controls.querySelector('.simp-plauseward').addEventListener('click', function(e) {
|
simp_controls.querySelector('.simp-plauseward').addEventListener('click', function(e) {
|
||||||
var eles = e.target.classList;
|
const eles = e.target.classList;
|
||||||
if (eles.contains('simp-plause')) {
|
if (eles.contains('simp-plause')) {
|
||||||
if (simp_audio.paused) {
|
if (simp_audio.paused) {
|
||||||
if (!simp_isLoaded) simp_loadAudio(simp_a_url[simp_a_index]);
|
if (!simp_isLoaded) simp_loadAudio(simp_a_url[simp_a_index]);
|
||||||
|
@ -240,7 +329,7 @@ function simp_startScript() {
|
||||||
|
|
||||||
// Audio volume
|
// Audio volume
|
||||||
simp_volume.addEventListener('click', function(e) {
|
simp_volume.addEventListener('click', function(e) {
|
||||||
var eles = e.target.classList;
|
const eles = e.target.classList;
|
||||||
if (eles.contains('simp-mute')) {
|
if (eles.contains('simp-mute')) {
|
||||||
if (eles.contains('fa-volume-up')) {
|
if (eles.contains('fa-volume-up')) {
|
||||||
eles.remove('fa-volume-up');
|
eles.remove('fa-volume-up');
|
||||||
|
@ -263,7 +352,7 @@ function simp_startScript() {
|
||||||
|
|
||||||
// Others
|
// Others
|
||||||
simp_others.addEventListener('click', function(e) {
|
simp_others.addEventListener('click', function(e) {
|
||||||
var eles = e.target.classList;
|
const eles = e.target.classList;
|
||||||
if (eles.contains('simp-plext')) {
|
if (eles.contains('simp-plext')) {
|
||||||
simp_isNext = simp_isNext && !simp_isRandom ? false : true;
|
simp_isNext = simp_isNext && !simp_isRandom ? false : true;
|
||||||
if (!simp_isRandom) simp_isRanext = simp_isRanext ? false : true;
|
if (!simp_isRandom) simp_isRanext = simp_isRanext ? false : true;
|
||||||
|
@ -296,7 +385,7 @@ if (document.querySelector('#simp')) {
|
||||||
var simp_a_url = simp_playlist.querySelectorAll('[data-src]');
|
var simp_a_url = simp_playlist.querySelectorAll('[data-src]');
|
||||||
var simp_a_index = 0;
|
var simp_a_index = 0;
|
||||||
var simp_isPlaying = false;
|
var simp_isPlaying = false;
|
||||||
var simp_isNext = false; //auto play
|
var simp_isNext = true; //auto play
|
||||||
var simp_isRandom = false; //play random
|
var simp_isRandom = false; //play random
|
||||||
var simp_isRanext = false; //check if before random starts, simp_isNext value is true
|
var simp_isRanext = false; //check if before random starts, simp_isNext value is true
|
||||||
var simp_isStream = false; //radio streaming
|
var simp_isStream = false; //radio streaming
|
||||||
|
@ -307,18 +396,18 @@ if (document.querySelector('#simp')) {
|
||||||
auto_load: false //auto load audio file
|
auto_load: false //auto load audio file
|
||||||
};
|
};
|
||||||
|
|
||||||
var simp_elem = '';
|
let simp_elem = '';
|
||||||
simp_elem += '<audio id="audio" preload><source src="" type="audio/mpeg"></audio>';
|
simp_elem += '<audio id="audio" preload><source src="" type="audio/mpeg"></audio>';
|
||||||
simp_elem += '<div class="simp-display"><div class="simp-album w-full flex-wrap"><div class="simp-cover"><i class="fa fa-music fa-5x"></i></div><div class="simp-info"><div class="simp-title">Title</div><div class="simp-artist">Artist</div></div></div></div>';
|
simp_elem += '<div class="simp-display"><div class="simp-album w-full flex-wrap"><div class="simp-cover"><i class="fa fa-music fa-5x"></i></div><div class="simp-info"><div class="simp-title">Title</div><div class="simp-artist">Artist</div></div></div></div>';
|
||||||
simp_elem += '<div class="simp-controls flex-wrap flex-align">';
|
simp_elem += '<div class="simp-controls flex-wrap flex-align">';
|
||||||
simp_elem += '<div class="simp-plauseward flex flex-align"><button type="button" class="simp-prev fa fa-backward" disabled></button><button type="button" class="simp-plause fa fa-play" disabled></button><button type="button" class="simp-next fa fa-forward" disabled></button></div>';
|
simp_elem += '<div class="simp-plauseward flex flex-align"><button type="button" class="simp-prev fa fa-backward" id="simp-previoustrack" disabled></button><button id="simp-plause" type="button" class="simp-plause fa fa-play" disabled></button><button id="simp-nexttrack" type="button" class="simp-next fa fa-forward" disabled></button></div>';
|
||||||
simp_elem += '<div class="simp-tracker simp-load"><input class="simp-progress" type="range" min="0" max="100" value="0" disabled/><div class="simp-buffer"></div></div>';
|
simp_elem += '<div class="simp-tracker simp-load"><input class="simp-progress" type="range" min="0" max="100" value="0" disabled/><div class="simp-buffer"></div></div>';
|
||||||
simp_elem += '<div class="simp-time flex flex-align"><span class="start-time">00:00</span><span class="simp-slash"> / </span><span class="end-time">00:00</span></div>';
|
simp_elem += '<div class="simp-time flex flex-align"><span class="start-time">00:00</span><span class="simp-slash"> / </span><span class="end-time">00:00</span></div>';
|
||||||
simp_elem += '<div class="simp-volume flex flex-align"><button type="button" class="simp-mute fa fa-volume-up"></button><input class="simp-v-slider" type="range" min="0" max="100" value="100"/></div>';
|
simp_elem += '<div class="simp-volume flex flex-align"><button type="button" class="simp-mute fa fa-volume-up"></button><input class="simp-v-slider" type="range" min="0" max="100" value="100"/></div>';
|
||||||
simp_elem += '<div class="simp-others flex flex-align"><button type="button" class="simp-plext fa fa-play-circle" title="Auto Play"></button><button type="button" class="simp-random fa fa-random" title="Random"></button><div class="simp-shide"><button type="button" class="simp-shide-top fa fa-caret-up" title="Show/Hide Album"></button><button type="button" class="simp-shide-bottom fa fa-caret-down" title="Show/Hide Playlist"></button></div></div>';
|
simp_elem += '<div class="simp-others flex flex-align"><button type="button" class="simp-plext fa fa-play-circle simp-active" title="Auto Play" ></button><button type="button" class="simp-random fa fa-random" title="Random"></button><div class="simp-shide"><button type="button" class="simp-shide-top fa fa-caret-up" title="Show/Hide Album"></button><button type="button" class="simp-shide-bottom fa fa-caret-down" title="Show/Hide Playlist"></button></div></div>';
|
||||||
simp_elem += '</div>'; //simp-controls
|
simp_elem += '</div>'; //simp-controls
|
||||||
|
|
||||||
var simp_player = document.createElement('div');
|
const simp_player = document.createElement('div');
|
||||||
simp_player.classList.add('simp-player');
|
simp_player.classList.add('simp-player');
|
||||||
simp_player.innerHTML = simp_elem;
|
simp_player.innerHTML = simp_elem;
|
||||||
ap_simp.insertBefore(simp_player, simp_playlist);
|
ap_simp.insertBefore(simp_player, simp_playlist);
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="d-flex align-items-center justify-content-center">
|
<div class="d-flex align-items-center justify-content-center">
|
||||||
<div class="simple-audio-player flex-column" id="simp" data-config='{"shide_top":false,"shide_btm":false,"auto_load":false}'>
|
<div class="simple-audio-player flex-column" id="simp" data-config='{"shide_top":false,"shide_btm":false,"auto_load":true}'>
|
||||||
<div class="simp-playlist">
|
<div class="simp-playlist">
|
||||||
<ul>
|
<ul>
|
||||||
{% for song in song_list %}
|
{% for song in song_list %}
|
||||||
<li><span class="simp-source" {% if song.image %}data-cover="{{ song.image.url }}"{% endif %} data-src="{{ song.file.url }}">{{ song.name }}</span><span class="simp-desc">{{ song.full_props }}</span></li>
|
<li><span class="simp-source" {% if song.image %}data-cover="{{ song.image.url }}"{% endif %} data-artists="{{ song.artists_names }}" data-albumn="{{ song.album_name }}" data-src="{{ song.file.url }}">{{ song.name }}</span><span class="simp-desc">{{ song.full_props }}</span></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
2894
poetry.lock
generated
2894
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
|
@ -82,7 +82,6 @@ xvfbwrapper = "^0.2.9"
|
||||||
vtk = "^9.2.6"
|
vtk = "^9.2.6"
|
||||||
ffmpeg-python = "^0.2.0"
|
ffmpeg-python = "^0.2.0"
|
||||||
cairosvg = "^2.7.0"
|
cairosvg = "^2.7.0"
|
||||||
textract = "^1.6.5"
|
|
||||||
spotipy = "2.16.1"
|
spotipy = "2.16.1"
|
||||||
django-robots = "^5.0"
|
django-robots = "^5.0"
|
||||||
django-tables2 = "^2.5.3"
|
django-tables2 = "^2.5.3"
|
||||||
|
@ -109,6 +108,7 @@ pytest-asyncio = "^0.21.1"
|
||||||
pytest-lambda = "^2.2.0"
|
pytest-lambda = "^2.2.0"
|
||||||
pgvector = "^0.2.2"
|
pgvector = "^0.2.2"
|
||||||
pycld2 = "^0.41"
|
pycld2 = "^0.41"
|
||||||
|
textract = "^1.6.5"
|
||||||
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user