agi.ffmpeg.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package agi
  2. import (
  3. "errors"
  4. "log"
  5. "os"
  6. "path/filepath"
  7. "github.com/robertkrimen/otto"
  8. uuid "github.com/satori/go.uuid"
  9. "imuslab.com/arozos/mod/agi/static"
  10. "imuslab.com/arozos/mod/agi/static/ffmpegutil"
  11. "imuslab.com/arozos/mod/utils"
  12. )
  13. /*
  14. AJGI FFmpeg adaptor Library
  15. This is a library for allow the use of ffmpeg via the arozos virtualized layer
  16. without the danger of directly accessing the bash / shell interface.
  17. Author: tobychui
  18. */
  19. func (g *Gateway) FFmpegLibRegister() {
  20. err := g.RegisterLib("ffmpeg", g.injectFFmpegFunctions)
  21. if err != nil {
  22. log.Fatal(err)
  23. }
  24. }
  25. func (g *Gateway) injectFFmpegFunctions(payload *static.AgiLibInjectionPayload) {
  26. vm := payload.VM
  27. u := payload.User
  28. scriptFsh := payload.ScriptFsh
  29. //scriptPath := payload.ScriptPath
  30. //w := payload.Writer
  31. //r := payload.Request
  32. vm.Set("_ffmpeg_conv", func(call otto.FunctionCall) otto.Value {
  33. //Get the input and output filepath
  34. vinput, err := call.Argument(0).ToString()
  35. if err != nil {
  36. g.RaiseError(err)
  37. return otto.FalseValue()
  38. }
  39. voutput, err := call.Argument(1).ToString()
  40. if err != nil {
  41. g.RaiseError(err)
  42. return otto.FalseValue()
  43. }
  44. if voutput == "" {
  45. //Output filename not provided. Not sure what format to convert
  46. g.RaiseError(err)
  47. return otto.FalseValue()
  48. }
  49. compression, err := call.Argument(2).ToInteger()
  50. if err != nil {
  51. //Do not use compression
  52. compression = 0
  53. }
  54. //Rewrite the vpath if it is relative
  55. vinput = static.RelativeVpathRewrite(scriptFsh, vinput, vm, u)
  56. voutput = static.RelativeVpathRewrite(scriptFsh, voutput, vm, u)
  57. //Translate the virtual path to realpath for the input file
  58. fsh, rinput, err := static.VirtualPathToRealPath(vinput, u)
  59. if err != nil {
  60. g.RaiseError(err)
  61. return otto.FalseValue()
  62. }
  63. //Translate the virtual path to realpath for the output file
  64. fsh, routput, err := static.VirtualPathToRealPath(voutput, u)
  65. if err != nil {
  66. g.RaiseError(err)
  67. return otto.FalseValue()
  68. }
  69. //Buffer the file to tmp
  70. //Note that even for local disk, it still need to be buffered to make sure
  71. //permission is in-scope as well as to avoid locking a file by child-process
  72. bufferedFilepath, err := fsh.BufferRemoteToLocal(rinput)
  73. if err != nil {
  74. g.RaiseError(err)
  75. return otto.FalseValue()
  76. }
  77. //fmt.Println(rinput, routput, bufferedFilepath)
  78. //Convert it to target format using ffmpeg
  79. outputTmpFilename := uuid.NewV4().String() + filepath.Ext(routput)
  80. outputBufferPath := filepath.Join(filepath.Dir(bufferedFilepath), outputTmpFilename)
  81. err = ffmpegutil.FFmpeg_conv(bufferedFilepath, outputBufferPath, int(compression))
  82. if err != nil {
  83. //FFmpeg conversion failed
  84. g.RaiseError(err)
  85. //Delete the buffered file
  86. os.Remove(bufferedFilepath)
  87. return otto.FalseValue()
  88. }
  89. if !utils.FileExists(outputBufferPath) {
  90. //Fallback check, to see if the output file actually exists
  91. g.RaiseError(errors.New("output file not found. Assume ffmpeg conversion failed"))
  92. //Delete the buffered file
  93. os.Remove(bufferedFilepath)
  94. return otto.FalseValue()
  95. }
  96. //Conversion completed
  97. //Delete the buffered file
  98. os.Remove(bufferedFilepath)
  99. //Upload the converted file to target disk
  100. src, err := os.OpenFile(outputBufferPath, os.O_RDONLY, 0755)
  101. if err != nil {
  102. g.RaiseError(err)
  103. //Delete the output buffer if failed
  104. os.Remove(outputBufferPath)
  105. return otto.FalseValue()
  106. }
  107. defer src.Close()
  108. err = fsh.FileSystemAbstraction.WriteStream(routput, src, 0775)
  109. if err != nil {
  110. g.RaiseError(err)
  111. //Delete the output buffer if failed
  112. os.Remove(outputBufferPath)
  113. return otto.FalseValue()
  114. }
  115. //Upload completed. Remove the remaining buffer file
  116. os.Remove(outputBufferPath)
  117. return otto.TrueValue()
  118. })
  119. vm.Run(`
  120. var ffmpeg = {};
  121. ffmpeg.convert = _ffmpeg_conv;
  122. `)
  123. }