'use strict' const fs = require( 'fs' ) , md5 = require( 'md5' ) , path = require( 'path' ) , gm = require( 'gm' ) , url = require( 'url' ) , range = require( 'range-2018.js' ) , DB = require( './db.js' ) , GetID = require( './get-id.js' ) , Now = require( './readable-timestamp.js' ) , Config = require( '../config.js' ) const Sizes = exports.Sizes = { S : 200 , N : 490 , M : 800 , L : 1600 } const SortAccounts = arrAccounts => arrAccounts .filter( a => a && a.trim( ) ) .sort( ( a,b ) => b[ 0 ] == a[ 0 ] ? 'i' == a[ 0xA ] : 'r' == a[ 0 ] ) const getHashFromPhotoURL = exports.getHashFromPhotoURL = PhotoURL => PhotoURL.match( /([^\/_]+)_?L\.jpg$/ )[ 1 ] const PhotoHashmapToSQL = exports.PhotoHashmapToSQL = PhotoHashmap => Object.keys( PhotoHashmap ) .map( k => `Photo_${ k } = "${ PhotoHashmap[ k ] }"` ) .join( ',' ) const SmallerSizes = exports.SmallerSizes = Object.keys( Sizes ) .filter( k => 'L' != k ) const HashOrNr = exports.HashOrNr = Hash => ( Hash && Hash.length && ( Hash.length > 2 ) ) ? Hash + '_' : Hash const getURL = exports.getURL = ({ SKU, Hash, Size, EXT }) => url.resolve ( Config.Paths.imgGlobal , path.join ( './' , GetID( SKU ) , HashOrNr( Hash ) + Size + ( EXT || '.jpg' ) ) ) const getFilepath = exports.getFilepath = ({ SKU, Hash, Size, EXT }) => path.join ( Config.Paths.imgLocal , GetID( SKU ) , HashOrNr( Hash ) + Size + ( EXT || '.jpg' ) ) const Resize = exports.Resize = ({ SKU, Hash }) => new Promise( ( resolve, reject ) => { SmallerSizes.forEach( ( Size, i, a ) => { gm( getFilepath({ SKU, Hash, Size : 'L' }) ) .resize( Sizes[ Size ], Sizes[ Size ] ) .write( getFilepath({ SKU, Hash, Size }), err => { if( err ) { reject( err ) } if( i == a.length - 1 ) { resolve( true ) } }) }) }).catch( err => console.error ( Now( ) , 'error resizing' , '(Photos.Resize)' , err ) ) const Convert_toBW = path => new Promise( ( resolve, reject ) => { gm( path ) .colorspace( 'GRAY' ) .write( path, err => { if( err ) { console.error ( Now( ) , 'error converting #10 to BW' , path , err ) reject( false ) } resolve( true ) }) }).catch( err => console.error ( Now( ) , 'error converting #10 to BW' , '(Photos.Convert_toBW)' , err ) ) const Process = exports.Process = ({ FilePath, SKU, Variant, Nr }) => new Promise( async ( resolve, reject ) => { if( 10 == Nr ) { console.info( Now( ), 'converting to BW...\n' ) await Convert_toBW( FilePath ) } const Hash = md5( fs.readFileSync( FilePath ) ) try { fs.mkdirSync( path.join( Config.Paths.imgLocal, GetID( SKU ) ) ) } catch( err ) { console.info( 'dir', path.join( Config.Paths.imgLocal, GetID( SKU ) ), 'exist' ) } fs.renameSync( FilePath, getFilepath({ SKU, Hash, Size : 'L' }) ) const Varinat_Exist = await DB.read( { table : 'Photos' , where : `( SKU = "${ SKU }" AND Variant = "${ Variant }" )` }) if( !Varinat_Exist || !Varinat_Exist.length ) { await createVariant({ SKU, Variant }) } await DB.update( { table : 'Photos' , set : ` Photo_${ Nr } = "${ getURL({ SKU, Hash, Size : 'L' }) }" , Need_update = TRUE ` , where : `( SKU = "${ SKU }" AND Variant = "${ Variant }" )` }) await Resize({ SKU, Hash }) resolve( true ) }).catch( err => console.error ( Now( ) , 'error processing' , '(Photos.Process)' , err ) ) const Renumber = exports.Renumber = ({ SKU, Variant, OldNr, NewNr }) => new Promise( async ( resolve, reject ) => { const OldHashmap = await getHashmap( { SKU , Variant , arrNumbers : range( 1,10 ) }) const Photo_toShift = OldHashmap[ OldNr ] let newArr = Object .keys( OldHashmap ) .map( k => OldHashmap[ k ] ) newArr.unshift( null ) newArr.splice( OldNr, 1 ) newArr.splice( NewNr, 0, Photo_toShift ) let NewHashmap = { } range( 1,10 ).map( Nr => { NewHashmap[ Nr ] = newArr[ Nr ] }) console.info ( Now( ) , 'Renumbering\n from :' , OldHashmap , 'to :' , NewHashmap ) const _res = await DB.update( { table : 'Photos' , set : PhotoHashmapToSQL( NewHashmap ) + ', Need_update = TRUE' , where : `( SKU = "${ SKU }" AND Variant = "${ Variant }" )` }) if( _res && _res.length ) { resolve( true ) } else { reject( _res ) } }).catch( err => console.error ( Now( ) , 'error renumbering' , '(Photos.Renumber)' , err ) ) const URLoccurrences = exports.URLoccurrences = ({ SKU, PhotoURL }) => new Promise( async ( resolve, reject ) => { const Photos_EQ_URL = range( 1,11 ) .map( Nr => `Photo_${ Nr } = "${ PhotoURL }"` ) .join( ' OR ' ) const _req = await DB.read( { table : 'Photos' , where : `( SKU = "${ SKU }" AND ( ${ Photos_EQ_URL } ) )` }) if( typeof _req == 'object' ) { let Occurrences = 0 _req.forEach( V => { for( Param in V ) { Occurrences += ( V[ Param ] == PhotoURL ) } }) resolve( Occurrences ) } reject( false ) }).catch( err => console.error ( Now( ) , 'error getting finding occurrences of an URL' , '(Photos.URLoccurrences)' , err ) ) const deletePhotoFiles = exports.deletePhotoFiles = ({ SKU, Hash }) => new Promise( async ( resolve, reject ) => { if( !Hash ) { console.error ( Now( ) , 'cant delete file, no Hash in params:' , { SKU, Hash } ) resolve( false ) return false } // check if filename exist anywhere else if( 1 < ( await URLoccurrences( { SKU , PhotoURL : getURL({ SKU, Hash }) }) ) ) { resolve( false ) return false } console.log( 'removing file:', SKU + '/' + Hash ) Object.keys( Sizes ).map ( Size => fs.unlinkSync( getFilepath({ SKU, Hash, Size }) ) ) resolve( true ) }).catch( err => console.error ( Now( ) , 'error deleting files' , '(Photos.deletePhotoFiles)' , err ) ) const removeOne = exports.removeOne = ({ SKU, Variant, Nr }) => new Promise( async ( resolve, reject ) => { const HashMap_req = await getHashmap( { SKU , Variant , arrNumbers : [ Nr ] }) const PhotoURL = HashMap_req[ Nr ] , Hash = getHashFromPhotoURL( PhotoURL ) console.log({ SKU, Variant, Nr, PhotoURL, Hash }) console.log ( Now( ) , 'removing photo' , '(Photos.removeOne):\n' , { SKU, Variant, Nr } ) if( 10 === Number( Nr ) ) { DB.update( { table : 'Photos' , set : `Photo_10 = ""` , where : `( SKU = "${SKU}" AND Variant = "${Variant}" )` }) } else { const OldHashmap = await getHashmap( { SKU , Variant , arrNumbers : range( 1,10 ).filter( _Nr => _Nr >= Nr ) }) let NewHashmap = { } Object.keys( OldHashmap ) .filter( _Nr => OldHashmap[ _Nr ] ) .forEach( _Nr => { NewHashmap[ _Nr ] = OldHashmap[ Number( _Nr ) + 1 ] || '' }) await DB.update( { table : 'Photos' , set : PhotoHashmapToSQL( NewHashmap ) , where : `( SKU = "${SKU}" AND Variant = "${Variant}" )` }) } resolve( await deletePhotoFiles({ SKU, Hash }) ) }).catch( err => console.error ( Now( ) , 'error removing one' , '(Photos.removeOne)' , err ) ) const getHashmap = exports.getHashmap = ({ SKU, Variant, arrNumbers }) => new Promise( async ( resolve, reject ) => { const _req = await DB.read( { table : 'Photos' , columns : ( arrNumbers || range( 1,11 ) ) .map( Nr => `Photo_${ Nr } AS "${ Nr }"` ) .join( ',' ) , where : `( SKU="${SKU}" AND Variant="${Variant}" )` }) if( _req.length && typeof _req == 'object' ) { resolve( _req[ 0 ] ) } reject( false ) }).catch( err => console.error ( Now( ) , 'error getting hashmap' , '(Photos.getHashmap)' , err ) ) const setDefaultVariant = exports.setDefaultVariant = async ({ SKU, Variant, oldVarinatRenamedTo }) => { const _req = await DB.read( { table : 'Photos' , columns : 'SKU, Variant, Accounts' , where : `SKU = "${ SKU }"` }) if( !_req.length ) { console.error ( Now( ) , 'no records of SKU' , SKU ) return false } let oldDefault = { Accounts :'' } , newDefault = { Accounts :'' } _req.forEach( V => { if( '_default' == V.Variant ) { oldDefault = V } if( Variant == V.Variant ) { newDefault = V } }) newDefault.Accounts = [ ...newDefault.Accounts.split( /[\r\n]+/g ) , ...oldDefault.Accounts.split( /[\r\n]+/g ) ] newDefault.Accounts = SortAccounts( newDefault.Accounts ) if( oldDefault.SKU ) { await DB.update( { table : 'Photos' , set : ` Variant = "${ oldVarinatRenamedTo }" , Accounts = "" ` , where : `( SKU = "${ SKU }" AND Variant = "_default" )` }) } await DB.update( { table : 'Photos' , set : ` Variant = "_default" , Accounts = "${ newDefault.Accounts.join('\n') }" , Need_update = TRUE ` , where : `( SKU = "${ SKU }" AND Variant = "${ Variant }" )` }) console.log ( Now( ) , `variant "${ Variant }"` , `of "${ SKU }" is now the default` , '(Photos.setDefaultVariant)' ) return true } const createVariant = exports.createVariant = ({ SKU, Variant, CopyOf }) => new Promise( async ( resolve, reject ) => { const _test = await DB.read( { table : 'Photos' , where : `( SKU = "${ SKU }" AND Variant = "${ Variant }" )` }) if( _test.length ) { resolve( true ) return true } const _req = await DB.read( { table : 'Photos' , where : `( SKU = "${ SKU }" AND Variant = "${ CopyOf || '_default' }" )` }) console.log( _req && _req.length && _req[ 0 ] || 'err DB.read [Photos.createVariant]' ) const Accounts = '_default' == Variant ? Config.Accounts.join( '\n' ) : '' await DB.create( { table : 'Photos' , set : ` SKU = "${ SKU }" , Variant = "${ Variant }" , Need_update = TRUE , Accounts = "${ Accounts }" ` + ( _req && _req.length ? `, Photo_10 = "${ _req[ 0 ].Photo_10 }"` + ( CopyOf ? ` , Photo_1 = "${ _req[ 0 ].Photo_1 }" , Photo_2 = "${ _req[ 0 ].Photo_2 }" , Photo_3 = "${ _req[ 0 ].Photo_3 }" , Photo_4 = "${ _req[ 0 ].Photo_4 }" , Photo_5 = "${ _req[ 0 ].Photo_5 }" , Photo_6 = "${ _req[ 0 ].Photo_6 }" , Photo_7 = "${ _req[ 0 ].Photo_7 }" , Photo_8 = "${ _req[ 0 ].Photo_8 }" , Photo_9 = "${ _req[ 0 ].Photo_9 }" ` : '' ) : '' ) }) resolve( true ) }).catch( err => console.error ( Now( ) , 'error creating Variant' , '(Photos.createVariant)' , err ) ) const assignVariantToAccount = exports.assignVariantToAccount = ({ SKU, Account, Variant }) => new Promise( async ( resolve, reject ) => { const AccountsBefore = await DB.read( { table : 'Photos' , columns : 'Variant, Accounts' , where : `( SKU = "${ SKU }" AND Accounts LIKE "%${ Account }%" )` }) if( AccountsBefore && AccountsBefore.length ) { const PrevVariant = AccountsBefore[ 0 ].Variant let arrAccountsToStay = AccountsBefore[ 0 ].Accounts.split(/[\r\n]+/g) arrAccountsToStay.splice( arrAccountsToStay.indexOf(Account), 1) console.log({ PrevVariant, arrAccountsToStay }) await DB.update( { table : 'Photos' , set : `Accounts = "${ arrAccountsToStay.join('\n') }"` , where : `( SKU = "${ SKU }" AND Variant = "${ PrevVariant }" )` }) } else { console.warn(Account, 'was unset before') } const AccountsInsertBefore = await DB.read( { table : 'Photos' , columns : 'Accounts' , set : `Accounts = "${ Account }"` , where : `( SKU = "${ SKU }" AND Variant = "${ Variant }" )` }) if( AccountsInsertBefore && AccountsInsertBefore.length ) { let arrUpdatedAccountsInsert = AccountsInsertBefore[ 0 ] .Accounts .split(/[\r\n]+/g) arrUpdatedAccountsInsert.push(Account) arrUpdatedAccountsInsert = SortAccounts( arrUpdatedAccountsInsert ) DB.update( { table : 'Photos' , set : ` Accounts = "${ arrUpdatedAccountsInsert.join('\n') }" , Need_update = TRUE ` , where : `( SKU = "${ SKU }" AND Variant = "${ Variant }" )` }) resolve( true ) } else { reject( 'error getting AccountsInsertBefore' ) } }).catch( err => console.error ( Now( ) , 'cant assign Variant to Account' , '(Photos.assignVariantToAccount)' , err ) ) const removeVariant = exports.removeVariant = ({ SKU, Variant }) => new Promise( async ( resolve, reject ) => { const VariantToDelete = await DB.read( { table : 'Photos' , where : `( SKU = "${ SKU }" AND Variant = "${ Variant }" )` }) if( !VariantToDelete.length ) { resolve( 'variant does not exist' ) return true } if( VariantToDelete[ 0 ].Accounts.trim( ) ) { const VariantsMigrateTo = await DB.read( { table : 'Photos' , where : `( SKU = "${ SKU }" AND Variant != "${ Variant }" )` }) if( VariantsMigrateTo.length ) { let AccountsToMigrate = [...new Set ( [ ...VariantToDelete[ 0 ].Accounts.split( /[\r\n]+/g ) , ...VariantsMigrateTo[ 0 ].Accounts.split( /[\r\n]+/g ) ] )] console.log( { VariantToDelete , VariantsMigrateTo , AccountsToMigrate }) await DB.update( { table : 'Photos' , set : `Accounts = "${ AccountsToMigrate.join('\n') }"` , where : `( SKU = "${ SKU }" AND Variant = "${ VariantsMigrateTo[ 0 ].Variant }" )` }) } } DB.delete( { table : 'Photos' , where : `( SKU = "${ SKU }" AND Variant = "${ Variant }" )` }) let Photos_toDelete = await getHashmap({ SKU, Variant }) if( Photos_toDelete ) { for( let i=1; i<11; i++ ) { if( Photos_toDelete[ i ] ) { await deletePhotoFiles( { SKU , Hash : Photos_toDelete[ i ] }) } } } resolve( true ) }).catch( err => console.error ( Now( ) , 'error removing variant' , '(Photos.removeVariant)' , err ) ) const readTable = exports.readTable = table => new Promise( async ( resolve, reject ) => resolve( await DB.read({ table }) ) ).catch( err => console.error ( Now( ) , 'error removing variant' , '(Photos.readTable)' , err ) ) const get_Photos_Since = exports.get_Photos_Since = ( since = 0 ) => new Promise( async ( resolve, reject ) => { const res = await DB.read( { table : 'Photos_Ordered' , where : ( 0 != since ) && `( Updated > "${ since }" AND Updated IS NOT NULL )` || '1' , order_by : 'id' }).catch( reject ) // console.log( 'get_Photos_Since:', since ) // console.log( 'res:', res ) resolve( res ) }).catch( err => console.error ( Now( ) , 'error removing variant' , '(Photos.readTable)' , err ) ) module.exports = exports