In this article, we’ll build a Music Player using Django. To get started, the first thing we’ll do is create an account, just like with other music players. After creating the account, we’ll log in. Once successfully logged in, we’ll be redirected to the home page. On the home page, we can search for songs based on our preferences we used an API to fetch songs. Additionally, we’ll have the option to add songs, and the corresponding pictures related to those songs will be displayed on the home page. We can easily play the songs and manage the login system through the admin panel.
Music Player using Django
Below is the step-by-step guide to playing music player
To install Django follow these steps.
Starting the Project Folder
To start the project use this command
django-admin startproject music_player_project
cd core
we create two apps for creating the app use this command
python manage.py startapp api
python manage.py startapp music_player_app
Now add this app to the ‘settings.py’
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"music_player_app",
"api",
"rest_framework"
]
File Structure
Setting Necessary Files
api/models.py :Below,code defines a Django model named “Song” with fields for id, title, category, artist, audio file, and audio image, along with their respective properties, and a string representation method returning the song title.
Python3
from django.db import models
class Song(models.Model):
id = models.AutoField(primary_key = True )
title = models.CharField(max_length = 100 )
categorie = models.CharField(max_length = 100 , null = True , default = None )
artist = models.CharField(max_length = 100 )
audio_file = models.FileField(upload_to = 'audio/' )
audio_img = models.FileField(upload_to = 'audio_img/' )
def __str__( self ):
return self .title
|
music_player_app/models.py : below code defines a custom Django user model named “CustomUser” that extends the AbstractUser model, adding fields for groups and user permissions, allowing users to be associated with multiple groups and permissions.
Python3
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
groups = models.ManyToManyField(
'auth.Group' ,
related_name = 'customuser_set' ,
related_query_name = 'user'
)
user_permissions = models.ManyToManyField(
'auth.Permission' ,
related_name = 'customuser_set' ,
related_query_name = 'user'
)
|
api/views.py : below code defines two Django Rest Framework views, “SongListCreateView” for handling the listing and creation of songs, and “SongDetailView” for handling the retrieval, update, and deletion of individual songs, using the “Song” model and its corresponding serializer “SongSerializer.”
Python3
from rest_framework import generics
from .models import Song
from .serializers import SongSerializer
class SongListCreateView(generics.ListCreateAPIView):
queryset = Song.objects. all ()
serializer_class = SongSerializer
class SongDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Song.objects. all ()
serializer_class = SongSerializer
|
music_player_app/views.py : below code defines Django views for user registration, login, and authenticated user actions such as viewing the home page, adding a song, and updating a song. It uses forms for registration and login, and requires authentication for certain actions.
Python3
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.contrib.auth import login
from .forms import RegistrationForm, LoginForm
def register(request):
if request.method = = 'POST' :
form = RegistrationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect( 'home' )
else :
form = RegistrationForm()
return render(request, 'music_player_app/register.html' , { 'form' : form})
def user_login(request):
if request.method = = 'POST' :
form = LoginForm(request, request.POST)
if form.is_valid():
user = form.get_user()
login(request, user)
return redirect( 'home' )
else :
form = LoginForm()
return render(request, 'music_player_app/login.html' , { 'form' : form})
@login_required
def index(request):
return render(request, 'music_player_app/index.html' )
@login_required
def AddSong(request):
return render(request, 'music_player_app/AddSong.html' )
@login_required
def UpdateSong(request, pk):
return render(request, 'music_player_app/UpdateSong.html' )
|
api/serializers.py : below code defines a Django Rest Framework serializer, “SongSerializer,” using the model “Song” and including all fields for serialization.
Python3
from rest_framework import serializers
from .models import Song
class SongSerializer(serializers.ModelSerializer):
class Meta:
model = Song
fields = '__all__'
|
music_palyer_app/forms.py : below code defines a registration form (“RegistrationForm”) and a login form (“LoginForm”) for a custom user model, extending default Django forms for user creation and authentication.
Python3
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from .models import CustomUser
class RegistrationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = [ 'username' , 'password1' , 'password2' ]
class LoginForm(AuthenticationForm):
class Meta:
model = CustomUser
fields = [ 'username' , 'password' ]
|
api/urls.py : below code sets up Django URL patterns for song listing/creation and song detail using “SongListCreateView” and “SongDetailView” views, respectively.
Python3
from django.urls import path
from .views import SongListCreateView, SongDetailView
urlpatterns = [
path( ' ' , SongListCreateView.as_view(), name = 'song-list-create' ),
path( 'songs/<int:pk>/' , SongDetailView.as_view(), name = 'song-detail' ),
]
|
music_player_app/urls.py : below code defines Django URL patterns for user authentication, registration, login, logout, and views for the home page, updating a song, and adding a song in the music player app. It includes paths for default authentication views, custom registration and login views, and specific URLs for app features.
Python3
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth.views import LogoutView
from music_player_app.views import register, user_login
from music_player_app.views import index, UpdateSong, AddSong
urlpatterns = [
path( 'accounts/' , include( 'django.contrib.auth.urls' )),
path( 'register/' , register, name = 'register' ),
path( 'login/' , user_login, name = 'login' ),
path( 'logout/' , LogoutView.as_view(next_page = '/login/' ), name = 'logout' ),
path( 'home' , index, name = 'home' ),
path( 'UpdateSong/<int:pk>' , UpdateSong, name = 'UpdateSong' ),
path( 'AddSong' , AddSong, name = 'AddSong' ),
]
|
Creating GUI
templates/music_player_app/html Files
AddSong.html : The HTML document presents a form for adding a song to a music player app, styled with Bootstrap. JavaScript functions validate the form, collect input data, and send a POST request to a Django API endpoint for song addition, with CSRF token handling.
HTML
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
< title >Add Song</ title >
integrity = "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin = "anonymous" />
</ head >
< body >
< div class = "container mt-5" >
< h2 >Add Song</ h2 >
< form id = "songForm" >
< div class = "form-group" >
< label for = "title" >Title:</ label >
< input type = "text" class = "form-control" id = "title" name = "title" required />
< div class = "invalid-feedback" >Title is required.</ div >
</ div >
< div class = "form-group" >
< label for = "artist" >Artist:</ label >
< input type = "text" class = "form-control" id = "artist" name = "artist" required />
< div class = "invalid-feedback" >Artist is required.</ div >
</ div >
< div class = "form-group" >
< label for = "categorie" >Categorie:</ label >
< input type = "text" class = "form-control" id = "categorie" name = "categorie" required />
< div class = "invalid-feedback" >Categorie is required.</ div >
</ div >
< div class = "form-group" >
< label for = "audio_file" >Audio File:</ label >
< input type = "file" class = "form-control-file" id = "audio_file" name = "audio_file"
accept = "audio/*" required />
< div class = "invalid-feedback" >Audio file is required.</ div >
</ div >
< div class = "form-group" >
< label for = "audio_img" >Audio Img:</ label >
< input type = "file" class = "form-control-file" id = "audio_img" name = "audio_img" required />
< div class = "invalid-feedback" >Audio Img is required.</ div >
</ div >
< button type = "button" class = "btn btn-primary" onclick = "postData()" >Submit</ button >
</ form >
</ div >
integrity = "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin = "anonymous" ></ script >
integrity = "sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin = "anonymous" ></ script >
integrity = "sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin = "anonymous" ></ script >
< script >
function postData() {
// Validate the form
if (validateForm()) {
let title = document.getElementById('title').value
let artist = document.getElementById('artist').value
let categorie = document.getElementById('categorie').value
let audio_file = document.getElementById('audio_file').files[0]
let audio_img = document.getElementById('audio_img').files[0]
const formData = new FormData()
formData.append('title', title)
formData.append('artist', artist)
formData.append('categorie', categorie)
formData.append('audio_file', audio_file)
formData.append('audio_img', audio_img)
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken')
},
body: formData
})
.then((response) => response.json())
.then((result) => {
console.log('Success:', result)
// Refresh the page after successful submission
location.reload()
})
.catch((error) => {
// Handle errors as needed
console.error('Error:', error)
})
}
}
function validateForm() {
const title = document.getElementById('title').value
const artist = document.getElementById('artist').value
const categorie = document.getElementById('categorie').value
const audio_file = document.getElementById('audio_file').files[0]
const audio_img = document.getElementById('audio_img').files[0]
const invalidFeedbacks = document.querySelectorAll('.invalid-feedback')
invalidFeedbacks.forEach((element) => {
element.style.display = 'none'
})
let isValid = true
if (!title.trim()) {
document.getElementById('title').nextElementSibling.style.display = 'block'
isValid = false
}
if (!categorie.trim()) {
document.getElementById('categorie').nextElementSibling.style.display = 'block'
isValid = false
}
if (!artist.trim()) {
document.getElementById('artist').nextElementSibling.style.display = 'block'
isValid = false
}
if (!audio_file) {
document.getElementById('audio_file').nextElementSibling.style.display = 'block'
isValid = false
}
if (!audio_img) {
document.getElementById('audio_img').nextElementSibling.style.display = 'block'
isValid = false
}
return isValid
}
// Function to get CSRF token from cookies
function getCookie(name) {
var cookieValue = null
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';')
for (var i = 0; i < cookies.length ; i++) {
var cookie = cookies [i].trim()
if (cookie.substring(0, name.length + 1) === name + '=') {
cookieValue = decodeURIComponent (cookie.substring(name.length + 1))
break
}
}
}
return cookieValue
}
</script>
</ body >
</ html >
|
index.html : The HTML document implements a music player interface using Bootstrap, featuring an audio player, playlist, search bar, and buttons for song management. JavaScript functions handle song retrieval, playback, deletion, and redirection to add/update songs. A search function interacts with the Saavn API to display results, and there’s a logout function.
HTML
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
< title >Music Player</ title >
< style >
body {
font-family: 'Arial', sans-serif;
text-align: center;
margin: 50px;
}
#audioPlayer {
width: 100%;
max-width: 400px;
margin: 20px auto;
}
#playlist {
list-style: none;
padding: 0;
}
#playlist li {
margin: 5px;
cursor: pointer;
transition: transform 0.3s ease-in-out;
display: flex;
justify-content: space-between;
align-items: center;
}
#playlist li:hover {
transform: scale(1.1);
}
</ style >
</ head >
< body >
< div class = "container" >
< div class = "player" >
< img style = "width:250px; height:250px;"
alt id = "audioPlayerimg" />
< br />
< audio id = "audioPlayer" class = "w-100" controls>Your browser does not support the audio element.</ audio >
</ div >
< label for = "Search" >Search Song:</ label >
< input type = "text" id = "Search" placeholder = "Enter song name" />
< button onclick = "SearchSongs()" >Search</ button >
< ul id = "playlist" class = "list-group" ></ ul >
< button type = "button" class = "btn btn-primary mt-3" data-toggle = "modal" data-target = "#addSongModal"
onclick = "AddSong()" >Add Song</ button >
< button type = "button" class = "btnlogout btn btn-danger mt-3" data-toggle = "modal"
data-target = "#addSongModal" onclick = "Logout()" >Logout</ button >
</ div >
< script >
document.addEventListener('DOMContentLoaded', function () {
fetchSongs();
});
const audioPlayer = document.getElementById('audioPlayer');
const audioPlayerimg = document.getElementById('audioPlayerimg');
const playlist = document.getElementById('playlist');
function fetchSongs() {
fetch('/api/songs/')
.then(response => response.json())
.then(songs => {
songs.forEach(song => {
playlist.innerHTML += `< li class = "list-group-item" onclick = "playSong('${song.audio_file}','${song.audio_img}')" >
< span >${song.title} - ${song.artist}-${song.categorie}</ span >
< div >
< button class = "btn btn-info btn-sm" onclick = "UpdateSong(${song.id})" >Update</ button >
< button class = "btn btn-danger btn-sm" onclick = "deleteSong(${song.id})" >Delete</ button >
</ div ></ li >`;
});
})
.catch(error => console.error('Error fetching data:', error));
}
function playSong(songSrc, songimg) {
console.log(songSrc);
console.log(songimg);
document.querySelectorAll('#playlist li').forEach((item) => {
item.style.transform = 'scale(1)';
});
event.target.style.transform = 'scale(1.2)';
audioPlayer.src = songSrc;
console.log(audioPlayerimg);
audioPlayerimg.src = songimg;
audioPlayer.play();
}
function deleteSong(songId) {
if (confirm('Are you sure you want to delete this song?')) {
fetch(`/api/songs/${songId}/`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}',
},
})
.then(response => {
if (response.ok) {
location.reload();
} else {
alert('Failed to delete the song. Please try again.');
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
function AddSong() {
window.location.href = `/AddSong`;
}
function UpdateSong(itemId) {
window.location.href = `/UpdateSong/${itemId}`;
}
function Logout(itemId) {
window.location.href = `/logout/`;
}
function SearchSongs() {
const SearchSong = document.getElementById('Search').value.toLowerCase();
// Saavn API endpoint for searching songs
// Query parameters for the search
const params = {
query: SearchSong,
};
// Request headers
const headers = {
'Content-Type': 'application/json',
};
// Make the GET request to search for songs
fetch(`${saavnSearchUrl}?${new URLSearchParams(params)}`, {
method: 'GET',
headers: headers,
})
.then(response => response.json())
.then(songData => {
playlist.innerHTML = '';
for (const song of songData.data.results) {
const songName = song.name;
const artistName = song.primaryArtists;
const highestQualityDownloadUrl = song.downloadUrl.find(downloadUrl => downloadUrl.quality === '320kbps');
const image150x150 = song.image.find(image => image.quality === '150x150');
const lowestQualityImage = song.image.find(image => image.quality === '50x50');
playlist.innerHTML += `< li class = "list-group-item" onclick = "playSong('${highestQualityDownloadUrl.link}','${image150x150.link}')" >< span >
< img src = "${lowestQualityImage.link}" >
${songName} by ${artistName}</ span >
</ li >`;
}
})
.catch(error => console.error('Error:', error));
}
</ script >
</ body >
</ html >
|
login.html : The HTML document presents a login form using Bootstrap styling, with a title, input fields for username and password, and a login button. It includes a link to register for an account if the user doesn’t have one. The form is submitted via POST request and includes a CSRF token for security.
HTML
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title >Login</ title >
</ head >
< body >
< div class = "container" >
< div class = "row" >
< div class = "col-md-6 offset-md-3" >
< h2 class = "mt-4 mb-4" >Login</ h2 >
< form method = "post" class = "mb-4" >
{% csrf_token %}
{{ form.as_p }}
< button type = "submit" class = "btn btn-primary" >Login</ button >
</ form >
< p >Don't have an account? < a href = "{% url 'register' %}" class = "btn btn-link" >Register here</ a >.</ p >
</ div >
</ div >
</ div >
</ body >
</ html >
|
register.html : The HTML document displays a registration form using Bootstrap styling, including fields for username, password, and password confirmation. The form is submitted via POST request and contains a CSRF token for security. Additionally, there’s a link to the login page for users who already have an account.
HTML
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
< title >Register</ title >
</ head >
< body >
< div class = "container" >
< div class = "row" >
< div class = "col-md-6 offset-md-3" >
< h2 class = "mt-4 mb-4" >Register</ h2 >
< form method = "post" class = "mb-4" >
{% csrf_token %}
{{ form.as_p }}
< button type = "submit" class = "btn btn-primary" >Register</ button >
</ form >
< p >Already have an account? < a href = "{% url 'login' %}" class = "btn btn-link" >Login here</ a >.</ p >
</ div >
</ div >
</ div >
</ body >
</ html >
|
UpdateSong.html : The HTML document is an update form for a song in a music player app, utilizing Bootstrap. JavaScript fetches existing song data, populates the form fields, and handles form submission with an API call for updating. CSRF token is included, and upon success, the user is redirected to the home page.
HTML
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
< title >Update Song</ title >
</ head >
< body >
< div class = "container mt-5" >
< h2 >Update Song</ h2 >
< form id = "updateForm" >
< div class = "form-group" >
< label for = "title" >Title</ label >
< input type = "text" class = "form-control" id = "title" name = "title" required />
< label for = "artist" >Artist</ label >
< input type = "text" class = "form-control" id = "artist" name = "artist" required />
< label for = "categorie" >Categorie</ label >
< input type = "text" class = "form-control" id = "categorie" name = "categorie" required />
< label for = "audio_file" >Audio File</ label >
< input type = "file" class = "form-control-file" id = "audio_file" name = "audio_file" accept = "audio/*" required />
< label for = "audio_img" >Audio Img</ label >
< input type = "file" class = "form-control-file" id = "audio_img" name = "audio_img" required />
</ div >
< button type = "submit" class = "btn btn-primary" >Update</ button >
</ form >
</ div >
< script >
document.addEventListener('DOMContentLoaded', function () {
// Fetch song data and fill the form fields
const url = window.location.pathname.split('/')
let songId = url[2]
fetch(`/api/songs/${songId}/`)
.then((response) => response.json())
.then((data) => {
document.getElementById('title').value = data.title
document.getElementById('artist').value = data.artist
document.getElementById('categorie').value = data.categorie
document.getElementById('audio_file').value = ''
document.getElementById('audio_img').value = ''
})
.catch((error) => console.error('Error fetching song data:', error))
// Submit form with API call
document.getElementById('updateForm').addEventListener('submit', function (event) {
event.preventDefault()
// Get form data
const formData = new FormData(this)
// Make API call to update song
fetch(`/api/songs/${songId}/`, {
method: 'PUT',
headers: {
'X-CSRFToken': getCookie('csrftoken') // Ensure to include CSRF token
},
body: formData
})
.then((response) => response.json())
.then((data) => {
alert('Song updated successfully!')
window.location.href = '/home' // Redirect to the song list page
})
.catch((error) => console.error('Error updating song:', error))
})
})
// Function to get CSRF token from cookies
function getCookie(name) {
var cookieValue = null
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';')
for (var i = 0; i < cookies.length ; i++) {
var cookie = cookies [i].trim()
if (cookie.substring(0, name.length + 1) === name + '=') {
cookieValue = decodeURIComponent (cookie.substring(name.length + 1))
break
}
}
}
return cookieValue
}
</script>
</ body >
</ html >
|
api/admin.py : here we register the models of api app.
Python3
from django.contrib import admin
from .models import Song
class SongAdmin(admin.ModelAdmin):
list_display = ( 'id' , 'title' , 'categorie' , 'artist' , )
search_fields = ( 'title' , 'artist' )
admin.site.register(Song, SongAdmin)
|
music_player_app/admin.py : here , we register the app of music_player_app app.
Python3
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
admin.site.register(CustomUser, UserAdmin)
|
Deployement of the Project
Run these commands to apply the migrations:
python3 manage.py makemigrations
python3 manage.py migrate
Run the server with the help of following command:
python3 manage.py runserver
Output
Video Demonstration
Share your thoughts in the comments
Please Login to comment...