from __future__ import annotations import uuid from sqlalchemy.ext.asyncio import AsyncSession from rehearsalhub.db.models import AudioVersion, Song from rehearsalhub.queue.redis_queue import RedisJobQueue from rehearsalhub.repositories.audio_version import AudioVersionRepository from rehearsalhub.repositories.song import SongRepository from rehearsalhub.schemas.audio_version import AudioVersionCreate from rehearsalhub.schemas.song import SongCreate, SongRead, SongUpdate from rehearsalhub.storage.nextcloud import NextcloudClient class SongService: def __init__( self, session: AsyncSession, job_queue: RedisJobQueue | None = None, storage: NextcloudClient | None = None, ) -> None: self._repo = SongRepository(session) self._version_repo = AudioVersionRepository(session) self._session = session self._queue = job_queue or RedisJobQueue(session) self._storage = storage async def create_song( self, band_id: uuid.UUID, data: SongCreate, creator_id: uuid.UUID, band_slug: str, creator: object | None = None, ) -> Song: from rehearsalhub.storage.nextcloud import NextcloudClient nc_folder = f"bands/{band_slug}/songs/{data.title.lower().replace(' ', '-')}/" storage = NextcloudClient.for_member(creator) if creator else self._storage try: await storage.create_folder(nc_folder) except Exception: nc_folder = None # best-effort song = await self._repo.create( band_id=band_id, title=data.title, status=data.status, notes=data.notes, nc_folder_path=nc_folder, created_by=creator_id, ) return song async def list_songs(self, band_id: uuid.UUID) -> list[SongRead]: songs = await self._repo.list_for_band(band_id) result = [] for song in songs: versions = song.versions read = SongRead.model_validate(song) read.version_count = len(versions) if versions: latest = max(versions, key=lambda v: v.version_number) read.latest_version_id = latest.id result.append(read) return result async def register_version( self, song_id: uuid.UUID, data: AudioVersionCreate, uploader_id: uuid.UUID, ) -> AudioVersion: if data.nc_file_etag: existing = await self._version_repo.get_by_etag(data.nc_file_etag) if existing: return existing version_number = await self._repo.next_version_number(song_id) version = await self._version_repo.create( song_id=song_id, version_number=version_number, nc_file_path=data.nc_file_path, nc_file_etag=data.nc_file_etag, label=data.label, format=data.format, file_size_bytes=data.file_size_bytes, analysis_status="pending", uploaded_by=uploader_id, ) await self._queue.enqueue( "transcode", {"version_id": str(version.id), "nc_file_path": data.nc_file_path}, ) return version