import { z } from 'zod'; /** * Android music apps whose media sessions we share by default (package → display name). * Anything else (video, podcasts, browsers) is ignored unless the user opts in, so * friends don't see YouTube videos or audiobooks in their feed. */ export const ANDROID_MUSIC_APPS: Record = { 'com.spotify.music': 'Spotify', 'com.google.android.apps.youtube.music': 'YouTube Music', 'com.apple.android.music': 'Apple Music', 'com.aspiro.tidal': 'TIDAL', 'deezer.android.app': 'Deezer', 'com.soundcloud.android': 'SoundCloud', 'com.amazon.mp3': 'Amazon Music', 'com.pandora.android': 'Pandora', 'com.qobuz.music': 'Qobuz', 'tv.plex.labs.plexamp': 'Plexamp', 'com.unicornsonlsd.finamp': 'Finamp', 'app.symfonium.android': 'Symfonium', 'com.bandcamp.android': 'Bandcamp', 'com.anghami': 'Anghami', 'com.jio.media.jiobeats': 'JioSaavn', 'com.maxmpz.audioplayer': 'Poweramp', 'org.videolan.vlc': 'VLC', 'com.kapp.youtube.final': 'YMusic', 'app.revanced.android.apps.youtube.music': 'YouTube Music', }; export const registerDeviceSchema = z.object({ platform: z.enum(['android', 'ios']), name: z.string().trim().min(1).max(100), }); const reportedTrack = z.object({ title: z.string().trim().min(1).max(500), artist: z.string().trim().max(500), album: z.string().trim().max(500).nullish(), durationMs: z.number().int().positive().max(24 * 3600_000).nullish(), }); /** What a phone reports when playback changes in a music app. */ export const devicePlaybackSchema = z .object({ /** Android package name (or iOS bundle id) of the playing app. */ app: z.string().trim().min(1).max(200), /** The app's label as the OS reports it; used for apps we don't know. */ appName: z.string().trim().max(100).nullish(), state: z.enum(['playing', 'paused', 'stopped']), track: reportedTrack.nullish(), positionMs: z.number().int().min(0).nullish(), /** Phone's clock (epoch ms) when positionMs was read; lets the server account for upload delay. */ positionAt: z.number().int().positive().nullish(), }) .refine((v) => v.state === 'stopped' || v.track, { message: 'track is required unless stopped', path: ['track'] }); export type DevicePlaybackInput = z.infer; export interface DeviceDTO { id: string; platform: 'android' | 'ios'; name: string; lastSeenAt: string | null; createdAt: string; }