0

I've written a small python script for mass converting audio files. It uses ffmpeg. The problem is it doesn't work for files with single quotes in their filenames.

script:


import os
import subprocess
import sys
from multiprocessing.pool import ThreadPool as Pool

source_dir="/home/kris/Music/test"
output_dir="/home/kris/Music/test_opus"

def worker(file):

    try:    
        dirname=os.path.dirname(file)
        file=file.replace("'","\\\\'")
        filename=file.split('.flac')[0]
        
        input_file=f"'{source_dir}/{file}'"
        output_file=f"'{output_dir}/{filename}.opus'"
        cmd="ffmpeg -n -i "+input_file+" -c:a libopus -b:a 320k "+output_file
        print(cmd)
        result = subprocess.call(cmd,stdout=subprocess.PIPE,shell=True)
    except:
        print('item error')
        

def start():    
    threads=os.cpu_count()  
    pool = Pool(threads)
    files=os.listdir(source_dir)
    for item in files:
        pool.apply_async(worker, (item,))

    pool.close()
    pool.join()
        
start()

Testing:

  1. Filename: I'm a file.flac

  2. When escaping single quote ' with double backslashes \\ - file=file.replace("'","\\\\'") the cmd for ffmpeg is:

ffmpeg -n -i '/home/kris/Music/test/I\\'m a file.flac' -c:a libopus -b:a 320k '/home/kris/Music/test_opus/I\\'m a file.opus'

ffmpeg returns an error: /home/kris/Music/test/I\\m: No such file or directory

  1. When escaping single quote ' with a single backslash \ - file=file.replace("'","\\'") the cmd for ffmpeg is:
ffmpeg -n -i '/home/kris/Music/test/I\'m a file.flac' -c:a libopus -b:a 320k '/home/kris/Music/test_opus/I\'m a file.opus'

I got an error:

/bin/sh: 1: Syntax error: Unterminated quoted string

According to ffmpeg docs: https://ffmpeg.org/ffmpeg-utils.html#toc-Examples escaping with single backslash should work.

XorOrNor
  • 8,868
  • 12
  • 48
  • 81
  • 1
    Shell quoting is always a headache and the best way to deal with it, imho, is to avoid it as much as possible. Unless you *really* need that shell, call `subprocess.call` with `shell=False` (this is, very sensibly, the default) and pass the command as a list, not a string. – Ture Pålsson Sep 18 '22 at 13:44
  • In addition, if you really have to quote something, use `shlex.quote` rather than manually guess what quotes or how many you need. The error message sounds like the file name already contains a literal single quote; but `shlex` already knows exactly how to cope with this, and all the myriad other corner cases. – tripleee Sep 18 '22 at 14:01

0 Answers0