commit
325ff8b550
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
@ -0,0 +1,25 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<value>
|
||||||
|
<list size="12">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="requests-oauthlib" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="Werkzeug" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="Flask-Assets" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="SQLAlchemy" />
|
||||||
|
<item index="4" class="java.lang.String" itemvalue="libsass" />
|
||||||
|
<item index="5" class="java.lang.String" itemvalue="requests" />
|
||||||
|
<item index="6" class="java.lang.String" itemvalue="Flask-SQLAlchemy" />
|
||||||
|
<item index="7" class="java.lang.String" itemvalue="Jinja2" />
|
||||||
|
<item index="8" class="java.lang.String" itemvalue="Flask-Login" />
|
||||||
|
<item index="9" class="java.lang.String" itemvalue="Flask" />
|
||||||
|
<item index="10" class="java.lang.String" itemvalue="typing_extensions" />
|
||||||
|
<item index="11" class="java.lang.String" itemvalue="grpc" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.11 (SpotiMIDI)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (SpotiMIDI)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/SpotiMIDI.iml" filepath="$PROJECT_DIR$/.idea/SpotiMIDI.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -0,0 +1,45 @@
|
|||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import mido
|
||||||
|
from mido import MidiFile
|
||||||
|
|
||||||
|
from StoppableThread import StoppableThread
|
||||||
|
from Requestor import Requestor
|
||||||
|
|
||||||
|
class MediaPlayer:
|
||||||
|
|
||||||
|
def __init__(self, host, port):
|
||||||
|
self.requestor = Requestor(host, port)
|
||||||
|
self.song_thread = None
|
||||||
|
self.output = mido.open_output()
|
||||||
|
|
||||||
|
def request_song(self, song_name):
|
||||||
|
bytes_song = self.requestor.getSong(song_name)
|
||||||
|
|
||||||
|
with open('received_song.mid', 'wb') as file:
|
||||||
|
file.write(bytes_song)
|
||||||
|
|
||||||
|
if self.song_thread is not None and self.song_thread.is_alive():
|
||||||
|
self.song_thread.stop()
|
||||||
|
|
||||||
|
self.song_thread = StoppableThread(target=self.play_song, args=('received_song.mid',))
|
||||||
|
self.song_thread.start()
|
||||||
|
|
||||||
|
def play_song(self, song_path):
|
||||||
|
mid = MidiFile(song_path)
|
||||||
|
try:
|
||||||
|
for msg in mid.play():
|
||||||
|
if threading.current_thread().stopped():
|
||||||
|
break
|
||||||
|
|
||||||
|
self.output.send(msg)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.output.reset()
|
||||||
|
|
||||||
|
def stop_song(self):
|
||||||
|
if self.song_thread is not None and self.song_thread.is_alive():
|
||||||
|
self.song_thread.stop()
|
@ -0,0 +1,32 @@
|
|||||||
|
import socket
|
||||||
|
|
||||||
|
|
||||||
|
class Requestor(object):
|
||||||
|
|
||||||
|
def __init__(self, host: str, port: int):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
|
||||||
|
|
||||||
|
def getSongList(self) -> list:
|
||||||
|
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
client.connect((self.host, self.port))
|
||||||
|
client.send('getSongList'.encode('utf-8'))
|
||||||
|
song_list = client.recv(1024).decode('utf-8')
|
||||||
|
client.close()
|
||||||
|
return song_list.split('\n')
|
||||||
|
|
||||||
|
def getSong(self, song_id: int) -> bytes:
|
||||||
|
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
client.connect((self.host, self.port))
|
||||||
|
client.send(f'getSong:{song_id}'.encode('utf-8'))
|
||||||
|
|
||||||
|
song_data = b""
|
||||||
|
while True:
|
||||||
|
chunk = client.recv(1024)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
song_data += chunk
|
||||||
|
|
||||||
|
client.close()
|
||||||
|
return song_data
|
@ -0,0 +1,13 @@
|
|||||||
|
import threading
|
||||||
|
|
||||||
|
class StoppableThread(threading.Thread):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self._stop_event = threading.Event()
|
||||||
|
self._pause_event = threading.Event()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._stop_event.set()
|
||||||
|
|
||||||
|
def stopped(self):
|
||||||
|
return self._stop_event.is_set()
|
@ -0,0 +1,40 @@
|
|||||||
|
import os
|
||||||
|
import tkinter as tk
|
||||||
|
|
||||||
|
from MediaPlayer import MediaPlayer
|
||||||
|
|
||||||
|
CLIENT_HOST = "localhost"
|
||||||
|
CLIENT_PORT = 9999
|
||||||
|
media_player = MediaPlayer(CLIENT_HOST, CLIENT_PORT)
|
||||||
|
|
||||||
|
def request_selected_song():
|
||||||
|
selected_song = song_listbox.get(song_listbox.curselection())
|
||||||
|
media_player.request_song(selected_song)
|
||||||
|
|
||||||
|
def stop_song():
|
||||||
|
media_player.stop_song()
|
||||||
|
|
||||||
|
root = tk.Tk()
|
||||||
|
root.title("Song Requestor")
|
||||||
|
|
||||||
|
song_listbox = tk.Listbox(root, height=10, width=50)
|
||||||
|
song_listbox.pack()
|
||||||
|
|
||||||
|
song_list = media_player.requestor.getSongList()
|
||||||
|
for song in song_list:
|
||||||
|
if song: # Check if song is not an empty string
|
||||||
|
song_listbox.insert(tk.END, song)
|
||||||
|
|
||||||
|
request_button = tk.Button(root, text="Play song", command=request_selected_song)
|
||||||
|
request_button.pack()
|
||||||
|
|
||||||
|
play_pause_button = tk.Button(root, text="Stop song", command=stop_song)
|
||||||
|
play_pause_button.pack()
|
||||||
|
|
||||||
|
def on_close():
|
||||||
|
media_player.stop_song()
|
||||||
|
os.remove('received_song.mid')
|
||||||
|
root.destroy()
|
||||||
|
|
||||||
|
root.protocol("WM_DELETE_WINDOW", on_close)
|
||||||
|
root.mainloop()
|
@ -0,0 +1,69 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from mido import MetaMessage
|
||||||
|
|
||||||
|
from Song import Song
|
||||||
|
import mido
|
||||||
|
|
||||||
|
class Library(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.songs = []
|
||||||
|
|
||||||
|
def initialize(self, songs_dir: str):
|
||||||
|
print("Initializing library")
|
||||||
|
for root, dirs, files in os.walk(songs_dir):
|
||||||
|
for i, file in enumerate(files):
|
||||||
|
if file.endswith('.mid'):
|
||||||
|
name = file.split('-')[0]
|
||||||
|
author = file.split('-')[1].split('.')[0]
|
||||||
|
path = os.path.join(root, file)
|
||||||
|
song = Song(i, name, author, path, self.get_request_count_from_midi_file(path))
|
||||||
|
print(f"Loaded song: {song.name} by {song.author} with {song.total_requests} requests")
|
||||||
|
self.songs.append(song)
|
||||||
|
|
||||||
|
def get_request_count_from_midi_file(self, file_path):
|
||||||
|
mid = mido.MidiFile(file_path)
|
||||||
|
|
||||||
|
for i, track in enumerate(mid.tracks):
|
||||||
|
for msg in track:
|
||||||
|
if msg.type == 'text' and 'Request count:' in msg.text:
|
||||||
|
return int(msg.text.split(': ')[1])
|
||||||
|
|
||||||
|
# If no 'text' MetaMessage with the request count is found, add one with a count of 0
|
||||||
|
msg = MetaMessage('text', text='Request count: 0')
|
||||||
|
mid.tracks[0].append(msg)
|
||||||
|
mid.save(file_path)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def get_song_list(self) -> str:
|
||||||
|
sorted = self.songs.copy()
|
||||||
|
sorted.sort(key=lambda x: x.total_requests, reverse=True)
|
||||||
|
song_list = ""
|
||||||
|
for song in sorted:
|
||||||
|
song_list += f"{song.id}: {song.name} by {song.author}\n"
|
||||||
|
return song_list
|
||||||
|
|
||||||
|
def get_song_by_id(self, id: int) -> Song:
|
||||||
|
for song in self.songs:
|
||||||
|
if song.id == id:
|
||||||
|
# Increment the request count
|
||||||
|
song.total_requests += 1
|
||||||
|
|
||||||
|
# Load the MIDI file
|
||||||
|
mid = mido.MidiFile(song.path)
|
||||||
|
|
||||||
|
# Remove the old 'text' MetaMessage with the request count
|
||||||
|
for i, track in enumerate(mid.tracks):
|
||||||
|
for j, msg in enumerate(track):
|
||||||
|
if msg.type == 'text' and 'Request count:' in msg.text:
|
||||||
|
del track[j]
|
||||||
|
|
||||||
|
# Add a new 'text' MetaMessage with the updated request count
|
||||||
|
mid.tracks[0].append(MetaMessage('text', text=f'Request count: {song.total_requests}'))
|
||||||
|
|
||||||
|
# Save the MIDI file
|
||||||
|
mid.save(song.path)
|
||||||
|
|
||||||
|
return song
|
||||||
|
return None
|
@ -0,0 +1,17 @@
|
|||||||
|
class Song(object):
|
||||||
|
|
||||||
|
def __init__(self, id: int, name: str, author: str, path: str, total_requests: int):
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.author = author
|
||||||
|
self.path = path
|
||||||
|
self.total_requests = total_requests
|
||||||
|
|
||||||
|
def get_total_requests(self) -> int:
|
||||||
|
return self.total_requests
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def get_data(self) -> bytes:
|
||||||
|
return open(self.path, 'rb').read()
|
@ -0,0 +1,39 @@
|
|||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from Library import Library
|
||||||
|
from Song import Song
|
||||||
|
import os
|
||||||
|
|
||||||
|
SONGS_DIR = os.path.join(os.path.dirname(__file__), 'songs')
|
||||||
|
|
||||||
|
library = Library()
|
||||||
|
library.initialize(SONGS_DIR)
|
||||||
|
|
||||||
|
def handle_client(client_socket):
|
||||||
|
request = client_socket.recv(1024).decode('utf-8')
|
||||||
|
print(f"Received: {request}")
|
||||||
|
|
||||||
|
if request == 'getSongList':
|
||||||
|
client_socket.send(library.get_song_list().encode('utf-8'))
|
||||||
|
|
||||||
|
elif request.startswith('getSong:'):
|
||||||
|
song_id = int(request.split(':')[1])
|
||||||
|
song: Song = library.get_song_by_id(song_id)
|
||||||
|
print(f"Sending song: {song.name} by {song.author} to {client_socket.getpeername()}")
|
||||||
|
client_socket.send(song.get_data())
|
||||||
|
client_socket.close()
|
||||||
|
|
||||||
|
def start_server():
|
||||||
|
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
server.bind(("0.0.0.0", 9999))
|
||||||
|
server.listen(5)
|
||||||
|
print("Listening on port 9999")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
client, addr = server.accept()
|
||||||
|
print(f"Accepted connection from: {addr[0]}:{addr[1]}")
|
||||||
|
client_handler = threading.Thread(target=handle_client, args=(client,))
|
||||||
|
client_handler.start()
|
||||||
|
|
||||||
|
start_server()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue