7月 042013
 

(2017/11/05追記)事情が古くなってきたので、「ゲーム動画のエンコード事情を整理」も参照してください。
(2015/12/21追記)VBScriptコードのAUCパス設定を修正、OneDriveでスクリプト公開。
(2014/08/17追記)VBScriptコードの比較ミスを修正。

AviUtlという有名なフリーソフトがあります。これは単体では動画のカットやフィルタなど簡単な編集ができますが、各種プラグインを導入することで対応フォーマットが増えたり、動画編集機能が大幅に強化されたり・・・とフリーソフトにしてはかなり優秀で、私もエクストルーパーズの動画をニコニコやYouTubeに投稿する際に利用させてもらいました。「拡張編集」プラグインで編集し、「拡張 x264 出力(GUI) Ex」プラグインで.mp4出力しています。編集と.mp4出力がこのソフト1本で完結するのですごくありがたい(もちろん必要な環境を準備すればの話)。

まあ、今回は動画編集じゃなくて、単にエンコードするだけの話なんですが。

HDキャプボ買ってから、エクストルーパーズの対戦動画をよく撮るようになったんですが、これがたまるたまる・・・。録画ファイルは.aviなのですが、そのままだと非常に重いので、.mp4にしないとすぐにHDDがパンパンになってしまいます。今まで、自分でファイル名を入力して、ひとつずつバッチ登録して、バッチ出力で寝てる間に一括してエンコードしてました。が、このバッチ登録は手で行う必要があったため、それがめんどくさくて後回しにしてると、動画がたまるたまる・・・。

で、自動化できないの!?というのが本題。

ここで紹介するスクリプトはOneDrive上に配置したので、下記からダウンロードして下さい。

自動でAviUtlの出力プラグインを利用する

肝心の方法ですが、調べたら、やっぱりありました。

大雑把にやってることを説明すると、AviUtlを外部から操作するための小規模なプログラムを集めた「AviUtl Control」をスクリプトから組み合わせて利用することで自動化を実現しています。「AviUtl Control」にサンプルとして用意されている sample1.vbs スクリプトでは、指定ディレクトリ直下のファイルすべてを出力プラグインによりエンコードしています。流れとしては、次のようになります。

  1. 起動しているAviUtlがあれば特定し、なければ起動する。
  2. 指定ディレクトリ直下のファイルのリストを得る。ファイルがなければ終了。
  3. 修正日時が最も古いファイルを指定の出力プラグインでエンコードし、指定ディレクトリに出力する。
  4. エンコード済みの入力ファイルを指定ディレクトリに移動。
  5. 2.に戻る。

このサンプルスクリプトは先頭にディレクトリの指定とかの記述があるので、ここを書き換えて実行すればいいわけです。スクリプトはVBScriptという言語で書かれていて、Windowsなら標準で使えるというものです。なので、今回必要なものをまとめると以下のようになります。

  • AviUtl本体
  • 利用したいAviUtl出力プラグイン(私は「拡張 x264 出力(GUI) Ex」を利用)
  • AviUtl Control(と付属するサンプルスクリプト)

サンプルスクリプトの修正

サンプルスクリプトでも最低限の要求は満たしていると思いますが、サブディレクトリが考慮されないとか、チェックが甘いので途中で実行をやめたいときに困るとかいった問題があるので、これに対応したいと思います。修正版は以下になります。

'''
''' AviUtlエンコード&ファイル移動
'''
' あるフォルダの中のサブディレクトリ下も含めたすべてのファイルについて、
' プロファイル&出力プラグイン指定でエンコードし、
' エンコードが終わったら指定のフォルダに移動する

' 元のサンプルスクリプトのtypo修正およびウインドウが消滅したとき
' スクリプトを停止するようにした

' VBScript 参考
' http://vbscript.g.hatena.ne.jp/cx20/20100131/1264906231
' http://www.vacant-eyes.jp/Tips/twsh/170.aspx

Option Explicit
'On Error Resume Next

' キャプチャしたファイルがあるフォルダ(最後の文字は"\")
Const SOURCE_FOLDER = "C:\Users\user\Videos\amarec\_auto_encode_targets\"

' エンコードが終わったファイルを移すフォルダ(最後の文字は"\") ※必ず SOURCE_FOLDER と違う場所にする
Const MOVE_FOLDER = "C:\Users\user\Videos\amarec\_auto_encode_complete\"

' エンコードしたデータを出力するフォルダ(最後の文字は"\")
Const OUTPUT_FOLDER = "C:\Users\user\Videos\aviutl\_auto_encoded\"

' プロファイル番号(メニューの一番上が0)
Const OUTPUT_PROFILE = 0

' 出力プラグイン番号(メニューの一番上が0)
Const OUTPUT_PLUGIN = 1

' 出力ファイルの拡張子
Const OUTPUT_EXT = ".mp4"

' AviUtlのフルパス
Const AVIUTL_PATH = "C:\Program File?(x86)\AviUtl\aviutl100\aviutl.exe"

' AviUtl Controlのフルパス(最後の文字は"\")
Const AUC_FOLDER = "C:\Program File?(x86)\AviUtl\auc_15\"

' !!! 以下、編集の必要なし !!!

Dim WSHShell, Fs
Set WSHShell = WScript.CreateObject("WScript.Shell")
Set Fs = CreateObject("Scripting.FileSystemObject")

Function Hash()
    Set Hash = CreateObject("Scripting.Dictionary")
End Function

Function Auc(command, arg)
    Auc = WSHShell.Run("""" & AUC_FOLDER & "auc_" & command & ".exe"" " & arg, 2, True)
End Function

Function WindowExists()
    WindowExists = (Auc("findwnd", "") <> 0) ' != 0
End function

' フォルダに含まれるファイルをすべて取得する(サブディレクトリ考慮)
Function GetFiles(objFolder)
    Dim files, objSubFolder, objSubFolders, tmpFiles, tmpFile
    Set files = Hash()
    For Each tmpFile In objFolder.Files
        If Fs.GetExtensionName(tmpFile.Name) <> "db" Then ' サムネイルファイルは除外
            files.Add files.Count, tmpFile
        End If
    Next
    Set objSubFolders = objFolder.SubFolders
    For Each objSubFolder In objSubFolders
        Set tmpFiles = GetFiles(objSubFolder)
        For Each tmpFile In tmpFiles.Items
            files.Add files.Count, tmpFile
        Next
    Next
    Set GetFiles = files
End Function

' 再帰的ディレクトリ作成
Sub CreateFolder(folder)
    Call WSHShell.Run("cmd /c mkdir " & """" & folder & """", 2, True)
End Sub

Dim hwnd, open

' AviUtlのウィンドウ番号を取得
hwnd = Auc("findwnd", "")
open = False
If hwnd = 0 Then
    hwnd = Auc("exec", """" & AVIUTL_PATH & """")
    If hwnd = 0 Then
        Call WScript.Quit(-1)
    End if
    open = True
End if

' キャプチャしたファイルがあるフォルダが空になるまで繰り返す
Dim srcFiles, srcFile, date, input, inputDir, subDir, output, outputDir, isFirst
Do While True
    Set srcFiles = GetFiles(Fs.GetFolder(SOURCE_FOLDER))

    If srcFiles.Count = 0 Then
        Exit Do
    End If

    ' SOURCE_FOLDER で一番古いファイルを探す
    isFirst = True
    For Each srcFile In srcFiles.Items
        If isFirst Then
            date = srcFile.DateLastModified
            input = srcFile.Path
            isFirst = False
        Elseif date > srcFile.DateLastModified then
            date = srcFile.DateLastModified
            input = srcFile.Path
        End if
    Next

    ' サブディレクトリを相対パスで得る
    inputDir = Fs.GetParentFolderName(input)
    If InStr(inputDir, SOURCE_FOLDER) = 1 Then ' SOURCE_FOLDER末尾の\まで含まれているならサブディレクトリ
        subDir = Mid(inputDir, Len(SOURCE_FOLDER) + 1, Len(inputDir) - Len(SOURCE_FOLDER)) & "\"
    Else
        subDir = ""
    End If

    ' 出力先ディレクトリがなければ作成
    outputDir = OUTPUT_FOLDER & subDir
    If Not Fs.FolderExists(outputDir) Then
        'Fs.CreateFolder(outputDir)
        CreateFolder outputDir
    End If
    output = outputDir & Fs.GetBaseName(input) & OUTPUT_EXT
    'MsgBox(input & vbCrLf & output)

    'Exit Do

    ' キャプチャしたファイルを開く
    If Not WindowExists() Then
        Exit Do
    End If
    Call Auc("open",    CStr(hwnd) & " """ & input & """")
    Call WScript.Sleep(3000)

    ' プロファイルを設定する
    If Not WindowExists() Then
        Exit Do
    End If
    Call Auc("setprof", CStr(hwnd) & " " & CStr(OUTPUT_PROFILE))
    Call WScript.Sleep(1000)

    ' 出力プラグインから出力する
    If Not WindowExists() Then
        Exit Do
    End If
    Call Auc("plugout", CStr(hwnd) & " " & CStr(OUTPUT_PLUGIN) & " """ & output & """")

    ' 出力が終わるまで待つ
    If Not WindowExists() Then
        Exit Do
    End If
    Call Auc("wait",    CStr(hwnd))

    ' 出力ファイルの存在確認
    If Not Not Fs.FileExists(outputDir) Then
        Exit Do
    End If

    ' ファイルを閉じる
    If Not WindowExists() Then
        Exit Do
    End If
    Call Auc("close",    CStr(hwnd))
    Call WScript.Sleep(3000)

    ' エンコードが終わったファイルを移動する(出力先ディレクトリがなければ作成)
    outputDir = MOVE_FOLDER & subDir
    If Not Fs.FolderExists(outputDir) Then
        'Fs.CreateFolder(outputDir)
        CreateFolder outputDir
    End If
    Call Fs.MoveFile(input, outputDir)

Loop

' スクリプトで起動したAviUtlを終了する
If open Then
    If WindowExists() Then
        Call Auc("exit", CStr(hwnd))
    End If
End if

元のスクリプトにtypoがあったのも修正しました(ver1.5)。VBScriptは初めて触ったんですが、言語仕様は難しくないのに、独特の癖があって意外と苦労しました・・・。主に参考にしたのは、以下のサイト。

あとは、FileSystemObjectの使い方を調べたとかですかね。

修正版では、サブディレクトリを考慮して動きます。入力ファイルが「{指定入力元ディレクトリ}/{サブディレクトリ}」にあるとすると、出力ファイルは「{指定出力先ディレクトリ}/{サブディレクトリ}」に吐き出され、エンコード済みの入力ファイルは「{指定移動先ディレクトリ}/{サブディレクトリ}」に移動されます。サブディレクトリの構造を維持するということです。対応するサブディレクトリが存在しなければ、作成します。

気をつけなければいけないのは、「指定した入力元ディレクトリ下にファイルが存在しなくなること」が終了条件なので、エンコード後のファイルの退避ができなければ終了しません(元のスクリプトも同じです)。つまり、入力元と移動先は別の場所にする必要があります。修正版は、起動中のAviUtlが存在するかどうか頻繁に確認し、なければ処理を中断する処理も追加しています。

他に気をつけたいのは、AviUtlを複数起動しているときの対応をしていないので、複数起動しないこと。それと、出力ファイルの存在確認を入れてみましたが、出力途中でも残る可能性があるので(実際、私の場合は残る)、途中でやめたかったら、速やかにエンコードの中止とAviUtlの終了を行ってください。AviUtlが終了すれば消滅を確認した後にスクリプトも終了します。

Pythonで書き直してみた

VBScriptクソクソ!とか叫びながら、ついでにPythonで書き直してみました。実行するにはPython 2.x系を導入している必要があります。

# -*- coding: utf-8 -*-

''' AviUtlエンコード&ファイル移動 '''

# あるディレクトリの中のサブディレクトリ下も含めたすべてのファイルについて、
# プロファイル&出力プラグイン指定でエンコードし、
# エンコードが終わったら指定のディレクトリに移動する

#################################################
# 設定
# パスの区切り文字は円マーク(バックスラッシュ)ではなく
# スラッシュを使う
# (raw文字列でも\uに反応しちゃうから)
#################################################

# キャプチャしたファイルがあるディレクトリ
INPUT_DIR = ur'C:/Users/user/Videos/amarec/_auto_encode_targets'

# エンコードが終わったファイルを移すディレクトリ
COMPLETED_DIR = ur'C:/Users/user/Videos/amarec/_auto_encode_complete'

# エンコードしたデータを出力するディレクトリ
OUTPUT_DIR = ur'C:/Users/user/Videos/aviutl/_auto_encoded'

# プロファイル番号(メニューの一番上が0)
OUTPUT_PROFILE = 0

# 出力プラグイン番号(メニューの一番上が0)
OUTPUT_PLUGIN = 1

# 入力ファイルの拡張子
INPUT_EXTENSIONS = [u'.avi', u'.mp4']

# 出力ファイルの拡張子
OUTPUT_EXTENSION = u'.mp4'

# AviUtlのフルパス
AVIUTL_PATH = ur'C:/Program File (x86)/AviUtl/aviutl100/aviutl.exe'

# AviUtl Controlのフルパス
AUC_DIR = ur'C:/Program File (x86)/AviUtl/aviutl100/auc_15'

#################################################
# 以下、処理開始
#################################################

import os, sys, codecs, locale, subprocess, time, shutil, traceback

# デリミタ(/)を末尾に追加
addDelimiter = lambda s: s if s.endswith('/') else s + '/'

INPUT_DIR = addDelimiter(INPUT_DIR)
COMPLETED_DIR = addDelimiter(COMPLETED_DIR)
OUTPUT_DIR = addDelimiter(OUTPUT_DIR)

print u'以下のパラメータが与えられました:'
print u'INPUT_DIR = %s' % INPUT_DIR
print u'COMPLETED_DIR = %s' % COMPLETED_DIR
print u'OUTPUT_DIR = %s' % OUTPUT_DIR
print u'OUTPUT_PROFILE = %s' % OUTPUT_PROFILE
print u'OUTPUT_PLUGIN = %s' % OUTPUT_PLUGIN
print u'INPUT_EXTENSIONS = %s' % INPUT_EXTENSIONS
print u'OUTPUT_EXTENSION = %s' % OUTPUT_EXTENSION
print u'AVIUTL_PATH = %s' % AVIUTL_PATH
print u'AUC_DIR = %s' % AUC_DIR
print

enc = lambda u: u.encode(locale.getpreferredencoding())
dec = lambda s: s.decode(locale.getpreferredencoding())
quote = lambda s: u'"%s"' % s
replace = lambda s: s.replace(u'/', u'\\')

def run(command):
    p = subprocess.Popen(command, stdout = subprocess.PIPE)
    output = p.stdout.read()
    p.wait()
    return output.strip()
#run = lambda command: subprocess.call(command, shell = True)

class AUC(object):
    def __init__(self):
        self._isAutoLaunch = False
        self._hwnd = self.findwnd()
        if self._hwnd == 0:
            self._hwnd = self.launch(AVIUTL_PATH)
            self._isAutoLaunch = (self._hwnd != 0)
    @staticmethod
    def _auc(command, *args):
        args = u' '.join(map(unicode, args))
        command = u'auc_%s.exe %s' % (command, args)
        print 'Run: ' + command
        return dec(run(enc(command)))
    def isAutoLaunch(self):
        return self._isAutoLaunch
    def exists(self):
        return self._hwnd != 0 and self._hwnd == self.findwnd()
    def launch(self, path):
        return int(self._auc(u'exec', quote(path)))
    def findwnd(self):
        return int(self._auc(u'findwnd'))
    def exit(self):
        self._auc(u'exit', self._hwnd)
        self._hwnd = 0
        self._isAutoLaunch = False
    def open(self, file):
        self._auc(u'open', self._hwnd, quote(replace(file)))
    def openadd(self, file):
        self._auc(u'openadd', self._hwnd, quote(replace(file)))
    def audioadd(self, file):
        self._auc(u'audioadd', self._hwnd, quote(replace(file)))
    def close(self):
        self._auc(u'close', self._hwnd)
    def setprof(self, profile):
        self._auc(u'setprof', self._hwnd, profile)
    def aviout(self, file):
        self._auc(u'aviout', self._hwnd, quote(replace(file)))
    def wavout(self, file):
        self._auc(u'wavout', self._hwnd, quote(replace(file)))
    def plugout(self, plugin, file):
        self._auc(u'plugout', self._hwnd, plugin, quote(replace(file)))
    def plugbatch(self, plugin, file):
        self._auc(u'plugbatch', self._hwnd, plugin, quote(replace(file)))
    def wait(self):
        self._auc(u'wait', self._hwnd)

def getFiles(dir):
    files = []
    for file in os.listdir(dir):
        path = dir + file
        if os.path.isfile(path):
            if os.path.splitext(path)[1] in INPUT_EXTENSIONS:
                files.append(path)
        else:
            files += getFiles(addDelimiter(path))
    return files

def getSubDir(path):
    dir = addDelimiter(os.path.split(path)[0])
    if dir.startswith(INPUT_DIR):
        return dir[len(INPUT_DIR):]
    else:
        return ''

try:
    os.chdir(AUC_DIR) # カレントディレクトリをAviUtl Controlのある場所へ変更

    files = getFiles(INPUT_DIR)
    files.sort()
    if len(files) == 0:
        print u'処理すべきファイルが見つかりませんでした'
        raw_input('Press Enter Key...')
        exit()

    auc = AUC()
    getmtime = os.path.getmtime

    print u'次のファイルを取得しました:'
    for file in files: print '-> ' + file
    print

    while auc.exists() and len(files) > 0:
        # 一番古いファイルから処理する
        inputFile = None
        mtime = 0
        for file in files:
            if inputFile == None or mtime > getmtime(file):
                inputFile = file
                mtime = getmtime(file)
        files.remove(inputFile)

        # サブディレクトリ取得とか出力先ディレクトリ設定とか
        basename = os.path.basename(inputFile)
        subDir = getSubDir(inputFile)
        completedDir = COMPLETED_DIR + subDir if COMPLETED_DIR != None else ''
        completedFile = completedDir + basename if COMPLETED_DIR != None else ''
        outputDir = OUTPUT_DIR + subDir
        outputFile = outputDir + os.path.splitext(basename)[0] + OUTPUT_EXTENSION

        print u'次の設定でエンコードを開始します:'
        print u'入力ファイル: %s' % inputFile
        print u'出力ファイル: %s' % outputFile
        print u'エンコード後ファイル: %s' % completedFile
        print

        # ファイルオープン、プロファイルの設定
        if not auc.exists(): break
        auc.open(inputFile)
        print u'ファイルをオープンしました'
        if not auc.exists(): break
        auc.setprof(OUTPUT_PROFILE)
        time.sleep(1.000)
        print u'プロファイルを設定しました'

        # 出力先ディレクトリがなければ作成
        if not os.path.exists(outputDir):
            os.makedirs(outputDir)
            print u'出力先ディレクトリを作成しました'

        # 出力プラグインによる出力
        if not auc.exists(): break
        auc.plugout(OUTPUT_PLUGIN, outputFile)
        print u'エンコード中です...'
        if not auc.exists(): break
        auc.wait()

        # ファイルクローズ
        if not os.path.exists(outputFile):
            break
        print u'エンコードが完了しました'

        # ファイルクローズ
        if not auc.exists(): break
        auc.close()
        print u'ファイルをクローズしました'
        time.sleep(3.000)

        # エンコードが終わったファイルを移動する
        # 移動先ディレクトリがなければ作成
        if COMPLETED_DIR != None:
            if not os.path.exists(completedDir):
                os.makedirs(completedDir)
                print u'移動先ディレクトリを作成しました'
            if not auc.exists(): break
            shutil.move(inputFile, completedFile)
            print u'エンコードが終了したファイルを移動しました'
        print

    if auc.isAutoLaunch() and auc.exists():
        auc.exit()

except:
    traceback.print_exc()
    raw_input('Press Enter Key...')

若干、機能を追加していますが、基本的にやってることは変わりません。エラーが起きたら、エラーを表示してエンターキーが押されるまで待機します。エラー内容を表示するだけでもないよりかなりマシだと思う。

ちゃんと動作確認してないけど、エンコード済みのファイルを移動させたくなかったら、COMPLETED_DIR を None にすれば多分対応してます。最初に作ったファイルリストが空になるまで処理を繰り返す形に変えたので、入力元ファイルを移動させなくてもいいってことです。

あと、これも試してませんが、plugout を plugbatch に置き換えれば、エンコードはせずにバッチ登録だけしてくれる筈です。そうすれば、一括でバッチ登録してくれるようになりますね。バッチ登録リストはAviUtlの方で管理していて、処理が完了していないものは自分でリストから消さない限りちゃんと保持してくれるメリットがあります。このように、あとでまとめてバッチ出力する場合では、入力ファイルを移動されると困るので、COMPLETED_DIR を None にしましょう。

「AviUtl Control」には、各実行ファイルのソースコードも付属しています。中身見ればわかりますが、やってることは単純です。PythonにはWin32APIのモジュールも用意されているので、Pythonだけですべて実装することもできそうですね。

ゴミの削除

エンコード済みのファイルや、エンコードの際に出力された必要のないファイル(「拡張 x264 出力(GUI) Ex」を使うと.statsファイルと.stats.mbtreeファイルができる)を消したいので、バッチファイル作りました。これを実行すると、バッチファイルの存在するディレクトリ以下に対して、削除処理を行います。

.aviファイルの削除

del /Q /S *.avi

.statsファイルと.stats.mbtreeファイルの削除

del /Q /S *.stats
del /Q /S *.stats.mbtree

おしまい。

これで今まで以上に動画撮りまくれるぞー!やったー!

  17 コメント

  1. VBScriptを利用させていただき、タスクスケジューラーと組み合わせて自動エンコードができるようになりました。大変助かっています。質問ですが、VBScriptの修正版をそのまま使っていますが、古いファイルからではなく新しいファイルからエンコードされています。古いファイルからエンコードするためにはどのようにすれば良いでしょうか?教えてください。お願いします。

  2. コメントいただきありがとうございます。お役に立てて嬉しいです。

    修正版コードを確認してみたら、確かに最新のファイルからエンコードされるようになっていました。これは元のスクリプトから存在しているミスで、直したつもりだったのですが・・・申し訳ないです。

    ファイルの最終更新日時をひとつずつ見ていくのですが、そのとき比較する条件が逆になっているのです。修正版で言えば、113行目の部分です。不等号が「<」になっていたのを「>」に修正しました。

    Elseif date > srcFile.DateLastModified then

    dateには今のところ最古の更新日時が入っていて、これと次のファイルの更新日時を見て、比較したファイルが古い(つまり、時間が小さい)ときに、dateを更新するという流れです。ここを間違えていたから、逆順になっていました。

    ご指摘いただきありがとうございました。

  3. 早速ご対応いただきありがとうございます。不等号の向きを変えたところ古いファイルからエンコードできるようになりました。本当にありがとうございました。

  4. VBのスクリプトを利用させていただいています。ありがとうございます。

    質問というか勝手なリクエストなのですが、既存のフォルダ構造のままで一括変換出来るスクリプトはありませんでしょうか?すでに大量のファイルがフォルダ構造に組み込まれており、これをそのままのフォルダ構造(もしくは階層をコピーしたもの)に変換出来ればと思っています。また特定のファイル形式だけを選択的に(例えばwmvのみ)を変換できればもっと便利になるのではと思っています。このようなスクリプトがあれば格段に便利になると思うのですが、このような処理は無理でしょうか?

    勝手なリクエストで申し訳ありません。

  5. コメントありがとうございます。役に立ったという声が聞けるのは嬉しいですね。

    私はエンコード元とエンコード先を明確に分けて管理することしか考えてなかったので、ここに掲載したものがすべてです。VBScriptは本件のためだけに学習し、元のスクリプトを改造しただけの知識で、しかも多くを忘れたので、新しいものをサクッと作れる状況ではないです。Pythonなら慣れているのですぐ書けますが、利用者には実行環境を導入してもらう手間が発生しますね。

    ただ、実際のところ、ここに掲載しているPythonスクリプトはVBScript版より高性能で、既にマフィンさんのいう要望(入力ファイルと出力ファイルの同居、入力ファイルの拡張子指定)は入っているんですよ。Python版の説明もよく読んでみてください。

    なので、急ぐようでしたらPythonをインストールしてもらって、Python版スクリプトを実行してもらうのが手っ取り早いです。入力ファイルの拡張子は複数指定できますので、見よう見まねで適宜書き換えてください。入力元と出力先を同じにする場合、ややこしいことになるので、入力ファイルと出力ファイルの拡張子は必ず異なるものにしてください。そうしないと、次回実行時に前回出力した動画が再びエンコード対象になってしまいます。

    また、Pythonスクリプトを使用する場合、2点気をつけることがあります。

    1点目。Pythonはバージョン2系と3系で互換性がなくて、ここに掲載したものは2系に合わせて書いたものなので、2系を導入する必要があります。

    2点目。スクリプトを適切な文字コードで保存することです。VBScriptは多分Windows標準で採用されているShift_JIS(CP932)という日本語コードをメモ帳などで無意識に使っているはずです。本記事に掲載したPythonスクリプトは1行目でUTF-8というユニコードを指定していますので、UTF-8(BOMなし)で保存します。例えば、TeraPadで編集して保存する際にUTF-8N形式を選択します。私の言っていることがよくわからなければ、メモ帳に貼り付けて、1行目の「utf-8」を「cp932」に書き換えて保存しても動作すると思うので、試してみてください。

    現在、プライベートが忙しいため、取り急ぎの返事としました。VBScriptでの対応はあまり期待しないでほしいです。ちなみに、PythonスクリプトはCtrl+Cで中断できます。VBScript(が利用しているWSH)ではこれができません。

  6. 早速のご返事ありがとうございます。これほど早くに丁寧なご返事がいただけるとは正直思っていませんでした。お忙しい中申し訳ありませんでした。

    まずはPythonを学習してみるところからはじめてみます。昔からプログラム言語関係は何度も挫折していて本当に苦手なのですが、もう一度取り組んでみることにします。あと、わがままついでにもう一点お願いしたいのですが、Python ver.2を導入するのと学習するのにお勧めのサイト等はありますでしょうか?いくつかヒットしたのですが、どれがいいのかまったく判断が出来なくて。色々勉強しながらウィザまんさんのコードを使えるところまでもっていきたいと思います。

  7. マフィンです。

    PythonのScriptでテストしてみました。要は機能を使いたいだけであればPython自体を理解する必要はなくて、ウィザまんさんが準備してくれた設定部分を書き換えるのみでOKなのですね。参考にしたサイトで、.pyファイルの置き場所にパス云々の記述があり、よく理解出来ませんでしたが(MS-DOSのconfig.sys?)どうにかテストは出来ました。しばらくこちらでテストしてみます。ありがとうございました。

  8. 少し返事が遅れました。

    そうですね、既に必要な機能は入っていますので、スクリプトの頭の方に寄せた設定項目を書き換えるだけでいいはずです。Pythonの文法に従う必要はありますが、スクリプト中に書いた注意点に気をつけてさえくれれば、見よう見まねでできると思います。

    Pythonインストーラは親切なので、パスの登録とファイルの関連付けまでは設定してくれたと思います。どのようにテストしているかわかりませんが、保存したスクリプトをダブルクリック(あるいは右クリック→「開く」)で実行することはできませんか?

    できないならば、ファイルの関連付けができていません。ファイルの関連付けは、特定の拡張子(Pythonスクリプトは.py)を標準で開くアプリケーションを決めておくことです。拡張子.pyで保存したスクリプトを右クリック→「プログラムから開く」で、Python(python.exe)を選択(リストになければ「C:\Python27」あたりを探してください)し、今後も使うみたいなチェックボックスがあればそこにチェック入れて、実行すれば関連付けされます。

    ちなみに、パス(Path)とは、ファイルの場所そのものですが、Windowsの環境変数にもPathという設定項目があります。ここに実行ファイルが置いてある場所を登録することで、そこが優先検索の対象となります。すると、実行ファイル名を短く記述して実行できたりします。ちゃんと登録できているなら、コマンドプロンプトで「python」と打てば、Pythonが対話モードで起動するはずです(Ctrl+Z→エンターで終了)。今回は気にしなくていいはずです。

    バッチファイルやコマンドプロンプト上からスクリプトを実行したければ、半角スペースで区切って「python スクリプト名」というコマンドになります。このように色々と実行方法はあるんですが、関連付けが一番楽だと思います。

  9. Pythonの方のスクリプトを使わせていただきました。ありがとうございます。
    バッチに登録したかったので201行目のplugoutをplugbatchに置き換えました(ここのplugoutだけでいいですよね?)。
    それだけではループがうまく行かず、207, 208行目のifからbreakまでをコメントアウトしたところ最後までいけました。
    最後にスクリプトのウインドウが消えていかにも完了って感じですが、ループ終了の処理をコメントアウトしているので・・・どうなんでしょう?
    ごめんなさい、スクリプトを使いたかっただけのど素人なので中途半端な感じの報告です。

  10. 返事が大幅に遅れてしまい申し訳ございません・・・。
    コメントは読んでいましたが、当時は睡眠時間を削るほど超多忙の日々を過ごしていたため、返事を書けず、そのまま忘れてしまいました。MA.INさんがまだ見ていらっしゃるかわかりませんが、他の人のためにも答えますね。

    仰るとおり、201行目の書き換えで動作するんじゃないかと思っていましたが、やはり動作確認してなかったのが甘かったですね・・・。
    自力で解決されたようですが、207~208行目のコメントアウト(行頭に#をつける)で正しいです。

    ここの処理は、「エンコード後の出力ファイルの存在確認をして、存在していなければ何か問題が起きているはずだから処理を打ち切る」という意図です。バッチ登録処理(plugbatch)に置き換えた場合、まだエンコードしていないのですから、出力ファイルがその時点で存在するはずがありませんね。なので、コメントアウトするしかありません。

    報告ありがとうございました。

    私の方でもちょっと直したい部分がありますので、ついでに整理したいなぁ・・・。

  11. Auc_v15でここの修正したVBScriptをsample01.vbsに上書きし使ってみたのですが、

    Function Auc(command, arg)
    Auc = WSHShell.Run(“””” & AUC_FOLDER & “auc_” & command & “.exe”” ” & arg, 2, True)
    End Function

    行53 文字5 でエラーを吐いてしまいます。 エラー: 指定されたファイルが見つかりません。
    OSはWin7 64bitです。
    また、Python版では変換したいファイルがあるフォルダ内の1つ目の動画しか変換されません…
    使用しているPythonのver.は 2.7.11です。
    お時間あるときにご対応お願い致します。

  12. ご報告ありがとうございます。

    VBScript版は私の方でミスリードがあったようです。AviUtl Controlが見つからないというエラーになります。スクリプト内では、AviUtl ControlをAviUtl本体から見た場所(相対パス)で指定するように説明していましたが、実際にはそういう処理になっていませんでした。チェックが足らず申し訳ありません。AviUtl本体と同様にフルパスで指定すれば動くはずです(記事中の説明も修正しました)。Python版は元々そのようになっています。

    Python版では入力ファイルの拡張子を指定する設定(INPUT_EXTENSIONS)を設けていますが、こちらは適切でしょうか?一度、各種設定を確認してみてほしいです。

    また、ここで紹介しているスクリプトはあくまでAviUtlの自動操作(ボタンクリックなどプログラムで呼んでる)であるので、変換中に別の作業をしないようお願いします。

    問題が解決しないようであれば、私からは判断材料が少ない状況ですので、どんなフォルダ構成で、どんな設定で利用しているか、といった詳細情報を頂けると助かります。Pythonのバージョンはそれで問題ありません。

  13. VBScript版のご対応ありがとうございます。

    Python版についてはINPUT_EXTENSIONSで入力する拡張子を指定しております。
    どうやら、L-SMASHの方で元ファイルと同じフォルダに作成されるファイル(拡張子 .lwi)が、
    必要な動画ファイル(.mkv など)の場合のみ、1つ目の動画しか変換されないようです(;´∀`)

    色々とフォルダ構成(Video_source)の場所を変えたり、外付けHDD(E:)やSSD(C:)などで試してみましたが、
    事前にAviUtl(v1.00)に変換したい動画ファイルを読み込ませておいて、
    変換したい動画のlwiファイルがフォルダ内に作成されている場合、ちゃんと変換されていきます。
    他の動画形式(.mp4 .webm)でも試しましたが、同じ症状でした。

    短い動画(5分未満)などであれば、問題なく変換されたりするので、
    lwiファイルの生成に一定以上の秒数が掛かる場合にエラーになるのかも…?

  14. 色々と状況調査と情報提供ありがとうございます。私の手元で問題を再現できないので、少しずつ問題の切り分けをして条件を絞っていくしかなさそうです。

    > どうやら、L-SMASHの方で元ファイルと同じフォルダに作成されるファイル(拡張子 .lwi)が、
    > 必要な動画ファイル(.mkv など)の場合のみ、1つ目の動画しか変換されないようです(;´∀`)

    変換できた1つ目のファイルのみ、対応するlwiファイルが生成されているという認識でよいでしょうか?

    また、VBScriptでもPythonでも結果は同じということでいいでしょうか?動画の長さによってちゃんと一括変換できることもあったということですので、スクリプト側の問題ではなさそうです。

    とりあえずAviUtlのバージョンは最新である1.00ですし、私も同じバージョンで動作確認できているので、本体のバージョンは問題ではないでしょう。

    lwiファイルについて調べましたが、L-SMASHの設定で生成しないようにもでき、必ずしも変換に必要なファイルではなさそうです。読み込み時に利用する補助ファイルと思ってください。

    スクリプト側でも、AivUtl Control側でも一定時間応答がないとキャンセルする処理は見つかりませんでした。lwiファイルの生成そのものが失敗しているか、その前処理でコケてるかもしれません。1つ目の変換は必ずうまくいくとするなら、状態をリセットせずに次の変換をしようとしてメモリ不足などで失敗している可能性はあるかなと思いました。

    従って、次にL-SMASH Works(lwinput.aui)のバージョンを疑いたいところです。lwiファイル生成の失敗が、L-SMASH内で起きた問題だとすれば、他のバージョンのL-SMASHを利用すればバグを回避できるかもしれません(新しくても古くても構いません)。L-SMASHは頻繁に更新されるので、取得したバージョンによってはバグが入り込んでいるかもしれません。L-SMASHのバージョンはAviUtlを起動して、「その他」メニューから「入力プラグイン情報」を選択すれば確認できます。私はr717で動いています。

    ひとまずはL-SMASHの問題かどうかを確認させてください。

    それでも解決しなければ、どこで終了しているか知る必要があります。以下の手順で、スクリプトをコマンドプロンプト上から起動して、出力結果を教えてほしいです。

    1. スクリプトを保存しているフォルダを、Shiftキー押しながら右クリック→「コマンドウィンドウをここで開く」。
    2. 「python スクリプトファイル名」コマンドを入力してエンター。もしここでpython.exeが見つからないと言われましたら、「python」を「C:\Python27\python.exe」に置き換えてください。コピーしてコマンドプロンプトに貼り付けると楽でしょう。
    3. スクリプトによる自動エンコードの終了を待ち、画面に出力された文字列をコピー(後述)して、ここのコメント欄に貼り付ける。ファイル名など公開したくない情報が含まれていましたら、該当部分を加工して構いません。

    コマンドプロンプト上の文字列コピーは面倒ですが、以下の手順でできます。

    1. コマンドプロンプトのタイトルバー上で右クリック→「編集」→「範囲指定」。
    2. ドラッグで文字列の範囲選択ができるようになるので、コピーしたい範囲を選択する。
    3. エンターキーを押す。

    やり取りが長くなるかもしれないので、Twitterアカウントをお持ちでしたら、私のTwitterアカウント(@wizamanwizaman)宛に連絡して頂けると、素早い返事ができると思います。

    またお手数おかけしてしまいますが、よろしくお願い致します。

  15. 1年くらい前にこのページに来た際にVB版を拝借し、ありがたく使わせてもらっていました

    ただ、数カ月ぶりに動かそうとしたらなぜだか急に動かなくなってしまい
    ファイルを動かした覚えもなく、再度こちらのページから持ってきても動かず…
    というわけでPhython版を導入してみました

    Phython自体初めて触るもので、2.7.12を公式サイトから導入してきました
    が、やはりこちらも動かない

    txtで作ってコピペしてからpyに変更して実行してみると、一瞬ウィンドウが出てすぐに消えてしまいます

    Phython付属エディタから実行するとエラー内容を教えてくれるようなのでやってみると、56行目でシンタックスエラーとなっていました
    ユニコード形式とstr形式を混ぜて使うなとかそんな内容らしいですが、今ググってちょっと調べた程度なのできちんと理解しているかは怪しいです

    でもって、56行目の
    print ‘以下のパラメータが与えられました:’
    のuを消去したところ先に進むようになりました

    150行目をはじめとするその他の行でも同様に引っかかったので、日本語を表示する部分においてuを消去してみました

    この対応で合っているのかはあまり自信がありませんが、他の人の助けになるかもと思いコメントしてみます
    変えてみてもし何かあっても責任はとれませんので自己責任で。
    とりあえず今のところ問題なく動いています

    管理人さん、便利なコードを公開していただきありがとうございます
    私が自分で一から作ることは不可能でした

    念のため、序盤部分についても載せておきます
    多分問題なく設定しているとは思うのですが…

    # キャプチャしたファイルがあるディレクトリ
    INPUT_DIR = ur’E:\rec’

    # エンコードが終わった元ファイルを移すディレクトリ
    COMPLETED_DIR = ur’E:\sumi’

    # エンコードしたデータを出力するディレクトリ
    OUTPUT_DIR = ur’E:\end’

    # プロファイル番号(メニューの一番上が0)
    OUTPUT_PROFILE = 0

    # 出力プラグイン番号(メニューの一番上が0)
    OUTPUT_PLUGIN = 1

    # 入力ファイルの拡張子
    INPUT_EXTENSIONS = [u’.avi’, u’.mp4′]

    # 出力ファイルの拡張子
    OUTPUT_EXTENSION = u’.mp4′

    # AviUtlのフルパス
    AVIUTL_PATH = ur’E:\AvuUtl/aviutl.exe’

    # AviUtl Controlのフルパス
    AUC_DIR = ur’E:\AvuUtl/auc’

    • 自分用に作ったものを公開しているため、あまり優しくない状態ですみません。もう少し扱いやすくしたいなと思い続けてはいるんですが、当分このままです。

      エラーの件ですが、多分、保存したスクリプトの文字エンコーディングが正しくないんだと思います。エラーメッセージいただければ何が起きているかだいたいわかると思いますが。

      ここではUTF-8(BOMなし)で保存するのが正しいです。5番のコメントにも書いたとおりなので、そっちも見てみてください。

      ただ、ちょっとPython触ったようなので、Pythonの文字エンコーディングまわりの事情をもう少し突っ込んで説明してみます。

      日本語テキストをデータで表現する方法(文字エンコーディング)はいくらか種類がありまして、一貫した文字エンコーディングで扱わないと、意図通りに動きません。Python2.x系での文字列データはstrとunicodeがあります(3.x系はunicodeがstrに統合されたため互換性がありません)。strはただのバイト列であり、それがどの文字エンコーディングによるデータかはわからないため、使用者がしっかり管理する必要があります。一方、unicodeはPythonにおけるユニコード表現ですので、日本語のようなマルチバイト文字もちゃんと1文字として扱えます。

      スクリプトを実行するとき、Windowsなのでコマンドプロンプト上で動かしているはずですが、コマンドプロンプトに文字列を表示させるにはCP932という文字エンコーディングで文字列を渡す必要があります。CP932は日本語版Windows標準の文字エンコーディングで、Shift_JISをMicrosoftが拡張したものです。CP932でないstrで文字列を表示しようとすると文字化けしたりエラーになったりします。unicodeであれば、その環境に合わせて自動変換してくれるので管理が楽になります。なので、私は極力unicodeに統一します。

      スクリプト内に日本語を含む時は、そのスクリプト自身がどの文字エンコーディングによって表現されているかをPythonに認識させなければ、正しく処理できません。元々の文字エンコーディングがわかって初めてunicodeにできたりするわけです。そのため、スクリプトの先頭で、文字エンコーディングを指定してやります。私のコードだとUTF-8を指定していますので、スクリプトをUTF-8で保存する必要があります。もしCP932で保存したいならば、1行目の「utf-8」と書いているところを「cp932」と書けばOKです(大文字・小文字は不問です)。文字エンコーディングを省略すると、環境次第で扱いが変わるため厄介です。

      それから、貼り付けてもらった設定部分に関してですが、バックスラッシュ(円記号)を使うのは避けてください。理由はスクリプト内のコメントとしても書いたのですが、「\u」という組み合わせの文字列が出てくると、ユニコード指定する特殊表記(エスケープシーケンス)として解釈されて意図しない動作をするためです。パス区切り文字はスラッシュに統一するのが安全です。

  16. ごめんなさい、動作問題ないとか言っておいて出力されたログ見たら文字化けしてました…
    しかもファイルの場所指定に¥使ってることを見落としてましたorz
    とりあえずだましだまし変換できてるってだけの状態ですねこれじゃ

    ということを報告しようと思ったらすでに詳細な返信がΣ
    余計な手間をかけてしまいすみません
    返信ありがとうございます

    後ほど5番のコメントを読んでいろいろ試してみますね

コメントを残す

Top