Files
MarietjeDjango/marietje/songs/services.py
2023-11-25 07:38:28 +01:00

88 lines
2.6 KiB
Python

import binascii
import socket
import struct
from django.conf import settings
from queues.models import PlaylistSong
from songs.models import Song
from django.db.models.functions import Coalesce
from django.db.models import Sum, Value
from django.db import transaction
from mutagen import File
class UploadException(Exception):
pass
def send_to_bertha(file):
"""Send a file to Berthad file storage."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(settings.BERTHA_HOST)
sock.sendall(struct.pack("<BQ", 4, file.size))
for chunk in file.chunks():
sock.sendall(chunk)
sock.shutdown(socket.SHUT_WR)
song_hash = binascii.hexlify(sock.recv(64))
sock.close()
return song_hash
def is_regular_queue(ps):
if not ps.played_at:
# Request is from the old times, assume good
return True
if not 0 <= ps.played_at.astimezone().weekday() <= 4:
return False # Queued in the weekend
if not 7 <= ps.played_at.astimezone().hour <= 22:
# Because of timezone shit, I allow for an extra hour of leeway
return False # Queued outside of regular opening hours
return True
@transaction.atomic
def request_weight(ps):
if is_regular_queue(ps):
return float(ps.song.duration)
# Count other requests for 10%
return 0.10 * float(ps.song.duration)
def get_upload_stats(user):
songs_queued = PlaylistSong.objects.select_related("song").filter(user=user, state=2, song__deleted=False)
queued_score = sum(request_weight(x) for x in songs_queued)
upload_score = Song.objects.filter(user=user, deleted=False).aggregate(x=Coalesce(Sum("duration"), Value(0)))["x"]
return {"queued_score": queued_score, "upload_score": upload_score}
def get_reputation(user) -> float:
try:
stats = get_upload_stats(user)
ratio = stats["queued_score"] / (2.0 * stats["upload_score"])
return ratio
except ZeroDivisionError:
return 99999.0 # high enough
def check_upload_stats(user):
# Allow upload if the user has a good reputation
# Score function:
# - U = duration * songs uploaded
# - Q = duration * songs queued
# - If 2*U < Q: allow upload (otherwise don't)
if user.is_superuser:
return True
ratio = get_reputation(user)
return ratio >= 1.0
def upload_file(file, artist, title, user):
duration = File(file).info.length
bertha_hash = send_to_bertha(file).decode("ascii")
if not bertha_hash:
raise UploadException("Files not uploaded correctly.")
return Song.objects.create(user=user, artist=artist, title=title, hash=bertha_hash, duration=duration)