Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
41227f0
fix(recorder): fix all bugs in recorder.py
dpnkrpl Apr 25, 2026
cf442d1
fix(utilities): replace sys.exit(1) with return None,None in search r…
dpnkrpl Apr 25, 2026
f205c37
fix(utilities): add player None guards to volume commands and remove …
dpnkrpl Apr 25, 2026
d88de8b
fix(utilities): guard player None in 'next' handler and sync AutoFetc…
dpnkrpl Apr 25, 2026
b78924c
fix(utilities): replace bare except in get_key() Windows path with sp…
dpnkrpl Apr 25, 2026
2bbce52
fix(utilities): fix mutable default argument in get_display() inner f…
dpnkrpl Apr 25, 2026
15a6e92
fix(utilities): prevent nested KeyError in handle_station_selection_menu
dpnkrpl Apr 25, 2026
54ac82e
fix(utilities): fix resource leak and bare except in background mode …
dpnkrpl Apr 25, 2026
fc560bd
refactor: improve application startup performance by deferring heavy …
dpnkrpl Apr 25, 2026
cd62e2b
feat: add interactive update notification modal and update installati…
dpnkrpl Apr 25, 2026
78e65f3
refactor: optimize ffplay process management using native system call…
dpnkrpl Apr 25, 2026
85c7051
feat: implement lazy-loaded country mapping and replace sys.exit call…
dpnkrpl Apr 25, 2026
766205d
refactor: optimize alias lookup with hash map and update file handlin…
dpnkrpl Apr 25, 2026
fdaab34
fix: improve history robustness with JSON error handling and atomic f…
dpnkrpl Apr 25, 2026
cc561b9
refactor: replace sys.exit calls with early returns and simplify hist…
dpnkrpl Apr 25, 2026
aabeee0
feat: enhance audio recording with codec mapping, metadata injection,…
dpnkrpl Apr 25, 2026
812c4a7
docs: add changelog entries for version 4.1.0 release
dpnkrpl Apr 25, 2026
23de67e
Merge branch 'main' into bugfix
dpnkrpl Sep 29, 2026
60aa602
refactor: optimize ffplay process management using native system call…
dpnkrpl Apr 25, 2026
a410762
docs: add changelog entries for version 4.1.0 release
dpnkrpl Apr 25, 2026
d423094
feat: add interactive recording library for managing, playing, and sh…
dpnkrpl Sep 30, 2026
c957334
fix: improve ffplay error handling and add unit tests #154
dpnkrpl Sep 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
## 4.1.0

1. Significant improvements on app loading speed :zap:
2. Recording quality improved 🎶
3. Added recording library command `rl` which allows you to play, delete, rename and shazam your recordings
4. Faster station lookup
5. Fix playback issues with ffplay
6. Optimize interaction with external player, improving play/pause time
7. Improved fuzzy find logic 🔍
8. Stability improvements for windows
9. Critical bug fixes 🐛
10. Minor UI fixes

## 4.0.3

1. Fix broken installation on python >=3.13
2. NOTE: shazam does not work anymore on python version >= 3.13


## 4.0.2

1. More information on the shazam identified track
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ Radioactive features a modern, **Vim-style command bar** at the bottom of the sc
| `i` | `info` | Show station details |
| `r` | `record` | Start/Stop recording |
| `rf` | `recordfile` | Record with a specific filename |
| `rl` | `recordings` | Recording library (select, rename, delete, Shazam) |
| `f` | `fav` | Add current station to favorites |
| `l` | `list` | Open favorite station selection menu |
| `s` | `search` | Search for a new station online |
Expand Down
118 changes: 73 additions & 45 deletions radioactive/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,11 @@
import os
import signal
import sys
import threading
from time import sleep

import psutil
from zenlog import log

from radioactive.alias import Alias
from radioactive.app import App
from radioactive.ffplay import Ffplay, kill_background_ffplays
from radioactive.handler import Handler
from radioactive.help import show_help
from radioactive.history import History
from radioactive.last_station import Last_station
from radioactive.parser import parse_options
from radioactive.paths import get_pid_path
from radioactive.utilities import (
check_sort_by_parameter,
handle_add_station,
handle_add_to_favorite,
handle_current_play_panel,
handle_direct_play,
handle_favorite_table,
handle_history_table,
handle_listen_keypress,
handle_play_last_station,
handle_play_random_station,
handle_record,
handle_save_last_station,
handle_save_to_history,
handle_search_stations,
handle_station_selection_menu,
handle_station_uuid_play,
handle_update_screen,
handle_user_choice_from_search_result,
handle_welcome_screen,
)

# globally needed as signal handler needs it
# to terminate main() properly
# Globally needed as signal handler needs them.
# These are assigned inside main() or final_step().
ffplay = None
player = None

Expand All @@ -49,7 +16,18 @@ def final_step(options, last_station, alias, handler, history, station_list=None
global ffplay # always needed
global player

# check target URL
from zenlog import log

from radioactive.ffplay import Ffplay
from radioactive.utilities import (
handle_add_to_favorite,
handle_current_play_panel,
handle_listen_keypress,
handle_record,
handle_save_last_station,
handle_save_to_history,
)

target_url = (options.get("target_url") or "").strip()
if target_url == "":
log.info("Type 's' to search for a station or '?' for help")
Expand Down Expand Up @@ -123,28 +101,62 @@ def final_step(options, last_station, alias, handler, history, station_list=None


def main():
from zenlog import log

log.level("info")

app = App()
from radioactive.app import App
from radioactive.parser import parse_options

app = App()
options = parse_options()

VERSION = app.get_version()

# --- Fast early exits: avoid all heavy imports ---
if options["version"]:
log.info("RADIO-ACTIVE : version {}".format(VERSION))
sys.exit(0)

handler = Handler()
alias = Alias()
alias.generate_map()
last_station = Last_station()
history = History()
from radioactive.help import show_help

if options["show_help_table"]:
show_help()
sys.exit(0)

# --- Deferred heavy imports (saves ~260ms when not needed) ---
import psutil

from radioactive.alias import Alias
from radioactive.ffplay import kill_background_ffplays
from radioactive.handler import Handler
from radioactive.history import History
from radioactive.last_station import Last_station
from radioactive.paths import get_pid_path
from radioactive.utilities import (
check_sort_by_parameter,
handle_add_station,
handle_add_to_favorite,
handle_current_play_panel,
handle_direct_play,
handle_favorite_table,
handle_history_table,
handle_play_last_station,
handle_play_random_station,
handle_record,
handle_search_stations,
handle_station_selection_menu,
handle_station_uuid_play,
handle_update_modal,
handle_update_screen,
handle_user_choice_from_search_result,
handle_welcome_screen,
)

alias = Alias()
alias.generate_map()
last_station = Last_station()
history = History()

if options["flush_fav_list"]:
sys.exit(alias.flush())

Expand Down Expand Up @@ -226,6 +238,11 @@ def cleanup():

handle_welcome_screen()

# Run update check in background so it never blocks the interactive prompt.
# The modal will be shown if an update is detected.
_update_thread = threading.Thread(target=app.is_update_available, daemon=True)
_update_thread.start()

# ------------------ SCHEDULED RECORDING MODE ------------------ #
from radioactive.feature_flags import RECORDING_FEATURE

Expand Down Expand Up @@ -314,6 +331,7 @@ def cleanup():
log.info(f"Target URL: {options['target_url']}")
else:
# We need to use handler to validate UUID
handler = Handler()
options["curr_station_name"], options["target_url"] = (
handle_station_uuid_play(handler, options["search_station_uuid"])
)
Expand Down Expand Up @@ -400,7 +418,15 @@ def cleanup():

options["sort_by"] = check_sort_by_parameter(options["sort_by"])

handle_update_screen(app)
# Construct Handler as late as possible — right before we actually need the API.
# This avoids the ~50ms init cost for flag-only paths (--list, --kill, etc.)
handler = Handler()

# Update check is already running in the background thread started above;
# wait briefly (non-blocking) so it can print before we proceed to prompts
_update_thread.join(timeout=0.4)
if app.update_available:
handle_update_modal(app)

# ----------- country ----------- #
if options["discover_country_code"]:
Expand Down Expand Up @@ -560,6 +586,8 @@ def cleanup():


def signal_handler(sig, frame):
from zenlog import log

log.debug("You pressed Ctrl+C!")
log.debug("Stopping the radio")
if ffplay and ffplay.is_playing:
Expand Down
Loading