added api to stream music, currently only API. also updated the dockerfile to automatically install ffmpeg (its git ignored)

This commit is contained in:
Max Młynarczyk 2025-05-30 19:18:24 +02:00
parent 8f54ac72b1
commit c4859dd4e5
5 changed files with 73 additions and 19 deletions

View file

@ -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:

View file

@ -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()
@ -515,4 +517,27 @@ def delete_message(request, message_id):
if request.method == "POST": if request.method == "POST":
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')

View file

@ -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>

View file

@ -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>

View file

@ -19,4 +19,5 @@ micawber
requests requests
django-webpush django-webpush
icalendar icalendar
sentry-sdk[django] sentry-sdk[django]
yt-dlp