見出し画像

オンライン動画をダウンロードするyt-dlpを使い定期巡回(前回実行時以降の動画をダウンロード)【その2】

yt-dlpを使い、定期巡回でオンライン動画を保存するバッチファイルについては、以前の記事で「処理時間が長く、あまり実用的ではない」と書いたが、ダウンロードした動画を記録して処理対象外とする --download-archive オプションを併用すれば良いのでは?と思い、改良してみた。
また、使い勝手を向上させるためにいくつかのオプションを加えたり、Windowsのタスクスケジューラと連携させるためにファイル構成を変更したりと、主に使い勝手方面でもろもろ改良してみたので、まとめ。


完成品のダウンロード

_dln.bat.txt
 … バッチファイル(起動用)。「_dln.bat」にリネームして使用する。
_dln_sub.bat.txt
 …バッチファイル(本体)。「_dln_sub.bat」にリネームして使用する。
_dln.txt
 … 巡回するチャンネル等のリスト。
  サンプルどおり記述すれば良いが、URLだけの指定も可能。
  (簡易標記の詳細は前回の記事を参照)
【サブフォルダ「ファイル仕分け」】
!fs.bat.txt
 … 仕分けツール起動用。「!fs.bat」にリネームして使用する。
DistributeFilesByName.vbs.txt
 … 仕分けツール本体。「DistributeFilesByName.vbs」にリネーム。

説明のようなもの

使い方

  1. 解凍してできたファイル群を同じフォルダに置く

  2. 「_dln.txt」を除く他のファイルについて、拡張子の「.txt」を消す
    「_dln.bat」「_dln_sub.bat」「!fs.bat」「DistributeFilesByName.vbs」となる

  3. それぞれのファイルの「【ここに~を指定】」の部分を、自分の環境にあわせて書き換える(動画保存先フォルダのパスなど)

  4. 「サブフォルダに振り分けて保存するか」「視聴履歴に記録するか」について、デフォルト動作を「_dln_sub.bat」中の「※デフォルト動作をここで指定してください」の部分で設定する

  5. 「!fs.bat」は動画保存先に移動しておくことを推奨

  6. 「_dln.txt」に巡回対象となるチャンネル等の情報を書く。
    ・チャンネル名、URL(http://~)、前回実行日(yyyymmdd)の順番にタブ区切りで記述する
    ・URLだけ記述しても動作するので、最初はこれがオススメ

  7. タスクスケジューラを使い、「_dln.bat」を定期的に起動するよう設定する。※タスクスケジューラの使い方はネット等で確認してください
    このとき、「セキュリティオプション」で「ユーザーがログオンしているかどうかにかかわらず実行する」を選択するとバックグラウンドで動作するのでオススメ

  8. タスクスケジューラでの設定に従い「_dln.bat」が起動し、動画保存先フォルダに自動的に動画が保存されていく

  9. サブフォルダに振り分けず保存した場合には、「!fs.bat」により保存済動画をサブフォルダに振り分けができるので、必要に応じて使用する

諸注意

※前回の記事の諸注意も参照のこと

  • 1か月程度使ってみて手元では概ね問題なく動作しているが、動作が心配な場合には、
    ・タスクスケジューラではなく、手動で起動する
    ・その場合、「_dl_sub.bat」を起動すれば足りる

  • バッチファイルを Ctrl+C等で強制的に止めた場合、「_dln.txt」が壊れることがあるので注意(このため3世代のバックアップを残す仕様になっている)

  • ログは際限なく増えていくので、適宜削除

前回からの改良点

以前にダウンロードした動画はスキップする

yt-dlpには、ダウンロードした動画のIDを自動で記録し、次回実行時にダウンロードをスキップする --download-archive という起動オプションが存在する。
この機能によるダウンロードのスキップは、そもそも対象動画を読み込みにいかないため高速で処理されるので、日付範囲指定でのスキップと比べ大幅に処理時間を短縮できるほか、視聴履歴にも残らないというメリットがある。
ただし、記録用にファイルが作成されるため、ファイル数が増える点はご承知を。このバッチファイルでは「archive.txt」というファイルが作成される。

rem yt-dlpのオプション
set "ytDlpOptions=--embed-thumbnail --embed-metadata --cookies cookies.txt --no-progress --windows-filenames --download-archive archive.txt"
【略】
yt-dlp !ytDlpOptions! --format !ytDlpFormat! -P "!savePath!" --output "%%(title)s [%%(uploader)s,%%(upload_date)s].%%(ext)s" --dateafter "!lastRun!" "!url!"

--download-archive を使えば日付範囲指定をしなくても実質的に「前回巡回以降の新しい動画をダウンロードする」ことができるので、日付範囲指定は不要ではある。
が、日付範囲指定はそのまま残すことにした。
・あっても邪魔にはならない
・すでに一定数の動画が登録されている再生リストからダウンロードする場合に、最近の分だけダウンロードしたい場合に使える可能性がある
ただ、日付範囲指定による分は処理速度の問題等が依然として残るので、使わない公算も高いのだけれど。

保存先としてサブフォルダを作成するかを選択できる

もともとチャンネルごとにサブフォルダを作るようにしていたが、毎日の巡回に使っていると「今日新しく保存した動画」だけ見たくなってきた。このとき、サブフォルダで分けていると、いちいちフォルダ階層をたどって一つずつクリックして動画を見るのが面倒くさい。
なので、サブフォルダを作らないでも保存できるように拡張した。
切り替えについては、起動時にオプションで指定できるとともに、ハードコーディングとしてデフォルトを指定できるようにした。

rem 起動オプションの初期化 (0=未指定、1=指定) ※デフォルト動作をここで指定してください
set "disableSubfolder=1"

:usage
echo.
echo 「_dln.bat」ヘルプ
echo 起動オプション:
echo   /s ,--Subfolder      ... 動画を保存する際、チャンネルごとのサブフォルダに保存する
echo   /ns,--No-Subfolder   ... 動画を保存する際、チャンネルごとのサブフォルダに保存しない
echo 現在のデフォルト指定:
echo (起動オプションの指定が無い場合、以下の挙動となります。)
if %disableSubfolder%==1 (
    echo   サブフォルダ:ルート保存モード(サブフォルダを作成しません)
) else (
    echo   サブフォルダ:サブフォルダ保存モード
)

rem 起動オプションの解析
:CheckOptions
if "%~1"=="" goto :ExitOptionAnalyze
REM オプションの指定形式を標準化
set "opt=%~1"
set "opt=%opt:/=-%"
set "opt=%opt:--=-%"
REM 各オプションの判定(小文字大文字を区別しない)
if /I "%opt%"=="-s" (
    set "disableSubfolder=0"
) else if /I "%opt%"=="-es" (
    set "disablesubfolder=0"
) else if /I "%opt%"=="-subfolder" (
    set "disablesubfolder=0"
) else if /I "%opt%"=="-enable-subfolder" (
    set "disablesubfolder=0"
) else if /I "%opt%"=="-ns" (
    set "disablesubfolder=1"
) else if /I "%opt%"=="-no-subfolder" (
    set "disableSubfolder=1"
) else if /I "%opt%"=="-ds" (
    set "disableSubfolder=1"
) else if /I "%opt%"=="-disable-subfolder" (
    set "disablesubfolder=1"
) else (
    rem echo 不正なオプションです: %~1
    rem goto :End
)
shift
goto :CheckOptions

:ExitOptionAnalyze
echo.
echo ------------------------------
if %disableSubfolder%==1 (
    echo ★ 起動オプション:ルート保存モード(サブフォルダを作成しません)
) else (
    echo ★ 起動オプション:サブフォルダ保存モード
)

    rem サブフォルダとしてチャンネル名を使用
    if "!disableSubfolder!"=="1" (
        set "savePath=%parentFolder%"
    ) else (
        set "savePath=%parentFolder%\!channelName!"
    )
    if not exist "!savePath!" mkdir "!savePath!"
    echo ★★ !savePath!
    echo.

ファイル名からサブフォルダに振り分けるツールを併用

上述の通り、観賞用にはフラットな階層構造が適しているが、保存時にはこれまで通りチャンネル別にサブフォルダで保存したかった。
このため、後から処理できるように別ツールとして振り分け処理ができるものを作ってみた。(例によってChatGPTでベースを作りながら)

ファイル名からチャンネル名を取得するためには正規表現が適しているが、バッチファイルでは実装が難しそうだった。(というか、完全なものができるのだろうか?)
このため、本体はVBScriptで作成したが、コマンドプロンプトからの起動が面倒(「cscript hoge.vbs」と「cscript」を入れる必要がある)なので、起動用のバッチファイルも別途準備した。
まぁ、エクスプローラからダブルクリックすればすむ話ではあるが…
ちなみに、今後の継続性の観点からVBScriptを避けようと思い、当初、Powershellで作成にチャレンジしてみたが、ChatGPTで作成されたベースがうまく機能せず、今回は断念。

DistributeFilesByName.vbs

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(".")

For Each objFile In objFolder.Files
    fileName = objFile.Name
    
    ' ファイル名のパターンに一致するか確認
    Set regEx = New RegExp
'    regEx.Pattern = "\[(.*),(\d{8})\]\.(.*)$"
    regEx.Pattern = "(.*)\[(.*),(\d{8})\]\.(.*)$"
    regEx.IgnoreCase = True
    regEx.Global = False
    
    If regEx.Test(fileName) Then
        Set matches = regEx.Execute(fileName)
'        namePart = matches(0).SubMatches(0)
        namePart = matches(0).SubMatches(1)
        
        ' フォルダが存在しない場合は作成
        targetFolder = objFSO.BuildPath(objFolder.Path, namePart)
        If Not objFSO.FolderExists(targetFolder) Then
            objFSO.CreateFolder(targetFolder)
        End If
        
        ' ファイルを移動
        If objFSO.FileExists(objFSO.BuildPath(targetFolder, objFile.Name)) Then
            WScript.Echo fileName & " は、同名ファイルがあるため移動しません。"
        Else
            objFile.Move objFSO.BuildPath(targetFolder, objFile.Name)
            WScript.Echo fileName & " を " & namePart & " に移動しました。"
        End If
    Else
        WScript.Echo fileName & " は指定のフォーマットに一致しません。"
    End If
Next

!fs.bat

@echo off
echo.
cscript "【ここにスクリプト DistributeFilesByName.vbs のフルパスを指定】"
echo.
echo.
pause

視聴履歴の記録をオンオフできる

サブフォルダの有無指定と同様に、視聴履歴の記録もオンオフを指定できるようにした。

rem 起動オプションの初期化 (0=未指定、1=指定) ※デフォルト動作をここで指定してください
set "enableHistory=1"

:usage
echo.
echo 「_dln.bat」ヘルプ
echo 起動オプション:
echo   /h ,--Enable-History ... 視聴履歴に記録を残す
echo                            ※注:実際にダウンロードしていなくてもチェックした動画全体が記録されます
echo   /nh,--No-History     ... 視聴履歴に記録を残さない
echo.
echo 現在のデフォルト指定:
echo (起動オプションの指定が無い場合、以下の挙動となります。)
if %enableHistory%==1 (
    echo   視聴履歴記録:履歴記録有効モード(YouTubeの視聴履歴に記録を残します)
) else (
    echo   視聴履歴記録:履歴記録無効モード
)

rem 起動オプションの解析
:CheckOptions
if "%~1"=="" goto :ExitOptionAnalyze
REM オプションの指定形式を標準化
set "opt=%~1"
set "opt=%opt:/=-%"
set "opt=%opt:--=-%"
REM 各オプションの判定(小文字大文字を区別しない)
if /I "%opt%"=="-h" (
    set "enableHistory=1"
) else if /I "%opt%"=="-history" (
    set "enableHistory=1"
) else if /I "%opt%"=="-eh" (
    set "enableHistory=1"
) else if /I "%opt%"=="-enable-history" (
    set "enableHistory=1"
) else if /I "%opt%"=="-nh" (
    set "enableHistory=0"
) else if /I "%opt%"=="-no-history" (
    set "enableHistory=0"
) else if /I "%opt%"=="-dh" (
    set "enableHistory=0"
) else if /I "%opt%"=="-disable-history" (
    set "enableHistory=0"
) else (
    rem echo 不正なオプションです: %~1
    rem goto :End
)
shift
goto :CheckOptions

:ExitOptionAnalyze
echo.
echo ------------------------------
if %enableHistory%==1 (
    echo ★ 起動オプション:履歴記録有効モード(YouTubeの視聴履歴に記録を残します)
) else (
    echo ★ 起動オプション:履歴記録無効モード
)

rem yt-dlpのオプション
set "ytDlpOptions=--embed-thumbnail --embed-metadata --cookies cookies.txt --no-progress --windows-filenames --download-archive archive.txt"
if %enableHistory%==1 set "ytDlpOptions=%ytDlpOptions% --mark-watched"
echo.
echo ★ yt-dlp起動オプション(フォーマット指定を除く):
echo   %ytDlpOptions%

タスクスケジューラからサイレントで起動する(動作ログを保存する)

定期巡回するためにはタスクスケジューラで勝手に起動してくれるのが都合が良い。
このとき、細かい使い勝手の課題として、
1 起動して黒い画面(コマンドプロンプト)が表示されるのが邪魔
 → これを表示しない方法があるか?
2 一方、画面表示が無くなると動画の保存に成功したかどうかわからない
 → 動作をログとして保存できないか
3 ログ保存した場合、巡回先が多くなるとログ全部を確認するのが面倒
 → ログの中から「保存したもの」「失敗したもの」だけ抽出したい

まず1だが、これはタスクスケジューラにおいて「ユーザーがログオンしているかどうかにかかわらず実行する」に設定すると、バックグラウンドで動作するようになり、黒い画面(コマンドプロンプト)が出ない。

画面下部の「ユーザーがログオンしているかどうかにかかわらず実行する」を選択する

次に2について、動作の肝である yt_dlp の挙動自体は標準出力に表示されるだけで、ログを取る機能はないようだ。このため、画面に表示しながら別途ログを作成するという方法は取れないことになる。
なので、いっそのことバッチファイルの動作全部の標準出力をファイルに保存しログとする方針にした。
このため、「バッチファイル本体を呼び出すバッチファイル」を別途作成し、このバッチファイルから本体をコールする際、出力先をファイルへリダイレクトすることとした。
つまり、バッチファイルが「本体」と「起動用」の2つとなった。

set d=%date:/=%
set t1=%time: =0%
set t2=%t1:~0,2%%t1:~3,2%%t1:~6,2%
set p=%~dp0
call "%p%_dln_sub.bat">!%d%-%t2%.log

最後に3について、yt-dlp を起動した後処理として、作成したログから findstr コマンドを使い、「保存した記録の行」と「失敗した記録の行」を抽出し別ファイルとして保存することにした。
「失敗した記録」についてはわかりやすく、元のバッチファイルで「ダウンロード失敗」と画面表示するようにしていたので、この言葉をキーワードとして抽出すれば良いはず。
一方、「保存した記録の行」は特に明確な「成功」の表示が無く、悩んだが yt-dlp が表示する「Merger」の言葉を拾うことにした。

findstr "Merger" !%d%-%t2%.log>!%d%-%t2%-summary.log
findstr "ダウンロード失敗" !%d%-%t2%.log>>!%d%-%t2%-summary.log

コード全体(一応掲載)

冒頭のzipファイルをダウンロードすれば実用上は足りるのだが、いちおうコード全体も掲載しておく。

起動用バッチファイル:_dln.bat

@echo off
setlocal
rem 動作対象フォルダをカレントとする
rem カレントを対象とする場合には、ここの記述はコメントアウトすること
rem ①カレントと動作対象フォルダのドライブが異なる場合は、まずドライブを指定(例:「D:」)
D:
rem ②次に動作対象フォルダをフルパスで指定(例:「D:\yt-dlp\bin」)
CD "【ここに _dln_sub.bat のフォルダをフルパスで指定】"

set d=%date:/=%
set t1=%time: =0%
set t2=%t1:~0,2%%t1:~3,2%%t1:~6,2%
set p=%~dp0
call "%p%_dln_sub.bat">!%d%-%t2%.log
findstr "Merger" !%d%-%t2%.log>!%d%-%t2%-summary.log
findstr "ダウンロード失敗" !%d%-%t2%.log>>!%d%-%t2%-summary.log
endlocal

本体:_dln_sub.bat

@echo off
rem yt_dlpを使用してダウンロードする[定期巡回用]
rem ・YouTubeへのログインあり(cookieを使用する) ※DL失敗を減らすため
rem ・ただし視聴履歴記録の有効無効は切り替え可能 ※大量DLを想定し任意切り替え可能としている
rem ・「_dln.txt」を参照し、指定されたURL及び前回巡回日に基づきダウンロードする
rem ・「archive.txt」にダウンロード済み動画を記録し次回以降スキップする
rem Usage:
rem  _dln [/s|/ns][/h|/nh][/?]
rem  ※オプションの意味はヘルプ表示を参照

setlocal enabledelayedexpansion

rem 起動オプションの初期化 (0=未指定、1=指定) ※デフォルト動作をここで指定してください
set "disableSubfolder=1"
set "enableHistory=1"

goto :main
:usage
echo.
echo 「_dln.bat」ヘルプ
echo.
echo   ファイルから前回実行時刻を読み取り、それ以降の動画をダウンロードする
echo   「_dln.txt」に「チャンネル名、URL、前回実行時刻」の順で対象を記述する(タブ区切り)
echo   ・チャンネル名と前回実行時刻は、実行ごとに更新される
echo   ・「URLのみ記述」でも動作可能
echo   ・「チャンネル名、URLのみ記述」「URL、前回実行時刻のみ記述」でも動作可能
echo.
echo 起動オプション:
echo   /s ,--Subfolder      ... 動画を保存する際、チャンネルごとのサブフォルダに保存する
echo   /ns,--No-Subfolder   ... 動画を保存する際、チャンネルごとのサブフォルダに保存しない
echo   /h ,--Enable-History ... 視聴履歴に記録を残す
echo                            ※注:実際にダウンロードしていなくてもチェックした動画全体が記録されます
echo   /nh,--No-History     ... 視聴履歴に記録を残さない
echo  /?,--Help           ... ヘルプを表示する(この画面)※ ?は実際には半角文字
echo.
echo 現在のデフォルト指定:
echo (起動オプションの指定が無い場合、以下の挙動となります。)
if %disableSubfolder%==1 (
    echo   サブフォルダ:ルート保存モード(サブフォルダを作成しません)
) else (
    echo   サブフォルダ:サブフォルダ保存モード
)
if %enableHistory%==1 (
    echo   視聴履歴記録:履歴記録有効モード(YouTubeの視聴履歴に記録を残します)
) else (
    echo   視聴履歴記録:履歴記録無効モード
)
echo   ※デフォルト指定の変更は、
echo    このバッチファイルの環境変数「disableSubfolder」「enableHistory」を直接修正してください
echo.
exit /b

:main

rem 動作対象フォルダをカレントとする
rem カレントを対象とする場合には、ここの記述はコメントアウトすること
rem ①カレントと動作対象フォルダのドライブが異なる場合は、まずドライブを指定(例:「D:」)
D:
rem ②次に動作対象フォルダをフルパスで指定(例:「D:\yt-dlp\bin」)
CD "【ここにyt-dlpのフォルダをフルパスで指定】"

rem 起動オプションの解析
:CheckOptions
if "%~1"=="" goto :ExitOptionAnalyze
REM オプションの指定形式を標準化
set "opt=%~1"
set "opt=%opt:/=-%"
set "opt=%opt:--=-%"
REM 各オプションの判定(小文字大文字を区別しない)
if /I "%opt%"=="-s" (
    set "disableSubfolder=0"
) else if /I "%opt%"=="-es" (
    set "disablesubfolder=0"
) else if /I "%opt%"=="-subfolder" (
    set "disablesubfolder=0"
) else if /I "%opt%"=="-enable-subfolder" (
    set "disablesubfolder=0"
) else if /I "%opt%"=="-ns" (
    set "disablesubfolder=1"
) else if /I "%opt%"=="-no-subfolder" (
    set "disableSubfolder=1"
) else if /I "%opt%"=="-ds" (
    set "disableSubfolder=1"
) else if /I "%opt%"=="-disable-subfolder" (
    set "disablesubfolder=1"
) else if /I "%opt%"=="-h" (
    set "enableHistory=1"
) else if /I "%opt%"=="-history" (
    set "enableHistory=1"
) else if /I "%opt%"=="-eh" (
    set "enableHistory=1"
) else if /I "%opt%"=="-enable-history" (
    set "enableHistory=1"
) else if /I "%opt%"=="-nh" (
    set "enableHistory=0"
) else if /I "%opt%"=="-no-history" (
    set "enableHistory=0"
) else if /I "%opt%"=="-dh" (
    set "enableHistory=0"
) else if /I "%opt%"=="-disable-history" (
    set "enableHistory=0"
) else if /I "%opt%"=="-?" (
    goto :usage
) else if /I "%opt%"=="-help" (
    goto :usage
) else if /I "%opt%"=="-usage" (
    goto :usage
) else (
    rem echo 不正なオプションです: %~1
    rem goto :End
)
shift
goto :CheckOptions

:ExitOptionAnalyze
echo.
echo ------------------------------
if %disableSubfolder%==1 (
    echo ★ 起動オプション:ルート保存モード(サブフォルダを作成しません)
) else (
    echo ★ 起動オプション:サブフォルダ保存モード
)
if %enableHistory%==1 (
    echo ★ 起動オプション:履歴記録有効モード(YouTubeの視聴履歴に記録を残します)
) else (
    echo ★ 起動オプション:履歴記録無効モード
)

rem CSVファイルのパス
set "csvFile=_dln.txt"

rem yt-dlpのオプション
set "ytDlpOptions=--embed-thumbnail --embed-metadata --cookies cookies.txt --no-progress --windows-filenames --download-archive archive.txt"
if %enableHistory%==1 set "ytDlpOptions=%ytDlpOptions% --mark-watched"
set ytDlpFormat="bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]"
echo.
echo ★ yt-dlp起動オプション(フォーマット指定を除く):
echo   %ytDlpOptions%

rem 親フォルダのパスを指定 (保存先のルートフォルダ)
set "parentFolder=【ここに保存先のルートフォルダをフルパスで指定】"
echo ★ 動画保存先:
echo   %parentFolder%
echo.

rem 現在時刻を取得 (前回実行時刻更新のため)
for /f %%A in ('powershell -Command "Get-Date -Format yyyyMMdd"') do set currentDateTime=%%A

rem CSVファイルをバックアップする(3世代)
:: パラメータ設定
set "BACKUP_DIR="          :: バックアップ先のフォルダ(末尾に\をつける。文字列中にカッコは指定できない。)※未指定なら動作対象フォルダに保存
set "BASE_NAME=_dln_bak"   :: バックアップファイルのベース名
set "EXT=.txt"             :: ファイル拡張子

:: バックアップ先フォルダが存在しない場合は作成
if "%BACKUP_DIR%"=="" (
    rem ダミー
) else (
    if not exist "%BACKUP_DIR%" (
        mkdir "%BACKUP_DIR%"
        echo ★ リストファイル用バックアップフォルダを作成しました。
    )
)

:: 古いバックアップを3世代保持する
if exist "%BACKUP_DIR%%BASE_NAME%_3%EXT%" (
    del "%BACKUP_DIR%%BASE_NAME%_3%EXT%"
)
if exist "%BACKUP_DIR%%BASE_NAME%_2%EXT%" (
    rename "%BACKUP_DIR%%BASE_NAME%_2%EXT%" "%BASE_NAME%_3%EXT%"
)
if exist "%BACKUP_DIR%%BASE_NAME%_1%EXT%" (
    rename "%BACKUP_DIR%%BASE_NAME%_1%EXT%" "%BASE_NAME%_2%EXT%"
)

:: 新しいバックアップをコピー
if exist "%BACKUP_DIR%%BASE_NAME%%EXT%" (
    rename "%BACKUP_DIR%%BASE_NAME%%EXT%" "%BASE_NAME%_1%EXT%"
)
copy "%csvFile%" "%BACKUP_DIR%%BASE_NAME%%EXT%"

echo ★ 対象リストのバックアップを作成

rem CSVファイルを読み込む
set /a lineNumber=0
for /f "tokens=1,2,3 delims=	" %%A in (%csvFile%) do (
    set "channelName=%%A"
    set "url=%%B"
    set "lastRun=%%C"
    set /a lineNumber+=1

echo ------------------------------

    rem URLが存在するか確認
    rem パターン1:列2が空白なら、列1にURLのみ記載されていると解釈
    if "!url!"=="" (
        set "url=%%A"
        set "channelName="
    )
    rem パターン2:空行の場合は、スキップ
    if "!url!"=="" (
        rem URLが無い場合はスキップ
        echo ★ URLが無いレコードをスキップします。
        goto :continue
    )
    rem パターン3:列1がURL(http~)なら、列1をURL、列2を前回実行時刻と解釈
    if "!channelName:~0,4!"=="http" (
        set "channelName="
        set "url=%%A"
        set "lastRun=%%B"
    )

    rem コメントアウト行なら読み飛ばす(「;」「'」「:」に対応)
    if "!url:~0,1!"==";" (
        echo ★ コメント行なのでスキップします。
        goto :continue
    )
    if "!url:~0,1!"=="'" (
        echo ★コメント行なのでスキップします。
        goto :continue
    )
    if "!url:~0,1!"==":" (
        echo ★コメント行なのでスキップします。
        goto :continue
    )

    rem チャンネル名が無い場合は、yt-dlpから取得
    if "!channelName!"=="" (
        echo ★ チャンネル名を取得しています: !url!
        for /f "delims=" %%D in ('yt-dlp --get-filename -o "%%(uploader)s" "!url!" 2^>nul') do set "channelName=%%D"
    )
    if "!channelName!"=="" set "channelName=*チャンネル名取得失敗"
    echo ★ チャンネル名: !channelName!

    rem サブフォルダとしてチャンネル名を使用
    if "!disableSubfolder!"=="1" (
        set "savePath=%parentFolder%"
    ) else (
        set "savePath=%parentFolder%\!channelName!"
    )
    if not exist "!savePath!" mkdir "!savePath!"
    echo ★★ !savePath!
    echo.

    rem 最後に実行した時刻が記録されているかチェック
    if "!lastRun!"=="" (
        rem 前回実行時刻が無い場合、全動画をダウンロード
        echo ★ ダウンロード開始: !url! ^(全動画^)
        echo.
        yt-dlp !ytDlpOptions! --format !ytDlpFormat! -P "!savePath!" --output "%%(title)s [%%(uploader)s,%%(upload_date)s].%%(ext)s" "!url!"
    ) else (
        rem 前回実行時刻がある場合、その時刻以降の動画のみをダウンロード
        echo ★ ダウンロード開始: !url! ^(!lastRun!以降^)
        echo.
        yt-dlp !ytDlpOptions! --format !ytDlpFormat! -P "!savePath!" --output "%%(title)s [%%(uploader)s,%%(upload_date)s].%%(ext)s" --dateafter "!lastRun!" "!url!"
    )

    rem ダウンロードが成功したかを確認
    if errorlevel 1 (
        rem 失敗: 前回実行時刻を削除 (nullで更新)
        echo.
        echo ★ ダウンロード失敗: !url! の実行時刻を削除
        call :updateCsv "!channelName!" "!url!" "" "!lineNumber!"
    ) else if errorlevel 0 (
        rem 成功: CSVファイルの該当行を更新
        echo.
        echo ★ 更新中: !url! のチャンネル名と実行時刻を更新
        call :updateCsv "!channelName!" "!url!" "!currentDateTime!" "!lineNumber!"
    ) else (
        rem 失敗: 前回実行時刻を削除 (nullで更新)
        echo.
        echo ★ ダウンロード失敗: !url! の実行時刻を削除
        call :updateCsv "!channelName!" "!url!" "" "!lineNumber!"
    )

    :continue
rem
echo.
)

echo ------------------------------
echo ★ すべてのレコードを処理完了
:end
echo.
exit /b

:updateCsv
rem 新しいCSVファイルを作成しながら更新
set "tempFile=temp.txt"
if exist "%tempFile%" del "%tempFile%"

rem チャンネル名にカッコが含まれている場合、エスケープ処理する
set myVar=%~1
set escapedVar=
for /l %%i in (0,1,256) do (
    set "char=!myVar:~%%i,1!"
    if "!char!"=="" goto :done
    if "!char!"=="(" (
        set escapedVar=!escapedVar!^(
    ) else if "!char!"==")" (
        set escapedVar=!escapedVar!^)
    ) else (
        set escapedVar=!escapedVar!!char!
    )
)
:done
rem

set /a lineCount=0
for /f "tokens=1,2,3 delims=	" %%A in (%csvFile%) do (
rem ここで、%1~%4は、サブルーチンが呼ばれたときに渡された値(現在の処理対象動画の情報)、
rem %%A~%%Cは、CSVから読み取った値
    set /a lineCount+=1
    if !lineCount!==%~4 (
        rem 行数が一致したなら、その行を指定値で更新する
        rem (%2で送られたURLに文字列エンコード(%xx)を含んでいるとECOHでの書き出し時に何故か%が誤解釈されるので、
        rem  美しくないが、!url!をそのまま使う(CSVの値である%%Bでも良いが、記述方法により%%BURLではない場合がある)
        echo !escapedVar!	!url!	%~3>>"%tempFile%"
    ) else (
        rem この先は通常は実行されない。初期ルーチンを残してあるもの。
        if "%%B"=="%~2" (
            echo !escapedVar!	%~2	%~3>>"%tempFile%"
        ) else (
            if "%%A"=="%~2" (
                echo !escapedVar!	%~2	%~3>>"%tempFile%"
            ) else (
            echo %%A	%%B	%%C>>"%tempFile%"
            )
        )
    )
)

rem 古いCSVファイルを削除し、新しいCSVファイルをリネーム
del %csvFile%
rename %tempFile% %csvFile%
exit /b

巡回先を記録するファイル:_dln.txt

; このファイルに巡回対象のURL等を記載します。【】内をそれぞれ実際の内容に書き換えてください。
; URLだけを記載しても動作します。(実行後、取得したチャンネル名等を用い、リストを自動的に更新します。)
; 各項目の間はTabで区切ってください。
; 先頭1文字目が「;」「'」「:」いずれかである場合、コメント行と見なします。
【チャンネル名】	【URL】	【前回実行日】

仕分けツール起動用:!fs.bat

@echo off
echo.
cscript "【ここにスクリプト DistributeFilesByName.vbs のフルパスを指定】"
echo.
echo.
pause

仕分けツール本体:DistributeFilesByName.vbs

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(".")

For Each objFile In objFolder.Files
    fileName = objFile.Name
    
    ' ファイル名のパターンに一致するか確認
    Set regEx = New RegExp
    regEx.Pattern = "(.*)\[(.*),(\d{8})\]\.(.*)$"
    regEx.IgnoreCase = True
    regEx.Global = False
    
    If regEx.Test(fileName) Then
        Set matches = regEx.Execute(fileName)
        namePart = matches(0).SubMatches(1)
        
        ' フォルダが存在しない場合は作成
        targetFolder = objFSO.BuildPath(objFolder.Path, namePart)
        If Not objFSO.FolderExists(targetFolder) Then
            objFSO.CreateFolder(targetFolder)
        End If
        
        ' ファイルを移動
        If objFSO.FileExists(objFSO.BuildPath(targetFolder, objFile.Name)) Then
            WScript.Echo fileName & " は、同名ファイルがあるため移動しません。"
        Else
            objFile.Move objFSO.BuildPath(targetFolder, objFile.Name)
            WScript.Echo fileName & " を " & namePart & " に移動しました。"
        End If
    Else
        WScript.Echo fileName & " は指定のフォーマットに一致しません。"
    End If
Next


いいなと思ったら応援しよう!