mytunes-pro 2.0.8__py3-none-any.whl → 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mytunes/app.py +72 -25
- {mytunes_pro-2.0.8.dist-info → mytunes_pro-2.1.0.dist-info}/METADATA +11 -4
- mytunes_pro-2.1.0.dist-info/RECORD +8 -0
- mytunes_pro-2.0.8.dist-info/RECORD +0 -8
- {mytunes_pro-2.0.8.dist-info → mytunes_pro-2.1.0.dist-info}/WHEEL +0 -0
- {mytunes_pro-2.0.8.dist-info → mytunes_pro-2.1.0.dist-info}/entry_points.txt +0 -0
- {mytunes_pro-2.0.8.dist-info → mytunes_pro-2.1.0.dist-info}/licenses/LICENSE +0 -0
- {mytunes_pro-2.0.8.dist-info → mytunes_pro-2.1.0.dist-info}/top_level.txt +0 -0
mytunes/app.py
CHANGED
|
@@ -44,7 +44,21 @@ MPV_SOCKET = "/tmp/mpv_socket"
|
|
|
44
44
|
LOG_FILE = "/tmp/mytunes_mpv.log"
|
|
45
45
|
PID_FILE = "/tmp/mytunes_mpv.pid"
|
|
46
46
|
APP_NAME = "MyTunes Pro"
|
|
47
|
-
APP_VERSION = "2.0
|
|
47
|
+
APP_VERSION = "2.1.0"
|
|
48
|
+
|
|
49
|
+
# Initial Locale Setup for WSL/Windows Multibyte/Emoji Harmony
|
|
50
|
+
try:
|
|
51
|
+
locale.setlocale(locale.LC_ALL, '')
|
|
52
|
+
except:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
# WSL Detection: EQ/IPC robustness logic
|
|
56
|
+
IS_WSL = False
|
|
57
|
+
if hasattr(os, 'uname'):
|
|
58
|
+
try:
|
|
59
|
+
IS_WSL = "microsoft" in os.uname().release.lower()
|
|
60
|
+
except: pass
|
|
61
|
+
|
|
48
62
|
|
|
49
63
|
# === [Strings & Localization] ===
|
|
50
64
|
STRINGS = {
|
|
@@ -305,7 +319,10 @@ class Player:
|
|
|
305
319
|
self.loading = False
|
|
306
320
|
self.loading_ts = 0
|
|
307
321
|
self.socket_fail_count = 0 # Track consecutive IPC failures
|
|
308
|
-
self.socket_ok = True
|
|
322
|
+
self.socket_ok = True # Socket health flag
|
|
323
|
+
self.last_socket_warn = 0 # Rate limit for socket error warnings
|
|
324
|
+
self.socket_retry_ts = 0 # Cool-down for reconnection attempts
|
|
325
|
+
|
|
309
326
|
|
|
310
327
|
# Cleanup pre-existing instance if any
|
|
311
328
|
# self.cleanup_orphaned_mpv() # Moved to play() per user request
|
|
@@ -360,10 +377,11 @@ class Player:
|
|
|
360
377
|
"--idle=yes"
|
|
361
378
|
]
|
|
362
379
|
|
|
363
|
-
# Inject Initial EQ (0ms Latency)
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
380
|
+
# Inject Initial EQ (0ms Latency) - Skip on WSL (causes freezing)
|
|
381
|
+
if not IS_WSL:
|
|
382
|
+
eq_af = self._get_eq_af_string(initial_eq_preset)
|
|
383
|
+
if eq_af:
|
|
384
|
+
cmd.append(f"--af={eq_af}")
|
|
367
385
|
|
|
368
386
|
cmd.append(url)
|
|
369
387
|
|
|
@@ -428,16 +446,29 @@ class Player:
|
|
|
428
446
|
self.send_cmd(["add", "volume", delta])
|
|
429
447
|
|
|
430
448
|
def send_cmd(self, command):
|
|
431
|
-
"""Send raw command list to MPV via JSON IPC."""
|
|
432
|
-
#
|
|
449
|
+
"""Send raw command list to MPV via JSON IPC with resilience."""
|
|
450
|
+
# 1. Fast-Detect Process Death
|
|
451
|
+
if self.current_proc and self.current_proc.poll() is not None:
|
|
452
|
+
self.socket_ok = False
|
|
453
|
+
self.current_proc = None
|
|
454
|
+
return None
|
|
455
|
+
|
|
456
|
+
# 2. Re-connection Cool-down (Throttle blocking connect() calls)
|
|
457
|
+
now = time.time()
|
|
458
|
+
if not self.socket_ok and now < self.socket_retry_ts:
|
|
459
|
+
return None
|
|
460
|
+
|
|
461
|
+
# 3. Pre-check: Skip if socket file doesn't exist (e.g. killed elsewhere)
|
|
433
462
|
if not os.path.exists(MPV_SOCKET):
|
|
434
463
|
self.socket_fail_count += 1
|
|
435
|
-
self.
|
|
464
|
+
if self.socket_fail_count >= 2:
|
|
465
|
+
self.socket_ok = False
|
|
466
|
+
self.socket_retry_ts = now + 1.5 # 1.5s cool-down
|
|
436
467
|
return None
|
|
437
468
|
|
|
438
469
|
try:
|
|
439
470
|
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
440
|
-
client.settimeout(0.5) # Fast timeout (Optimization for Sleep/Wake resilience)
|
|
471
|
+
client.settimeout(0.5) # Fast timeout (Optimization for Sleep/Wake/WSL resilience)
|
|
441
472
|
client.connect(MPV_SOCKET)
|
|
442
473
|
cmd_str = json.dumps({"command": command}) + "\n"
|
|
443
474
|
client.send(cmd_str.encode('utf-8'))
|
|
@@ -451,23 +482,29 @@ class Player:
|
|
|
451
482
|
if b"\n" in chunk: break
|
|
452
483
|
|
|
453
484
|
client.close()
|
|
454
|
-
# Success:
|
|
485
|
+
# Success: Fully Restore health
|
|
455
486
|
self.socket_fail_count = 0
|
|
456
487
|
self.socket_ok = True
|
|
457
488
|
return json.loads(response.decode('utf-8'))
|
|
458
489
|
except:
|
|
459
490
|
self.socket_fail_count += 1
|
|
460
|
-
if self.socket_fail_count >=
|
|
491
|
+
if self.socket_fail_count >= 2:
|
|
461
492
|
self.socket_ok = False
|
|
493
|
+
self.socket_retry_ts = now + 1.5 # 1.5s cool-down
|
|
462
494
|
return None
|
|
463
495
|
|
|
464
496
|
def get_property(self, prop):
|
|
497
|
+
"""Skip IPC if health is bad to prevent TUI freezing."""
|
|
498
|
+
if not self.socket_ok:
|
|
499
|
+
return None
|
|
465
500
|
res = self.send_cmd(["get_property", prop])
|
|
466
501
|
if res and "data" in res:
|
|
467
502
|
return res["data"]
|
|
468
503
|
return None
|
|
469
504
|
|
|
470
505
|
def set_property(self, prop, value):
|
|
506
|
+
if not self.socket_ok:
|
|
507
|
+
return
|
|
471
508
|
self.send_cmd(["set_property", prop, value])
|
|
472
509
|
|
|
473
510
|
def toggle_pause(self):
|
|
@@ -493,6 +530,8 @@ class Player:
|
|
|
493
530
|
|
|
494
531
|
def set_equalizer(self, preset_name):
|
|
495
532
|
"""Apply 10-band equalizer preset using lavfi."""
|
|
533
|
+
if IS_WSL:
|
|
534
|
+
return # Skip EQ on WSL (causes freezing)
|
|
496
535
|
af_str = self._get_eq_af_string(preset_name)
|
|
497
536
|
self.set_property("af", af_str)
|
|
498
537
|
|
|
@@ -639,24 +678,29 @@ class MyTunesApp:
|
|
|
639
678
|
def update_playback_state(self):
|
|
640
679
|
# Poll MPV for state with throttling to reduce CPU/IPC overhead
|
|
641
680
|
try:
|
|
681
|
+
# 0. Health Check: If socket is known-bad, exit immediately to keep TUI responsive
|
|
682
|
+
if not self.player.socket_ok:
|
|
683
|
+
return
|
|
684
|
+
|
|
642
685
|
# 1. Mandatory every loop: Current time (for progress bar)
|
|
643
686
|
t = self.player.get_property("time-pos")
|
|
644
|
-
if t is
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
self.player.loading = False
|
|
648
|
-
|
|
649
|
-
# Update Resume Data (Memory) - Throttle save logic
|
|
650
|
-
if self.current_track and self.playback_duration > 30:
|
|
651
|
-
if self.playback_time / self.playback_duration > 0.99:
|
|
652
|
-
self.dm.set_progress(self.current_track['url'], 0)
|
|
653
|
-
elif self.playback_time > 10:
|
|
654
|
-
self.dm.set_progress(self.current_track['url'], self.playback_time)
|
|
687
|
+
if t is None:
|
|
688
|
+
# If even time-pos fails, bail out immediately to prevent cascaded delay
|
|
689
|
+
return
|
|
655
690
|
|
|
691
|
+
self.playback_time = float(t)
|
|
692
|
+
if self.player.loading and self.playback_time >= 0:
|
|
693
|
+
self.player.loading = False
|
|
694
|
+
|
|
695
|
+
# Update Resume Data (Memory) - Throttle save logic
|
|
696
|
+
if self.current_track and self.playback_duration > 30:
|
|
697
|
+
if self.playback_time / self.playback_duration > 0.99:
|
|
698
|
+
self.dm.set_progress(self.current_track['url'], 0)
|
|
699
|
+
elif self.playback_time > 10:
|
|
700
|
+
self.dm.set_progress(self.current_track['url'], self.playback_time)
|
|
656
701
|
|
|
657
702
|
|
|
658
703
|
# Safety: If loading takes too long (> 8s), force reset to allow error handling/skip
|
|
659
|
-
# Consolidated redundancy checks into a single clean block
|
|
660
704
|
now = time.time()
|
|
661
705
|
if self.player.loading and (now - self.player.loading_ts > 8):
|
|
662
706
|
self.player.loading = False
|
|
@@ -686,6 +730,7 @@ class MyTunesApp:
|
|
|
686
730
|
if time.time() - getattr(self, 'last_save_time', 0) > 10:
|
|
687
731
|
self.dm.save_data()
|
|
688
732
|
self.last_save_time = time.time()
|
|
733
|
+
|
|
689
734
|
|
|
690
735
|
except: pass
|
|
691
736
|
|
|
@@ -1821,8 +1866,10 @@ class MyTunesApp:
|
|
|
1821
1866
|
|
|
1822
1867
|
def check_autoplay(self):
|
|
1823
1868
|
# Auto-play next track from Global Queue
|
|
1824
|
-
|
|
1869
|
+
if not self.running: return
|
|
1825
1870
|
if self.player.loading: return
|
|
1871
|
+
# Logic check: Skip if playback just started or socket is dead to prevent feedback loops
|
|
1872
|
+
if not self.player.socket_ok: return
|
|
1826
1873
|
|
|
1827
1874
|
try:
|
|
1828
1875
|
is_idle = self.player.get_property("idle-active")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mytunes-pro
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: A lightweight, keyboard-centric terminal player for streaming YouTube music.
|
|
5
5
|
Author-email: loxo <loxo5432@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/postgresql-co-kr/mytunes
|
|
@@ -18,9 +18,9 @@ Requires-Dist: yt-dlp
|
|
|
18
18
|
Requires-Dist: pusher
|
|
19
19
|
Dynamic: license-file
|
|
20
20
|
|
|
21
|
-
# 🎵 MyTunes Pro - Professional TUI Edition v2.0
|
|
21
|
+
# 🎵 MyTunes Pro - Professional TUI Edition v2.1.0
|
|
22
22
|
|
|
23
|
-
## 🚀 Terminal-based Media Workflow Experiment v2.0
|
|
23
|
+
## 🚀 Terminal-based Media Workflow Experiment v2.1.0
|
|
24
24
|
|
|
25
25
|
> [!IMPORTANT]
|
|
26
26
|
> **Legal Disclaimer:** This project is a personal, non-commercial research experiment for developer education.
|
|
@@ -216,7 +216,7 @@ Executes immediately without worrying about input language status.
|
|
|
216
216
|
|
|
217
217
|
# 🎵 MyTunes Pro (Experimental Media Tool - KR)
|
|
218
218
|
|
|
219
|
-
## 🚀 터미널 기반 미디어 워크플로우 실험 v2.0
|
|
219
|
+
## 🚀 터미널 기반 미디어 워크플로우 실험 v2.1.0
|
|
220
220
|
|
|
221
221
|
> [!IMPORTANT]
|
|
222
222
|
> **법적 면책 고지:** 본 프로젝트는 개발자 교육 및 연구를 목적으로 하는 개인적, 비상업적 실험입니다.
|
|
@@ -390,6 +390,13 @@ Windows 환경에서 한글 검색이 안 되거나 설치가 어려운 분들
|
|
|
390
390
|
|
|
391
391
|
## 🔄 Changelog
|
|
392
392
|
|
|
393
|
+
### v2.1.0 (2026-02-02)
|
|
394
|
+
- **Zero-Freeze IPC Resilience**: Implemented a "Fast-Fail" mechanism that detects mpv process death within 0.1ms via `poll()`, preventing TUI freezes.
|
|
395
|
+
- **Fail-Early Polling**: Main loop now aborts all remaining IPC property checks immediately if any call fails, maintaining a smooth 5fps even on broken connections.
|
|
396
|
+
- **Connection Throttling**: Added a 1.5-second "cool-down" period for reconnection attempts to minimize blocking time on Windows/WSL environments.
|
|
397
|
+
- **Multibyte Harmony**: Explicitly configured `locale.setlocale` to ensure stable emoji and CJK character rendering across different terminal environments.
|
|
398
|
+
- **Improved Autoplay Stability**: Autoplay logic now skips status checks when the socket is unhealthy to prevent feedback loops.
|
|
399
|
+
|
|
393
400
|
### v2.0.8 (2026-02-02)
|
|
394
401
|
- **Windows/WSL Socket Recovery**: Fixed UI freezing when mpv socket disconnects during window switching.
|
|
395
402
|
- **IPC Resilience**: Added socket pre-check and failure counter to prevent blocking on broken connections.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
mytunes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
mytunes/app.py,sha256=h3HseVdqdM03G5AX1TGghi7rs5FVDxBcZeooXSiyd6U,83870
|
|
3
|
+
mytunes_pro-2.1.0.dist-info/licenses/LICENSE,sha256=lOrP0EIjxcgJia__W3f3PVDZkRd2oRzFkyH2g3LRRCg,1063
|
|
4
|
+
mytunes_pro-2.1.0.dist-info/METADATA,sha256=ieQwUOTyg6pfC2lBY6VHk_GE_-6JgG396xK_8ISh3l4,29755
|
|
5
|
+
mytunes_pro-2.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
6
|
+
mytunes_pro-2.1.0.dist-info/entry_points.txt,sha256=6-MsC13nIgzLvrREaGotc32FgxHx_Iuu1z2qCzJs1_4,65
|
|
7
|
+
mytunes_pro-2.1.0.dist-info/top_level.txt,sha256=KWzdFyNNG_sO7GT83-sN5fYArP4_DL5I8HYIwgazXyY,8
|
|
8
|
+
mytunes_pro-2.1.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
mytunes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
mytunes/app.py,sha256=bD54KYF1Y5_Ic-XoZfEij7tKGd_ZhwaJCpc43pZzgQw,82247
|
|
3
|
-
mytunes_pro-2.0.8.dist-info/licenses/LICENSE,sha256=lOrP0EIjxcgJia__W3f3PVDZkRd2oRzFkyH2g3LRRCg,1063
|
|
4
|
-
mytunes_pro-2.0.8.dist-info/METADATA,sha256=it24t91wwsXbqfyipIADChgHJy5fCHVsNzBZwC1DFWo,28977
|
|
5
|
-
mytunes_pro-2.0.8.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
6
|
-
mytunes_pro-2.0.8.dist-info/entry_points.txt,sha256=6-MsC13nIgzLvrREaGotc32FgxHx_Iuu1z2qCzJs1_4,65
|
|
7
|
-
mytunes_pro-2.0.8.dist-info/top_level.txt,sha256=KWzdFyNNG_sO7GT83-sN5fYArP4_DL5I8HYIwgazXyY,8
|
|
8
|
-
mytunes_pro-2.0.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|