import * as fs from 'fs' ; import { inject , injectable } from 'inversify' ; import * as path from 'path' ; import * as apid from '../../../../api' ; import Reserve from '../../../db/entities/Reserve' ; import DateUtil from '../../../util/DateUtil' ; import FileUtil from '../../../util/FileUtil' ; import StrUtil from '../../../util/StrUtil' ; import IVideoUtil from '../../api/video/IVideoUtil' ; import IChannelDB from '../../db/IChannelDB' ; import IProgramDB from '../../db/IProgramDB' ; import IVideoFileDB from '../../db/IVideoFileDB' ; import IConfigFile , { RecordedDirInfo } from '../../IConfigFile' ; import IConfiguration from '../../IConfiguration' ; import ILogger from '../../ILogger' ; import ILoggerModel from '../../ILoggerModel' ; import IRecordingUtilModel , { RecFilePathInfo } from './IRecordingUtilModel' ; @ injectable ( ) export default class RecordingUtilModel implements IRecordingUtilModel { private log : ILogger ; private config : IConfigFile ; private channelDB : IChannelDB ; private programDB : IProgramDB ; private videoFileDB : IVideoFileDB ; private videoUtil : IVideoUtil ; constructor ( @ inject ( 'ILoggerModel' ) logger , @ inject ( 'IConfiguration' ) configuration , @ inject ( 'IChannelDB' ) channelDB , @ inject ( 'IProgramDB' ) programDB , @ inject ( 'IVideoFileDB' ) videoFileDB , @ inject ( 'IVideoUtil' ) videoUtil ) { this . log = logger . getLogger ( ) ; this . config = configuration . getConfig ( ) ; this . channelDB = channelDB ; this . programDB = programDB ; this . videoFileDB = videoFileDB ; this . videoUtil = videoUtil ; } param reserve param isEnableTmp return public async getRecPath ( reserve , isEnableTmp ) { let parentDir = null ; let subDir = '' ; if ( isEnableTmp === true && typeof this . config . recordedTmp !== 'undefined' ) { parentDir = { name : 'tmp' , path : this . config . recordedTmp , } ; } else { if ( reserve . parentDirectoryName === null ) { parentDir = this . config . recorded [ 0 ] ; } else { for ( const d of this . config . recorded ) { if ( d . name === reserve . parentDirectoryName ) { parentDir = d ; break ; } } } if ( parentDir === null ) { parentDir = this . config . recorded [ 0 ] ; } subDir = reserve . directory === null ? '' : reserve . directory ; } let channelName = reserve . channelId . toString ( 10 ) ; let halfWidthChannelName = channelName ; let sid = 'NULL' ; try { const channel = await this . channelDB . findId ( reserve . channelId ) ; if ( channel !== null ) { channelName = channel . name ; halfWidthChannelName = channel . halfWidthName ; sid = channel . serviceId . toString ( 10 ) ; } } catch ( err ) { this . log . system . warn ( `channel name get error: ${ reserve . channelId }` ) ; } let programName = reserve . name ; if ( reserve . isTimeSpecified === true ) { const program = await this . programDB . findChannelIdAndTime ( reserve . channelId , reserve . startAt ) ; programName = program === null ? '番組名なし' : program . name ; } let fileName = reserve . recordedFormat === null ? this . config . recordedFormat : reserve . recordedFormat ; const jaDate = DateUtil . getJaDate ( new Date ( reserve . startAt ) ) ; fileName = fileName . replace ( /%YEAR%/g , DateUtil . format ( jaDate , 'yyyy' ) ) . replace ( /%SHORTYEAR%/g , DateUtil . format ( jaDate , 'YY' ) ) . replace ( /%MONTH%/g , DateUtil . format ( jaDate , 'MM' ) ) . replace ( /%DAY%/g , DateUtil . format ( jaDate , 'dd' ) ) . replace ( /%HOUR%/g , DateUtil . format ( jaDate , 'hh' ) ) . replace ( /%MIN%/g , DateUtil . format ( jaDate , 'mm' ) ) . replace ( /%SEC%/g , DateUtil . format ( jaDate , 'ss' ) ) . replace ( /%DOW%/g , DateUtil . format ( jaDate , 'w' ) ) . replace ( /%TYPE%/g , reserve . channelType ) . replace ( /%CHID%/g , reserve . channelId . toString ( 10 ) ) . replace ( /%CHNAME%/g , channelName ) . replace ( /%HALF_WIDTH_CHNAME%/g , halfWidthChannelName ) . replace ( /%CH%/g , reserve . channel ) . replace ( /%SID%/g , sid ) . replace ( /%ID%/g , reserve . id . toString ( ) ) . replace ( /%TITLE%/g , programName === null ? 'NULL' : programName ) . replace ( /%HALF_WIDTH_TITLE%/g , reserve . halfWidthName === null ? 'NULL' : reserve . halfWidthName ) ; fileName = StrUtil . replaceFileName ( fileName ) ; const dir = path . join ( parentDir . path , subDir ) ; try { await FileUtil . access ( dir , fs . constants . R_OK | fs . constants . W_OK ) ; } catch ( err ) { if ( typeof err . code !== 'undefined' && err . code === 'ENOENT' ) { this . log . system . info ( `mkdirp: ${ dir }` ) ; await FileUtil . mkdir ( dir ) ; } else { this . log . system . fatal ( `dir permission error: ${ dir }` ) ; this . log . system . fatal ( err ) ; throw err ; } } const newFileName = await this . getFileName ( parentDir . path , subDir , fileName , this . config . recordedFileExtension ) ; return { parendDir : parentDir , subDir : subDir , fileName : newFileName , fullPath : path . join ( parentDir . path , subDir , newFileName ) , } ; } param parentDir param subDir param fileName param extension param conflict private async getFileName ( parentDir , subDir , fileName , extension , conflict = 0 ) { const conflictStr = conflict === 0 ? '' : `(${ conflict })` ; const newFileName = `${ fileName }${ conflictStr }${ extension }` ; const fileFullPath = path . join ( parentDir , subDir , newFileName ) ; try { await FileUtil . stat ( fileFullPath ) ; return this . getFileName ( parentDir , subDir , fileName , extension , conflict + 1 ) ; } catch ( err ) { return newFileName ; } } param reserve param videoFileId return public async movingFromTmp ( reserve , videoFileId ) { const oldVideoFilePath = await this . videoUtil . getFullFilePathFromId ( videoFileId ) ; if ( oldVideoFilePath === null ) { throw new Error ( 'VideoFilePathIsNull' ) ; } if ( typeof this . config . recordedTmp === 'undefined' ) { throw new Error ( 'RecordedTmpIsUndefined' ) ; } const newRecPath = await this . getRecPath ( reserve , false ) ; let isSuccessRenameFile = false ; this . log . system . info ( `move file: ${ oldVideoFilePath } -> ${ newRecPath . fullPath }` ) ; try { await FileUtil . rename ( oldVideoFilePath , newRecPath . fullPath ) ; isSuccessRenameFile = true ; } catch ( err ) { this . log . system . debug ( `rename file error: ${ oldVideoFilePath } -> ${ newRecPath . fullPath }` ) ; this . log . system . debug ( err ) ; } if ( isSuccessRenameFile === false ) { try { await FileUtil . copyFile ( oldVideoFilePath , newRecPath . fullPath ) ; } catch ( err ) { this . log . system . error ( `copy file error: ${ oldVideoFilePath } -> ${ newRecPath . fullPath }` ) ; throw err ; } } try { await this . videoFileDB . updateFilePath ( { videoFileId : videoFileId , parentDirectoryName : newRecPath . parendDir . name , filePath : path . join ( newRecPath . subDir , newRecPath . fileName ) , } ) ; } catch ( err ) { this . log . system . error ( `update VideoFileDB path error: ${ videoFileId }` ) ; this . log . system . error ( err ) ; if ( isSuccessRenameFile === true ) { this . log . system . info ( `rollback renamed file: ${ newRecPath . fullPath } -> ${ oldVideoFilePath }` ) ; try { await FileUtil . rename ( newRecPath . fullPath , oldVideoFilePath ) ; } catch ( e ) { this . log . system . error ( `rollback renamed file error: ${ newRecPath . fullPath } -> ${ oldVideoFilePath }` ) ; this . log . system . error ( e ) ; throw err ; } } else { this . log . system . info ( `delete copied file: ${ newRecPath . fullPath }` ) ; try { await FileUtil . unlink ( newRecPath . fullPath ) ; } catch ( e ) { this . log . system . error ( `delete copied file error: ${ newRecPath . fullPath }` ) ; this . log . system . error ( e ) ; throw err ; } } } if ( isSuccessRenameFile === false ) { this . log . system . info ( `delete old file: ${ oldVideoFilePath }` ) ; try { await FileUtil . unlink ( oldVideoFilePath ) ; } catch ( err ) { this . log . system . error ( `delete old file error: ${ oldVideoFilePath }` ) ; throw err ; } } return newRecPath . fullPath ; } param videoFileId return public async updateVideoFileSize ( videoFileId ) { this . log . system . info ( `update file size: ${ videoFileId }` ) ; const videoFileFulPath = await this . videoUtil . getFullFilePathFromId ( videoFileId ) ; if ( videoFileFulPath === null ) { throw new Error ( 'VideoFilePathIsNull' ) ; } try { const fileSize = await FileUtil . getFileSize ( videoFileFulPath ) ; await this . videoFileDB . updateSize ( videoFileId , fileSize ) ; } catch ( err ) { this . log . system . error ( `update file size error: ${ videoFileId }` ) ; this . log . system . error ( err ) ; } } }