@@ -168,6 +168,7 @@ def _add(v: dict) -> None:
168168 videos .append (v )
169169
170170 # ── Strategy 1: module ExternalTool items ─────────────────────────────────
171+ s1_count = 0
171172 try :
172173 for module in course .get_modules ():
173174 for item in module .get_module_items ():
@@ -180,16 +181,21 @@ def _add(v: dict) -> None:
180181 "module_name" : module .name ,
181182 "title" : item .title ,
182183 "lti_url" : url ,
183- "item_id" : item .id , # integer Canvas item ID
184- "viewer_url" : None , # resolved via LTI launch
184+ "item_id" : item .id ,
185+ "viewer_url" : None ,
185186 })
187+ s1_count += 1
186188 except Exception as e :
187- tqdm .write (f" [warn] modules scan { course .id } : { e } " )
189+ tqdm .write (f" [warn] Strategy 1 (modules) { course .id } : { e } " )
190+ tqdm .write (f" [info] Strategy 1 (modules): { s1_count } video(s)" )
188191
189192 # ── Strategy 2: Panopto folder API (Videos/Panopto tab) ───────────────────
193+ s2_count = 0
190194 try :
195+ tqdm .write (f" [info] Strategy 2 (Panopto tab): launching browser…" )
191196 folder_id , panopto_cookies , bearer_token = _get_panopto_tab_folder (course .id )
192197 if folder_id :
198+ tqdm .write (f" [info] Strategy 2: folder_id={ folder_id } , scanning…" )
193199 for s in _iter_panopto_folder (folder_id , panopto_cookies , bearer_token = bearer_token ):
194200 viewer_url = (f"https://{ PANOPTO_HOST } /Panopto/Pages/Viewer.aspx"
195201 f"?id={ s ['session_id' ]} " )
@@ -199,24 +205,27 @@ def _add(v: dict) -> None:
199205 "module_name" : s .get ("folder_name" , "Videos/Panopto" ),
200206 "title" : s ["name" ],
201207 "lti_url" : None ,
202- "item_id" : s ["session_id" ], # UUID string used as key
208+ "item_id" : s ["session_id" ],
203209 "viewer_url" : viewer_url ,
204210 "_panopto_cookies" : panopto_cookies ,
205211 })
212+ s2_count += 1
213+ else :
214+ tqdm .write (f" [warn] Strategy 2: could not find Panopto folder (browser may have failed)" )
206215 except Exception as e :
207- tqdm .write (f" [warn] Panopto folder scan { course .id } : { e } " )
216+ tqdm .write (f" [warn] Strategy 2 (Panopto folder) { course .id } : { e } " )
217+ tqdm .write (f" [info] Strategy 2 (Panopto tab): { s2_count } video(s)" )
208218
209219 # ── Strategy 3: Canvas Pages scan (fallback only) ─────────────────────────
210- # Only run if strategies 1+2 found nothing. Pages embed Panopto using
211- # delivery/embed IDs that differ from SessionIDs and cannot be resolved
212- # as standalone sessions, so we avoid false entries when the folder API
213- # already provides complete coverage.
214220 if not videos :
221+ tqdm .write (f" [info] Strategy 3 (pages scan): trying…" )
215222 try :
223+ s3_before = len (videos )
216224 for v in _find_panopto_in_pages (course ):
217225 _add (v )
226+ tqdm .write (f" [info] Strategy 3 (pages scan): { len (videos ) - s3_before } video(s)" )
218227 except Exception as e :
219- tqdm .write (f" [warn] pages scan { course .id } : { e } " )
228+ tqdm .write (f" [warn] Strategy 3 ( pages scan) { course .id } : { e } " )
220229
221230 return videos
222231
@@ -305,46 +314,59 @@ def _iter_panopto_folder(
305314 if bearer_token :
306315 headers ["Authorization" ] = f"Bearer { bearer_token } "
307316
308- results : list [dict ] = []
309-
310- r = _sess .post (
311- f"https://{ PANOPTO_HOST } /Panopto/Services/Data.svc/GetSessions" ,
312- json = {"queryParameters" : {
313- "folderID" : folder_id ,
314- "startIndex" : 0 ,
315- "maxResults" : 500 ,
316- "sortColumn" : 1 ,
317- "sortAscending" : True ,
318- "getFolderData" : True , # required to get Subfolders list
319- "includeArchived" : True ,
320- "includePlaylists" : True ,
321- "includePlaceholderSessions" : False ,
322- }},
323- headers = headers ,
324- timeout = 30 ,
325- )
326- if r .status_code != 200 :
327- return results
328-
329- d = r .json ().get ("d" ) or {}
330-
331- for s in d .get ("Results" ) or []:
332- # DeliveryID is what Viewer.aspx and DeliveryInfo.aspx use for streaming.
333- # SessionID is the internal DB identifier and does NOT work with DeliveryInfo.
334- sid = s .get ("DeliveryID" ) or s .get ("SessionID" )
335- name = s .get ("SessionName" , "" )
336- duration = s .get ("Duration" ) # None/0 → navigation placeholder, not a video
337- if sid and name and duration :
338- results .append ({
339- "session_id" : sid ,
340- "name" : name ,
341- "folder_name" : folder_name ,
342- })
317+ results : list [dict ] = []
318+ subfolders : list [dict ] = [] # collected from first page (getFolderData)
319+ PAGE_SIZE = 250
320+ start_idx = 0
321+
322+ while True :
323+ r = _sess .post (
324+ f"https://{ PANOPTO_HOST } /Panopto/Services/Data.svc/GetSessions" ,
325+ json = {"queryParameters" : {
326+ "folderID" : folder_id ,
327+ "startIndex" : start_idx ,
328+ "maxResults" : PAGE_SIZE ,
329+ "sortColumn" : 1 ,
330+ "sortAscending" : True ,
331+ "getFolderData" : start_idx == 0 , # only needed on first page
332+ "includeArchived" : True ,
333+ "includePlaylists" : True ,
334+ "includePlaceholderSessions" : False ,
335+ }},
336+ headers = headers ,
337+ timeout = 30 ,
338+ )
339+ if r .status_code != 200 :
340+ tqdm .write (f" [warn] GetSessions HTTP { r .status_code } for folder { folder_id } " )
341+ break
342+
343+ d = r .json ().get ("d" ) or {}
344+ page_results = d .get ("Results" ) or []
345+
346+ for s in page_results :
347+ sid = s .get ("DeliveryID" ) or s .get ("SessionID" )
348+ name = s .get ("SessionName" , "" )
349+ if sid and name :
350+ results .append ({
351+ "session_id" : sid ,
352+ "name" : name ,
353+ "folder_name" : folder_name ,
354+ })
355+
356+ if start_idx == 0 :
357+ subfolders = d .get ("Subfolders" ) or []
358+
359+ start_idx += len (page_results )
360+ total = d .get ("TotalNumberOfResults" ) or 0
361+ if not page_results or start_idx >= total :
362+ break
343363
344- for sf in d .get ("Subfolders" ) or []:
364+ # Recurse into subfolders
365+ for sf in subfolders :
345366 sf_id = sf .get ("ID" ) or sf .get ("FolderID" ) or sf .get ("Id" )
346367 sf_name = sf .get ("Name" ) or folder_name
347368 if sf_id :
369+ tqdm .write (f" [info] Scanning subfolder: { sf_name } " )
348370 results .extend (
349371 _iter_panopto_folder (sf_id , cookies , sf_name , bearer_token , _sess )
350372 )
0 commit comments