From 9da689782accf1c60d3d34505d8e16c2a5092ce0 Mon Sep 17 00:00:00 2001 From: Maksim Totmin Date: Sun, 31 May 2026 21:45:49 +0700 Subject: [PATCH] fix: clamp default audio bitrate to 160 kbps for iPod Nano 7 compatibility --- src/audio_converter.py | 11 +++++++---- src/cli.py | 12 ++++++------ src/config_loader.py | 2 +- src/main.py | 10 +++++----- src/youtube_downloader.py | 4 ++-- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/audio_converter.py b/src/audio_converter.py index 2e373ee..8cad08e 100644 --- a/src/audio_converter.py +++ b/src/audio_converter.py @@ -43,7 +43,7 @@ class AudioConverter: track_info: TrackInfo, input_file: str, format: str = "m4a", - audio_bitrate: int = 256, + audio_bitrate: int = 160, overwrite: bool = False) -> str: """ Convert audio file to iPod-compatible format @@ -90,10 +90,13 @@ class AudioConverter: logger.warning(f"Existing file is too small ({file_size} bytes), re-converting") os.remove(output_file) - # Build ffmpeg command — iPod Nano 7 is strict about AAC parameters: + # iPod Nano 7 is strict about AAC parameters: # - AAC-LC profile only (HE-AAC causes playback issues/crashes) # - 44100 Hz sample rate (firmware expects this exactly) - # - Max 160 kbps for stereo (higher bitrates may truncate playback) + # - Max 160 kbps for stereo (higher bitrates cause clicks/truncation) + if audio_bitrate > 160: + logger.warning("Bitrate %d kbps exceeds iPod Nano 7 max (160 kbps), clamping to 160", audio_bitrate) + audio_bitrate = 160 codec = "aac" if format == "m4a" else "libmp3lame" cmd = [ "ffmpeg", "-y", "-i", input_file, @@ -190,7 +193,7 @@ class AudioConverter: output_file: Optional[str] = None, resolution: str = "640x480", video_bitrate: int = 1500, - audio_bitrate: int = 256, + audio_bitrate: int = 160, overwrite: bool = False) -> str: """ Convert video file to iPod-compatible format diff --git a/src/cli.py b/src/cli.py index 800d851..3fe1d26 100644 --- a/src/cli.py +++ b/src/cli.py @@ -74,8 +74,8 @@ class YouTubeToIPodCLI: "--quality", "-q", help="Audio quality in kbps", type=int, - choices=[128, 192, 256, 320], - default=256 + choices=[96, 112, 128, 160], + default=160 ) # Video support @@ -128,7 +128,7 @@ class YouTubeToIPodCLI: return parser.parse_args() - def download(self, url: str, format: str = "m4a", quality: int = 256, video: bool = False) -> List[TrackInfo]: + def download(self, url: str, format: str = "m4a", quality: int = 160, video: bool = False) -> List[TrackInfo]: """ Download audio/video from YouTube @@ -156,9 +156,9 @@ class YouTubeToIPodCLI: tracks: List[TrackInfo], output_dir: str, format: str = "m4a", - quality: int = 256, - video: bool = False, - resolution: str = "640x480") -> List[Tuple[TrackInfo, str]]: + quality: int = 160, + video: bool = False, + resolution: str = "640x480") -> List[Tuple[TrackInfo, str]]: """ Convert downloaded tracks to iPod-compatible format diff --git a/src/config_loader.py b/src/config_loader.py index 96075cd..d4e51b9 100644 --- a/src/config_loader.py +++ b/src/config_loader.py @@ -97,7 +97,7 @@ class ConfigLoader: self.config.set('General', 'output_dir', os.path.join(os.path.expanduser("~"), "Music", "iPod")) self.config.set('General', 'format', 'm4a') - self.config.set('General', 'quality', '256') + self.config.set('General', 'quality', '160') self.config.set('General', 'clean_temp', 'true') # Video section diff --git a/src/main.py b/src/main.py index 019e057..49243f7 100644 --- a/src/main.py +++ b/src/main.py @@ -69,7 +69,7 @@ class WorkerThread(QThread): url = self.kwargs.get("url") output_dir = self.kwargs.get("output_dir", "downloads") fmt = self.kwargs.get("format", "m4a") - quality = self.kwargs.get("quality", 256) + quality = self.kwargs.get("quality", 160) self.progress_signal.emit(0, f"Initializing download from {url}") @@ -93,7 +93,7 @@ class WorkerThread(QThread): local_files = self.kwargs.get("local_files", []) output_dir = self.kwargs.get("output_dir", "converted") fmt = self.kwargs.get("format", "m4a") - quality = self.kwargs.get("quality", 256) + quality = self.kwargs.get("quality", 160) valid_tracks = [ t for t in tracks @@ -314,9 +314,9 @@ class MainWindow(QMainWindow): self.format_combo.addItems(["m4a", "mp3"]) quality_label = QLabel("Quality (kbps):") self.quality_spin = QSpinBox() - self.quality_spin.setRange(128, 320) - self.quality_spin.setValue(256) - self.quality_spin.setSingleStep(32) + self.quality_spin.setRange(96, 160) + self.quality_spin.setValue(160) + self.quality_spin.setSingleStep(16) format_layout.addWidget(format_label) format_layout.addWidget(self.format_combo) format_layout.addWidget(quality_label) diff --git a/src/youtube_downloader.py b/src/youtube_downloader.py index 4c16ba8..21df87f 100644 --- a/src/youtube_downloader.py +++ b/src/youtube_downloader.py @@ -215,7 +215,7 @@ class YouTubeDownloader: logger.error(f"Error extracting playlist info: {e}") return [] - def download_audio(self, track_info: TrackInfo, format: str = "m4a", quality: int = 256) -> Optional[str]: + def download_audio(self, track_info: TrackInfo, format: str = "m4a", quality: int = 160) -> Optional[str]: """ Download audio from YouTube video @@ -341,7 +341,7 @@ class YouTubeDownloader: if self.progress_callback: self.progress_callback(95, f"Download error: {self.current_track.title}") - def process_url(self, url: str, format: str = "m4a", quality: int = 256) -> List[TrackInfo]: + def process_url(self, url: str, format: str = "m4a", quality: int = 160) -> List[TrackInfo]: """ Process a YouTube URL (video or playlist)