@@ -8,8 +8,11 @@ import (
88 "sync"
99 "time"
1010
11+ stdpath "path"
12+
1113 "github.com/OpenListTeam/OpenList/v4/internal/conf"
1214 "github.com/OpenListTeam/OpenList/v4/internal/db"
15+ "github.com/OpenListTeam/OpenList/v4/internal/fs"
1316 "github.com/OpenListTeam/OpenList/v4/internal/media"
1417 "github.com/OpenListTeam/OpenList/v4/internal/media/scraper"
1518 "github.com/OpenListTeam/OpenList/v4/internal/model"
@@ -306,6 +309,75 @@ func ClearMediaDB(c *gin.Context) {
306309 common .SuccessResp (c )
307310}
308311
312+ // ClearMediaScrape 清空刮削数据(保留扫描记录,但清空所有刮削结果字段)
313+ // 参数:media_type 可选,留空表示所有类型;item_id 暂不支持(统一为类型/全部)
314+ func ClearMediaScrape (c * gin.Context ) {
315+ mediaType := model .MediaType (c .Query ("media_type" ))
316+ affected , err := db .ClearMediaScrapedData (mediaType )
317+ if err != nil {
318+ common .ErrorResp (c , err , 500 )
319+ return
320+ }
321+ // 同步清掉对应媒体配置中的最近刮削时间
322+ if mediaType != "" {
323+ if cfg , e := db .GetMediaConfig (mediaType ); e == nil && cfg != nil {
324+ cfg .LastScrapeAt = nil
325+ _ = db .SaveMediaConfig (cfg )
326+ }
327+ } else {
328+ if cfgs , e := db .GetAllMediaConfigs (); e == nil {
329+ for i := range cfgs {
330+ cfgs [i ].LastScrapeAt = nil
331+ _ = db .SaveMediaConfig (& cfgs [i ])
332+ }
333+ }
334+ }
335+ common .SuccessResp (c , gin.H {"affected" : affected })
336+ }
337+
338+ // DeleteInvalidMedia 删除已失效的媒体条目(即对应文件 / 文件夹在存储中已不存在)
339+ // 参数:media_type 可选,留空表示扫描所有类型
340+ // 返回:检测总数、被删除条目数
341+ func DeleteInvalidMedia (c * gin.Context ) {
342+ mediaType := model .MediaType (c .Query ("media_type" ))
343+
344+ items , err := db .ListAllValidMediaItemsForCheck (mediaType )
345+ if err != nil {
346+ common .ErrorResp (c , err , 500 )
347+ return
348+ }
349+
350+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Minute )
351+ defer cancel ()
352+
353+ var invalidIDs []uint
354+ for i := range items {
355+ it := & items [i ]
356+ // 拼接文件 / 文件夹的完整 VFS 路径
357+ // - 文件夹模式 (is_folder=true): folder_path 即文件夹自身(扫描根 + 文件夹名 视情况而定);这里直接使用 folder_path/file_name
358+ // - 普通文件: folder_path 是所在目录,file_name 是文件名
359+ fullPath := stdpath .Join (it .FolderPath , it .FileName )
360+ if fullPath == "" || fullPath == "/" {
361+ continue
362+ }
363+ if _ , gerr := fs .Get (ctx , fullPath , & fs.GetArgs {NoLog : true }); gerr != nil {
364+ invalidIDs = append (invalidIDs , it .ID )
365+ }
366+ }
367+
368+ if len (invalidIDs ) > 0 {
369+ if err := db .DeleteMediaItemsByIDs (invalidIDs ); err != nil {
370+ common .ErrorResp (c , err , 500 )
371+ return
372+ }
373+ }
374+ log .Infof ("删除已失效媒体条目:检测 %d 条,删除 %d 条 (media_type=%q)" , len (items ), len (invalidIDs ), mediaType )
375+ common .SuccessResp (c , gin.H {
376+ "checked" : len (items ),
377+ "deleted" : len (invalidIDs ),
378+ })
379+ }
380+
309381// ==================== 扫描与刮削 ====================
310382
311383// ScanMediaReq 扫描请求
0 commit comments