@@ -127,9 +127,10 @@ export function TTSScreen() {
127127 const playbackState = usePlaybackState ( ) ;
128128 const progress = useProgress ( ) ;
129129
130- const isPlaying = playbackState . state === State . Playing ;
131- const isPaused = playbackState . state === State . Paused ;
132- const isLoadingAudio = playbackState . state === State . Loading || playbackState . state === State . Buffering ;
130+ const currentState = playbackState ?. state ;
131+ const isPlaying = currentState === State . Playing ;
132+ const isPaused = currentState === State . Paused ;
133+ const isLoadingAudio = currentState === State . Loading || currentState === State . Buffering ;
133134
134135 const [ currentParagraph , setCurrentParagraph ] = useState ( 0 ) ;
135136 const [ paragraphProgress , setParagraphProgress ] = useState ( 0 ) ;
@@ -167,8 +168,11 @@ export function TTSScreen() {
167168 ] ,
168169 progressUpdateEventInterval : 1 ,
169170 } ) ;
171+ // Reset player to clear any stuck loading/buffering state
172+ await TrackPlayer . reset ( ) ;
170173 } catch ( e ) {
171- // Player may already be set up
174+ // Player may already be set up, try reset anyway
175+ try { await TrackPlayer . reset ( ) ; } catch { }
172176 }
173177 } ) ( ) ;
174178 return ( ) => {
@@ -184,37 +188,46 @@ export function TTSScreen() {
184188 setLoadError ( null ) ;
185189 try {
186190 const apiClient = getApiClient ( ) ;
191+ console . log ( '[TTSScreen] Starting load, book.id:' , book . id ) ;
187192
188193 // Providers
189194 const provRes = await apiClient . getTtsProviders ( ) ;
195+ console . log ( '[TTSScreen] Providers response:' , JSON . stringify ( provRes ) ) ;
190196 if ( cancelled ) return ;
191197 const ps = ( provRes . success && provRes . data ?. providers ) || [ ] ;
192198 setProviders ( ps ) ;
193199 const enabledNames = ps . filter ( ( p ) => p . enabled ) . map ( ( p ) => p . name ) ;
194200 const finalProvider = enabledNames . includes ( provider )
195201 ? provider
196202 : enabledNames [ 0 ] || "edge" ;
203+ console . log ( '[TTSScreen] Final provider:' , finalProvider ) ;
197204 if ( finalProvider !== provider ) setProvider ( finalProvider ) ;
198205
199206 // Voices
200207 const vRes = await apiClient . getVoices ( finalProvider ) ;
208+ console . log ( '[TTSScreen] Voices response:' , JSON . stringify ( vRes ) ) ;
201209 if ( cancelled ) return ;
202210 const vs = ( vRes . success && vRes . data ) || [ ] ;
203211 setVoices ( vs ) ;
204212 if ( ! voiceId && vs [ 0 ] ) setVoiceId ( vs [ 0 ] . id ) ;
205213
206214 // Chapters
215+ console . log ( '[TTSScreen] Loading chapters for book:' , book . id ) ;
207216 const chRes = await apiClient . getChapters ( book . id ) ;
217+ console . log ( '[TTSScreen] Chapters response:' , JSON . stringify ( chRes ) ) ;
208218 if ( cancelled ) return ;
209219 if ( ! chRes . success || ! chRes . data || chRes . data . length === 0 ) {
220+ console . log ( '[TTSScreen] No chapters found' ) ;
210221 setLoadError ( "本书暂无章节内容,请先解析章节。" ) ;
211222 return ;
212223 }
213224 setChapters ( chRes . data ) ;
214225
215- // Load first chapter paragraphs
226+ // Load first chapter (loadChapter will skip empty ones)
227+ console . log ( '[TTSScreen] Loading chapter 0, voice:' , vs [ 0 ] ?. id || voiceId , 'provider:' , finalProvider ) ;
216228 await loadChapter ( 0 , vs [ 0 ] ?. id || voiceId , finalProvider ) ;
217229 } catch ( e ) {
230+ console . error ( '[TTSScreen] Load error:' , e ) ;
218231 setLoadError ( ( e as Error ) . message || "加载失败" ) ;
219232 } finally {
220233 if ( ! cancelled ) setLoading ( false ) ;
@@ -303,14 +316,32 @@ export function TTSScreen() {
303316 } , [ ttsStore ] ) ;
304317
305318 const loadChapter = async ( ci : number , vid : string , prov : string ) => {
319+ console . log ( '[TTSScreen] loadChapter called, ci:' , ci , 'vid:' , vid , 'prov:' , prov ) ;
306320 setChapterIndex ( ci ) ;
307321 try {
308322 const apiClient = getApiClient ( ) ;
323+ console . log ( '[TTSScreen] Fetching paragraphs for book:' , book . id , 'chapter:' , ci ) ;
309324 const pRes = await apiClient . getChapterParagraphs ( book . id , ci ) ;
325+ console . log ( '[TTSScreen] Paragraphs response:' , JSON . stringify ( pRes ) . substring ( 0 , 500 ) ) ;
310326 if ( ! pRes . success || ! pRes . data ) {
327+ console . log ( '[TTSScreen] Failed to load paragraphs:' , pRes ) ;
311328 setLoadError ( "加载章节失败" ) ;
312329 return ;
313330 }
331+ console . log ( '[TTSScreen] Paragraphs loaded, count:' , pRes . data . paragraphs ?. length ) ;
332+
333+ // If chapter has no paragraphs, try next chapter
334+ if ( ! pRes . data . paragraphs || pRes . data . paragraphs . length === 0 ) {
335+ const nextChapter = ci + 1 ;
336+ if ( nextChapter < chapters . length ) {
337+ console . log ( '[TTSScreen] Chapter empty, trying next:' , nextChapter ) ;
338+ return loadChapter ( nextChapter , vid , prov ) ;
339+ } else {
340+ setLoadError ( "没有可朗读的内容" ) ;
341+ return ;
342+ }
343+ }
344+
314345 setChapterTitle ( pRes . data . title ) ;
315346 setParagraphs ( pRes . data . paragraphs ) ;
316347 setCurrentParagraph ( 0 ) ;
@@ -763,7 +794,7 @@ export function TTSScreen() {
763794 style = { [ styles . iconButton , { backgroundColor : theme . colors . surface } ] }
764795 onPress = { handleSkipBack }
765796 >
766- < Ionicons name = "play-skip-back" size = { 22 } color = { theme . colors . text } />
797+ < Ionicons name = "play-skip-back-outline " size = { 22 } color = { theme . colors . text } />
767798 </ TouchableOpacity >
768799
769800 { /* Play/Pause */ }
@@ -788,7 +819,7 @@ export function TTSScreen() {
788819 style = { [ styles . iconButton , { backgroundColor : theme . colors . surface } ] }
789820 onPress = { handleSkipForward }
790821 >
791- < Ionicons name = "play-skip-forward" size = { 22 } color = { theme . colors . text } />
822+ < Ionicons name = "play-skip-forward-outline " size = { 22 } color = { theme . colors . text } />
792823 </ TouchableOpacity >
793824
794825 { /* Chapter picker */ }
0 commit comments