……………쉘스크립트를 모릅니다……………
그래서 시켰습니다. 인공지능에게.. 몇십 번의 대화 끝에 필요한 것을 완성하였습니다.
리눅스 환경도 익숙하지 않아서.. 시간은 좀 걸렸습니다.
점점 더 빨라지겠지요.
부족한 건 설치하도록 안내하고, 버그는 log를 주면 분석해서 잡아주더군요.
마지막에 완성된 후, 병렬처리로 만들어 달라고 요청했을 때는 1초만에 바꾸어 주더군요.
앞으로 이전까지의 방식으로 개발할 일은 사라질 겁니다.
#!/bin/bash
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
WATCH_DIR="$PWD"
FFMPEG="/usr/bin/ffmpeg"
LOCKFILE="/tmp/convert.lock"
LOGFILE="$PWD/convert.log"
MIN_FREE_GB=5
MAX_LOAD=14.0
log() {
echo "[$(date)] $1" | tee -a "$LOGFILE"
}
wait_for_complete() {
local file="$1"
local prev_size=0
local cur_size=0
local wait_count=0
log "업로드 완료 대기: $file"
while [ $wait_count -lt 30 ]; do
cur_size=$(stat -c %s "$file" 2>/dev/null)
[ "$cur_size" = "$prev_size" ] && break
prev_size=$cur_size
sleep 10
wait_count=$((wait_count+1))
done
log "업로드 완료 감지: $file"
}
check_disk_space() {
local dir="$1"
local free_gb=$(df -P "$dir" | awk 'NR==2 {print int($4/1024/1024)}')
log "디스크 여유 공간: ${free_gb}GB"
[ "$free_gb" -ge "$MIN_FREE_GB" ]
}
wait_for_low_load() {
while true; do
local load=$(awk '{print $1}' /proc/loadavg)
is_low=$(awk -v l="$load" -v m="$MAX_LOAD" 'BEGIN{print (l<m)?1:0}')
if [ "$is_low" = "1" ]; then
log "시스템 부하 정상(load average: $load). 변환 진행."
break
else
log "시스템 부하가 높음($load), 대기 중..."
sleep 30
fi
done
}
convert_file() {
SRC="$1"
[ -z "$SRC" ] && return
[ ! -f "$SRC" ] && return
# 락 획득(이미 있으면 대기)
while ! ( set -o noclobber; echo "$$" > "$LOCKFILE" ) 2>/dev/null; do
log "다른 변환 작업이 진행 중입니다. 대기 중..."
sleep 10
done
trap 'rm -f "$LOCKFILE"; exit' INT TERM EXIT
BASENAME="$(basename "$SRC")"
DIRNAME="$(dirname "$SRC")"
EXT="${BASENAME##*.}"
FILENAME="${BASENAME%.*}"
TMP_VAAPI="${DIRNAME}/${FILENAME}_vaapi.mkv"
TMP_X265="${DIRNAME}/${FILENAME}_x265.mkv"
FINAL_OUT="${DIRNAME}/${FILENAME}.mkv"
# 변환 시작 전 임시 파일 삭제
if [ -f "$TMP_VAAPI" ]; then
log "임시 VAAPI 파일 삭제: $TMP_VAAPI"
rm -f "$TMP_VAAPI"
fi
if [ -f "$TMP_X265" ]; then
log "임시 x265 파일 삭제: $TMP_X265"
rm -f "$TMP_X265"
fi
[ -f "$FINAL_OUT" ] && { log "이미 변환된 파일이 있어 건너뜀: $FINAL_OUT"; rm -f "$LOCKFILE"; trap - INT TERM EXIT; return; }
wait_for_complete "$SRC"
check_disk_space "$DIRNAME" || { log "디스크 공간 부족. $SRC 변환 건너뜀."; rm -f "$LOCKFILE"; trap - INT TERM EXIT; return; }
wait_for_low_load
log "VAAPI 변환 시작: $SRC → $TMP_VAAPI"
nice -n 10 "$FFMPEG" -vaapi_device /dev/dri/renderD128 -nostdin -v fatal -i "$SRC" \
-vf 'format=nv12,hwupload,scale_vaapi=w=1280:h=720:force_original_aspect_ratio=decrease' \
-c:v hevc_vaapi -b:v 4M -maxrate 4M -bufsize 8M -profile:v main \
-c:a aac -b:a 128k -ac 2 -ar 44100 \
-y "$TMP_VAAPI" >/dev/null 2>>"$LOGFILE"
if [ $? -eq 0 ] && [ -f "$TMP_VAAPI" ]; then
log "VAAPI 변환 성공: $TMP_VAAPI"
rm -f "$SRC"
log "원본 삭제: $SRC"
mv "$TMP_VAAPI" "$FINAL_OUT"
log "변환 파일을 원래 이름(.mkv)으로 변경: $FINAL_OUT"
rm -f "$LOCKFILE"
trap - INT TERM EXIT
return
else
log "VAAPI 변환 실패, libx265로 재시도: $SRC"
[ -f "$TMP_VAAPI" ] && rm "$TMP_VAAPI"
nice -n 10 "$FFMPEG" -nostdin -v fatal -i "$SRC" \
-c:v libx265 -preset medium -crf 23 -r 29.97 -threads 12 \
-vf "scale=1280:720:flags=bicubic,setsar=1,pad=1280:720:(ow-iw)/2:(oh-ih)/2" \
-c:a aac -b:a 128k -ac 2 -ar 44100 \
-y "$TMP_X265" >/dev/null 2>>"$LOGFILE"
if [ $? -eq 0 ] && [ -f "$TMP_X265" ]; then
log "libx265 변환 성공: $TMP_X265"
rm -f "$SRC"
log "원본 삭제: $SRC"
mv "$TMP_X265" "$FINAL_OUT"
log "변환 파일을 원래 이름(.mkv)으로 변경: $FINAL_OUT"
else
log "libx265 변환 실패: $SRC"
[ -f "$TMP_X265" ] && rm "$TMP_X265"
fi
rm -f "$LOCKFILE"
trap - INT TERM EXIT
fi
}
# 1. 기존 파일(서브폴더 포함) 모두 변환
log "기존 파일 전체 변환 시작: $WATCH_DIR (서브폴더 포함)"
find "$WATCH_DIR" -type f \( -iname '*.mp4' -o -iname '*.avi' -o -iname '*.mov' -o -iname '*.flv' -o -iname '*.mpg' -o -iname '*.mpeg' -o -iname '*.wmv' -o -iname '*.webm' \) | while read -r EXISTFILE; do
[ -z "$EXISTFILE" ] && continue
[ ! -f "$EXISTFILE" ] && continue
convert_file "$EXISTFILE" &
wait
done
log "기존 파일 전체 변환 완료"
# 2. 신규 파일(서브폴더 포함) 감시 및 변환
log "폴더 감시 시작(서브폴더 포함): $WATCH_DIR"
command -v inotifywait >/dev/null 2>&1 || { echo "inotifywait가 필요합니다. apt install inotify-tools"; exit 1; }
inotifywait -m -r -e create --format '%w%f' "$WATCH_DIR" | while read -r NEWFILE; do
[ -z "$NEWFILE" ] && continue
[ ! -f "$NEWFILE" ] && continue
EXT="${NEWFILE##*.}"
case "$EXT" in
mp4|avi|mov|flv|mpg|mpeg|wmv|webm)
convert_file "$NEWFILE" &
wait
;;
*)
;;
esac
done
………………………………………………………………………………………………………………………….
복잡해졌네... 내용을 분석해줘.
답변
네, 스크립트가 조금 복잡해졌지만,
동시 변환 1개만 보장하고,
find 루프와 inotifywait 감시 루프가 동시에 돌아도 안정적으로 동작하도록 설계된 구조입니다.
아래에 각 부분별로 쉽게 분석해드릴게요!
1. 전역 변수 및 환경 설정
WATCH_DIR: 감시/변환할 폴더(현재 디렉터리)
FFMPEG: 사용할 ffmpeg 바이너리 경로
LOCKFILE: 동시 변환 방지용 락 파일 경로
LOGFILE: 로그 파일
MIN_FREE_GB/MAX_LOAD: 디스크/부하 체크 기준
2. 주요 함수
log()
로그 메시지를 파일과 화면에 동시에 출력
wait_for_complete()
파일 업로드가 끝났는지 감지(파일 크기가 10초마다 변하는지 체크, 5분까지 대기)
check_disk_space()
변환 디렉터리의 남은 디스크 공간이 MIN_FREE_GB 이상인지 검사
wait_for_low_load()
시스템 load average가 MAX_LOAD 미만이 될 때까지 30초마다 체크(부하가 낮아질 때까지 대기)
3. 핵심: convert_file() 함수
동작 순서
LOCKFILE 획득(중복 변환 방지)
이미 LOCKFILE이 있으면 10초마다 대기, 락이 풀릴 때까지 기다림
락을 획득하면 바로 변환 시작
파일명/경로/임시파일명/최종파일명 변수 설정
예: 원본이 movie.mp4면 임시는 movie_vaapi.mkv, 최종은 movie.mkv
이미 변환된 파일/임시파일이 있으면 건너뜀
업로드 완료/디스크/부하 체크
VAAPI 하드웨어 인코딩 시도
성공하면 원본 삭제, 임시파일을 movie.mkv로 이름 변경
실패 시 libx265 소프트웨어 인코딩 시도
성공하면 원본 삭제, 임시파일을 movie.mkv로 이름 변경
락 해제 및 trap 해제
변환이 끝나면 LOCKFILE 삭제, 비정상 종료 시에도 trap으로 삭제
4. 기존 파일 일괄 변환 루프
find로 모든 기존 동영상 파일을 검색해서
convert_file을 background로 실행(&) 후 wait로 직렬 처리즉, 루프는 빠르게 돌지만 실제 변환은 1개씩만 실행됨
5. 신규 파일 감시 루프
inotifywait로 폴더(서브폴더 포함)에서 새 동영상 파일 생성 감시
새 파일이 감지되면 convert_file을 background로 실행(
&) 후 wait로 직렬 처리
6. 동시 변환 1개만 보장
find 루프와 inotifywait 루프가 동시에 동작해도
convert_file 함수에서 LOCKFILE을 획득해야만 변환을 시작
→ 두 루프가 서로 다른 프로세스여도 동시 변환이 절대 발생하지 않음
7. 기타
임시 파일명/최종 파일명 관리로
변환 중 파일 충돌, 미완성 파일 문제 방지trap으로 강제 종료 시에도 LOCKFILE 자동 삭제
요약
동시 변환 1개만 보장 (find, inotifywait 루프가 동시에 돌아도)
변환 중에는 LOCKFILE로 다른 변환 차단
VAAPI 실패 시 libx265 fallback
변환 성공 시 원본 삭제, 결과는 .mkv로 저장
부하/디스크/업로드 완료 등 꼼꼼히 체크
더 간단한 구조,
동시 변환 개수 조절(예: 2~3개까지 허용),
혹은 queue 방식이 필요하다면
언제든 요청해 주세요!
사양을 바보도 알수 있게 만들어줘.
답변
알겠습니다!
"바보도 알 수 있게" 아주 쉽게,
이 스크립트의 동작 방식과 사양을 설명해 드릴게요.
🖥️ 이 스크립트, 뭐 하는 거야?
동영상 파일을 자동으로 변환해줘요.
변환이 끝나면 원본 파일은 지우고, 변환된 파일을 .mkv로 저장해요.
한 번에 하나씩만 변환해요. (여러 개 동시에 안 함)
새 파일이 들어오면(폴더 감시) 자동으로 변환해줘요.
⚙️ 어떻게 동작해?
폴더에 있는 모든 동영상 파일을 하나씩 변환해요.
변환이 끝나면, 원래 파일은 삭제하고,
변환된 파일은 원래 이름에 .mkv 확장자로 바꿔줘요.새로운 동영상 파일이 폴더에 들어오면
자동으로 변환을 시작해요.동시에 2개, 3개 변환? 안 돼요!
한 번에 딱 하나만 변환해요.
🛡️ 왜 한 번에 하나만 변환해?
여러 개를 동시에 변환하면 컴퓨터가 너무 힘들어져서
느려지거나 멈출 수 있어요.그래서 안전하게 하나씩만 변환해요.
🚦 변환 순서
변환 중에 다른 변환이 시작되면?
"다른 변환 작업이 진행 중입니다. 대기 중..."
이라고 뜨고, 앞에 거 끝날 때까지 기다려요.
변환 성공하면?
원본 파일은 삭제!
변환된 파일은 .mkv로 이름 바꿔서 저장!
변환 실패하면?
임시 파일도 싹~ 지워요.
📂 변환되는 파일 예시
movie.mp4→ 변환 후 →movie.mkvvideo.avi→ 변환 후 →video.mkv
🧑🔬 이 스크립트의 장점!
자동 변환: 사람이 일일이 안 해도 됨!
원본 자동 삭제: 저장 공간 아끼기!
한 번에 하나만 변환: 컴퓨터 과부하 방지!
새 파일도 자동 변환: 폴더에 넣기만 하면 끝!
📝 사용법
스크립트 파일을 저장하고,
실행 권한을 주고(
chmod +x 파일명.sh),실행하면 끝! (
./파일명.sh)
🤖 한마디 요약
이 스크립트는 "동영상 파일을 폴더에 넣기만 하면,
자동으로 변환해서 .mkv로 만들어주고,
원본은 지워주는 똑똑한 도우미"입니다!
(한 번에 하나씩만 변환해서 컴퓨터가 힘들지 않아요)
더 궁금한 점 있으면 언제든 물어보세요!
교수님에게 제공하는 품질.
교수님께 제공하는 영상 변환 품질에 대해
아주 쉽게, 그리고 신뢰할 수 있게 설명드리면 아래와 같습니다.
1. 변환 결과 파일의 해상도와 포맷
2. 비디오 품질 설정
비디오 코덱: HEVC(H.265, vaapi 하드웨어 인코딩 우선, 실패 시 x265 소프트웨어 인코딩)
→ 최신 고효율 압축 방식으로,
같은 화질에서 더 작은 용량을 제공합니다356.비트레이트: 4Mbps
→ 720p 영상에서 "고화질"로 평가되는 값(2~5Mbps가 일반적)3.오디오: AAC, 128kbps, 스테레오
→ 강의/일반 영상에 충분한 음질.
3. 인코딩 품질의 의미
화질:
원본이 4K, FHD 등 더 높은 해상도여도
720p로 변환되므로,
대부분의 온라인 강의·프레젠테이션·유튜브 영상과
동등하거나 그 이상의 품질을 보장합니다35.용량:
한 시간짜리 영상 기준 약 1.5~2GB 내외(영상 내용에 따라 다름).
4. 전문가적 설명(참고)
비트레이트(4Mbps)와 해상도(720p)는
온라인 강의, 학술 발표, 유튜브 업로드 등
공개/공유 목적에 적합한 고화질 표준입니다35.HEVC(H.265) 코덱은
기존 H.264보다 파일 크기는 더 작고,
화질은 동등하거나 더 좋게 유지됩니다356.mkv 포맷은
자막, 여러 오디오 트랙 등도 지원하며
장기 보관/배포에 적합합니다6.
5. 추가 안내
6. 아주 쉽게 한 줄 요약
교수님께 제공되는 영상은
"일반적으로 강의·유튜브·온라인 공개에 쓰이는
고화질(HD, 720p), 최신 압축방식(HEVC),
누구나 쉽게 볼 수 있는 .mkv 파일"로
변환되어 전달됩니다.
필요하다면
더 높은 해상도(1080p, 4K)
더 낮은 용량
자막/오디오 추가
등도 맞춤 안내 가능합니다!