728x90
반응형

ffmpeg을 이용하여 동영상을 인코딩하는 데스크톱 앱을 만들어야 했다.

 

빠르게 만들기 위해 GitHub에 있을까하고 검색해보니 역시나 있었다.

 

만드는 과정에서 추가로 필요했던 기능들은 다음과 같았다.

  1. 새로 고침 막기

  2. 메뉴 수정

  3. asar로 빌드했을 때 실행 파일 불러오기

새로 고침을 막는 것은 우선 단축키가 아무 일도 하지 않도록 했다.

 

그리고 기본 메뉴에 새로 고침이 있었는데 이것 역시 제거하면서 불필요한 메뉴들을 모두 없앴다.

 

fluent-ffmpeg의 경우 ffmpeg 실행 파일을 불러 쓸 수 있었는데

 

asar로 빌드하는 경우 실행 파일도 같이 포함되어 만들어지나 파일 실행은 할 수 없었다.

 

그래서 ffmpeg, ffprobe 가 특정 디렉토리에 존재하도록 하고 이 디렉토리에 접근할 수 있어야 했다.

 

extraFiles를 이용하여 특정 디렉토리에 파일을 복사할 수 있었다.

 

그리고 getAppPath()로 특정 디렉토리에 접근하여 파일을 불러올 수 있었다.

 

작성한 코드 일부는 다음과 같다.

const { app, globalShortcut, BrowserWindow, Menu } = require('electron');
const path = require('path');
const ffmpeg = require('fluent-ffmpeg');
const isPackaged = process.mainModule.filename.indexOf('app.asar') !== -1;

if (process.platform === "darwin") {
  if (isPackaged) {
    ffmpeg.setFfmpegPath(path.join(path.dirname(app.getAppPath()), '..', './Resources', 'bin/ffmpeg'));
    ffmpeg.setFfprobePath(path.join(path.dirname(app.getAppPath()), '..', './Resources', 'bin/ffprobe'));
  } else {
    ffmpeg.setFfmpegPath(path.join(__dirname, '.', 'bin/osx/ffmpeg'));
    ffmpeg.setFfprobePath(path.join(__dirname, '.', 'bin/osx/ffprobe'));
  }
} else if (process.platform === 'win32') {
  ffmpeg.setFfmpegPath(path.join(__dirname, '.', 'bin/win32/ffmpeg.exe'));
  ffmpeg.setFfprobePath(path.join(__dirname, '.', 'bin/win32/ffprobe.exe'));
} else {
  ffmpeg.setFfmpegPath(path.join(__dirname, '.', 'bin/win64/ffmpeg.exe'));
  ffmpeg.setFfprobePath(path.join(__dirname, '.', 'bin/win64/ffprobe.exe'));
}

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      devTools: false
    }
  });

  const isMac = process.platform === 'darwin';
  const template = [
    ...(isMac ? [{
      label: app.name,
      submenu: [
        { role: 'about' },
        { type: 'separator' },
        { role: 'hide' },
        { role: 'hideothers' },
        { role: 'unhide' },
        { type: 'separator' },
        { role: 'quit' }
      ]
    }] : []),
    {
      role: 'help',
      submenu: [
        {
          label: 'Learn More',
          click: async () => {
            await shell.openExternal('https://electronjs.org');
          }
        }
      ]
    }
  ];
  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
  win.setMenuBarVisibility(false);

  win.loadFile('index.html');

  win.webContents.openDevTools();
}

app.whenReady().then(createWindow);

app.on('browser-window-focus', () => {
    globalShortcut.register('CommandOrControl+R', () => {
    console.log('CommandOrControl+R is pressed: Shortcut Disabled');
});
globalShortcut.register('F5', () => {
    console.log('F5 is pressed: Shortcut Disabled');
});
});

app.on('browser-window-blur', () => {
    globalShortcut.unregister('CommandOrControl+R');
    globalShortcut.unregister('F5');
});
"build": {
  "productName": "My App",
  "appId": "com.my.app",
  "files": [
    "!bin/*"
  ],
  "extraFiles": [
    {
      "from": "bin/osx",
      "to": "Resources/bin",
      "filter": [
        "**/*"
      ]
    }
  ],
  "asar": true,
  "protocols": {
    "name": "myapp",
    "schemes": [
      "myapp"
    ]
  },
  "mac": {
    "target": [
      "dmg"
    ],
    "icon": "resources/icon.icns"
  },
  "dmg": {
    "internetEnabled": true
  },
  "win": {
    "target": [
      "zip",
      "nsis"
    ],
    "icon": "./resources/icon.ico"
  },
  "nsis": {
    "oneClick": false,
    "allowToChangeInstallationDirectory": true
  },
  "directories": {
    "buildResources": "./resources/installer/",
    "output": "./dist/",
    "app": "."
  }
}

 

참고 문헌

  1. https://github.com/likethemammal/electron-ffmpeg-example

  2. https://ffmpeg.zeranoe.com/builds/

  3. https://github.com/fluent-ffmpeg/node-fluent-ffmpeg

  4. https://www.electronjs.org/docs/api/menu

  5. https://discuss.atom.io/t/prevent-browserwindow-from-reloading/20541/3

반응형

'JS' 카테고리의 다른 글

[JS] 크롬에서 fetch 실패  (0) 2020.12.31
[JS] video.js 자동 재생이 안될 때 해결 방법  (0) 2020.09.24
[JS] PWA 만들기  (0) 2020.02.20
[JS] ejs SyntaxError  (0) 2020.01.25
[JS] RegExp 재사용할 때 문제 해결  (0) 2020.01.25
728x90
반응형

Go 프로젝트에서 http 클라이언트를 만들어서 활용하고 있었는데 어느날 서버에 응답이 없었다.

 

그래서 서버에 들어가서 확인해보니 too many open files라는 에러가 나고 있었다.

 

netstat으로 확인해보니 CLOSE_WAIT 상태로 많이 쌓여 있었다.

 

어디가 문제인가 살펴보니 defer resp.Body.Close() 를 하는데 상태값이 200인 경우에만 닫도록 돼있었다.

 

(문서에서 Body를 읽은 경우엔 항상 닫아주라고 써있다.)

resp, err := http.Get("http://example.com/")
if err != nil {
    return
} else if resp.StatusCode != 200 {
    return
}
defer resp.Body.Close()

다음과 같이 수정한 이후 경과를 살펴보니 잘 동작하는 것을 확인할 수 있었다.

resp, err := http.Get("http://example.com/")
if err != nil {
    return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
    return
}

 

참고 문헌

  1. https://golang.org/pkg/net/http/

반응형
728x90
반응형

echo 프레임워크로 파일 업로더 서버를 구축했다.

 

쿡북에서 단일 파일, 여러 파일을 업로드하는 예제가 있어 이를 바탕으로 쉽게 만들 수 있었다.

 

하지만 업로드를 하는 클라이언트에서 폼의 키값을 다른 것으로 하는 경우를 고려해야 했다.

 

그래서 여러 파일을 업로드하는 코드를 참고하여 입맛에 맞게 수정했다.

func upload(c echo.Context) error {
    form, err := c.MultipartForm()
    if err != nil {
        return err
    }

    for _, file := range form.File {
        // Source
        src, err := file[0].Open()
        if err != nil {
            return err
        }
        defer src.Close()

        // Destination
        dst, err := os.Create(file[0].Filename)
        if err != nil {
            return err
        }
        defer dst.Close()

        // Copy
        if _, err = io.Copy(dst, src); err != nil {
            return err
        }
    }
}

그리고 용량이 큰 파일을 업로드하는 경우에 디스크 사용량이 파일 사이즈의 2배가 늘어났다.

 

원인을 찾아보니 go에서 설정한 메모리(32 MB + 10MB)보다 큰 경우 /tmp/에 파일을 저장하고 있었다.

 

다음과 같이 하면 파일을 원하는 곳에 복사한 다음 임시 파일을 모두 제거할 수 있다.

func upload(c echo.Context) error {
    defer func() {
        form, err := ctx.MultipartForm()
	    if err != nil {
    	    return
        }
    	form.RemoveAll()
    }()
    ...
}

 

참고 문헌

  1. https://echo.labstack.com/cookbook/file-upload
  2. https://github.com/labstack/echo/blob/master/context.go#L369
  3. https://github.com/golang/go/blob/master/src/mime/multipart/formdata.go#L86
반응형
728x90
반응형

저번에 해결이 된 줄 알고 좋아했지만 곧 정책 위반으로 삭제가 됐다.

 

이번엔 확실히 해결하기 위해 더 가열차게 검색을 하여 찾아봤다.

 

React Native의 라이프 사이클을 다시 한번 확인했다.

 

 

앱이 로딩되는 중에 광고를 가져오면 안된다고 하여 어느 시점에 해야하는지 살펴봤다.

 

기존에는 constructor에서 호출하고 있었고 setTimeout으로 했지만 

 

아직 앱이 로딩 중이기에 구글에서 위반이라고 판단한 것 같았다.

 

그래서 확실하게 componentDidMount에서 호출하여 광고를 가져오도록 했다.

 

그리고 앱 시작하고 스플래시 화면 다음에 전면 광고가 나오도록 했는데

 

광고를 가져오는 시점이 변경되어 광고가 나오는 시점도 변경했다.

 

2주 정도 지난 현재까지 위반 메일이 오지않는 것으로 봐서 해결된 것 같다.

 

참고 문헌

  1. https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

  2. https://support.google.com/admob/answer/6201362?ref_topic=2745287

반응형

+ Recent posts