feat(app-phone): полноценный UI телефона — каталог Jellyfin, детали, пульт очков, реальное скачивание с S3
- 4 экрана по макету sketches/phone-ui: Каталог (табы Фильмы/Сериалы/Папки, поиск, бейджи зеркала), Детали (озвучки, статус конвертации, чипы скачивания), Очки (пульт, статус), Чат-заглушка - S3 SigV4 presign (S3Presigner): mirror-client read-only креды; watch() и DownloadManager подписывают URL (бакет приватный) - DownloadManager: реальное скачивание video+audio с прогрессом, readTimeout=0 (длинные файлы) - PhoneActions.watch: параметр presign (app-host работает без изменений) - TESTING.md: блок телефонных кейсов TP-1..6
This commit is contained in:
@@ -4,6 +4,9 @@ import android.app.Application
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import pw.binom.viewmate.core.media.JellyfinClient
|
||||
import pw.binom.viewmate.core.media.MirrorClient
|
||||
@@ -12,15 +15,27 @@ import pw.binom.viewmate.core.phone.PhoneState
|
||||
|
||||
/**
|
||||
* Application телефона: создаёт клиентов (Jellyfin/mirror), PhoneState,
|
||||
* WS-сервер (GlassesServer) и PhoneActions. Синглтон-доступ через companion.
|
||||
* WS-сервер (GlassesServer), DownloadManager и PhoneActions. Синглтон-доступ через companion.
|
||||
*/
|
||||
class PhoneApp : Application() {
|
||||
|
||||
lateinit var state: PhoneState
|
||||
private set
|
||||
lateinit var server: GlassesServer
|
||||
private set
|
||||
lateinit var actions: PhoneActions
|
||||
val state: PhoneState = PhoneState()
|
||||
|
||||
/** WS-сервер для очков. */
|
||||
val server: GlassesServer = GlassesServer(port = PhoneConfig.SERVER_PORT, state = state)
|
||||
|
||||
/** HTTP-клиенты бэкендов (Jellyfin-каталог, media-mirror). */
|
||||
val jellyfin: JellyfinClient = JellyfinClient(PhoneConfig.JELLYFIN_URL, PhoneConfig.JELLYFIN_API_KEY)
|
||||
val mirror: MirrorClient = MirrorClient(PhoneConfig.MIRROR_URL, PhoneConfig.MIRROR_API_KEY)
|
||||
|
||||
private val _userId = MutableStateFlow("")
|
||||
/** Первый пользователь Jellyfin (подтягивается один раз при старте). */
|
||||
val userId: StateFlow<String> = _userId.asStateFlow()
|
||||
|
||||
/** Реальное скачивание зеркал на телефон (с прогрессом). */
|
||||
val downloadManager: DownloadManager by lazy { DownloadManager(this, mirror) }
|
||||
|
||||
var actions: PhoneActions? = null
|
||||
private set
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
@@ -29,18 +44,13 @@ class PhoneApp : Application() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
|
||||
state = PhoneState()
|
||||
server = GlassesServer(port = PhoneConfig.SERVER_PORT, state = state)
|
||||
|
||||
val jellyfin = JellyfinClient(PhoneConfig.JELLYFIN_URL, PhoneConfig.JELLYFIN_API_KEY)
|
||||
val mirror = MirrorClient(PhoneConfig.MIRROR_URL, PhoneConfig.MIRROR_API_KEY)
|
||||
|
||||
server.start()
|
||||
log("server", "WS-сервер запущен на ${PhoneConfig.SERVER_PORT}")
|
||||
|
||||
// первый пользователь Jellyfin — один раз при старте
|
||||
scope.launch {
|
||||
val userId = runCatching { jellyfin.users().firstOrNull()?.Id }.getOrNull()
|
||||
?: ""
|
||||
_userId.value = userId
|
||||
if (userId.isBlank()) log("app", "не удалось получить пользователя Jellyfin")
|
||||
actions = PhoneActions(
|
||||
jellyfin = jellyfin,
|
||||
|
||||
Reference in New Issue
Block a user