import Path from 'path' ; import { Router } from 'express' ; import { execute } from 'proper-job' ; import Walk from 'walk' ; import type Types from '@vimtur/common' ; import { ImportUtils } from '../cache/import-utils' ; import { wrap } from '../express-async' ; import Config from '../config' ; import type { Database , RouterTask } from '../types' ; type FilterResults = Types . Scanner . FilterResults ; export class Scanner { public static results ? : FilterResults ; public static async getFileList ( ) { const dir = Config . get ( ) . libraryPath ; const options = { followLinks : false , } ; const walker = Walk . walk ( dir , options ) ; const files = [ ] ; walker . on ( 'file' , ( root , fileStats , next ) => { try { ImportUtils . getType ( fileStats . name ) ; files . push ( Path . relative ( dir , Path . resolve ( root , fileStats . name ) ) ) ; } catch ( err ) { } finally { next ( ) ; } } ) ; return new Promise < string [ ] > ( ( resolve ) => { walker . on ( 'end' , ( ) => { resolve ( files ) ; } ) ; } ) ; } public static async filterNewAndMissing ( databasePaths , fileList , db ) { const results = { newPaths : [ ] , missingPaths : [ ] , } ; const databasePathsMap = Scanner . arrayAsMap ( databasePaths ) ; const fileListMap = Scanner . arrayAsMap ( fileList ) ; await ImportUtils . wait ( ) ; for ( const file of fileList ) { if ( ! databasePathsMap [ file ] ) { const isDeleted = await db . isDeletedPath ( file ) ; if ( isDeleted ) { console . log ( `Ignoring deleted path: ${ file }` ) ; } else { results . newPaths . push ( file ) ; } } } await ImportUtils . wait ( ) ; for ( const file of databasePaths ) { if ( ! fileListMap [ file ] && ! results . missingPaths . includes ( file ) ) { results . missingPaths . push ( file ) ; } } await ImportUtils . wait ( ) ; return results ; } private static arrayAsMap ( arr ) { const result = { } ; for ( const el of arr ) { result [ el ] = el ; } return result ; } } export function getTask ( db ) { const router = Router ( ) ; router . get ( '/results' , wrap ( async ( ) => { return { data : Scanner . results ? { newPaths : Scanner . results . newPaths . length , missingPaths : Scanner . results . missingPaths . length , } : { newPaths : 0 , missingPaths : 0 , } , } ; } ) ) ; router . get ( '/results/missing' , wrap ( async ( ) => { return { data : Scanner . results ? Scanner . results . missingPaths : [ ] , } ; } ) ) ; router . get ( '/results/new' , wrap ( async ( ) => { return { data : Scanner . results ? Scanner . results . newPaths : [ ] , } ; } ) ) ; return { id : 'SCAN' , description : 'Scan for new files' , router , runner : ( ) => { return execute ( async ( ) => { const files = await Scanner . getFileList ( ) ; const mediaList = await db . subsetFields ( { } , { path : 1 } ) ; const normalisedPaths = [ ] ; for ( const media of mediaList ) { normalisedPaths . push ( media . path ) ; } Scanner . results = await Scanner . filterNewAndMissing ( normalisedPaths , files , db ) ; return [ ] ; } , ( ) => Promise . resolve ( ) ) ; } , } ; }