Foltia with AppleScript

Foltia animelockerのトラブルシューティング

Applescript+yt-dipを使ってYouTube動画をダウンロードする

初めに

最近YouTubeを見ることが多くなったんですが、消えてなくなっちゃう事もあるので出来たら手元に残しておきたいです。
結構Webでフリー系でダウンロードをやってくれるサーバーもありますが、720pのSDグレードで、1080pのHDグレードで落とせるのは有料になってますよね。
私はマックユーザーなんでマックで簡単に夜間に寝ている間に落としてもらえると、出勤のときにスマホのVLCに入れて、移動中に聞きながら出勤ってのも出来て良いなと思うんですよね。

歴史 & 4KPlayerの終焉

かなり以前はGetter1とか良いフリーウエアーがあったんですが最近見かけませんね。 私も去年まで中華系ソフト4KPlayerってのがあって、それを使っていたんですが。2025年の秋頃からサイトが消えてなくなり、そしてYoutubeのダウンロードのためのライブラリーの更新も無くなってしまったんで、ちょっと将来的な運用は難しそうですね。 

そこで今回Google Geminiに聞いてみると、Linux系のアプリyt-dipが良いということらしくて、今回試しに導入してみました。 もちろんAppleScriptで運用しますよ。

まずはMacPortsで準備

マックでLinux系のアプリを使うための管理ツール、私自身今までMacPortsを使っていたんで、それはインストールしてあったんですが大分更新してなかったんでいろいろトラブルがあり修正してみましたわ。 MacPortsを入れてから恐らくメジャーなシステムOSアップデートを2回程やっているせいで、まずは、MacPortsのバージョンが対応してなかったことと、利用に必要なCommandLineToolのライブラリーに古い設定が残っていて、それをインストールし直ししました。 この辺については私個人の問題なのでちょっと省略します。

 

MacPortsはbrewと同じ、Linuxのopen-source communityのライブラリーを使えるので重宝します。 インストール方法は3ステップ。

  1. このサイトから自分のOSにあった MacPorts-2.12.5-26-Tahoe.pkg をタウンロードしてインストール
  2. MacOSのサポートしているCommand Line Toolを xcode-select --install とターミナルに打ち込むことでインストラーが起動してインストールしてくれます。
  3. 起動の初回にX-codeのライセンスアグリーメントの案内が出るので、文章を読んで最後にTerminalに agree つまり同意ですね、って打ち込んでエンターキーを打ち込むとその後はMacPortsを使うことが出来ます。

まずMacPortsのライブラリーを最新版にするため

  • sudo port selfupdate 

次に今までインストールしている種々のプログラムをアップデートしておくために下のコマンドをたたく

  • sudo port upgrade outdated

これでしばらくするとライブラリーが更新されます。

Youtube用のアプリ ffmpegとyt-dipをインストール

  • sudo port install ffmpeg
  • sudo port install yt-dip

これで一応いろいろ入れるけど大丈夫か確認を送ってきますので YesのYを入力してエンターするとインストールが始まります。 時々リンクエラーがあったりしてダウンロード出来ないものもあるので最後に

  • sudo port selfupdate && sudo port upgrade yt-dlp

で確認して終了です。

これからが本番AppleScript

さてこれからはAppleScriptでの操作ですね。 今回はSafari等のURLをファインダーにドロップした時にでるリンクファイル 拡張子.weblocをアプレット型AppleScriptにドロップして、連続してファイルをダウンロードしてもらおうって寸法です。 

 

##使用方法として、自分のDropbox/Scripts/module/と言うフォルダにTubeD.scptをおいて置きます。 アプレットに書き出した後も簡単にscptファイルを書き換えるだけで運用させるためですね。 この設計思想はM-loopとおんなじかな。

 

tell application "Finder" to set temp to selection as alias list --動作テスト用、ファイルを選択して動作させるとドロップしたときの挙動をテスト出来ます。

 

return main(temp) of me

 

on open theFiles --Dropさせてループさせます。

return main(theFiles)

end open

 

on main(theFiles)

set TubeD to load script file (((path to home folder from user domain) as string) & "Dropbox:Scripts:module:TubeD.scpt") as alias

repeat with aFile in theFiles

##アップルの.weblocファイルからURLを抽出する アップルスクリプト

tell application "System Events" to set plistFile to property list file (POSIX path of aFile)

tell application "System Events" to set extractedURL to value of property list item "URL" of contents of plistFile

##抽出したURLを使ってyt-dipに命令を出します。

if extractedURL starts with "https://www.youtube.com/" then DLYoutube({url_:extractedURL}) of TubeD

end repeat

end main

 

 

on DLYoutube(q)

set q to q & {url_:"", oFile:(path to desktop folder as string), terminal:true}

--oFileの場所を変えると自分の好きな場所にダウンロードファイルを落としてくれます。 

--set T to load script file (((path to home folder from user domain) as string) & "Dropbox:Scripts:module:Terminal.scpt") as alias --もとはこうやってロードしてるんですが一枚のファイルにする為変更してます。

if q's url_ is "" then return false

--set cmd to "/opt/local/bin/yt-dlp -f 'bestvideo+bestaudio/best' --merge-output-format mp4 -P " & quoted form of (q's oFile)'s POSIX path & " " & quoted form of q's url_

--2026/8/24追記、上記だと最近動かないみたいです。 Youtubeの設定変更でcookiesを認識しに行くようになったようで、当方はSafariなのでSafariで設定してます。

set cmd to "/opt/local/bin/yt-dlp  -f 'bestvideo+bestaudio/best' --merge-output-format mp4 -P " & quoted form of (q's oFile)'s POSIX path & " " & quoted form of q's url_ & "  --cookies-from-browser safari"

if q's terminal then

term({ActionList:{"", "$Here", cmd, "$Here"}, ti:1, wSec:3600, closeWindow:true, WaitBusy:true}) of me

else

do shell script cmd

--qの{terminal:false}に設定するとアップルスクリプト内でyt-dipを動作させてくれますが、問題があります。 どうも動画ファイルと音声ファイルが別々に保存されてしまうみたいで。 

end if

end DLYoutube

 

 

#これは以前M-loopで使ってましたね。 moduleから参照するとファイルが分かれちゃうんで、本体にコピーしました。 

on term(q) --termは基本的に返値なし

set q to {error_:""} & q & {tab_:"", ActionList:{}, wSec:10, ti:0.1, FailSafe:true, WaitBusy:false, closeWindow:true}

--FailSafeはfalseならチェックなし所定時間経過したら進む。

--Prompt1, Action1, Prompt2, Action2 の順番で処理し、空白が出現した段階で停止する。

--"$Here"ならproHere, "$There"ならProThereを入れる

set ProHere to "@*~"

set ProThere to "@*~"

if (do shell script "Echo " & (system info)'s system version & "|sed -e 's/\\(^[0-9]*.[0-9]*\\)[^0-9].*/\\1/g'") ≤ 10.13 then set ProHere to "~*\\$"

set max to round ((length of q's ActionList) / 2)

--偶数個ないと最後は実行されない。

--tell application "Terminal" to close every window

repeat with i from 1 to max

if q's tab_ is "" then

tell application "Terminal" to set q's tab_ to do script item (i * 2 - 1) of q's ActionList

else

tell application "Terminal" to do script (item (i * 2 - 1) of q's ActionList) in q's tab_

end if

delay q's ti

set wPrompt to item (i * 2) of q's ActionList

if wPrompt is "$Here" or wPrompt is "$local" or wPrompt is "$client" then set wPrompt to ProHere

if wPrompt is "$There" or wPrompt is "$remote" or wPrompt is "$server" then set wPrompt to ProThere

set q to WaitPrompt({tab_:q's tab_, wPrompt:wPrompt} & q) of me & q

if q's FailSafe then return {error_:"ActionList:" & item (i * 2) of q's ActionList & "が返却されない"} & q

end repeat

if q's closeWindow then tell application "Terminal" to close window 1

return q

end term

 

--パスワードプロンプトはBusy待機中にBusyになる

--return WaitPrompt({wPrompt:"@iMac-17 ~ %"})

on WaitPrompt(q)

set q to q & {tab_:"", wSec:10, FailSafe:true, wPrompt:"", ti:0.1, SerchFullText:false, WaitBusy:true, LastLine:""}

if q's tab_ is "" then tell application "Terminal" to set q's tab_ to tab 1 of window 1

if q's wPrompt is "" then

delay q's ti

return {FailSafe:false} & q

end if

tell application "Terminal" to tell q's tab_

if q's WaitBusy then

repeat while busy is true

delay q's ti

end repeat

end if

if q's wPrompt is "" then return q

set a to ""

repeat q's wSec times

set history_ to history as string

set Len to 1

try

set Len to (length of history_) - 1000

end try

if Len < 1 then set Len to 1

set history_ to text Len thru end of history_

if q's SerchFullText then

set a to do shell script "echo " & quoted form of (history as string) & "|grep -v '^$'|tr -d '^\\r'|tr \\\\r \\\\n|grep '" & q's wPrompt & "'||true"

else

try

set a to do shell script "echo " & quoted form of (history_) & "|grep -v '^$'|tr -d '^\\r'|tr \\\\r \\\\n|tail -n 2|grep -B 1 '" & q's wPrompt & "'||true"

end try

end if

if a is not "" then

set q's FailSafe to false

exit repeat

end if

delay 1

end repeat

end tell

return {LastLine:a} & q

end WaitPrompt

 

こんな感じで作ったSCPTファイルからアプレットに書き出しして、ファイルをドロップするとデスクトップにYoutubeのファイルがダウンロードされて保管されます。  もちろん最初に実行するときにシステムから許可の確認が来ます。 Dropboxフォルダへのアクセス。 System Eventsの使用の許可、Terminalへのアクセスの許可ですね。 一度やってしまえばその後はシステムに記録されますのでその後は聞かれることは無いでしょう。

自分的にはYoutubeの見たい映像のURLファイル weblocファイルをドロップしておいて、まとめてドラッグドロップして全部まとめてダウンロードして使ってます。

 

Web小説を読む:電子書籍

今私がこってるのは、Web小説ですかね? よく読ませてもらってます。 昨日デンスケ先生の四度目は嫌な死属性魔術師を読み終えました。 もう3年前に完結した小説でしたが、長くて2週間ぐらいかかっちゃいましたね。 読書に時間がかかってスクリプトの記事が進まなくて・・。 そこが悩みの種でもありますな。

今自分で動かしてるWeb小説をダウンロードしてくるためのスクリプトは、一応不自由なく動作しているんですが、大分前に組んだスクリプトで、継ぎ足しながら作ったので、皆さんに紹介する前に少しわかりやすいように整形しておく必要がありそうです。 自分でもどこにバグが潜んでるかわかりませんしね。 

ちなみにもう一度設計図を書いてみました。(下図 笑)

私プログラム書くときに最初のデザインはこんな感じの落書きから始まってます。 全体を見回してサブルーティン(アップルスクリプトなのでサブハンドラですかね)を区切り直してからまだご紹介ですかね。 

今使ってるのは、小説家になろう、小説家になろうR18、カクヨム、アルファポリス、ノベルアップさん用ですかね。 読み込みのための操作が一番厳しいのがアルファポリスさんですね。 あと年齢認証があってクッキーを食う必要のあるR18小説家になろうは、実際にSafariでページを一度閲覧する必要があるのでちょっと面倒ではありますね。

基本的に、一つのページに、章、サブタイトル、本文、次のページって項目があるので、それを抽出してゆくことになります。 章はあったりなかったりしますし、1ページ目から読み込む場合と、ある程度読み込んだファイルに追加で書き込んでいったりする場合のために、いろいろな入力条件検出、出力様式のチョイスが必要となるわけで・・。そのあたりのデザインも結構面倒くさいですし、もう一度デバックテストする必要がありそうですね。 

1−2週間したらオープンしようと思ってたんですが、全部書き直して整形する都合上、もう少し時間かかりそうですね。 ゴールデンウイーク明けぐらいですかね?

 

Android StudioとEpubor Kindle Converterを使ったE-pub化-4:電子書籍

で最後にこのファイルをアップル純正のBook.appに登録して見ましょう

こんな感じで。 後でキンドル本って解りやすいように、マイコレクションを作って分けておいた方がやりやすいです。 この本はiCloud driveを介して自分のMacやiPhone iPadなどで共有するので、読み終えたらブックから外しておいた方がドライブスペースの節約になります。 

こうやって登録されて

ダブルクリックすると読めるようになってます

ブック.appの設定

Book.appで管理するメリットはやはり既読部位の自動リンクと書籍データーのリンク機能でしょうね。 Book.appは確か起動する際にリンクするiCloudアカウントを登録する必要があったかと思います。

まだリンクしていなければ、ブックのメニューアプリで、アカウントからサインインを選んでみてください。 もしかしたらデバイスの認証も必要になるかな? 今解除したくないんで申し訳ないけどその設定画面のキャプチャーはなしです。

ブックの設定画面で、同期中にコレクション、ブックマーク、およびハイライトはチェックしときましょう

5GBまではフリーアカウントなのでとっておいて損はないでしょうね。 画像とか動画とかをiCloudで共有しているとすぐにいっぱいになっちゃうので、そういう方はお金払ってスペースを拡張してみてください。

次はiCloudの設定をしてみましょう

設定画面Apple IDで iCloudをクリック

まずはiCloudを使用しているアプリ、 その他のアプリを表示をクリックしてみます

ブックをクリックしてOnにしておきます。次はiCloud Driveの設定です

iCloud Driveをクリックするとこのウインドウが出ます。 iCloud Driveに同期しているアプリケーションをクリックすると詳細が確認できます。 これはもしかしたら1番のその他のアプリの設定と連動しているかもしれませんね。 まあ確認の意味でも開いて見てください。

iCloud Driveに同期しているアプリケーションでブックのONを確認。 このあたりの設定を自分のすべてのアップル製品で設定しておくと、登録したEpub本や、しおりや読書履歴が全デバイスでリンクしてくれて、管理が楽になります。 

読書が楽しくなりますよ。

 

次回までまた1週間ほど間を開けてから、Web小説をE-pubに変換する方法について解説してみましょう。 次回からはまたアップルスクリプト多めになります。

 

2024/06/28追記: どうやらKindle側が古いアプリへの対応をやめてしまったみたいで、2024年5月以降に出版されたKindleにEpubor Kindle Converterは対応しなくなったみたいです。 まあおとなしくKindleで読書することにしますけど、Kindle for Macをもうちょっとなんとかしてほしいです。

Android StudioとEpubor Kindle Converterを使ったE-pub化-3:電子書籍

縦書きにする理由

若い方々は案外横書きの本にも慣れておられるんだと思います。 例えばWeb小説なんかはほとんど横書きですしね。 外国語の本や技術書・理系の教科書なんかも横書きですね。 ただ私ぐらいの年齢になると文学は縦書きだなって脳が思ってるみたいで、横書きだと集中出来ないんです。 まあ気にならない人は今回の章ははスキップして次のVol-4を見てもらいましょう。

縦書きにする方法

kakuyomu.jp

上記のリンクを見て勉強しましたわ。 先ずは○○.opfファイルと◇◇.ccsファイルの内容の変更が必要となります。 まあこちらは後でアップルスクリプトで操作するので置いておきましょう。 詳しくはこのエリファス1810さんのページを読んでみてください。 

雑談となりますが、このE-pubファイルの拡張子をZIPにして解凍後のファイルを見ると、html形式で書かれていることが解ります。 cssも使ってますしね。 本当に良く出来たファイルシステムです。 

上記の2個のファイルに記載を追加した後、このファイルをZIPに再圧縮するわけですが、その際大事な事はファイルを圧縮する順番が大事って事ですね。 普通にフォルダ毎まとめて圧縮してもE-pubリーダーは上手く読み込んでくれなくなります。 

上記のリンクはそのパートの詳しい情報ですね。 やり方としては先ずmimetypeというファイルを先に圧縮し、その圧縮ファイルに他のファイルを追加圧縮すると言う方法をとっています。  ではアップルスクリプトで見てみましょうかね?

アップルスクリプトによるEpub-縦書変換

このスクリプトは元のファイルをユーザホームフォルダ最上位に EpuborKindleと言うフォルダを作成してその中にEpubファイルがある事を前提にしています。 もの凄く完成度を高くしたいのでしたら、ドラッグドロップ型のアプレット型アップルスクリプトを作った方が良いでしょうが、そんなに頻度は多くありませんし、いちいち皆さん自分の環境でアプレットを作製する手間を考えたらこのままアップルスクリプトエディター上で動作させた方が楽だなと思います。 

 

 

--set q to {iEpub:( (path to home folder from user domain) as string) & "EpuborKindle:続・魔法科高校の劣等生 メイジアン・カンパニー(7) (電撃文庫) - 佐島 勤.epub"}

 

tell application "Finder"

     tell folder ( ( (path to home folder from user domain) as string) & "EpuborKindle:")

          set KindleList to every file

          repeat with aFile in KindleList as list

               main({iEpub:aFile as string}) of me

          end repeat

     end tell

end tell

 

 

on main(q)

     set q to q & {tmp:POSIX path of ( (path to home folder from user domain) as string) & "tmp/", iEpub:"", oFolder:POSIX path of ( (path to home folder from user domain) as string) & "EpuborKindle_v/"}

     try

          do shell script "touch " & q's tmp

     on error

          do shell script "mkdir " & q's tmp

     end try

     try

          do shell script "touch " & q's oFolder

     on error

          do shell script "mkdir " & q's oFolder

     end try

     

     

     set iEpub to (q's iEpub)'s POSIX path

     tell application "Finder" to set iEpubName to name of (q's iEpub as alias)

     

     ##tmpに展開

     do shell script "unzip -o -d " & q's tmp & " " & quoted form of iEpub

     set tategaki to "html {-webkit-writing-mode: vertical-rl; -webkit-text-orientation: mixed; -epub-writing-mode: vertical-rl; -epub-text-orientation: mixed; writing-mode: vertical-rl; text-orientation: mixed;}"

     

     do shell script "echo " & quoted form of tategaki & " >> " & q's tmp & "stylesheet.css"

     

     do shell script "cd ~/tmp/ && zip -m kindle mimetype"

     delay 0.2

     do shell script "cd ~/tmp/ && ls -1|grep -v 'kindle.zip'| zip -r -m -@ kindle.zip"

     

     tell application "Finder"

          set kindle to ( ( (path to home folder from user domain) as string) & "tmp:kindle.zip") as alias

          do shell script "mv -f ~/tmp/kindle.zip ~/EpuborKindle_v/"

          try

               set Old to ( ( (path to home folder from user domain) as string) & "EpuborKindle_v:" & iEpubName) as alias

               delete Old

          end try

          set kindle to ( ( (path to home folder from user domain) as string) & "EpuborKindle_v:kindle.zip") as alias

          set name of kindle to iEpubName

     end tell

end main

 

こんな感じでどうでしょう?

これで実行してみると

こんな感じでepuborKindle_vフォルダ内に縦書きになったEpubデーターが出現します

 

Android StudioとEpubor Kindle Converterを使ったE-pub化-2:電子書籍

前回の続き

前回これに沿って説明してきましたね。 前回Kindle APKをインストールするところで終わっていたのでその後から続けて行きます。

Kindle APKをインストールするとアプリ画面で

こんな風にKindleアプリがインストールできます。 クリックして開いてみましょう。

クリックするとスタート画面が出ます。ご自分のAmazonアカウントを入力してログインする必要があります。 ちなみにこのログインの最初のこのページだけは、キーボード入力じゃなくて、下のバーチャルキーボードを使わないと先に進めないってサイトには書いてありました。 エンドレスループするらしいですね。 この後の入力は普通にキーボード入力で大丈夫そうです
サインインすると、Captchaによる目視入力をさせられ、その後登録しているメッセンジャーへ6桁のパスワードが送られてきて2段階で認証完了です。

開くとこんな風に書籍を閲覧、ダウンロード出来ます。 ダウンロードするとエミュレーター内にデーターが保存出来て、Epubor Kindle converterから認識出来る様になります。 この際最初の記載に戻りますが、エミュレーターがStarting up状態だと次のステップに行けないので、きちんと起動するまでアンドロイドスタジオの再起動などしてみてください。

Device ManagerがStarting up状態でなくて、こうやって完全に起動していないとなぜかEpuborから認識されない様に思いますし。 こうなってないとエミュレーターを停止することが出来なくなってます。 これが解るまで、このエミュレーターの再インストールを私10回ぐらいくりかえしました。

Epubor Kindle Converter

Epubor Kindle Converter User Guide のトップにある、Step 1 Download the latest versionからKindle Converter for freeをダウンロードしてきます。 このアプリは3回まではフリーで変換出来ますが、それ以降はお金払わないと使えません。 起動すると

こんな感じのウインドウが出てきます、左側のテーブルリストにダウンロードしたKindleファイルが見えれば成功です。 右の大きなウインドウにドラッグして。

下のConvert to Epubをクリックすると、自分の指定したフォルダ(トップのフォルダですね)、私の場合は ~/EpuborKindleというフォルダを作ってそちらに入れてます。

で出来たファイルをアップルのブックに入れると読める訳ですが、ここで決定的な問題が発生します。 それはこの電子書籍が横書きになっちゃう事なんですよ。 普通の本に慣れちゃってるとこの横書きってのは気分が出ないし、読みづらいです。 なので次はそれを縦書きに変更する方法を説明しましょう。