@@ -19,21 +19,17 @@ import (
1919// DownloadAllAnimeSmartRange downloads a range of episodes exclusively for AllAnime.
2020// It prioritizes high-quality mirrors and writes AniSkip sidecar files for intro/outro skipping.
2121func DownloadAllAnimeSmartRange (anime * models.Anime , startEp , endEp int , quality string ) error {
22- // Validate source
23- if ! isAllAnimeSourceAPI (anime ) {
24- return fmt .Errorf ("AllAnime Smart Range is only available for AllAnime sources" )
25- }
26-
27- if quality == "" {
28- quality = "best"
22+ // Validate
23+ if err := validateSmartRangeInputs (anime , startEp , endEp , & quality ); err != nil {
24+ return err
2925 }
3026
3127 util .Debug ("AllAnime Smart Range start" ,
3228 "anime" , anime .Name ,
3329 "range" , fmt .Sprintf ("%d-%d" , startEp , endEp ),
3430 "quality" , quality )
3531
36- // Fetch episodes using enhanced path (this enables AniSkip enrichment when MAL ID is available )
32+ // Fetch episodes using enhanced path (enables AniSkip enrichment)
3733 episodes , err := GetAnimeEpisodesEnhanced (anime )
3834 if err != nil {
3935 return fmt .Errorf ("failed to get episodes: %w" , err )
@@ -56,40 +52,28 @@ func DownloadAllAnimeSmartRange(anime *models.Anime, startEp, endEp int, quality
5652 ep := episodes [i - 1 ]
5753 filePath := filepath .Join (outDir , fmt .Sprintf ("%d.mp4" , i ))
5854
59- if stat , err := os . Stat (filePath ); err == nil && stat . Size () > 1024 {
55+ if alreadyDownloaded (filePath ) {
6056 util .Infof ("Episode %d already exists, skipping" , i )
61- // Even if file exists, (re)write sidecar if needed
62- _ = WriteAniSkipSidecar (filePath , & ep )
57+ _ = WriteAniSkipSidecar (filePath , & ep ) // refresh sidecar if needed
6358 continue
6459 }
6560
66- util .Infof ("Resolving stream URL for episode %d..." , i )
67- streamURL , err := GetEpisodeStreamURLEnhanced (& ep , anime , quality )
68- if err != nil || streamURL == "" {
69- // Fallback to non-enhanced AllAnime-aware function
70- streamURL , err = GetEpisodeStreamURL (& ep , anime , quality )
71- if err != nil || streamURL == "" {
72- util .Errorf ("Failed to get stream URL for episode %d: %v" , i , err )
73- continue
74- }
61+ streamURL , err := resolveStreamURLForEpisode (& ep , anime , quality )
62+ if err != nil {
63+ util .Errorf ("Failed to get stream URL for episode %d: %v" , i , err )
64+ continue
7565 }
7666
77- util .Debug ("Stream URL resolved" , "episode" , i , "len" , len (streamURL ))
78-
79- // Prefer yt-dlp for HLS and known hosters used by AllAnime
8067 if err := smartDownload (streamURL , filePath ); err != nil {
8168 util .Errorf ("Download failed for episode %d: %v" , i , err )
8269 continue
8370 }
8471
85- // Write AniSkip sidecar markers if available
8672 if err := WriteAniSkipSidecar (filePath , & ep ); err != nil {
8773 util .Debugf ("Failed to write AniSkip sidecar for episode %d: %v" , i , err )
8874 }
89-
9075 util .Infof ("Episode %d downloaded successfully" , i )
9176 }
92-
9377 return nil
9478}
9579
@@ -100,13 +84,8 @@ func smartDownload(url, dest string) error {
10084 return err
10185 }
10286
103- // Use yt-dlp for most AllAnime links (m3u8, wixmp, blogger, sharepoint, etc.)
104- if strings .Contains (url , ".m3u8" ) ||
105- strings .Contains (url , "master.m3u8" ) ||
106- strings .Contains (url , "wixmp.com" ) || strings .Contains (url , "repackager.wixmp.com" ) ||
107- strings .Contains (url , "blogger.com" ) ||
108- strings .Contains (url , "sharepoint.com" ) ||
109- strings .Contains (url , "allanime" ) || strings .Contains (url , "allmanga" ) {
87+ // Use yt-dlp for HLS/known hosters
88+ if shouldUseYtDlp (url ) {
11089 ctx := context .Background ()
11190 ytdlp .MustInstall (ctx , nil )
11291 dl := ytdlp .New ().Output (dest )
@@ -211,3 +190,56 @@ func sanitizeSmart(name string) string {
211190 }
212191 return name
213192}
193+
194+ // Helpers to reduce complexity
195+
196+ // validateSmartRangeInputs ensures correct source and quality defaulting
197+ func validateSmartRangeInputs (anime * models.Anime , startEp , endEp int , quality * string ) error {
198+ if ! isAllAnimeSourceAPI (anime ) {
199+ return fmt .Errorf ("AllAnime Smart Range is only available for AllAnime sources" )
200+ }
201+ if quality != nil && * quality == "" {
202+ * quality = "best"
203+ }
204+ if startEp < 1 || endEp < startEp {
205+ return fmt .Errorf ("invalid range %d-%d" , startEp , endEp )
206+ }
207+ return nil
208+ }
209+
210+ // shouldUseYtDlp decides if yt-dlp is preferred for a given URL
211+ func shouldUseYtDlp (u string ) bool {
212+ l := strings .ToLower (u )
213+ return strings .Contains (l , ".m3u8" ) ||
214+ strings .Contains (l , "master.m3u8" ) ||
215+ strings .Contains (l , "wixmp.com" ) || strings .Contains (l , "repackager.wixmp.com" ) ||
216+ strings .Contains (l , "blogger.com" ) ||
217+ strings .Contains (l , "sharepoint.com" ) ||
218+ strings .Contains (l , "allanime" ) || strings .Contains (l , "allmanga" )
219+ }
220+
221+ // alreadyDownloaded checks if the file exists and seems valid (>1KB)
222+ func alreadyDownloaded (path string ) bool {
223+ if st , err := os .Stat (path ); err == nil {
224+ return st .Size () > 1024
225+ }
226+ return false
227+ }
228+
229+ // resolveStreamURLForEpisode resolves the streaming URL with enhanced fallback
230+ func resolveStreamURLForEpisode (ep * models.Episode , anime * models.Anime , quality string ) (string , error ) {
231+ if ep == nil || anime == nil {
232+ return "" , fmt .Errorf ("nil episode or anime" )
233+ }
234+ url , err := GetEpisodeStreamURLEnhanced (ep , anime , quality )
235+ if err == nil && url != "" {
236+ util .Debug ("Stream URL resolved (enhanced)" , "len" , len (url ))
237+ return url , nil
238+ }
239+ url , err = GetEpisodeStreamURL (ep , anime , quality )
240+ if err != nil || url == "" {
241+ return "" , fmt .Errorf ("fallback stream URL resolution failed: %w" , err )
242+ }
243+ util .Debug ("Stream URL resolved (fallback)" , "len" , len (url ))
244+ return url , nil
245+ }
0 commit comments