commit 33e4f8cac35e74d59accf3ea81b8fc7f0474dae5 Author: Denis Tereshkin Date: Wed Mar 5 22:50:55 2025 +0700 Initial commit diff --git a/app.py b/app.py new file mode 100644 index 0000000..51c55ed --- /dev/null +++ b/app.py @@ -0,0 +1,29 @@ +from flask import Flask, render_template, send_file, jsonify +import json +import os + +app = Flask(__name__) + +# Configuration +MUSIC_DIR = 'static/audio' +COVERS_DIR = 'static/covers' + +@app.route('/') +def index(): + return render_template('index.html') + +@app.route('/tracks') +def get_tracks(): + with open('tracks.json') as f: + return jsonify(json.load(f)) + +@app.route('/audio/') +def serve_audio(filename): + return send_file(os.path.join(MUSIC_DIR, filename)) + +@app.route('/cover/') +def serve_cover(filename): + return send_file(os.path.join(COVERS_DIR, filename)) + +if __name__ == '__main__': + app.run(debug=True) diff --git a/static/audio/blat_vim.mp3 b/static/audio/blat_vim.mp3 new file mode 100644 index 0000000..eba81d6 Binary files /dev/null and b/static/audio/blat_vim.mp3 differ diff --git a/static/audio/buddies_and_discord.mp3 b/static/audio/buddies_and_discord.mp3 new file mode 100644 index 0000000..3a3f320 Binary files /dev/null and b/static/audio/buddies_and_discord.mp3 differ diff --git a/static/audio/buddies_and_multicast.mp3 b/static/audio/buddies_and_multicast.mp3 new file mode 100644 index 0000000..131f7d7 Binary files /dev/null and b/static/audio/buddies_and_multicast.mp3 differ diff --git a/static/audio/buddies_and_vim.mp3 b/static/audio/buddies_and_vim.mp3 new file mode 100644 index 0000000..fa50a13 Binary files /dev/null and b/static/audio/buddies_and_vim.mp3 differ diff --git a/static/audio/cannon.mp3 b/static/audio/cannon.mp3 new file mode 100644 index 0000000..d031375 Binary files /dev/null and b/static/audio/cannon.mp3 differ diff --git a/static/audio/closed_contour.mp3 b/static/audio/closed_contour.mp3 new file mode 100644 index 0000000..f3c9060 Binary files /dev/null and b/static/audio/closed_contour.mp3 differ diff --git a/static/audio/dev_anthem.mp3 b/static/audio/dev_anthem.mp3 new file mode 100644 index 0000000..4af9dd7 Binary files /dev/null and b/static/audio/dev_anthem.mp3 differ diff --git a/static/audio/developer_in_vim.mp3 b/static/audio/developer_in_vim.mp3 new file mode 100644 index 0000000..fdfdab3 Binary files /dev/null and b/static/audio/developer_in_vim.mp3 differ diff --git a/static/audio/digital_thug_life.mp3 b/static/audio/digital_thug_life.mp3 new file mode 100644 index 0000000..09a29cd Binary files /dev/null and b/static/audio/digital_thug_life.mp3 differ diff --git a/static/audio/fake_licenses.mp3 b/static/audio/fake_licenses.mp3 new file mode 100644 index 0000000..0668e80 Binary files /dev/null and b/static/audio/fake_licenses.mp3 differ diff --git a/static/audio/hacker_and_ltpx.mp3 b/static/audio/hacker_and_ltpx.mp3 new file mode 100644 index 0000000..5b39518 Binary files /dev/null and b/static/audio/hacker_and_ltpx.mp3 differ diff --git a/static/audio/late_evening.mp3 b/static/audio/late_evening.mp3 new file mode 100644 index 0000000..01e1926 Binary files /dev/null and b/static/audio/late_evening.mp3 differ diff --git a/static/audio/ltp-x.mp3 b/static/audio/ltp-x.mp3 new file mode 100644 index 0000000..ec97460 Binary files /dev/null and b/static/audio/ltp-x.mp3 differ diff --git a/static/audio/multicast_case.mp3 b/static/audio/multicast_case.mp3 new file mode 100644 index 0000000..795aa42 Binary files /dev/null and b/static/audio/multicast_case.mp3 differ diff --git a/static/audio/poisoned_mellon.mp3 b/static/audio/poisoned_mellon.mp3 new file mode 100644 index 0000000..5418205 Binary files /dev/null and b/static/audio/poisoned_mellon.mp3 differ diff --git a/static/audio/refactoring.mp3 b/static/audio/refactoring.mp3 new file mode 100644 index 0000000..ffa5bbf Binary files /dev/null and b/static/audio/refactoring.mp3 differ diff --git a/static/audio/rfc3376.mp3 b/static/audio/rfc3376.mp3 new file mode 100644 index 0000000..a56310b Binary files /dev/null and b/static/audio/rfc3376.mp3 differ diff --git a/static/audio/shampoo.mp3 b/static/audio/shampoo.mp3 new file mode 100644 index 0000000..4e32a00 Binary files /dev/null and b/static/audio/shampoo.mp3 differ diff --git a/static/audio/shtiblets.mp3 b/static/audio/shtiblets.mp3 new file mode 100644 index 0000000..ea1e1d5 Binary files /dev/null and b/static/audio/shtiblets.mp3 differ diff --git a/static/audio/switch_and_arps.mp3 b/static/audio/switch_and_arps.mp3 new file mode 100644 index 0000000..1273e08 Binary files /dev/null and b/static/audio/switch_and_arps.mp3 differ diff --git a/static/audio/thieves_and_hackers.mp3 b/static/audio/thieves_and_hackers.mp3 new file mode 100644 index 0000000..3b21aeb Binary files /dev/null and b/static/audio/thieves_and_hackers.mp3 differ diff --git a/static/audio/traffic.mp3 b/static/audio/traffic.mp3 new file mode 100644 index 0000000..3d2ccbc Binary files /dev/null and b/static/audio/traffic.mp3 differ diff --git a/static/audio/vim_trap.mp3 b/static/audio/vim_trap.mp3 new file mode 100644 index 0000000..b0841fa Binary files /dev/null and b/static/audio/vim_trap.mp3 differ diff --git a/static/covers/blat_vim.jpeg b/static/covers/blat_vim.jpeg new file mode 100644 index 0000000..dea0467 Binary files /dev/null and b/static/covers/blat_vim.jpeg differ diff --git a/static/covers/buddies_and_discord.jpeg b/static/covers/buddies_and_discord.jpeg new file mode 100644 index 0000000..c8acbc6 Binary files /dev/null and b/static/covers/buddies_and_discord.jpeg differ diff --git a/static/covers/buddies_and_multicast.jpeg b/static/covers/buddies_and_multicast.jpeg new file mode 100644 index 0000000..3722e12 Binary files /dev/null and b/static/covers/buddies_and_multicast.jpeg differ diff --git a/static/covers/buddies_and_vim.jpeg b/static/covers/buddies_and_vim.jpeg new file mode 100644 index 0000000..8909d26 Binary files /dev/null and b/static/covers/buddies_and_vim.jpeg differ diff --git a/static/covers/cannon.jpeg b/static/covers/cannon.jpeg new file mode 100644 index 0000000..220bcb9 Binary files /dev/null and b/static/covers/cannon.jpeg differ diff --git a/static/covers/closed_contour.jpeg b/static/covers/closed_contour.jpeg new file mode 100644 index 0000000..b55ce6c Binary files /dev/null and b/static/covers/closed_contour.jpeg differ diff --git a/static/covers/dev_anthem.jpeg b/static/covers/dev_anthem.jpeg new file mode 100644 index 0000000..82072a3 Binary files /dev/null and b/static/covers/dev_anthem.jpeg differ diff --git a/static/covers/developer_in_vim.jpeg b/static/covers/developer_in_vim.jpeg new file mode 100644 index 0000000..bc8cc67 Binary files /dev/null and b/static/covers/developer_in_vim.jpeg differ diff --git a/static/covers/digital_thug_life.jpg b/static/covers/digital_thug_life.jpg new file mode 100644 index 0000000..8138631 Binary files /dev/null and b/static/covers/digital_thug_life.jpg differ diff --git a/static/covers/digital_thug_life.webp b/static/covers/digital_thug_life.webp new file mode 100644 index 0000000..6a30f4a Binary files /dev/null and b/static/covers/digital_thug_life.webp differ diff --git a/static/covers/fake_licenses.jpeg b/static/covers/fake_licenses.jpeg new file mode 100644 index 0000000..3a1bbcb Binary files /dev/null and b/static/covers/fake_licenses.jpeg differ diff --git a/static/covers/hacker_and_ltpx.jpeg b/static/covers/hacker_and_ltpx.jpeg new file mode 100644 index 0000000..fccfcdc Binary files /dev/null and b/static/covers/hacker_and_ltpx.jpeg differ diff --git a/static/covers/late_evening.jpeg b/static/covers/late_evening.jpeg new file mode 100644 index 0000000..df56b18 Binary files /dev/null and b/static/covers/late_evening.jpeg differ diff --git a/static/covers/ltp-x.jpeg b/static/covers/ltp-x.jpeg new file mode 100644 index 0000000..e5229fc Binary files /dev/null and b/static/covers/ltp-x.jpeg differ diff --git a/static/covers/multicast_case.jpeg b/static/covers/multicast_case.jpeg new file mode 100644 index 0000000..a6c2df7 Binary files /dev/null and b/static/covers/multicast_case.jpeg differ diff --git a/static/covers/poisoned_watermellon.jpeg b/static/covers/poisoned_watermellon.jpeg new file mode 100644 index 0000000..2216025 Binary files /dev/null and b/static/covers/poisoned_watermellon.jpeg differ diff --git a/static/covers/refactoring.jpeg b/static/covers/refactoring.jpeg new file mode 100644 index 0000000..199a81b Binary files /dev/null and b/static/covers/refactoring.jpeg differ diff --git a/static/covers/rfc3376.jpeg b/static/covers/rfc3376.jpeg new file mode 100644 index 0000000..24dc5a8 Binary files /dev/null and b/static/covers/rfc3376.jpeg differ diff --git a/static/covers/shampoo.jpg b/static/covers/shampoo.jpg new file mode 100644 index 0000000..4fea0f8 Binary files /dev/null and b/static/covers/shampoo.jpg differ diff --git a/static/covers/shtiblets.jpeg b/static/covers/shtiblets.jpeg new file mode 100644 index 0000000..28390ee Binary files /dev/null and b/static/covers/shtiblets.jpeg differ diff --git a/static/covers/switch_and_arps.jpeg b/static/covers/switch_and_arps.jpeg new file mode 100644 index 0000000..206a77a Binary files /dev/null and b/static/covers/switch_and_arps.jpeg differ diff --git a/static/covers/thieves_and_hackers.jpeg b/static/covers/thieves_and_hackers.jpeg new file mode 100644 index 0000000..d56162d Binary files /dev/null and b/static/covers/thieves_and_hackers.jpeg differ diff --git a/static/covers/traffic.jpeg b/static/covers/traffic.jpeg new file mode 100644 index 0000000..d2854fa Binary files /dev/null and b/static/covers/traffic.jpeg differ diff --git a/static/covers/vim_trap.jpeg b/static/covers/vim_trap.jpeg new file mode 100644 index 0000000..52e50be Binary files /dev/null and b/static/covers/vim_trap.jpeg differ diff --git a/static/css/styles.css b/static/css/styles.css new file mode 100644 index 0000000..b5015e8 --- /dev/null +++ b/static/css/styles.css @@ -0,0 +1,91 @@ +body { + background: linear-gradient(135deg, #1a1a1a 0%, #0a0a0a 100%); +} + +.player-card { + border-radius: 15px !important; + box-shadow: 0 10px 30px rgba(0,0,0,0.3); + position: sticky; + top: 20px; +} + +.playlist { + max-height: calc(100vh - 40px); + padding-bottom: 20px; +} + +.btn-outline-light:hover { + background-color: rgba(255,255,255,0.1); +} + +.list-group-item { + background-color: transparent !important; + color: white !important; + border-color: #333 !important; + cursor: pointer; + transition: all 0.2s; +} + +.list-group-item:hover { + background-color: rgba(255,255,255,0.05) !important; +} + +.list-group-item.active { + background-color: rgba(255,255,255,0.1) !important; + border-color: #666 !important; +} + +.sticky-column { + position: -webkit-sticky; + position: sticky; + top: 0; + height: 100vh; + overflow-y: auto; +} + +.scrollable-column { + height: 100vh; + overflow-y: auto; +} + +#progress-container { + transition: all 0.2s; +} + +#progress-container:hover { + height: 10px !important; +} + +#progress-bar { + transition: width 0.1s linear; +} + +/* Mobile responsiveness */ +@media (max-width: 768px) { + .sticky-column { + height: auto; + position: relative; + } + + .scrollable-column { + height: 50vh; + } +} + +/* Custom Scrollbar */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: #1a1a1a; +} + +::-webkit-scrollbar-thumb { + background: #333; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: #444; +} diff --git a/static/js/player.js b/static/js/player.js new file mode 100644 index 0000000..d59d37d --- /dev/null +++ b/static/js/player.js @@ -0,0 +1,118 @@ +let currentTrackIndex = 0; +let tracks = []; +const audio = new Audio(); +let isPlaying = false; + +// DOM Elements +const playBtn = document.getElementById('playBtn'); +const trackList = document.getElementById('track-list'); +const currentTimeElem = document.getElementById('current-time'); +const durationElem = document.getElementById('duration-time'); +const progressContainer = document.getElementById('progress-container'); +const progressBar = document.getElementById('progress-bar'); + +// Fetch tracks and initialize +fetch('/tracks') + .then(response => response.json()) + .then(data => { + tracks = data; + renderPlaylist(); + loadTrack(currentTrackIndex); + }); + +function renderPlaylist() { + trackList.innerHTML = tracks.map((track, index) => ` +
+ ${track.title} ${track.artist} +${track.explicit === 'true' ? '' : ''} +
+ `).join(''); +} + +function playTrack(index) { + currentTrackIndex = index; + const items = document.querySelectorAll('.list-group-item') + items.forEach(item => item.classList.remove('active')); + items[index].classList.add('active'); + items[index].scrollIntoView( { behavior: 'smooth', block: 'nearest'}) + + loadTrack(index); + audio.play(); + isPlaying = true; + updatePlayButton(); +} + +function loadTrack(index) { + const track = tracks[index]; + audio.src = `/audio/${track.file}`; + document.getElementById('cover').src = `/cover/${track.cover}`; + document.getElementById('song-title').textContent = track.title; + document.getElementById('song-artist').textContent = track.artist; +} + +function togglePlay() { + if (isPlaying) { + audio.pause(); + } else { + audio.play(); + } + isPlaying = !isPlaying; + updatePlayButton(); +} + +function updatePlayButton() { + const icon = playBtn.querySelector('i'); + icon.classList.toggle('bi-play-fill', !isPlaying); + icon.classList.toggle('bi-pause-fill', isPlaying); +} + +function nextTrack() { + currentTrackIndex = (currentTrackIndex + 1) % tracks.length; + playTrack(currentTrackIndex); +} + +function previousTrack() { + currentTrackIndex = (currentTrackIndex - 1 + tracks.length) % tracks.length; + playTrack(currentTrackIndex); +} + +// Time formatting +function formatTime(seconds) { + const minutes = Math.floor(seconds / 60); + seconds = Math.floor(seconds % 60); + return `${minutes}:${seconds.toString().padStart(2, '0')}`; +} + +// Audio event listeners +audio.addEventListener('timeupdate', () => { + const progressPercent = (audio.currentTime / audio.duration) * 100; + progressBar.style.width = `${progressPercent}%`; + + currentTimeElem.textContent = formatTime(audio.currentTime); + durationElem.textContent = formatTime(duration); +}); + +audio.addEventListener('loadedmetadata', () => { + durationElem.textContent = formatTime(audio.duration); +}); + +audio.addEventListener('ended', nextTrack); +audio.addEventListener('play', () => { + isPlaying = true; + updatePlayButton(); +}); +audio.addEventListener('pause', () => { + isPlaying = false; + updatePlayButton(); +}); + +progressContainer.addEventListener('click', (e) => { + if (!audio.duration) return; + + const rect = progressContainer.getBoundingClientRect(); + const x = e.clientX - rect.left; + const percentage = x / progressContainer.offsetWidth; + const seekTime = percentage * audio.duration; + audio.currentTime = seekTime; +}); diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..15ab107 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,70 @@ + + + + + + Нейрошансон и не только + + + + + + + + +
+
+
+
+ + Album Cover + + +
+

-

+

-

+ + +
+
+
+ + +
+ 0:00 + 0:00 +
+ + +
+ + + +
+
+
+
+ + +
+
+
Playlist
+
+ +
+
+
+
+
+ + + + + + diff --git a/tracks.json b/tracks.json new file mode 100644 index 0000000..a25a9cb --- /dev/null +++ b/tracks.json @@ -0,0 +1,163 @@ +[ + { + "file": "switch_and_arps.mp3", + "title": "Switch и Арпы", + "artist": "Suno", + "cover": "switch_and_arps.jpeg", + "explicit": "false" + }, + { + "file": "thieves_and_hackers.mp3", + "title": "Воры и Хакеры", + "artist": "Suno", + "cover": "thieves_and_hackers.jpeg", + "explicit": "false" + }, + { + "file": "buddies_and_discord.mp3", + "title": "Пацаны и Discord", + "artist": "Suno/Gemma2", + "cover": "buddies_and_discord.jpeg", + "explicit": "false" + }, + { + "file": "blat_vim.mp3", + "title": "Блатной Vim", + "artist": "Suno", + "cover": "blat_vim.jpeg", + "explicit": "false" + }, + { + "file": "buddies_and_vim.mp3", + "title": "Пацаны и Vim", + "artist": "Suno", + "cover": "buddies_and_vim.jpeg", + "explicit": "false" + }, + { + "file": "fake_licenses.mp3", + "title": "Палёные Лицензии", + "artist": "Suno/Gemma2", + "cover": "fake_licenses.jpeg", + "explicit": "true" + }, + { + "file": "hacker_and_ltpx.mp3", + "title": "Хакер и LTP-X", + "artist": "Suno/Gemma2", + "cover": "hacker_and_ltpx.jpeg", + "explicit": "false" + }, + { + "file": "shtiblets.mp3", + "title": "Штиблеты в Лесу", + "artist": "Suno", + "cover": "shtiblets.jpeg", + "explicit": "false" + }, + { + "file": "digital_thug_life.mp3", + "title": "Цифровой Блатняк (Digital Thug Life)", + "artist": "Riffusion", + "cover": "digital_thug_life.jpg", + "explicit": "true" + }, + { + "file": "buddies_and_multicast.mp3", + "title": "Кореша и Мультикаст", + "artist": "Suno", + "cover": "buddies_and_multicast.jpeg", + "explicit": "false" + }, + { + "file": "cannon.mp3", + "title": "Пушка Рикошет", + "artist": "Suno", + "cover": "cannon.jpeg", + "explicit": "false" + }, + { + "file": "closed_contour.mp3", + "title": "Закрытый Контур", + "artist": "Suno", + "cover": "closed_contour.jpeg", + "explicit": "false" + }, + { + "file": "dev_anthem.mp3", + "title": "Гимн Разработчика xPON", + "artist": "Suno", + "cover": "dev_anthem.jpeg", + "explicit": "false" + }, + { + "file": "developer_in_vim.mp3", + "title": "Программист в Ловушке", + "artist": "Suno", + "cover": "developer_in_vim.jpeg", + "explicit": "false" + }, + { + "file": "late_evening.mp3", + "title": "Поздний Вечер", + "artist": "Suno", + "cover": "late_evening.jpeg", + "explicit": "false" + }, + { + "file": "ltp-x.mp3", + "title": "LTP-X", + "artist": "Suno", + "cover": "ltp-x.jpeg", + "explicit": "false" + }, + { + "file": "multicast_case.mp3", + "title": "Дело о Потерянном Мультикасте", + "artist": "Suno", + "cover": "multicast_case.jpeg", + "explicit": "false" + }, + { + "file": "poisoned_mellon.mp3", + "title": "Отравленный Арбуз", + "artist": "Suno", + "cover": "poisoned_watermellon.jpeg", + "explicit": "false" + }, + { + "file": "refactoring.mp3", + "title": "Рефакторинг Боли", + "artist": "Suno", + "cover": "refactoring.jpeg", + "explicit": "false" + }, + { + "file": "rfc3376.mp3", + "title": "RFC3376", + "artist": "Suno", + "cover": "rfc3376.jpeg", + "explicit": "false" + }, + { + "file": "traffic.mp3", + "title": "Пробки и Крик", + "artist": "Suno", + "cover": "traffic.jpeg", + "explicit": "false" + }, + { + "file": "vim_trap.mp3", + "title": "В ловушке Vim'a", + "artist": "Suno", + "cover": "vim_trap.jpeg", + "explicit": "false" + }, + { + "file": "shampoo.mp3", + "title": "Шампунь", + "artist": "Suno", + "cover": "shampoo.jpg", + "explicit": "false" + } +]