a
    &Ci(R                     @   s   d Z ddlZddlZddlZddlZddlZddlZddlZddlm	Z	m
Z
mZmZ ddlmZ G dd dZdddd	Zed
kre  dS )u  
PLAYER - звуковой плеер для воспроизведения аудиофайлов на Raspberry Pi и других системах.
- Поддерживаемые форматы: WAV, MP3, OGG, FLAC, M4A, AAC
- Управление воспроизведением: воспроизведение, пауза, остановка, управление громкостью
- Очередь воспроизведения: возможность добавления нескольких файлов
- Поддержка различных аудиоустройств через ALSA
- Потокобезопасная реализация

	Назначение: 
		1. Воспроизведение звуковых файлов
		2. Управление очередью воспроизведения
		3. Контроль громкости и состояния воспроизведения
	Возможности:
		1. Использование как класса, используемого приложением
		2. Использование как самостоятельного приложения
	Синтаксис:
		player.py [--uid] [--device] [-f file] [-v volume] [-a action] [-o output]
		где:
			--uid       	-   Уникальный идентификатор. 
								Параметр не обязательный. 
								Предназначен для идентификации запроса.
			--device		-   Аудиоустройство. 
								Параметр не обязательный.
								По умолчанию: 'default'
			-f file			- 	Звуковой файл для воспроизведения. 
								Параметр не обязательный.
								Может быть указан несколько раз для добавления нескольких файлов.
			-v volume		- 	Уровень громкости (0-100). 
								Параметр не обязательный.
			-a action		- 	Действие. 
								Параметр не обязательный.
								Допускается следующий формат:
									- play - начать воспроизведение
									- stop - остановить воспроизведение
									- pause - поставить на паузу
									- resume - продолжить воспроизведение
									- status - получить статус
									- shutdown - завершить работу плеера
			-o output		- 	Тип результата. 
								Параметр не обязательный.
								Допускается	следующий формат:
									- параметр не указан - тихий режим, без вывода результата
									- text - тип text
									- json - тип json	

	Ответ в формате:
	json 
		{
			status: код ответа от сервиса (например 200 - ок|400 - не коррекнтый запрос|520 - неизвестная ошибка| и т.д.)
			message: описание ответа
			request: {
				uid: уникальный идентификатор
				device: аудиоустройство
				file: список файлов для воспроизведения
				volume: уровень громкости
				action: действие
				output: тип результата
			}
			response: {
				state: состояние плеера
				volume: текущая громкость
				current_file: текущий файл
				queue_size: размер очереди
				queue_files: файлы в очереди
				device: аудиоустройство
				thread_alive: статус потока
				idle_timeout: таймаут простоя
			}                                                    
	text
		status<код ответа> - <описание ответа>
		uid<уникальный идентификатор> device<аудиоустройство> file<список файлов> volume<уровень громкости> action<действие> output<тип результата>
		state<состояние плеера>
		volume<текущая громкость>
		current_file<текущий файл>
		queue_size<размер очереди>
		device<аудиоустройство>
		thread_alive<статус потока>
		idle_timeout<таймаут простоя>
		
	Модуль написан в 2025 году.
    N)ListOptionalDictAny)Pathc                   @   s@  e Zd ZdZdZdZdZdZdZdZ	dZ
efed	d
ddZd	dddZd	dddZd	dddZed	dddZed	dddZee d	dddZd	dddZd	ddd Zd	dd!d"Zd	dd#d$Zd	dd%d&Zeeef dd'd(Zee dd)d*Zd2ee e d+d,d-Z!ee dd.d/Z"d	dd0d1Z#d	S )3PLAYERz0.1defaultP   g      @stoppedZplayingZpausedZidleN)devicereturnc                 C   sf   || _ | j| _| j| _d | _t | _t	
 | _t	 | _t	 | _t	 | _d | _d| _|   d S )Nr   )r   DEFAULT_VOLUMEcurrent_volume
STATE_IDLEstatecurrent_filequeueQueue
play_queue	threadingRLocklockEvent_stop_event_pause_event_shutdown_event_playback_thread_last_activity_time_setup_audio)selfr    r    -/var/aqua_controller/devices/player/player.py__init__m   s    




zPLAYER.__init__r   c              
   C   sH   z|  | j W n2 tyB } ztd| W Y d }~n
d }~0 0 d S )Nu:   Ошибка настройки аудиосистемы: )
set_volumer   	ExceptionIOError)r   er    r    r!   r      s    zPLAYER._setup_audioc                 C   sz   | j ` | jd u s| j sX| j  | j  tj| jddd| _| j	  t

 | _W d    n1 sl0    Y  d S )NTZSoundPlayback)targetdaemonname)r   r   is_aliver   clearr   r   Thread_playback_workerstarttimer   r   r    r    r!   _ensure_playback_thread   s    



zPLAYER._ensure_playback_threadc                 C   s  d }d }| j  sʐzp| j r|rp| d u rp|  z|jdd W n$ tjyn   |  |  Y n0 d }| j	
 sz| j	  | j	  W qt tjy   Y qY qt0 qt| j | j| _d | _W d    n1 s0    Y  W q| j rh|r| d u r|tjj | j& | j| jkr8| j| _W d    n1 sN0    Y  td W qn"|r| d u r|tjj z$| j	jdd}d }t | _| j | j| _|| _W d    n1 s0    Y  zzj| drt dd| j!|g}nBzt dd	d
| j!|g}W n& t"yN   t ddd|g}Y n0 | d u r| j s| j  rzq| j r|tjj | j r| j s| j  std q| j s| j  s|tjj td qP|rN| d u rN|  z|jdd W n& tjyL   |  |  Y n0 W n: t#y } z t$d| d| W Y d }~n
d }~0 0 W | j	  d }n| j	  d }0 W n tjy   t }|d u r|}|| | j%kr&| j | j&| _d | _W d    n1 s0    Y  Y W q| j: | j| jkrX| j| jkrX| j&| _d | _W d    n1 sn0    Y  Y W qY n0 W q t#y } z&t'd|  W Y d }~qW Y d }~qd }~0 0 q| j2 | j| jkr| j&| _d | _d | _(W d    n1 s
0    Y  d S )N   timeoutg?g      ?.wavZaplay-DZmpg123z-q-aZ	omxplayer-olocalu7   Ошибка воспроизведения файла : uL   Ошибка в рабочем потоке воспроизведения: ))r   is_setr   poll	terminatewait
subprocessTimeoutExpiredkillr   empty
get_nowait	task_doner   Emptyr   STATE_STOPPEDr   r   r   send_signalsignalSIGSTOPSTATE_PAUSEDr0   sleepSIGCONTgetr   STATE_PLAYINGlowerendswithPopenr   FileNotFoundErrorr%   r&   IDLE_TIMEOUTr   printr   )r   current_processZidle_start_time	file_pathr'   current_timer    r    r!   r.      s    


$(

&

$,



&&$zPLAYER._playback_worker)volumer   c                 C   s   | j  zhd|  krdks(n tdtjdd| jdd| dgd	d	d
}|jdkr^|| _ntd|j W n2 t	y } ztd| W Y d }~n
d }~0 0 W d    n1 s0    Y  d S )Nr   d   uD   Громкость должна быть в диапазоне 0-100Zamixerr7   ZssetZPCM%T)capture_outputtextu4   Ошибка установки громкости: )
r   
ValueErrorr@   runr   
returncoder   r&   stderrr%   )r   rY   resultr'   r    r    r!   r$   +  s    
zPLAYER.set_volume)rW   r   c                 C   s   | j  tj|s"td| | |s:td| | j| | j	
 rZ| j	  |   | j| j| jfv r|| j| _W d    n1 s0    Y  d S )Nu   Файл не найден: u:   Неподдерживаемый формат файла: )r   ospathexistsrS   _is_supported_formatr^   r   putr   r<   r,   r2   r   r   rG   rO   )r   rW   r    r    r!   play>  s    


zPLAYER.play)
file_pathsr   c                 C   s>   | j $ |D ]}| | qW d    n1 s00    Y  d S N)r   rh   )r   ri   rW   r    r    r!   play_multipleV  s    zPLAYER.play_multiplec                 C   s`   | j F | j  | j| _d | _| jr>| j r>| jjdd W d    n1 sR0    Y  d S )Ng       @r4   )	r   r   setrG   r   r   r   r+   joinr1   r    r    r!   stop\  s    
zPLAYER.stopc                 C   sH   | j . | j| jkr&| j  | j| _W d    n1 s:0    Y  d S rj   )r   r   rO   r   rl   rK   r1   r    r    r!   pauseg  s    
zPLAYER.pausec                 C   sH   | j . | j| jkr&| j  | j| _W d    n1 s:0    Y  d S rj   )r   r   rK   r   r,   rO   r1   r    r    r!   resumen  s    
zPLAYER.resumec              	   C   sh   | j N | j sFz| j  | j  W q tjyB   Y qFY q0 qW d    n1 sZ0    Y  d S rj   )r   r   rC   rD   rE   r   rF   r1   r    r    r!   clear_queueu  s    

zPLAYER.clear_queuec                 C   sj   | j P | j  | j  | jr:| j r:| jjdd | j| _d | _	W d    n1 s\0    Y  d S )Ng      @r4   )
r   r   rl   r   r   r+   rm   rG   r   r   r1   r    r    r!   shutdown  s    

zPLAYER.shutdownc              
   C   s|   | j b | j }| j}| j| j|||dkr8t| jjng | j| j	d uoP| j	
 | jdW  d    S 1 sn0    Y  d S )Nr   )r   rY   r   
queue_sizeZqueue_filesr   Zthread_aliveZidle_timeout)r   r   qsizer   r   r   listr   r   r   r+   rT   )r   rs   r   r    r    r!   
get_status  s    
zPLAYER.get_statusc                 C   s6   | j  t| jjW  d    S 1 s(0    Y  d S rj   )r   ru   r   r   r1   r    r    r!   	get_queue  s    zPLAYER.get_queue)r5   r   c              
   C   sD   z| j  W S  ty> } ztd| W Y d }~n
d }~0 0 d S )NuS   Ошибка ожидания завершения воспроизведения: )r   rm   r%   r&   )r   r5   r'   r    r    r!   wait_until_finished  s    zPLAYER.wait_until_finishedc                 C   s   h d}t |j }||v S )N>   z.aacz.mp3r6   z.m4az.flacz.ogg)r   suffixrP   )r   rW   Zsupported_extensionsZfile_extr    r    r!   rf     s    zPLAYER._is_supported_formatc                 C   s   |    d S rj   )rr   r1   r    r    r!   __del__  s    zPLAYER.__del__)N)$__name__
__module____qualname__VERSIONZDEFAULT_DEVICEr   rT   rG   rO   rK   r   strr"   r   r2   r.   intr$   rh   r   rk   rn   ro   rp   rq   rr   r   r   rv   rw   r   floatboolrx   rf   rz   r    r    r    r!   r   `   s4    
r   r#   c            
   
   C   s  d} d}i }zt jdd}|jdtdd dd |jd	tdd
dd |jddtdddd |jddtddd |jddtdg dd dd |jddtdddgd dd | }W n2 ty } zd} d | }W Y d }~n
d }~0 0 d }| dkr>zt|jd!}W n< ty< } z"d"} d#|j d$| }W Y d }~n
d }~0 0 | dkrF|d urFz|j	d url|
|j	 |jd%kr|jr||j td& nn|jd'kr|  nX|jd(kr|  nB|jd)kr|  n,|jd*kr|  n|jd+kr| }n W n< tyD } z"d,} d-|j d.| }W Y d }~n
d }~0 0 zJ|jd ur|j dkrd/|  d0| d1}||jd urd2|j d3nd47 }||jd urd5|j d3nd47 }||jd urd6|j d3nd47 }||j	d urd7|j	 d3nd47 }||jd urd8|j d3nd47 }||jd ur>d9|j d3nd47 }|d:7 }|jd+kr| D ]\}}	|| d;|	 d17 }q`|d:7 }t| |jd ur|j dkri }| |d+< t||d<< i |d=< |jd ur|j|d= d>< |jd ur |j|d= d?< |jd ur|j|d= d@< |j	d ur4|j	|d= dA< |jd urN|j|d= dB< |jd urh|j|d= dC< |jd+kr|||dD< ttj|ddEdF W nV ty } z<dG} dHt|j dI| }td/|  d0| dJ W Y d }~n
d }~0 0 d S )KN   okuA   Управление воспроизведением звука.)descriptionz--uidFu>   Уникальный идентификатор запроса)typerequiredr   helpz--devicer   u%   Тип аудиоустройстваz-fz--fileappendu?   Звуковой файл для воспроизведения)r   actionr   r   z-vz--volumeu+   Уровень громкости (0 - 100))r   r   r   r8   z--action)rh   rn   ro   rp   statusrr   u   Тип действия)r   r   choicesr   r   r9   z--outputr]   jsonu+   Формат вывода: text или jsoni  uA   Ошибка обработки командной строки: )r   i  u   Аудиоустройство u    недоступно: rh   g      ?rn   ro   rp   rr   r   i  u3   Ошибка выполнения действия r;   zstatus<z> - <z>
zuid<z>  zdevice<zfile<zvolume<zaction<zoutput<
<messagerequestuidr   filerY   r   outputresponser3   )Zensure_asciiindenti  u4   Ошибка формирования ответа: z => >)argparseArgumentParseradd_argumentr   r   
parse_argsr%   r   r   rY   r$   r   r   rk   r0   rL   rn   ro   rp   rr   rv   r   rP   r   itemsrU   r   dumpsr   r{   )
_statusZ_status_messageZ_resultparserargsr'   Z	my_PLAYERZ	_responsekeyvaluer    r    r!   main  s     
(




(""""""r   __main__)__doc__r   r   r@   r0   rc   r   r   typingr   r   r   r   pathlibr   r   r   r{   r    r    r    r!   <module>   s   R  Ui