нужно передать python vars в команду bash avconv

bash hacker здесь промочил ноги с помощью python. Я почти перенес один из своих скриптов bash на python, но не могу понять, как передать переменные python команде bash avconv для преобразования аудиофайлов .ogg в файлы .mp3 (sigh mp3 player не воспроизводит .ogg).

в любом случае, я думал, что вставлю то, что у меня есть, и все работает так, как хотелось бы, до последних четырех строк «если» в последнем цикле «для». Конечно, bash/avconv не распознает python var 'f', переданный из цикла python 'for', и не уверен, как это сделать.

#!/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)

ОБНОВЛЕНО: вставка только последнего раздела вышеприведенного скрипта, касающегося случайного выбора песни, который пытается преобразовать '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

Что выглядит примерно так (иш).

Вывод терминала с раскомментированным окончательным «подпроцессом» выглядит так:

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» расширением «.mp3» как часть процесса avconv? Боже, это выглядит близко, если у кого-нибудь могут быть предложения, которые помогут мне освободиться от дома.

Кстати, я прочесывал Интернет и, в частности, stackoverflow в поисках подходящей помощи. Поэтому у меня есть RTFM, прежде чем обращаться за помощью. Я просто не могу заставить эту последнюю часть взаимодействовать с оболочкой bash.

спасибо, поспи


person nanker    schedule 30.12.2013    source источник
comment
Не используйте os.system, когда модуль subprocess может дать вам лучшие инструменты. Здесь на SO уже есть множество сообщений о том, как добавлять аргументы в команды subprocess.   -  person Martijn Pieters    schedule 30.12.2013


Ответы (2)


Некоторое время назад я создал аналогичный скрипт для преобразования тонны видео в формат, который можно было бы воспроизводить на одной из моих игровых консолей. Вот что я бы изменил в вашем текущем коде:

Как рекомендовал 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 или возможности установить его на этот компьютер. Однако я заменил строки shutil и 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'))])

Надеюсь это поможет.

person Mike S.    schedule 30.12.2013
comment
Спасибо за подробный ответ. Это точно помогло мне на моем пути, но я все еще застрял, пытаясь передать python vars 'destDir' и 'f' в качестве вывода avconv. Я вставил модификацию, основанную на ваших полезных предложениях (включая предупреждение Мартина Питерса против использования «os.system» и выбор «подпроцесса»). Может быть, у вас есть дополнительные сведения, чтобы помочь мне преодолеть этот последний горб. Ценится. - person nanker; 31.12.2013
comment
@nanker Часть проблемы, с которой вы столкнулись, была рассмотрена в моем обновленном ответе. Я извиняюсь, что не уловил это раньше, но я забыл, что avconv, официально известный как FFmpeg, требует имени файла (по крайней мере, расширения) для вывода. - person Mike S.; 31.12.2013
comment
спасибо, что нашли время, чтобы найти более тщательное решение; к сожалению, «avconv» все еще срабатывает. Вот что возвращает терминал, когда ему не удается преобразовать avconv из .ogg в .mp3: 05:20 python $ ./ranSongAlt.py ['12_Rip_Her_To_Shreds.ogg', ...] avconv version 0.8.9-6:0.8.9-1, Copyright ... Input #0, ogg, from '/media/multiMediaA_intHdA720Gb/music/rockAndHeavier/Blondie/The_Best_Of_Blondie/12_Rip_Her_To_Shreds.ogg': Duration: 00:03:23.29, start: 0.000000, ... /home/keithpc/tmp/rsList/%ogg/mp3/12_Rip_Her_To_Shreds..mp3: No such file or directory - person nanker; 01.01.2014
comment
пытаюсь разобраться с этим форумом, но похоже, что комментарии ограничены количеством символов, поэтому мне пришлось сократить приведенную выше ошибку, возвращаемую terminakl. Я надеюсь, что это все еще читается достаточно легко. Я добавил оператор «print songList» непосредственно перед последним «циклом for», чтобы поместить ошибку «подпроцесса» в лучший контекст. У вас была возможность ознакомиться с решением, которое я разместил ниже? Любые комментарии и предложения о том, как избежать написания сценариев на питоне, будут оценены. Я так привык к «башингу» скриптов, что мне трудно мыслить вне оболочки. - person nanker; 01.01.2014
comment
@nanker Я не совсем уверен, что вы имеете в виду, говоря, что избегаете написания сценариев на питоне. Python — очень надежный язык сценариев и программирования, хотя у него есть свои особенности, как и у любого другого языка. Однако, глядя на предоставленную вами ошибку, я вижу Shreds..mp3: No such file or directory, это говорит мне о том, что строка avconv нуждается в настройке, чтобы исключить лишний период. Что касается вашего решения ниже, я не знаком с модулем re, я имею в виду, что знаю, для чего он нужен, но сам его не использовал. Одна из причин, по которой я прибегнул к словарю, другая — возможность расширять словарь по мере необходимости. - person Mike S.; 02.01.2014
comment
Я пошел дальше и обновил свой ответ, чтобы исключить лишний период, на случай, если кому-то будет любопытно. Проблема возникла из-за f.strip('ogg'), она должна была быть f.strip('.ogg'). - person Mike S.; 03.01.2014

Хорошо, вот, я понял проблему. Я вместо того, чтобы использовать переключатель bash /%ogg/mp3 для изменения расширения файла во время процесса avconv, мне нужно создать переменную sNameOgg, а затем использовать python regex-sub (я большой SED'r, поэтому я рад Вместо этого я обнаружил способ python сделать это, но все же немного критичный (ish) atm), чтобы создать var 'sNameMp3' и передать его в модифицированном var 'destDir', включенном в окончательный subprocess.call.

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])

Для меня все еще немного туманно в отношении всего сценария, поскольку я в основном собираю его по одному фрагменту/фрагменту/блоку за раз, и мне все еще нужно включать функции пользовательского ввода, но, глядя на рабочий результат, он читает чертовски чище, чем его аналог из bash, и это меня радует. Сладкий!

person nanker    schedule 31.12.2013