Pour cela j'ai fait un petit module pour piloter le fantastique outil qu'est ExifTool.
Vu que Ar-s me l'a demandé.
Voici le code
ExifTool se charge une seul fois... et on lui envoie les commandes via le StdIn
pour télécharger Exiftool : https://www.sno.phy.queensu.ca/~phil/exiftool/
2019-02-02 Version 4.6
Code : Tout sélectionner
; ********************************************************************
; Program: ETUE (Easy To use Exiftool)
; Version: 4.6
; Description: use the verry good Perl Package Exiftool
; Author: Thyphoon
; Date: January, 2019
; License: Free, unrestricted, credit
; appreciated but not required.
; Note: Please share improvement !
;
; Exiftool info: ExifTool is a platform-independent Perl library plus a command-line application for reading, writing and editing meta information in a wide variety of files
; Exiftool Web: https://www.sno.phy.queensu.ca/~phil/exiftool/
; Exiftool author: Phil Harvey
; ********************************************************************
EnableExplicit
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
#SP = "\"
CompilerCase #PB_OS_Linux
#SP = "/"
CompilerCase #PB_OS_MacOS
#SP = "/"
CompilerEndSelect
CompilerIf #PB_Compiler_Thread=#False
CompilerError("You must enable Compiler threadsafe")
End
CompilerEndIf
DeclareModule Exiftool
Declare.b SetExecutableFilePath(FilePath.s)
Declare.s GetExecutableFilePath()
Declare Start()
Declare.b IsRun()
Declare.b Command(cmd.s)
Declare.i Execute(Event.i=0,WindowEvent.i=0,*ReturnDataProcedure=0)
Declare.b Stop()
Declare.s GetResultStdout(ExecuteID.i)
Declare.s GetResultStdErr(ExecuteID.i)
Declare.i GetResultRawOut(ExecuteID.i)
Declare.b WaitExecute(ExecuteID.i,TimeOut.i=#False)
Declare.b WaitReady(TimeOut.i=5000)
Declare FreeResult(ExecuteID.i)
Declare Quit()
EndDeclareModule
Module Exiftool
#Verbose=#True ;TODO #True or #False if you want a exiftool Log file
Prototype.i ReturnData(ExecuteID.i)
Structure Execute
ReadyToUse.b ;#True if Stdout and StdErr is ready to use
StdErr.s
*RawOut
Event.i
EventWindow.i
ReturnData.ReturnData
EndStructure
Structure param
VerboseFileId.i
VerboseMutex.i
NumberCount.i
Map Execute.Execute()
ExecuteMutex.i
Thread.i
ProgFilePath.s
ProgHandle.i
EndStructure
Global param.param
param\ExecuteMutex=CreateMutex()
param\VerboseMutex=CreateMutex()
Procedure StartVerbose()
If #Verbose=#True
LockMutex(param\VerboseMutex)
param\VerboseFileId=CreateFile(#PB_Any,"ExifTool.log")
UnlockMutex(param\VerboseMutex)
EndIf
EndProcedure
Procedure Verbose(txt.s)
If #Verbose=#True
LockMutex(param\VerboseMutex)
If IsFile(param\VerboseFileId)
WriteStringN(param\VerboseFileId,txt)
EndIf
UnlockMutex(param\VerboseMutex)
EndIf
EndProcedure
Procedure StopVerbose()
If #Verbose=#True
LockMutex(param\VerboseMutex)
CloseFile(param\VerboseFileId)
UnlockMutex(param\VerboseMutex)
EndIf
EndProcedure
Procedure.b SetExecutableFilePath(FilePath.s)
If FileSize(FilePath)>0
param\ProgFilePath=FilePath
ProcedureReturn #True
Else
MessageRequester("Exiftools","Exiftool No Found",#PB_MessageRequester_Error)
ProcedureReturn #False
EndIf
EndProcedure
Procedure.s GetExecutableFilePath()
ProcedureReturn param\ProgFilePath
EndProcedure
Procedure.s GetResultStdout(ExecuteID.i)
Protected Stdout.s
LockMutex(param\ExecuteMutex)
If param\Execute(Str(ExecuteID))\RawOut>0
Stdout=PeekS(param\Execute(Str(ExecuteID))\RawOut,MemorySize(param\Execute(Str(ExecuteID))\RawOut),#PB_UTF8)
Else
Stdout=""
Verbose("NO RAWOUT")
EndIf
UnlockMutex(param\ExecuteMutex)
ProcedureReturn Stdout
EndProcedure
Procedure.s GetResultStdErr(ExecuteID.i)
Protected StdErr.s
LockMutex(param\ExecuteMutex)
StdErr=param\Execute(Str(ExecuteID))\StdErr
UnlockMutex(param\ExecuteMutex)
ProcedureReturn StdErr
EndProcedure
Procedure.i GetResultRawOut(ExecuteID.i)
Protected *Data
LockMutex(param\ExecuteMutex)
If param\Execute(Str(ExecuteID))\RawOut>0
*Data=param\Execute(Str(ExecuteID))\RawOut
Else
*Data=0
EndIf
UnlockMutex(param\ExecuteMutex)
ProcedureReturn *Data
EndProcedure
Procedure LaunchExifTool(p.l=0)
Protected Output.s,Stdout.s,StdoutEnd.s,StdErr.s,ExecuteID.s
Protected result.i
Protected dataSize.i
Protected *data
*buffer = 0
Protected ForceEnd.b=#False
param\ProgHandle=RunProgram(param\ProgFilePath,"-stay_open True -@ -",GetPathPart(param\ProgFilePath),#PB_Program_Open|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error|#PB_Program_Hide)
If IsProgram(param\ProgHandle)
ItIsEnd=#False
While ProgramRunning(param\ProgHandle)
StdErr.s=ReadProgramError(param\ProgHandle)
If StdErr<>"":Verbose("/!\ ExifTool ERROR :"+StdErr+"<"):Debug "/!\ ExifTool ERROR :"+StdErr+"<":EndIf
result.i=AvailableProgramOutput(param\ProgHandle)
If result>0
*buffer = AllocateMemory(result)
ReadProgramData(param\ProgHandle,*buffer,result)
Stdout=PeekS(*buffer,result,#PB_UTF8)
;Test only the End to found the {ready
StdoutEnd=PeekS(*buffer+result-15,15,#PB_Ascii)
FindStart.i=FindString(StdoutEnd,"{ready",0)
If FindStart>0
FindStop.i=FindString(StdoutEnd,"}",FindStart+6)
Debug("FIND {Ready"+Mid(Stdout,FindStart+6,FindStop-(FindStart+6))+"} Start:"+Str(FindStart)+" Stop:"+Str(FindStop))
ItIsEnd=#True
Else
ItIsEnd=#False
EndIf
If *data=0
*data = AllocateMemory(result)
dataSize=0
time.i=ElapsedMilliseconds()
Else
time.i=ElapsedMilliseconds()
dataSize=MemorySize(*data)
EndIf
If ItIsEnd=#True
; Delete {readyx} from *buffer
NoreadySize=Len(Right(StdoutEnd,Len(StdoutEnd)-FindStart+1)) ; {ready1} = 10
Verbose("NoreadySize="+Str(NoreadySize))
Else
NoreadySize=0
EndIf
If dataSize+result-NoreadySize>0 ; protect if empty return
*data = ReAllocateMemory(*data,dataSize+result-NoreadySize)
Verbose("ReAllocateMemory("+Str(*Data)+","+Str(dataSize)+"+"+Str(result)+"-"+Str(NoreadySize)+")")
CopyMemory(*buffer,*data+dataSize,MemorySize(*buffer)-NoreadySize)
Debug "FinalBuffer:"+Str(*Data+dataSize)
Else
;If return is empty No need more memory
FreeMemory(*data):*data=0
EndIf
FreeMemory(*buffer):
;if it's the end
If ItIsEnd=#True;FindString(Stdout,"{ready",0);Left(Stdout,6)="{ready"
ItIsEnd=#False; ready to continue
ExecuteID=Mid(StdoutEnd,FindStart+6,FindStop-(FindStart+6)) ;at the end chr(13)+chr(10)
Verbose(">Execute{"+ExecuteID+"}")
LockMutex(param\ExecuteMutex)
param\execute(ExecuteID)\StdErr=StdErr
param\Execute(ExecuteID)\RawOut=*data
param\Execute(ExecuteID)\ReadyToUse=#True
;Send Event to inform the result is available
If param\execute(ExecuteID)\Event<>0
PostEvent(param\execute(ExecuteID)\Event,param\execute(ExecuteID)\EventWindow,0,0,Val(ExecuteID))
Verbose("Post Event:"+Str(param\execute(ExecuteID)\Event)+" from Window:"+Str(param\execute(ExecuteID)\EventWindow))
EndIf
;If Callback use id
If param\execute(ExecuteID)\ReturnData<>0
param\execute(ExecuteID)\ReturnData(Val(ExecuteID)) ; TODO out this function to Call it afer Unlockmutex
EndIf
*data=0
UnlockMutex(param\ExecuteMutex)
EndIf
EndIf
Wend
;0 on success, or 1 if an error occurred, or 2 if all files failed
Verbose("Code Fin :"+Str(ProgramExitCode(param\ProgHandle)))
CloseProgram(param\ProgHandle) ; Ferme la connection vers le programme
Else
Debug "No Found"+ param\ProgFilePath
End
EndIf
EndProcedure
Procedure Start()
StartVerbose()
param\Thread=CreateThread(@LaunchExifTool(),0)
EndProcedure
Procedure.b IsRun()
ProcedureReturn IsThread(param\Thread)
EndProcedure
Procedure.b Command(cmd.s)
Verbose("Command>"+cmd)
If IsProgram(param\ProgHandle) And ProgramRunning(param\ProgHandle)
WriteProgramStringN(param\ProgHandle,Cmd)
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
Procedure.i Execute(Event.i=0,EventWindow.i=0,*ReturnDataProcedure=0)
Protected ExecuteID.s
param\NumberCount=param\NumberCount+1
ExecuteID=Str(param\NumberCount)
Command("-execute"+ExecuteID)
LockMutex(param\ExecuteMutex)
param\execute(ExecuteID)\Event=Event
param\execute(ExecuteID)\EventWindow=EventWindow
param\execute(ExecuteID)\ReturnData=*ReturnDataProcedure
UnlockMutex(param\ExecuteMutex)
ProcedureReturn param\NumberCount
EndProcedure
Procedure.b Stop()
Protected Ex.i
Command("-stay_open")
Command("False")
Ex=Execute()
WaitExecute(Ex,-1)
EndProcedure
Procedure.b WaitReady(TimeOut.i=5000)
Protected StartTime.i=ElapsedMilliseconds()
Repeat
If TimeOut>0 And ElapsedMilliseconds()>StarTime+TimeOut
ProcedureReturn #False
EndIf
Delay(100)
Until IsProgram(param\ProgHandle)
Verbose("ExifTool is Ready")
ProcedureReturn #True
EndProcedure
Procedure.b WaitExecute(ExecuteID.i,TimeOut.i=#False)
Protected StartTime.i=ElapsedMilliseconds()
Repeat
LockMutex(param\ExecuteMutex)
If FindMapElement(param\Execute(),Str(ExecuteID)) And param\Execute()\ReadyToUse=#True
UnlockMutex(param\ExecuteMutex)
Verbose("Wait Execute"+Str(ExecuteID)+" and it's finish")
ProcedureReturn #True
EndIf
UnlockMutex(param\ExecuteMutex)
If TimeOut>0 And ElapsedMilliseconds()>StarTime+TimeOut
Verbose("No Finish Execute"+Str(ExecuteID)+" Timeout ! ")
ProcedureReturn #False
EndIf
Delay(500)
Until IsProgram(param\ProgHandle)=0
EndProcedure
Procedure FreeResult(ExecuteID.i)
LockMutex(param\ExecuteMutex)
If param\Execute(Str(ExecuteID))\RawOut>0
FreeMemory(param\Execute(Str(ExecuteID))\RawOut)
EndIf
DeleteMapElement(param\Execute(),Str(ExecuteID))
UnlockMutex(param\ExecuteMutex)
EndProcedure
Procedure Quit()
If IsRun()
Stop()
Protected time.i=ElapsedMilliseconds()
Repeat
Delay(1)
If ElapsedMilliseconds()>time+5000
MessageRequester("ExifTool","Can't Stop ExifTool Thread... ",#PB_MessageRequester_Warning|#PB_MessageRequester_Ok)
Break
EndIf
Until Exiftool::IsRun()=#False
EndIf
EndProcedure
EndModule
;- MAIN TEST
CompilerIf #PB_Compiler_IsMainFile
UseJPEGImageDecoder()
Enumeration
#ModeExtractPreviewImage
#ModeSimpleLoadImage
EndEnumeration
;In this Demo You can choose the MOde to do a preview image
#Mode=#ModeExtractPreviewImage ;TODO <- You Canc hange the MODE
Enumeration
#Win_Main
#Gdt_LoadImage
#Gdt_Image
#Gdt_MetaList
#Gdt_Description
#Gdt_State
#Gdt_OnOff
#Gdt_Save
EndEnumeration
Exiftool::SetExecutableFilePath(GetCurrentDirectory()+"exiftool"+#SP+"exiftool.exe") ; TODO Change the path to found the exiftool.exe
Exiftool::Start()
Enumeration #PB_Event_FirstCustomValue
#Cust_Event_Exiftools_Read_Metadatas
#Cust_Event_Exiftools_Write_Metadatas
EndEnumeration
Procedure ReturnData(ExecuteID)
Protected Stdout.s,StdError.s
Stdout=Exiftool::GetResultStdout(ExecuteID)
StdError=Exiftool::GetResultStdErr(ExecuteID)
If StdError<>""
MessageRequester("Exiftool",StdError,#PB_MessageRequester_Info)
EndIf
If Stdout<>""
MessageRequester("Exiftool",Stdout,#PB_MessageRequester_Error)
EndIf
EndProcedure
;test waitexecute
Exiftool::WaitReady(-1) ;TODO First wait Exiftool is initialized
Exiftool::Command("-ver")
Define Ei.i=Exiftool::Execute()
If Exiftool::WaitExecute(Ei,-1)=#True;TODO Wait the Exiftool data return berfore continue -1 = No wait limit. But you can put a TimeOut
Debug "You Use Exiftool version "+Exiftool::GetResultStdout(Ei)+"You can Thanks Phil Harvey for this great tool"
If Exiftool::GetResultStdErr(Ei)<>""
Debug "You Have an Error:"+Exiftool::GetResultStdErr(Ei)
EndIf
Else
Debug "Go out before Execute finish"
EndIf
Exiftool::FreeResult(Ei) ;TODO After take data clean used memory
;Declare.i Execute(Event.i=0,WindowEvent.i=0,*ReturnDataProcedure=0)
If OpenWindow(#Win_Main, 0, 0, 800, 250,"ExifTool test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ButtonGadget(#Gdt_LoadImage,0,0,320,25,"Load Image")
CanvasGadget(#Gdt_Image,0,25,320,200)
ListIconGadget(#Gdt_MetaList,800-400,0,400,250,"Name",200)
AddGadgetColumn(#Gdt_MetaList,1,"Value",200)
StringGadget(#Gdt_Description,0,225,320,25,""):GadgetToolTip(#Gdt_Description,"Description")
TextGadget(#Gdt_State,340,10,50,25,"")
ButtonGadget(#Gdt_OnOff,340,50,50,25,"ON/OFF")
ButtonGadget(#Gdt_Save,320,225,50,25,"SAVE")
Define Event.i
Repeat
Event=WaitWindowEvent()
Select Event
Case #PB_Event_Gadget
Select EventGadget()
Case #Gdt_LoadImage
Define FilePath.s
Define.l Width,Height,x,y
Define.i Image
Define.f ImgRatio,ContRatio
FilePath.s=OpenFileRequester("Choose Image", "", "*.jpg|*.pef", 0 )
If FilePath<>"" And FileSize(FilePath)>0 And (LCase(GetExtensionPart(FilePath))="jpg" Or LCase(GetExtensionPart(FilePath))="pef")
Select #Mode
Case #ModeExtractPreviewImage
Exiftool::Command("-b")
;Exiftool::Command("-ThumbnailImage")
Exiftool::Command("-JpgFromRaw")
;TODO This Toow Line are very Important to Support Path With Special Character Or You Will Have "File Not Found" Error.
Exiftool::Command("-charset")
Exiftool::Command("FILENAME=utf8")
Exiftool::Command(FilePath)
Ei.i=Exiftool::Execute()
If Exiftool::WaitExecute(Ei,-1)=#True
If Exiftool::GetResultRawOut(Ei)>0 ;Always check if your are a Memory pointer or 0
Debug MemorySize(Exiftool::GetResultRawOut(Ei))
Image.i=CatchImage(#PB_Any,Exiftool::GetResultRawOut(Ei))
Else
MessageRequester("Oups !","No Preview Image on this file May be use a photo and not juste a jpg image",#PB_MessageRequester_Info|#PB_MessageRequester_Ok)
EndIf
Exiftool::FreeResult(Ei) ;TODO After take data clean used memory
EndIf
Case #ModeSimpleLoadImage
Image.i=LoadImage(#PB_Any,FilePath)
EndSelect
If Image>0
ImgRatio = ImageWidth(Image) / ImageHeight(Image)
ContRatio = GadgetWidth(#Gdt_Image) /GadgetHeight(#Gdt_Image)
If ImgRatio<ContRatio
Width=ImageWidth(Image)*GadgetHeight(#Gdt_Image)/ImageHeight(Image)
height=GadgetHeight(#Gdt_Image)
x=(GadgetWidth(#Gdt_Image)-Width)/2
y=0
Else
Width=GadgetWidth(#Gdt_Image)
height=ImageHeight(Image)*GadgetWidth(#Gdt_Image)/ImageWidth(Image)
x=0
y=(GadgetHeight(#Gdt_Image)-height)/2
EndIf
StartDrawing(CanvasOutput(#Gdt_Image))
DrawImage(ImageID(Image),x,y,Width,Height)
StopDrawing()
EndIf
Exiftool::Command("-s") ; -s[NUM] (-short) Short output format
Exiftool::Command("-args") ; -args (-argFormat) Format metadata as exiftool arguments
Exiftool::Command("-a") ; -a (-duplicates) Allow duplicate tags to be extracted
Exiftool::Command("-g") ; -g[NUM...] (-groupHeadings) Organize output by tag group
Exiftool::Command("-G") ; -G[NUM...] (-groupNames) Print group name for each tag
Exiftool::Command("-n") ; -n (--printConv) No print conversion (output the coordinates as signed decimal degrees)
;TODO This Toow Line are very Important to Support Path With Special Character Or You Will Have "File Not Found" Error.
Exiftool::Command("-charset")
Exiftool::Command("FILENAME=utf8")
Exiftool::Command(FilePath)
Exiftool::Execute(#Cust_Event_Exiftools_Read_Metadatas,#Win_main) ;TODO Exemple to use Event to return data
EndIf
Case #Gdt_OnOff
If Exiftool::IsRun()
Debug "Debug OFF"
Exiftool::Stop()
Else
Debug "Debug ON"
Exiftool::Start()
EndIf
Case #Gdt_Save
Exiftool::Command("-Exif:ImageDescription="+GetGadgetText(#Gdt_Description))
;TODO This Toow Line are very Important to Support Path With Special Character Or You Will Have "File Not Found" Error.
Exiftool::Command("-charset")
Exiftool::Command("FILENAME=utf8")
Exiftool::Command(FilePath)
Exiftool::Execute(0,0,@ReturnData()) ;TODO Exemple to Use CallBack to return data
EndSelect
Case #Cust_Event_Exiftools_Read_Metadatas
Define n.l
Define ExecuteID.i
Define Backdata.s
Define line.s
Define pos.l
Define name.s
Define value.s
ExecuteID.i=EventData() ;TODO Get ExecuteID who send this Event
Backdata.s=Exiftool::GetResultStdout(ExecuteID) ;Get the Exiftool Return
Exiftool::FreeResult(Ei) ;TODO After take data clean used memory
ClearGadgetItems(#Gdt_MetaList)
Backdata=ReplaceString(Backdata,Chr(13)+Chr(10),Chr(13))
For n=1 To CountString(Backdata,Chr(13))
line=StringField(Backdata,n,Chr(13))
pos=FindString(line,"=")
name=Trim(Mid(line,1,pos-1))
value=Trim(Mid(line,pos+1,Len(line)-pos+1))
If name="-EXIF:ImageDescription"
SetGadgetText(#Gdt_Description,value)
EndIf
AddGadgetItem(#Gdt_MetaList,-1,name+Chr(10)+value)
Next
EndSelect
;Just Check if Exiftool is Ready
If Exiftool::IsRun()
SetGadgetText(#Gdt_State,"ON")
Else
SetGadgetText(#Gdt_State,"OFF")
EndIf
Until Event=#PB_Event_CloseWindow
Exiftool::Stop()
EndIf
CompilerEndIf