1212from .player import PlayerManager
1313from .discord_rpc import DiscordRPCManager
1414from .models import QualityOption
15+ from .utils import download_file
16+ from .history import HistoryManager
1517
1618class AniCliArApp :
1719 def __init__ (self ):
1820 self .ui = UIManager ()
1921 self .api = AnimeAPI ()
2022 self .rpc = DiscordRPCManager ()
2123 self .player = PlayerManager (rpc_manager = self .rpc , console = self .ui .console )
24+ self .history = HistoryManager ()
2225
2326 def run (self ):
2427 atexit .register (self .cleanup )
@@ -48,7 +51,7 @@ def main_loop(self):
4851 self .ui .print ()
4952
5053 search_prompt = Panel (
51- Text ("Search Anime (or 'q' to quit) " , style = "info" , justify = "center" ),
54+ Text ("S Search | R Relevant/Featured (MAL) | Q Quit " , style = "info" , justify = "center" ),
5255 box = HEAVY ,
5356 padding = (0 , 4 ),
5457 border_style = COLOR_BORDER
@@ -67,19 +70,28 @@ def main_loop(self):
6770 if query .lower () in ['q' , 'quit' , 'exit' ]:
6871 break
6972
70- if not query :
71- continue
72-
73- self .rpc .update_searching ()
73+ results = []
7474
75- results = self .ui .run_with_loading (
76- "Searching anime..." ,
77- self .api .search_anime ,
78- query
79- )
75+ if query .lower () == 'r' :
76+ self .rpc .update_searching ()
77+ # Fetching from Jikan (MAL) with auto-SFW filtering
78+ results = self .ui .run_with_loading (
79+ "Fetching currently airing anime from MAL..." ,
80+ self .api .get_mal_season_now
81+ )
82+ elif query .lower () == 's' :
83+ term = Prompt .ask (f"{ padding } Enter Search Term: " , console = self .ui .console ).strip ()
84+ if term :
85+ self .rpc .update_searching ()
86+ results = self .ui .run_with_loading ("Searching..." , self .api .search_anime , term )
87+ elif query :
88+ self .rpc .update_searching ()
89+ results = self .ui .run_with_loading ("Searching..." , self .api .search_anime , query )
90+ else :
91+ continue
8092
8193 if not results :
82- self .ui .render_message ("✗ No Results" , f"No results found for ' { query } ' " , "error" )
94+ self .ui .render_message ("✗ No Results" , f"No results found. " , "error" )
8395 continue
8496
8597 self .handle_anime_selection (results )
@@ -94,6 +106,22 @@ def handle_anime_selection(self, results):
94106 return
95107
96108 selected_anime = results [anime_idx ]
109+
110+ # --- BRIDGE LOGIC: MAL to Internal API ---
111+ if not selected_anime .id :
112+ internal_results = self .ui .run_with_loading (
113+ f"Syncing '{ selected_anime .title_en } '..." ,
114+ self .api .search_anime ,
115+ selected_anime .title_en
116+ )
117+
118+ if not internal_results :
119+ self .ui .render_message ("✗ Not Found" , f"Sorry, '{ selected_anime .title_en } ' hasn't been uploaded to the server yet." , "error" )
120+ continue
121+
122+ selected_anime = internal_results [0 ]
123+ # -----------------------------------------
124+
97125 self .rpc .update_viewing_anime (selected_anime .title_en , selected_anime .thumbnail )
98126
99127 episodes = self .ui .run_with_loading (
@@ -115,33 +143,72 @@ def handle_anime_selection(self, results):
115143 break
116144
117145 def handle_episode_selection (self , selected_anime , episodes ):
146+ current_idx = 0
147+
118148 while True :
119- ep_idx = self .ui .episode_selection_menu (selected_anime .title_en , episodes , self .rpc , selected_anime .thumbnail )
149+ last_watched = self .history .get_last_watched (selected_anime .id )
150+
151+ ep_idx = self .ui .episode_selection_menu (
152+ selected_anime .title_en ,
153+ episodes ,
154+ self .rpc ,
155+ selected_anime .thumbnail ,
156+ last_watched_ep = last_watched
157+ )
120158
121159 if ep_idx == - 1 :
122160 sys .exit (0 )
123161 elif ep_idx is None :
124162 self .rpc .update_browsing ()
125163 return True
126164
127- selected_ep = episodes [ ep_idx ]
165+ current_idx = ep_idx
128166
129- server_data = self .ui .run_with_loading (
130- "Loading servers..." ,
131- self .api .get_streaming_servers ,
132- selected_anime .id ,
133- selected_ep .number
134- )
135-
136- if not server_data :
137- self .ui .render_message (
138- "✗ No Servers" ,
139- "No servers available for this episode." ,
140- "error"
167+ while True :
168+ selected_ep = episodes [current_idx ]
169+
170+ server_data = self .ui .run_with_loading (
171+ "Loading servers..." ,
172+ self .api .get_streaming_servers ,
173+ selected_anime .id ,
174+ selected_ep .number
141175 )
142- continue
143-
144- self .handle_quality_selection (selected_anime , selected_ep , server_data )
176+
177+ if not server_data :
178+ self .ui .render_message (
179+ "✗ No Servers" ,
180+ "No servers available for this episode." ,
181+ "error"
182+ )
183+ break
184+
185+ action_taken = self .handle_quality_selection (selected_anime , selected_ep , server_data )
186+
187+ # --- UPDATED LOGIC HERE ---
188+ # Check for both "watch" AND "download"
189+ if action_taken == "watch" or action_taken == "download" :
190+ next_action = self .ui .post_watch_menu ()
191+
192+ if next_action == "Next Episode" :
193+ if current_idx + 1 < len (episodes ):
194+ current_idx += 1
195+ continue
196+ else :
197+ self .ui .render_message ("Info" , "No more episodes!" , "info" )
198+ break
199+ elif next_action == "Previous Episode" :
200+ if current_idx > 0 :
201+ current_idx -= 1
202+ continue
203+ else :
204+ self .ui .render_message ("Info" , "This is the first episode." , "info" )
205+ break
206+ elif next_action == "Replay" :
207+ continue
208+ else :
209+ break
210+ else :
211+ break
145212
146213 def handle_quality_selection (self , selected_anime , selected_ep , server_data ):
147214 current_ep_data = server_data .get ('CurrentEpisode' , {})
@@ -159,21 +226,22 @@ def handle_quality_selection(self, selected_anime, selected_ep, server_data):
159226 "No MediaFire servers found for this episode." ,
160227 "error"
161228 )
162- return
229+ return None
163230
164- idx = self .ui .quality_selection_menu (
231+ result = self .ui .quality_selection_menu (
165232 selected_anime .title_en ,
166233 selected_ep .display_num ,
167234 available ,
168235 self .rpc ,
169236 selected_anime .thumbnail
170237 )
171238
172- if idx == - 1 :
239+ if result == - 1 :
173240 sys .exit (0 )
174- if idx is None :
175- return
241+ if result is None :
242+ return None
176243
244+ idx , action = result
177245 quality = available [idx ]
178246 server_id = current_ep_data .get (quality .server_key )
179247
@@ -184,14 +252,25 @@ def handle_quality_selection(self, selected_anime, selected_ep, server_data):
184252 )
185253
186254 if direct_url :
187- self .player .play (direct_url , f"{ selected_anime .title_en } - Ep { selected_ep .display_num } ({ quality .name } )" )
188- self .rpc .update_selecting_episode (selected_anime .title_en , selected_anime .thumbnail )
255+ filename = f"{ selected_anime .title_en } - Ep { selected_ep .display_num } [{ quality .name .split ()[1 ]} ].mp4"
256+
257+ if action == 'download' :
258+ success = download_file (direct_url , filename , self .ui .console )
259+ # Save download as "watched" in history so you can jump to it next time
260+ self .history .mark_watched (selected_anime .id , selected_ep .display_num , selected_anime .title_en )
261+ return "download"
262+ else :
263+ self .player .play (direct_url , f"{ selected_anime .title_en } - Ep { selected_ep .display_num } ({ quality .name } )" )
264+ self .history .mark_watched (selected_anime .id , selected_ep .display_num , selected_anime .title_en )
265+ self .rpc .update_selecting_episode (selected_anime .title_en , selected_anime .thumbnail )
266+ return "watch"
189267 else :
190268 self .ui .render_message (
191269 "✗ Error" ,
192270 "Failed to extract direct link from MediaFire." ,
193271 "error"
194272 )
273+ return None
195274
196275 def handle_exit (self ):
197276 self .ui .clear ()
0 commit comments