prasanth.thangavel commited on
Commit
51c43d6
·
1 Parent(s): 2a59b87

Add code for the music player

Browse files
Files changed (7) hide show
  1. .dockerignore +6 -0
  2. .gitattributes +5 -0
  3. .gitignore +5 -0
  4. Dockerfile +23 -0
  5. app.py +20 -0
  6. requirements.txt +2 -0
  7. templates/index.html +319 -0
.dockerignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # .dockerignore
2
+ __pycache__
3
+ *.pyc
4
+ .DS_Store
5
+ .git
6
+ .env
.gitattributes CHANGED
@@ -33,3 +33,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.mp3 filter=lfs diff=lfs merge=lfs -text
37
+ *.wav filter=lfs diff=lfs merge=lfs -text
38
+ *.ogg filter=lfs diff=lfs merge=lfs -text
39
+ *.flac filter=lfs diff=lfs merge=lfs -text
40
+ *.m4a filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Exclude audio source files from regular Git
2
+ !static/audio/*.mp3
3
+ !static/audio/*.wav
4
+ !static/audio/*.ogg
5
+ !static/audio/*.flac
Dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Python 3.11 slim base image for smaller size
2
+ FROM python:3.11-slim
3
+
4
+ # Create non-root user for security
5
+ RUN useradd -m -u 1000 user
6
+ USER user
7
+ ENV PATH="/home/user/.local/bin:$PATH"
8
+
9
+ # Set working directory for app
10
+ WORKDIR /app
11
+
12
+ # Copy requirements with correct ownership first for better layer caching
13
+ COPY --chown=user requirements.txt .
14
+
15
+ # Install Python dependencies
16
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
17
+
18
+ # Copy application code with correct ownership (all files from current directory to container)
19
+ COPY --chown=user . .
20
+
21
+ # Run gunicorn server on port 7860
22
+ # Format: gunicorn -b host:port module_name:app_instance
23
+ CMD ["gunicorn", "-b", "0.0.0.0:7860", "app:app"]
app.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ from flask import Flask, render_template, send_from_directory
3
+ import os
4
+
5
+ app = Flask(__name__)
6
+
7
+ # Add route for serving audio files
8
+ @app.route('/audio/<path:filename>')
9
+ def serve_audio(filename):
10
+ return send_from_directory('static/audio', filename)
11
+
12
+ @app.route("/")
13
+ def home():
14
+ # Get list of audio files
15
+ audio_dir = os.path.join('static', 'audio')
16
+ audio_files = [f for f in os.listdir(audio_dir) if f.endswith(('.mp3', '.wav'))]
17
+ return render_template("index.html", audio_files=audio_files)
18
+
19
+ if __name__ == "__main__":
20
+ app.run(debug=True)
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ flask==2.0.1
2
+ gunicorn==20.1.0
templates/index.html ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Music Player</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <style>
7
+ :root {
8
+ --primary: #6200ee;
9
+ --surface: #fff;
10
+ --on-surface: #121212;
11
+ --accent: #03dac6;
12
+ --error: #b00020;
13
+ }
14
+
15
+ * {
16
+ box-sizing: border-box;
17
+ -webkit-tap-highlight-color: transparent;
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+
22
+ body {
23
+ font-family: -apple-system, system-ui, BlinkMacSystemFont;
24
+ background: #fafafa;
25
+ color: var(--on-surface);
26
+ line-height: 1.5;
27
+ }
28
+
29
+ .sw-container {
30
+ padding: 16px;
31
+ max-width: 600px;
32
+ margin: 0 auto;
33
+ }
34
+
35
+ .sw-player {
36
+ background: var(--surface);
37
+ border-radius: 16px;
38
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
39
+ padding: 20px;
40
+ margin-bottom: 16px;
41
+ }
42
+
43
+ .sw-header {
44
+ font-size: 1.25rem;
45
+ font-weight: 600;
46
+ text-align: center;
47
+ margin-bottom: 20px;
48
+ }
49
+
50
+ .sw-audio {
51
+ width: 100%;
52
+ height: 48px;
53
+ margin: 16px 0;
54
+ }
55
+
56
+ .sw-btn-group {
57
+ display: grid;
58
+ grid-template-columns: repeat(2, 1fr);
59
+ gap: 12px;
60
+ margin: 16px 0;
61
+ }
62
+
63
+ .sw-btn {
64
+ background: var(--primary);
65
+ color: white;
66
+ border: none;
67
+ border-radius: 24px;
68
+ padding: 12px 24px;
69
+ font-size: 0.9rem;
70
+ font-weight: 500;
71
+ text-transform: uppercase;
72
+ letter-spacing: 0.5px;
73
+ touch-action: manipulation;
74
+ transition: transform 0.2s;
75
+ }
76
+
77
+ .sw-btn:active {
78
+ transform: scale(0.96);
79
+ }
80
+
81
+ .sw-dropdown {
82
+ width: 100%;
83
+ padding: 12px;
84
+ border: 2px solid #e0e0e0;
85
+ border-radius: 12px;
86
+ font-size: 1rem;
87
+ margin-bottom: 16px;
88
+ }
89
+
90
+ .sw-tracklist {
91
+ margin-top: 20px;
92
+ }
93
+
94
+ .sw-track {
95
+ padding: 16px;
96
+ margin: 8px 0;
97
+ border-radius: 12px;
98
+ background: #f5f5f5;
99
+ transition: background 0.2s;
100
+ }
101
+
102
+ .sw-track.active {
103
+ background: #e8f5ff;
104
+ border-left: 4px solid var(--primary);
105
+ }
106
+
107
+ .sw-modal {
108
+ position: fixed;
109
+ inset: 0;
110
+ background: rgba(0,0,0,0.6);
111
+ display: none;
112
+ align-items: center;
113
+ justify-content: center;
114
+ padding: 16px;
115
+ }
116
+
117
+ .sw-modal-content {
118
+ background: var(--surface);
119
+ width: 100%;
120
+ max-width: 400px;
121
+ border-radius: 16px;
122
+ padding: 24px;
123
+ }
124
+
125
+ .sw-input {
126
+ width: 100%;
127
+ padding: 12px;
128
+ border: 2px solid #e0e0e0;
129
+ border-radius: 12px;
130
+ font-size: 1rem;
131
+ margin: 16px 0;
132
+ }
133
+
134
+ .sw-checkbox-list {
135
+ max-height: 300px;
136
+ overflow-y: auto;
137
+ margin: 16px 0;
138
+ border: 1px solid #e0e0e0;
139
+ border-radius: 12px;
140
+ }
141
+
142
+ .sw-checkbox-item {
143
+ padding: 12px 16px;
144
+ border-bottom: 1px solid #e0e0e0;
145
+ }
146
+
147
+ @media (hover: hover) {
148
+ .sw-btn:hover {
149
+ background: #7722ff;
150
+ }
151
+ .sw-track {
152
+ padding: 20px;
153
+ }
154
+ </style>
155
+ </head>
156
+ <body>
157
+ <div class="sw-container">
158
+ <div class="sw-player">
159
+ <h1 class="sw-header">Music Player</h1>
160
+
161
+ <audio id="swAudio" class="sw-audio" controls>
162
+ Your browser does not support audio playback.
163
+ </audio>
164
+
165
+ <div class="sw-btn-group">
166
+ <button class="sw-btn" onclick="swPrevious()">Previous</button>
167
+ <button class="sw-btn" onclick="swNext()">Next</button>
168
+ </div>
169
+
170
+ <select class="sw-dropdown" id="swPlaylistSelect" onchange="swChangePlaylist()">
171
+ <option value="all">All Songs</option>
172
+ </select>
173
+
174
+ <div class="sw-btn-group">
175
+ <button class="sw-btn" onclick="swShowPlaylistModal()">Create Playlist</button>
176
+ <button class="sw-btn" onclick="swDeletePlaylist()">Delete Playlist</button>
177
+ </div>
178
+
179
+ <div class="sw-tracklist">
180
+ {% for file in audio_files %}
181
+ <div class="sw-track" onclick="swPlay('{{ file }}')">
182
+ {{ file }}
183
+ </div>
184
+ {% endfor %}
185
+ </div>
186
+ </div>
187
+ </div>
188
+
189
+ <div class="sw-modal" id="swPlaylistModal">
190
+ <div class="sw-modal-content">
191
+ <h2>Create New Playlist</h2>
192
+ <input type="text" class="sw-input" id="swPlaylistName" placeholder="Playlist Name">
193
+ <div class="sw-checkbox-list" id="swSongCheckboxes"></div>
194
+ <div class="sw-btn-group">
195
+ <button class="sw-btn" onclick="swCreatePlaylist()">Save</button>
196
+ <button class="sw-btn" onclick="swCloseModal()">Cancel</button>
197
+ </div>
198
+ </div>
199
+ </div>
200
+
201
+ <script>
202
+ const swAudio = document.getElementById('swAudio');
203
+ const swTracks = {{ audio_files|tojson|safe }};
204
+ let swCurrentIndex = 0;
205
+
206
+ function swPlay(file) {
207
+ swAudio.src = `/audio/${file}`;
208
+ swAudio.play();
209
+ updateActiveTracks();
210
+ }
211
+
212
+ function swNext() {
213
+ if (swCurrentIndex < swTracks.length - 1) {
214
+ swCurrentIndex++;
215
+ swPlay(swTracks[swCurrentIndex]);
216
+ }
217
+ }
218
+
219
+ function swPrevious() {
220
+ if (swCurrentIndex > 0) {
221
+ swCurrentIndex--;
222
+ swPlay(swTracks[swCurrentIndex]);
223
+ }
224
+ }
225
+
226
+ function updateActiveTracks() {
227
+ document.querySelectorAll('.sw-track').forEach((track, index) => {
228
+ track.classList.toggle('active', index === swCurrentIndex);
229
+ });
230
+ }
231
+
232
+ // Auto-play next track
233
+ swAudio.addEventListener('ended', swNext);
234
+
235
+ // Play first track if available
236
+ if (swTracks.length > 0) {
237
+ swPlay(swTracks[0]);
238
+ }
239
+
240
+ const swPlaylists = JSON.parse(localStorage.getItem('swPlaylists') || '{}');
241
+ let swCurrentPlaylist = 'all';
242
+
243
+ function swLoadPlaylists() {
244
+ const select = document.getElementById('swPlaylistSelect');
245
+ select.innerHTML = '<option value="all">All Songs</option>';
246
+ Object.keys(swPlaylists).forEach(name => {
247
+ select.innerHTML += `<option value="${name}">${name}</option>`;
248
+ });
249
+ }
250
+
251
+ function swShowPlaylistModal() {
252
+ const modal = document.getElementById('swPlaylistModal');
253
+ const checkboxes = document.getElementById('swSongCheckboxes');
254
+ checkboxes.innerHTML = swTracks.map(song =>
255
+ `<div class="sw-checkbox-item">
256
+ <input type="checkbox" id="${song}" value="${song}">
257
+ <label for="${song}">${song}</label>
258
+ </div>`
259
+ ).join('');
260
+ modal.style.display = 'flex';
261
+ }
262
+
263
+ function swCloseModal() {
264
+ document.getElementById('swPlaylistModal').style.display = 'none';
265
+ }
266
+
267
+ function swCreatePlaylist() {
268
+ const name = document.getElementById('swPlaylistName').value;
269
+ if (!name) return alert('Please enter playlist name');
270
+
271
+ const songs = Array.from(document.querySelectorAll('#swSongCheckboxes input:checked'))
272
+ .map(cb => cb.value);
273
+ if (!songs.length) return alert('Select at least one song');
274
+
275
+ swPlaylists[name] = songs;
276
+ localStorage.setItem('swPlaylists', JSON.stringify(swPlaylists));
277
+ swLoadPlaylists();
278
+ swCloseModal();
279
+ }
280
+
281
+ function swChangePlaylist() {
282
+ swCurrentPlaylist = document.getElementById('swPlaylistSelect').value;
283
+ swCurrentIndex = 0;
284
+ swUpdateTracks();
285
+ if (swGetCurrentTracks().length > 0) {
286
+ swPlay(swGetCurrentTracks()[0]);
287
+ }
288
+ }
289
+
290
+ function swDeletePlaylist() {
291
+ if (swCurrentPlaylist === 'all') return;
292
+
293
+ delete swPlaylists[swCurrentPlaylist];
294
+ localStorage.setItem('swPlaylists', JSON.stringify(swPlaylists));
295
+ swCurrentPlaylist = 'all';
296
+ swLoadPlaylists();
297
+ swChangePlaylist();
298
+ }
299
+
300
+ function swGetCurrentTracks() {
301
+ return swCurrentPlaylist === 'all' ? swTracks : swPlaylists[swCurrentPlaylist];
302
+ }
303
+
304
+ function swUpdateTracks() {
305
+ const tracks = swGetCurrentTracks();
306
+ document.querySelector('.sw-tracklist').innerHTML = tracks.map((file, index) =>
307
+ `<div class="sw-track ${index === swCurrentIndex ? 'active' : ''}"
308
+ onclick="swPlay('${file}')">
309
+ ${file}
310
+ </div>`
311
+ ).join('');
312
+ }
313
+
314
+ // Initialize playlists
315
+ swLoadPlaylists();
316
+ </script>
317
+ </body>
318
+ </html>
319
+