1

bash 黑客在这里用 python 弄湿了我的脚。我几乎已经将我的一个 bash 脚本移植到 python,但我似乎无法弄清楚如何将 python vars 传递给 bash 命令 avconv 以将 .ogg 音频文件转换为 .mp3 文件(叹息mp3 播放器不播放 .ogg)。

无论如何,我想我会粘贴我到目前为止所拥有的内容,并且在最终的“for”循环中的“if”的最后四行之前,它都可以正常工作。当然 bash/avconv 不能识别从 python 'for' 循环传递的 python var 'f',并且不知道如何去做。

#!/usr/bin/python

#genre = GET SONG GENRE DIR
#if genre == rock+
#    sGenreDir = /media/multiMediaA_intHdA720Gb/music/heavierAlt

import os

def get_filepaths(directory):

    #This function will generate the file names in a directory tree by walking the tree either top-down or bottom-up. For each directory in the tree rooted at directory top (including top itself), it yields a 3 tuple (dirpath, dirnames, filenames).

    file_paths = []     # create list to store full paths to each song contained recursively within

    # walk the tree
    for root, directories, files in os.walk(directory):

        for filename in files:

            # join the two strings in order to know the fill file path
            filepath = os.path.join(root, filename)

            file_paths.append(filepath)     # add it to the list

    return file_paths

# run the avove function and store its results in a variable
#full_file_paths = get_filepaths("/media/multiMediaA_intHdA720Gb/music/heavierAlt")
full_file_paths = get_filepaths("/media/multiMediaA_intHdA720Gb/music/rockAndHeavier")

#print len(full_file_paths)

cntr = 0
for f in full_file_paths:

    if not (f.endswith(".mp3") or f.endswith(".ogg")):
        del full_file_paths[cntr]

    cntr = cntr + 1

#print len(full_file_paths)

# create random song list avoiding duplicate songs
destDir =  "/home/keithpc/tmp/rsList"    # will be selected by user
numSongs = 10   # number of songs will be chosen by the user
songList = []   # list to hold randomly selected songs
cntr = 1    # that will be incremented for each song appended to list 'songList'
while cntr <= numSongs:    #begin random song selection/list creation
    from random import choice
    newSong = choice(full_file_paths)

    if not newSong in songList:    # 'if' newSong is not yet list element then add it and ++cntr
        songList.append(newSong)
        cntr = cntr + 1

print len(songList)
for f in songList:
    print f

for f in songList:
    if f.endswith(".mp3"):    # cp .mp3 to player
        import shutil
        shutil.copy2(f, destDir)
    else:    # need to avconv .ogg before copying to player
        import os
        cmd = 'avconv -i f -c:a libmp3lame -q:a 4 -map_metadata 0:s $destDir/f/%ogg/mp3'
        os.system(cmd)

更新:仅粘贴上述脚本的最后一部分,处理随机歌曲选择,尝试将 avconv 'ogg' 动态转换为 '.mp3' 从 src 复制到 dest

# begin to compile random song list
#destDir =  "/home/keithpc/tmp/rsList"   # will be var supplied by user ; employed in 'for' loop below instead
numSongs = 3   # number of songs var will be supplied by user
songList = []   # list to hold randomly selected songs
cntr = 1    # that will be incremented for each song appended to list 'songList'
while cntr <= numSongs:     # 'while' loop to iterate until 'numSongs' is matched by 'cntr'
    from random import choice
    newSong = choice(lFullFilePaths)    # randomly selected song from list 'lFullFilePaths' to add to list 'songList' if it is not a duplicate

    if not newSong in songList:     # 'if' var does not exist in list 'songList' then add it and cntr++
        songList.append(newSong)
        cntr = cntr + 1

#print len(songList)
#for f in songList:
#    print f

for f in songList:
    destDir =  "/home/keithpc/tmp/rsList"   # will be var supplied by user ; needs to reset each iteration or else it concantanates with previous song details
    if f.endswith(".mp3")   # 'if' song type is '.mp3' then copy direct to mp3 player
        import shutil
        shutil.copy2(f, destDir)
    else:   # 'else' song type must be '.ogg' and so must be avcon'd to mp3 on the fly to mp3 player
        import re   # regex function to extract the song name from its path
        sName = re.sub(r'^.*/', r'', f)     # extract song name from it's path
        #print sName
        import subprocess
        destDir = destDir + "/" + sName + "/%ogg/mp3"   # create single var 'destDir' containing args bash/avconv should need to convert '.ogg' sending its output as '.mp3' to mp3 player
        print(destDir)
        subprocess.call(['avconv', '-i', '%s' % f, '-c:a', 'libmp3lame', '-q:a', '4', '-map_metadata', '0:s', '%s' % destDir])

注释掉最终的“子进程”命令时的终端输出如下:

07:54 python $ ./ranS*.py
3
/media/multiMediaA_intHdA720Gb/music/rockAndHeavier/Radiohead/The_Best_Of/02_Paranoid_Android.ogg
/media/multiMediaA_intHdA720Gb/music/rockAndHeavier/Rod_Stewart/If_We_Fall_In_Love_Tonight/
15_All_For_Love__With_Bryan_Adams_And_Sting.ogg
/media/multiMediaA_intHdA720Gb/music/rockAndHeavier/Smashing_PumpkinsThe/Siamese_Dream/12_sweet_sweet.ogg
02_Paranoid_Android.ogg
/home/keithpc/tmp/rsList/02_Paranoid_Android.ogg/%ogg/mp3
15_All_For_Love__With_Bryan_Adams_And_Sting.ogg
/home/keithpc/tmp/rsList/15_All_For_Love__With_Bryan_Adams_And_Sting.ogg/%ogg/mp3
12_sweet_sweet.ogg
/home/keithpc/tmp/rsList/12_sweet_sweet.ogg/%ogg/mp3

这看起来是正确的(ish)。

最终“子进程”未注释的终端输出如下所示:

07:57 python $ ./ranS*.py
3
/media/multiMediaA_intHdA720Gb/music/rockAndHeavier/Guns_N_Roses/Patience_Live_At_The_Ritz_EP_Japanese/03_I_Used_To_Love_Her.ogg
/media/multiMediaA_intHdA720Gb/music/rockAndHeavier/SmithsThe/The_Best_Of_The_Smiths_Vol_1/05_girlfriend_in_a_coma_the_best_of_the_smiths_vol._1.mp3
/media/multiMediaA_intHdA720Gb/music/rockAndHeavier/Oasis/The_Masterplan/14_The_Masterplan.ogg
03_I_Used_To_Love_Her.ogg
/home/keithpc/tmp/rsList/03_I_Used_To_Love_Her.ogg/%ogg/mp3
avconv version 0.8.9-6:0.8.9-1, Copyright (c) 2000-2013 the Libav developers
  built on Nov  3 2013 02:10:51 with gcc 4.7.2
Input #0, ogg, from '/media/multiMediaA_intHdA720Gb/music/rockAndHeavier/Guns_N_Roses/Patience_Live_At_The_Ritz_EP_Japanese/03_I_Used_To_Love_Her.ogg':
  Duration: 00:02:48.69, start: 0.000000, bitrate: 186 kb/s
    Stream #0.0: Audio: vorbis, 44100 Hz, stereo, s16, 192 kb/s
    Metadata:
      ARTIST          : Guns N' Roses
      ALBUM           : Patience (Live At The Ritz) (EP - Japanese)
      TITLE           : I Used To Love Her
      DATE            : 1989
      GENRE           : Rock
      track           : 03
      CDDB            : 680adf09
Unable to find a suitable output format for '/home/keithpc/tmp/rsList/03_I_Used_To_Love_Her.ogg/%ogg/mp3'
14_The_Masterplan.ogg
/home/keithpc/tmp/rsList/14_The_Masterplan.ogg/%ogg/mp3
avconv version 0.8.9-6:0.8.9-1, Copyright (c) 2000-2013 the Libav developers
  built on Nov  3 2013 02:10:51 with gcc 4.7.2
Input #0, ogg, from '/media/multiMediaA_intHdA720Gb/music/rockAndHeavier/Oasis/The_Masterplan/14_The_Masterplan.ogg':
  Duration: 00:05:22.80, start: 0.000000, bitrate: 197 kb/s
    Stream #0.0: Audio: vorbis, 44100 Hz, stereo, s16, 192 kb/s
    Metadata:
      ARTIST          : Oasis
      ALBUM           : The Masterplan
      TITLE           : The Masterplan
      DATE            : 1998
      GENRE           : Rock
      track           : 14
      CDDB            : c00fd90e
Unable to find a suitable output format for '/home/keithpc/tmp/rsList/14_The_Masterplan.ogg/%ogg/mp3'

也许问题出在 /%ogg/mp3 试图将 '.ogg' ext 替换为 '.mp3' ext 作为 avconv 进程的一部分?天哪,它看起来很接近,如果有人可以帮助我回家的话。

顺便说一句,我一直在搜索 inet,尤其是 stackoverflow,以寻求适用的帮助。所以在寻求帮助之前我有 RTFM。我似乎无法让最后一部分与 bash shell 交互。

谢谢,午睡

4

2 回答 2

1

不久前,我创建了一个类似的脚本,将大量视频转换为可以在我的一个游戏机上播放的格式。这是我将在您当前的代码中更改的内容:

正如Martijn Pieters建议的那样,我不会使用 os.system 来调用 avconv,而是试试这个:

for f in songList:
    if f.endswith(".mp3"):    # cp .mp3 to player
        import shutil
        shutil.copy2(f, destDir)
    else:    # need to avconv .ogg before copying to player
        import subprocess
        subprocess.call(['avconv', '-i', '%s' % f, '-c:a',
                         'libmp3lame', '-q:a', '4', '-map_metadata',
                         '0:s', '$destDir/f/%ogg/mp3'])

这是更改的简要说明。首先,os.system现在使用subprocess.call的是导入的代码而不是os. 其次,我将传递给的命令重新格式化avconv为使用'%s' % f而不是直接f,这将告诉 Python“嘿,使用存储的内容f来填充这个空白!”,而原始代码说“Python,在这里使用 f!”。在重新格式化传递的命令时,您会注意到每个段都包含在引号中,这是由于子进程如何解释传递给它的参数。我相信其他人可以比我更好地解释为什么和如何,但简单地说,每个参数之间都会自动添加一个空格。

- 编辑 -

好的,在查看了更新的信息后,我回过头来调整了提供的代码。由于我不在家并且目前没有 avconv 或无法在这台计算机上安装它,因此我无法对此进行全面测试。然而,我确实用打印语句替换了shutiland行,一切“似乎”都可以工作。subprocess.call

这是代码,下面将简要说明更改。

#!/usr/bin/python

import os
import subprocess
import shutil
from random import choice

def get_filepaths(directory):
    filenames = {}
    for root, directories, files in os.walk(directory):
        for file in files:
            filenames[file] = os.path.join(root, file)
    return filenames

def remove_file(d, key):
    r = dict(d)
    del r[key]
    return r

full_file_paths = get_filepaths("/media/multiMediaA_intHdA720Gb/music/rockAndHeavier")

to_remove = []

for file, file_path in full_file_paths.items():
    if not (file.endswith('.mp3') or file.endswith('.ogg')):
        to_remove.append(file)

for item in to_remove:
    full_file_paths = remove_file(full_file_paths, item)

destDir = "/home/keithpc/tmp/rsList"
songList = []
numSongs = 10
cntr = 1

while cntr <= numSongs:
    newSong = choice(full_file_paths.keys())
    if newSong not in songList:
        songList.append(newSong)
        cntr += 1

for f in songList:
    finalDest = destDir + '/%ogg/mp3'
    if f.endswith('.mp3'):
        shutil.copy2(full_file_paths.get(f), '%s/%s' %(destDir, f))
    else:
        subprocess.call(['avconv', '-i', '%s' % full_file_paths.get(f), '-c:a',
            'libmp3lame', '-q:a', '4', '-map_metadata', '0:s',
            '%s/%s.mp3' % (finalDest, f.strip('ogg'))])

所做的第一个更改是在get_filepaths定义中,我觉得最后重命名会更容易一些。它不是像以前那样构建一个列表,而是使用文件的实际文件名作为键创建一个字典,值是文件的整个路径。

接下来是

to_remove = []

def remove_file(d, key):
    r = dict(d)
    del r[key]
    return r

for file, file_path in full_file_paths.items():
    if not (file.endswith('.mp3') or file.endswith('.ogg')):
        to_remove.append(file)

for item in to_remove:
    full_file_paths = remove_file(full_file_paths, item)

这类似于您最初删除不需要的文件但重写以处理字典的方式。

最后一个变化是最后一个for循环。这种调整主要是因为我忘记了 avconv(正式名称为 FFmpeg)的一个重要内容,那就是 avconv 要求我们告诉它目标文件名是什么。如果我没记错的话,shutil 还需要知道目标文件名,至少它在 Windows 上不能正常工作。

for f in songList:
    finalDest = destDir + '/%ogg/mp3'
    if f.endswith('.mp3'):
        shutil.copy2(full_file_paths.get(f), '%s/%s' %(destDir, f))
    else:
        subprocess.call(['avconv', '-i', '%s' % full_file_paths.get(f), '-c:a',
            'libmp3lame', '-q:a', '4', '-map_metadata', '0:s',
            '%s/%s.mp3' % (finalDest, f.strip('.ogg'))])

希望这可以帮助。

于 2013-12-30T13:41:58.090 回答
0

好的,在这里,我发现了问题。我不是在 avconv 过程中使用 bash 开关 /%ogg/mp3 来更改文件扩展名,而是我需要创建一个 'sNameOgg' var,然后使用 python regex-sub(我是一个重要的 SED'r,所以我很高兴我发现了 python 的方式来代替它,但仍然有点神秘(ish)atm)来创建 var 'sNameMp3' 并将其传递到最终 subprocess.call 中包含的修改后的 var 'destDir' 中。

else:
    import re   # regex function to extract the song name from its path
    sNameOgg = re.sub(r'^.*/', r'', f)
    #print sNameOgg
    sNameMp3 = re.sub(r'\.ogg', r'.mp3', sNameOgg)
    #print sNameMp3
    import subprocess
    destDir = destDir + "/" + sNameMp3    # + "/%ogg/mp3"
    print destDir
    subprocess.call(['avconv', '-i', '%s' % f, '-c:a', 'libmp3lame', '-q:a', '4', '-map_metadata', '0:s', '%s' % destDir])

关于整个脚本,我的 atm 仍然有点模糊,因为我基本上一次将一个块/块/块放在一起,我仍然需要合并用户输入功能,但是查看工作结果它读取比它的 bash 对应物干净得多,这让我很高兴。甜的!

于 2013-12-31T01:39:21.933 回答