agi.ffmpeg.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package agi
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "os/exec"
  7. "path/filepath"
  8. "github.com/robertkrimen/otto"
  9. "imuslab.com/arozos/mod/agi/static"
  10. "imuslab.com/arozos/mod/utils"
  11. )
  12. /*
  13. AJGI FFmpeg adaptor Library
  14. This is a library for allow the use of ffmpeg via the arozos virtualized layer
  15. without the danger of directly accessing the bash / shell interface.
  16. Author: tobychui
  17. */
  18. func (g *Gateway) FFmpegLibRegister() {
  19. err := g.RegisterLib("ffmpeg", g.injectFFmpegFunctions)
  20. if err != nil {
  21. log.Fatal(err)
  22. }
  23. }
  24. /*
  25. FFmepg functions
  26. */
  27. func ffmpeg_conv(input string, output string, compression int) error {
  28. var cmd *exec.Cmd
  29. switch {
  30. case isVideo(input) && isVideo(output):
  31. // Video to video with resolution compression
  32. cmd = exec.Command("ffmpeg", "-i", input, "-vf", fmt.Sprintf("scale=-1:%d", compression), output)
  33. case (isAudio(input) || isVideo(input)) && isAudio(output):
  34. // Audio or video to audio with bitrate compression
  35. cmd = exec.Command("ffmpeg", "-i", input, "-b:a", fmt.Sprintf("%dk", compression), output)
  36. case isImage(output):
  37. // Resize image with width compression
  38. cmd = exec.Command("ffmpeg", "-i", input, "-vf", fmt.Sprintf("scale=%d:-1", compression), output)
  39. default:
  40. // Handle other cases or leave it for the user to implement
  41. return fmt.Errorf("unsupported conversion: %s to %s", input, output)
  42. }
  43. // Set the output of the command to os.Stdout so you can see it in your console
  44. cmd.Stdout = os.Stdout
  45. // Set the output of the command to os.Stderr so you can see any errors
  46. cmd.Stderr = os.Stderr
  47. // Run the command
  48. err := cmd.Run()
  49. if err != nil {
  50. return fmt.Errorf("error running ffmpeg command: %v", err)
  51. }
  52. return nil
  53. }
  54. // Helper functions to check file types
  55. func isVideo(filename string) bool {
  56. videoFormats := []string{
  57. ".mp4", ".mkv", ".avi", ".mov", ".flv", ".webm",
  58. }
  59. return utils.StringInArray(videoFormats, filepath.Ext(filename))
  60. }
  61. func isAudio(filename string) bool {
  62. audioFormats := []string{
  63. ".mp3", ".wav", ".aac", ".ogg", ".flac",
  64. }
  65. return utils.StringInArray(audioFormats, filepath.Ext(filename))
  66. }
  67. func isImage(filename string) bool {
  68. imageFormats := []string{
  69. ".jpg", ".png", ".gif", ".bmp", ".tiff", ".webp",
  70. }
  71. return utils.StringInArray(imageFormats, filepath.Ext(filename))
  72. }
  73. func main() {
  74. // Example usage
  75. err := ffmpeg_conv("input.mp4", "output.mp4", 720)
  76. if err != nil {
  77. fmt.Println("Error:", err)
  78. }
  79. }
  80. func (g *Gateway) injectFFmpegFunctions(payload *static.AgiLibInjectionPayload) {
  81. vm := payload.VM
  82. u := payload.User
  83. scriptFsh := payload.ScriptFsh
  84. //scriptPath := payload.ScriptPath
  85. //w := payload.Writer
  86. //r := payload.Request
  87. vm.Set("_ffmpeg_conv", func(call otto.FunctionCall) otto.Value {
  88. //Get the input and output filepath
  89. vinput, err := call.Argument(0).ToString()
  90. if err != nil {
  91. g.RaiseError(err)
  92. return otto.FalseValue()
  93. }
  94. voutput, err := call.Argument(1).ToString()
  95. if err != nil {
  96. g.RaiseError(err)
  97. return otto.FalseValue()
  98. }
  99. if voutput == "" {
  100. //Output filename not provided. Not sure what format to convert
  101. g.RaiseError(err)
  102. return otto.FalseValue()
  103. }
  104. //Rewrite the vpath if it is relative
  105. vinput = static.RelativeVpathRewrite(scriptFsh, vinput, vm, u)
  106. voutput = static.RelativeVpathRewrite(scriptFsh, voutput, vm, u)
  107. //Translate the virtual path to realpath for the input file
  108. fsh, rinput, err := static.VirtualPathToRealPath(vinput, u)
  109. if err != nil {
  110. g.RaiseError(err)
  111. return otto.FalseValue()
  112. }
  113. //Translate the virtual path to realpath for the output file
  114. fsh, routput, err := static.VirtualPathToRealPath(voutput, u)
  115. if err != nil {
  116. g.RaiseError(err)
  117. return otto.FalseValue()
  118. }
  119. //Buffer the file to tmp
  120. //Note that even for local disk, it still need to be buffered to make sure
  121. //permission is in-scope as well as to avoid locking a file by child-process
  122. bufferedFilepath, err := fsh.BufferRemoteToLocal(rinput)
  123. if err != nil {
  124. g.RaiseError(err)
  125. return otto.FalseValue()
  126. }
  127. fmt.Println(rinput, routput, bufferedFilepath)
  128. return otto.TrueValue()
  129. })
  130. }