mirror of
https://github.com/rudy3333/eversync.git
synced 2025-07-02 00:56:02 +00:00
added api to stream music, currently only API. also updated the dockerfile to automatically install ffmpeg (its git ignored)
This commit is contained in:
parent
8f54ac72b1
commit
c4859dd4e5
5 changed files with 73 additions and 19 deletions
|
@ -49,6 +49,7 @@ urlpatterns = [
|
||||||
path('inbox/', views.inbox, name='inbox'),
|
path('inbox/', views.inbox, name='inbox'),
|
||||||
path('chat', views.chat_page, name="chat"),
|
path('chat', views.chat_page, name="chat"),
|
||||||
path('delete/<int:message_id>/', views.delete_message, name='delete_message'),
|
path('delete/<int:message_id>/', views.delete_message, name='delete_message'),
|
||||||
|
path('stream_song/', views.stream_song, name='stream_song'),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.http import HttpResponse, JsonResponse
|
from django.http import HttpResponse, JsonResponse, FileResponse
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, PasswordChangeForm
|
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, PasswordChangeForm
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
@ -19,6 +19,8 @@ from webpush import send_user_notification
|
||||||
from icalendar import Calendar, Event as IcalEvent
|
from icalendar import Calendar, Event as IcalEvent
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
import json
|
import json
|
||||||
|
import tempfile
|
||||||
|
from yt_dlp import YoutubeDL
|
||||||
|
|
||||||
providers = micawber.bootstrap_basic()
|
providers = micawber.bootstrap_basic()
|
||||||
|
|
||||||
|
@ -516,3 +518,26 @@ def delete_message(request, message_id):
|
||||||
message = get_object_or_404(Message, id=message_id, sender=request.user)
|
message = get_object_or_404(Message, id=message_id, sender=request.user)
|
||||||
message.delete()
|
message.delete()
|
||||||
return redirect('chat')
|
return redirect('chat')
|
||||||
|
|
||||||
|
def stream_song(request):
|
||||||
|
query = request.GET.get("query")
|
||||||
|
if not query:
|
||||||
|
return JsonResponse({"error": "No search query provided"}, status=400)
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
ytpdl_options = {
|
||||||
|
'format': 'bestaudio[ext=m4a]/bestaudio/best',
|
||||||
|
'noplaylist': 'true',
|
||||||
|
'outtmpl': f'{tmpdir}/%(title)s.%(ext)s',
|
||||||
|
'postprocessors': [{
|
||||||
|
'key': 'FFmpegExtractAudio',
|
||||||
|
'preferredcodec': 'mp3',
|
||||||
|
}],
|
||||||
|
'quiet': True
|
||||||
|
}
|
||||||
|
|
||||||
|
with YoutubeDL(ytpdl_options) as ytpdl:
|
||||||
|
info = ytpdl.extract_info(f"ytsearch1:{query} audio", download=True)
|
||||||
|
filepath = ytpdl.prepare_filename(info['entries'][0]).replace('.webm', '.mp3').replace('.m4a', '.mp3')
|
||||||
|
return FileResponse(open(filepath, 'rb'), content_type='audio/mpeg')
|
||||||
|
|
|
@ -164,6 +164,11 @@
|
||||||
chat
|
chat
|
||||||
</div></a>
|
</div></a>
|
||||||
|
|
||||||
|
<a href="/music" style="text-decoration: none;"><div class="service-button music">
|
||||||
|
<i class="fa-solid fa-music"></i>
|
||||||
|
music
|
||||||
|
</div></a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,12 @@
|
||||||
color: white;
|
color: white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
#pomodoro-settings {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
display: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -45,6 +51,15 @@
|
||||||
<p id="timer-display">25:00</p>
|
<p id="timer-display">25:00</p>
|
||||||
<button onclick="startPomodoro()">Start</button>
|
<button onclick="startPomodoro()">Start</button>
|
||||||
<button onclick="stopPomodoro()">Stop</button>
|
<button onclick="stopPomodoro()">Stop</button>
|
||||||
|
<button onclick="toggleSettings()">Settings</button>
|
||||||
|
|
||||||
|
<div id="pomodoro-settings">
|
||||||
|
<h4>Settings</h4>
|
||||||
|
<label for="session-minutes">Session Length (minutes):</label>
|
||||||
|
<input type="number" id="session-minutes" min="1" value="25" style="font-size: 1.2rem; width: 60px;">
|
||||||
|
<br><br>
|
||||||
|
<button onclick="applySettings()">Apply</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
|
@ -74,21 +89,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Dropdown menu code
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
const toggle = document.querySelector('.dropdown-toggle');
|
|
||||||
const menu = document.querySelector('.dropdown-menu');
|
|
||||||
toggle.addEventListener('click', function () {
|
|
||||||
menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Pomodoro timer code
|
|
||||||
let pomodoroInterval = null;
|
let pomodoroInterval = null;
|
||||||
let totalSeconds = 25 * 60;
|
let totalSeconds = 25 * 60;
|
||||||
|
let sessionLength = 25;
|
||||||
let running = false;
|
let running = false;
|
||||||
|
|
||||||
function updateDisplay() {
|
function updateDisplay() {
|
||||||
|
@ -97,6 +101,13 @@
|
||||||
document.getElementById('timer-display').textContent = `${minutes}:${seconds}`;
|
document.getElementById('timer-display').textContent = `${minutes}:${seconds}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applySettings() {
|
||||||
|
const input = document.getElementById('session-minutes');
|
||||||
|
sessionLength = parseInt(input.value) || 25;
|
||||||
|
totalSeconds = sessionLength * 60;
|
||||||
|
updateDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
function startPomodoro() {
|
function startPomodoro() {
|
||||||
if (running) return;
|
if (running) return;
|
||||||
running = true;
|
running = true;
|
||||||
|
@ -113,7 +124,7 @@
|
||||||
function stopPomodoro() {
|
function stopPomodoro() {
|
||||||
running = false;
|
running = false;
|
||||||
clearInterval(pomodoroInterval);
|
clearInterval(pomodoroInterval);
|
||||||
totalSeconds = 25 * 60;
|
totalSeconds = sessionLength * 60;
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,10 +132,21 @@
|
||||||
document.getElementById('pomodoro-box').style.display = 'block';
|
document.getElementById('pomodoro-box').style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSettings() {
|
||||||
|
const settings = document.getElementById('pomodoro-settings');
|
||||||
|
settings.style.display = settings.style.display === 'block' ? 'none' : 'block';
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
togglePomodoro(); // Automatically show the timer when the page loads
|
togglePomodoro();
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
|
|
||||||
|
const dropdownToggle = document.querySelector('.dropdown-toggle');
|
||||||
|
const dropdownMenu = document.querySelector('.dropdown-menu');
|
||||||
|
dropdownToggle.addEventListener('click', () => {
|
||||||
|
dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -20,3 +20,4 @@ requests
|
||||||
django-webpush
|
django-webpush
|
||||||
icalendar
|
icalendar
|
||||||
sentry-sdk[django]
|
sentry-sdk[django]
|
||||||
|
yt-dlp
|
Loading…
Add table
Add a link
Reference in a new issue