1010import tempfile
1111import time
1212import urllib .request
13+ from pypdf import PdfReader
1314from dataclasses import dataclass
1415from pathlib import Path
1516from typing import TYPE_CHECKING , Callable , Iterator , Optional , TextIO
@@ -107,17 +108,18 @@ def ensure_model(config: ReedConfig, print_fn: Callable = console.print) -> None
107108
108109
109110def _default_play_cmd () -> list [str ]:
110- if platform .system () == "Darwin" :
111+ system = platform .system ()
112+ if system == "Darwin" :
111113 return ["afplay" ]
112- if platform . system () == "Linux" :
114+ if system == "Linux" :
113115 for cmd , args in [
114116 ("paplay" , []),
115117 ("aplay" , []),
116118 ("ffplay" , ["-nodisp" , "-autoexit" ]),
117119 ]:
118120 if shutil .which (cmd ):
119121 return [cmd , * args ]
120- if platform . system () == "Windows" :
122+ if system == "Windows" :
121123 if shutil .which ("powershell" ):
122124 return [
123125 "powershell" ,
@@ -132,17 +134,18 @@ def _default_play_cmd() -> list[str]:
132134
133135
134136def _default_clipboard_cmd () -> list [str ]:
135- if platform .system () == "Darwin" :
137+ system = platform .system ()
138+ if system == "Darwin" :
136139 return ["pbpaste" ]
137- if platform . system () == "Linux" :
140+ if system == "Linux" :
138141 for cmd , args in [
139142 ("wl-paste" , []),
140143 ("xclip" , ["-selection" , "clipboard" , "-o" ]),
141144 ("xsel" , ["--clipboard" , "--output" ]),
142145 ]:
143146 if shutil .which (cmd ):
144147 return [cmd , * args ]
145- if platform . system () == "Windows" :
148+ if system == "Windows" :
146149 return ["powershell" , "-Command" , "Get-Clipboard" ]
147150 raise ReedError ("No supported clipboard tool found" )
148151
@@ -237,7 +240,7 @@ def _iter_pdf_pages(
237240 if page_selection :
238241 page_indices = _parse_pdf_pages (page_selection , total_pages )
239242 else :
240- page_indices = list ( range (total_pages ) )
243+ page_indices = range (total_pages )
241244
242245 found_any = False
243246 for index in page_indices :
@@ -411,17 +414,18 @@ def interactive_loop(
411414 if not text :
412415 continue
413416
414- if text .lower () in quit_set :
417+ cmd = text .lower ()
418+ if cmd in quit_set :
415419 return 0
416- elif text . lower () == help_cmd :
420+ elif cmd == help_cmd :
417421 print_help (print_fn )
418422 print_fn ("" )
419423 continue
420- elif text . lower () == clear_cmd :
424+ elif cmd == clear_cmd :
421425 clear_fn ()
422426 print_banner (print_fn )
423427 continue
424- elif text . lower () == replay_cmd :
428+ elif cmd == replay_cmd :
425429 if last_text :
426430 speak_line (last_text )
427431 print_fn ("" )
@@ -500,9 +504,13 @@ def main(
500504 help = "Seconds of silence between sentences" ,
501505 )
502506 args = parser .parse_args (argv )
503- if args .pages and not args .file :
504- print_error ("--pages requires --file <PDF>" , print_fn )
505- return 1
507+ if args .pages :
508+ if not args .file :
509+ print_error ("--pages requires --file <PDF>" , print_fn )
510+ return 1
511+ if Path (args .file ).suffix .lower () != ".pdf" :
512+ print_error ("--pages can only be used with PDF files" , print_fn )
513+ return 1
506514
507515 # Resolve model: None → default, short name → data dir path
508516 if args .model is None :
@@ -565,12 +573,14 @@ def main(
565573 output = args .output ,
566574 )
567575
576+ # Ensure model is available before any speaking mode
577+ try :
578+ ensure_model (config , print_fn )
579+ except ReedError as e :
580+ print_error (str (e ), print_fn )
581+ return 1
582+
568583 if _should_enter_interactive (args , stdin ):
569- try :
570- ensure_model (config , print_fn )
571- except ReedError as e :
572- print_error (str (e ), print_fn )
573- return 1
574584 loop_fn = interactive_loop_fn or interactive_loop
575585 code = loop_fn (
576586 speak_line = lambda line : speak_text (
@@ -583,9 +593,7 @@ def main(
583593 try :
584594 assert stdin is not None
585595
586- # PDF: generate and play one page at a time
587596 if args .file and Path (args .file ).suffix .lower () == ".pdf" :
588- ensure_model (config , print_fn )
589597 for page_num , total , page_text in _iter_pdf_pages (
590598 Path (args .file ), args .pages
591599 ):
@@ -599,7 +607,6 @@ def main(
599607 print_error ("No text to read." , print_fn )
600608 return 1
601609
602- ensure_model (config , print_fn )
603610 speak_text (text , config , run = run , print_fn = print_fn )
604611 except ReedError as e :
605612 print_error (str (e ), print_fn )
0 commit comments