@@ -1704,6 +1704,11 @@ pub struct DownloadManager {
17041704 /// dropped) from pause/park/cancel paths so the worker stops its
17051705 /// extractor streams promptly; the pump tail also drops them.
17061706 video_abort : RefCell < HashMap < u64 , tokio:: sync:: oneshot:: Sender < ( ) > > > ,
1707+ /// Rows currently capturing a live stream. Pause/cancel/park only
1708+ /// signal these (no task abort, no status preset): the worker
1709+ /// finalizes the partial and its message drives the row to Done.
1710+ /// Set per attempt in `spawn_video`; dropped on terminal messages.
1711+ live_rows : RefCell < std:: collections:: HashSet < u64 > > ,
17071712 /// Set by shutdown(): stale engine futures must not re-persist or
17081713 /// re-mark rows once the authoritative shutdown persist has run.
17091714 draining : Cell < bool > ,
@@ -1743,6 +1748,7 @@ impl DownloadManager {
17431748 server_mtime : RefCell :: new ( HashMap :: new ( ) ) ,
17441749 video_sources : RefCell :: new ( HashMap :: new ( ) ) ,
17451750 video_abort : RefCell :: new ( HashMap :: new ( ) ) ,
1751+ live_rows : RefCell :: new ( std:: collections:: HashSet :: new ( ) ) ,
17461752 draining : Cell :: new ( false ) ,
17471753 } ) ;
17481754 // Live preferences: raising the download limit must wake queued
@@ -1844,6 +1850,12 @@ impl DownloadManager {
18441850 self . video_sources . borrow ( ) . get ( & id) . cloned ( )
18451851 }
18461852
1853+ /// Whether the row is currently capturing a live stream (stop-and-keep
1854+ /// applies: pausing/cancelling finalizes instead of discarding).
1855+ pub fn is_live_video ( & self , id : u64 ) -> bool {
1856+ self . live_rows . borrow ( ) . contains ( & id)
1857+ }
1858+
18471859 /// Validate, dedupe and queue a download, starting it when a slot is free.
18481860 ///
18491861 /// # Errors
@@ -1967,9 +1979,7 @@ impl DownloadManager {
19671979 page_url : & str ,
19681980 dest_dir : Option < & str > ,
19691981 filename : Option < & str > ,
1970- quality : & str ,
1971- audio_only : bool ,
1972- video_format_id : Option < & str > ,
1982+ choices : crate :: video:: VideoChoices ,
19731983 ) -> Result < DownloadItem , String > {
19741984 let url = normalize_url ( page_url) ?;
19751985 if !crate :: video:: is_video_page ( & url) {
@@ -1992,7 +2002,7 @@ impl DownloadManager {
19922002 } )
19932003 } ) ;
19942004 let item = DownloadItem :: new ( self . alloc_id ( ) , & url, & name, & dir) ;
1995- item. set_detail ( if audio_only {
2005+ item. set_detail ( if choices . audio_only {
19962006 gettext ( "Waiting to resolve audio…" )
19972007 } else {
19982008 gettext ( "Waiting to resolve media…" )
@@ -2003,9 +2013,10 @@ impl DownloadManager {
20032013 page_url : url,
20042014 media_url : None ,
20052015 expires_at : None ,
2006- quality : quality. to_string ( ) ,
2007- audio_only,
2008- video_format_id : video_format_id. map ( |s| s. to_string ( ) ) ,
2016+ quality : choices. quality ,
2017+ audio_only : choices. audio_only ,
2018+ is_live : choices. is_live ,
2019+ video_format_id : choices. video_format_id ,
20092020 } ,
20102021 ) ;
20112022 Ok ( self . insert ( item) )
@@ -2182,11 +2193,19 @@ impl DownloadManager {
21822193 page_url,
21832194 quality,
21842195 audio_only,
2196+ is_live,
21852197 video_format_id,
21862198 ..
21872199 } ) = self . video_source ( item. id ( ) )
21882200 {
2189- return self . spawn_video ( item, page_url, quality, audio_only, video_format_id) ;
2201+ return self . spawn_video (
2202+ item,
2203+ page_url,
2204+ quality,
2205+ audio_only,
2206+ video_format_id,
2207+ is_live,
2208+ ) ;
21902209 }
21912210 let connections = ( opts. connections . max ( 1 ) as usize ) . min ( 16 ) ;
21922211 let timeout = Duration :: from_secs ( opts. timeout . max ( 1 ) as u64 ) ;
@@ -2580,6 +2599,7 @@ impl DownloadManager {
25802599 }
25812600 this. running . borrow_mut ( ) . remove ( & id) ;
25822601 this. video_abort . borrow_mut ( ) . remove ( & id) ;
2602+ this. live_rows . borrow_mut ( ) . remove ( & id) ;
25832603 if this. draining . get ( ) {
25842604 return ;
25852605 }
@@ -2673,6 +2693,7 @@ impl DownloadManager {
26732693 quality : String ,
26742694 audio_only : bool ,
26752695 video_format_id : Option < String > ,
2696+ is_live : bool ,
26762697 ) {
26772698 let id = item. id ( ) ;
26782699 let generation = self . epoch . borrow ( ) . get ( & id) . cloned ( ) . unwrap_or ( 0 ) + 1 ;
@@ -2681,6 +2702,13 @@ impl DownloadManager {
26812702 let ( abort_tx, abort_rx) = tokio:: sync:: oneshot:: channel ( ) ;
26822703 // Overwrite any stale sender: its task is dead or guarded stale.
26832704 self . video_abort . borrow_mut ( ) . insert ( id, abort_tx) ;
2705+ // Live rows finalize in the worker on stop: track them so
2706+ // pause/cancel/park signal without aborting or presetting.
2707+ if is_live {
2708+ self . live_rows . borrow_mut ( ) . insert ( id) ;
2709+ } else {
2710+ self . live_rows . borrow_mut ( ) . remove ( & id) ;
2711+ }
26842712 let opts = DownloadOptions :: from_settings ( & self . settings ) ;
26852713 let job = crate :: video:: VideoJob {
26862714 item_id : id,
@@ -2692,6 +2720,7 @@ impl DownloadManager {
26922720 timeout_secs : opts. timeout . max ( 1 ) as u64 ,
26932721 user_agent : opts. user_agent . clone ( ) ,
26942722 video_format_id,
2723+ is_live,
26952724 cookies_browser : self . settings . cookies_browser ( ) ,
26962725 } ;
26972726 let handle = tokio_rt ( ) . spawn ( async move {
@@ -2751,6 +2780,14 @@ impl DownloadManager {
27512780
27522781 /// Pause a running download, freeing its slot for the next queued item.
27532782 pub fn pause ( self : & Rc < Self > , id : u64 ) {
2783+ if self . live_rows . borrow ( ) . contains ( & id) {
2784+ // Live captures finalize in the worker: signal it and let
2785+ // its Finished flip the row to Done. Aborting the task or
2786+ // presetting Paused would drop the recording or strand it.
2787+ // The pump tail frees the slot on completion.
2788+ self . stop_video_worker ( id) ;
2789+ return ;
2790+ }
27542791 self . stop_video_worker ( id) ;
27552792 if let Some ( handle) = self . running . borrow ( ) . get ( & id) {
27562793 handle. abort ( ) ;
@@ -2864,8 +2901,13 @@ impl DownloadManager {
28642901
28652902 /// Stop the engine for `id`, keeping file, bitmap and progress, and
28662903 /// mark it queued. Unlike pause the row yields its slot; unlike cancel
2867- /// nothing is deleted and progress is kept.
2904+ /// nothing is deleted and progress is kept. Live rows only signal:
2905+ /// the worker finalizes and its message completes the row.
28682906 fn park ( & self , id : u64 ) {
2907+ if self . live_rows . borrow ( ) . contains ( & id) {
2908+ self . stop_video_worker ( id) ;
2909+ return ;
2910+ }
28692911 self . stop_video_worker ( id) ;
28702912 if let Some ( handle) = self . running . borrow ( ) . get ( & id) {
28712913 handle. abort ( ) ;
@@ -2927,11 +2969,17 @@ impl DownloadManager {
29272969 }
29282970
29292971 fn cancel_inner ( & self , id : u64 , keep_partial : bool ) {
2972+ // Live rows keep what's recorded (Stop, not Cancel): signal the
2973+ // worker and skip the task abort plus the Cancelled preset, so
2974+ // its Finished still lands. Discard via explicit row removal.
2975+ let live = self . live_rows . borrow ( ) . contains ( & id) ;
29302976 self . stop_video_worker ( id) ;
2931- if let Some ( handle) = self . running . borrow ( ) . get ( & id) {
2932- handle. abort ( ) ;
2977+ if !live {
2978+ if let Some ( handle) = self . running . borrow ( ) . get ( & id) {
2979+ handle. abort ( ) ;
2980+ }
2981+ self . running . borrow_mut ( ) . remove ( & id) ;
29332982 }
2934- self . running . borrow_mut ( ) . remove ( & id) ;
29352983 self . pending_names . borrow_mut ( ) . remove ( & id) ;
29362984 let had_segments = self . segment_state . borrow_mut ( ) . remove ( & id) . is_some ( ) ;
29372985 self . torrent_pieces . borrow_mut ( ) . remove ( & id) ;
@@ -2948,8 +2996,10 @@ impl DownloadManager {
29482996 if crate :: torrent:: is_torrent ( & item. url ( ) ) && item. status ( ) != DownloadStatus :: Done {
29492997 crate :: torrent:: forget_download ( id) ;
29502998 }
2951- item. set_status ( DownloadStatus :: Cancelled ) ;
2952- item. set_detail ( item. status ( ) . label ( ) ) ;
2999+ if !live {
3000+ item. set_status ( DownloadStatus :: Cancelled ) ;
3001+ item. set_detail ( item. status ( ) . label ( ) ) ;
3002+ }
29533003 }
29543004 }
29553005
0 commit comments