mytunes-pro 2.0.7__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 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.7"
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 = {
@@ -304,6 +318,11 @@ class Player:
304
318
  self.current_proc = None
305
319
  self.loading = False
306
320
  self.loading_ts = 0
321
+ self.socket_fail_count = 0 # Track consecutive IPC failures
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
+
307
326
 
308
327
  # Cleanup pre-existing instance if any
309
328
  # self.cleanup_orphaned_mpv() # Moved to play() per user request
@@ -339,6 +358,9 @@ class Player:
339
358
 
340
359
  # 2. Fallback: Clean up and start fresh (Aggressive)
341
360
  self.cleanup_orphaned_mpv()
361
+ # Reset socket health for fresh start
362
+ self.socket_fail_count = 0
363
+ self.socket_ok = True
342
364
 
343
365
  self.stop()
344
366
  self.loading = True
@@ -355,10 +377,11 @@ class Player:
355
377
  "--idle=yes"
356
378
  ]
357
379
 
358
- # Inject Initial EQ (0ms Latency)
359
- eq_af = self._get_eq_af_string(initial_eq_preset)
360
- if eq_af:
361
- cmd.append(f"--af={eq_af}")
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}")
362
385
 
363
386
  cmd.append(url)
364
387
 
@@ -423,10 +446,29 @@ class Player:
423
446
  self.send_cmd(["add", "volume", delta])
424
447
 
425
448
  def send_cmd(self, command):
426
- """Send raw command list to MPV via JSON IPC."""
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)
462
+ if not os.path.exists(MPV_SOCKET):
463
+ self.socket_fail_count += 1
464
+ if self.socket_fail_count >= 2:
465
+ self.socket_ok = False
466
+ self.socket_retry_ts = now + 1.5 # 1.5s cool-down
467
+ return None
468
+
427
469
  try:
428
470
  client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
429
- client.settimeout(0.5) # Fast timeout (Optimization for Sleep/Wake resilience)
471
+ client.settimeout(0.5) # Fast timeout (Optimization for Sleep/Wake/WSL resilience)
430
472
  client.connect(MPV_SOCKET)
431
473
  cmd_str = json.dumps({"command": command}) + "\n"
432
474
  client.send(cmd_str.encode('utf-8'))
@@ -440,17 +482,29 @@ class Player:
440
482
  if b"\n" in chunk: break
441
483
 
442
484
  client.close()
485
+ # Success: Fully Restore health
486
+ self.socket_fail_count = 0
487
+ self.socket_ok = True
443
488
  return json.loads(response.decode('utf-8'))
444
489
  except:
490
+ self.socket_fail_count += 1
491
+ if self.socket_fail_count >= 2:
492
+ self.socket_ok = False
493
+ self.socket_retry_ts = now + 1.5 # 1.5s cool-down
445
494
  return None
446
495
 
447
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
448
500
  res = self.send_cmd(["get_property", prop])
449
501
  if res and "data" in res:
450
502
  return res["data"]
451
503
  return None
452
504
 
453
505
  def set_property(self, prop, value):
506
+ if not self.socket_ok:
507
+ return
454
508
  self.send_cmd(["set_property", prop, value])
455
509
 
456
510
  def toggle_pause(self):
@@ -476,6 +530,8 @@ class Player:
476
530
 
477
531
  def set_equalizer(self, preset_name):
478
532
  """Apply 10-band equalizer preset using lavfi."""
533
+ if IS_WSL:
534
+ return # Skip EQ on WSL (causes freezing)
479
535
  af_str = self._get_eq_af_string(preset_name)
480
536
  self.set_property("af", af_str)
481
537
 
@@ -622,24 +678,29 @@ class MyTunesApp:
622
678
  def update_playback_state(self):
623
679
  # Poll MPV for state with throttling to reduce CPU/IPC overhead
624
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
+
625
685
  # 1. Mandatory every loop: Current time (for progress bar)
626
686
  t = self.player.get_property("time-pos")
627
- if t is not None:
628
- self.playback_time = float(t)
629
- if self.player.loading and self.playback_time >= 0:
630
- self.player.loading = False
631
-
632
- # Update Resume Data (Memory) - Throttle save logic
633
- if self.current_track and self.playback_duration > 30:
634
- if self.playback_time / self.playback_duration > 0.99:
635
- self.dm.set_progress(self.current_track['url'], 0)
636
- elif self.playback_time > 10:
637
- 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
638
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)
639
701
 
640
702
 
641
703
  # Safety: If loading takes too long (> 8s), force reset to allow error handling/skip
642
- # Consolidated redundancy checks into a single clean block
643
704
  now = time.time()
644
705
  if self.player.loading and (now - self.player.loading_ts > 8):
645
706
  self.player.loading = False
@@ -669,6 +730,7 @@ class MyTunesApp:
669
730
  if time.time() - getattr(self, 'last_save_time', 0) > 10:
670
731
  self.dm.save_data()
671
732
  self.last_save_time = time.time()
733
+
672
734
 
673
735
  except: pass
674
736
 
@@ -1804,8 +1866,10 @@ class MyTunesApp:
1804
1866
 
1805
1867
  def check_autoplay(self):
1806
1868
  # Auto-play next track from Global Queue
1807
- # Guard: Don't autoplay if we are currently loading a track
1869
+ if not self.running: return
1808
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
1809
1873
 
1810
1874
  try:
1811
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.7
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.7
21
+ # 🎵 MyTunes Pro - Professional TUI Edition v2.1.0
22
22
 
23
- ## 🚀 Terminal-based Media Workflow Experiment v2.0.7
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.7
219
+ ## 🚀 터미널 기반 미디어 워크플로우 실험 v2.1.0
220
220
 
221
221
  > [!IMPORTANT]
222
222
  > **법적 면책 고지:** 본 프로젝트는 개발자 교육 및 연구를 목적으로 하는 개인적, 비상업적 실험입니다.
@@ -390,6 +390,18 @@ 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
+
400
+ ### v2.0.8 (2026-02-02)
401
+ - **Windows/WSL Socket Recovery**: Fixed UI freezing when mpv socket disconnects during window switching.
402
+ - **IPC Resilience**: Added socket pre-check and failure counter to prevent blocking on broken connections.
403
+ - **Automatic Recovery**: New playback automatically restarts mpv if socket is unhealthy.
404
+
393
405
  ### v2.0.7 (2026-02-02)
394
406
  - **Performance Optimization**: Improved keyboard responsiveness on Windows/WSL by implementing EQ detection caching.
395
407
  - **Data Management**: Limited resume data to 500 entries with automatic FIFO cleanup to prevent JSON bloat.
@@ -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=TMTcxZyBga5IYkJQPRiDKpZ6nEn5OCd62jySxTfmMd8,81539
3
- mytunes_pro-2.0.7.dist-info/licenses/LICENSE,sha256=lOrP0EIjxcgJia__W3f3PVDZkRd2oRzFkyH2g3LRRCg,1063
4
- mytunes_pro-2.0.7.dist-info/METADATA,sha256=zXK3JFIv8PpoLquQF0Xh17ebRmMCSMSDCJP0mUHEtvk,28648
5
- mytunes_pro-2.0.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
6
- mytunes_pro-2.0.7.dist-info/entry_points.txt,sha256=6-MsC13nIgzLvrREaGotc32FgxHx_Iuu1z2qCzJs1_4,65
7
- mytunes_pro-2.0.7.dist-info/top_level.txt,sha256=KWzdFyNNG_sO7GT83-sN5fYArP4_DL5I8HYIwgazXyY,8
8
- mytunes_pro-2.0.7.dist-info/RECORD,,