| 001 | #!/usr/bin/env osascript |
| 002 | #coding: utf-8 |
| 003 | ----+----1----+----2----+-----3----+----4----+----5----+----6----+----7 |
| 004 | (* |
| 005 | 管理者権限不要の |
| 006 | ユーザーインストール |
| 007 | /Users/ユーザーID/Library/Application Support/bin/ffmpeg |
| 008 | に |
| 009 | インストールします |
| 010 |
|
| 011 | ffmpegの公式リンクのバイナリーがインテル用なので |
| 012 | https://evermeet.cx/ffmpeg/ |
| 013 | ARM用にMAKEされている |
| 014 | ダウンロードサイトはドイツの『野良サイト』を利用しています |
| 015 | https://ffmpeg.martin-riedl.de/ |
| 016 |
|
| 017 | 品質について私は保証しません(でも、まぁ大丈夫じゃないか?な?) |
| 018 |
|
| 019 | 現在あるバイナリーはゴミ箱に入れてからダウンロードしますので |
| 020 | 置き換え・アップデートになります |
| 021 |
|
| 022 | v1 初回作成 |
| 023 | v2 ffmepgのバージョンが上がったのでURLを更新した |
| 024 | v2.1 ffmepgのバージョンが上がったのでURLを更新した v8版 |
| 025 | 20250830 ffmpegのv8版 このffmpegはwhisper文字起こし機能を含んでいません |
| 026 | v2.1.1 ffmepgのバージョンが上がったのでURLを更新した v8.0.1版 |
| 027 | v2.2 リンボリックリンクによってバージョン管理できるようにした |
| 028 | v2.2.1 アトリビュートを削除しておくように変更 |
| 029 | v2.3 URLの入力をしやすいように上部にまとめた |
| 030 |
|
| 031 | com.cocolog-nifty.quicktimer.icefloe *) |
| 032 | ----+----1----+----2----+-----3----+----4----+----5----+----6----+----7 |
| 033 | use AppleScript version "2.8" |
| 034 | use framework "Foundation" |
| 035 | use framework "AppKit" |
| 036 | use scripting additions |
| 037 | property refMe : a reference to current application |
| 038 | set appFileManager to refMe's NSFileManager's defaultManager() |
| 039 |
|
| 040 | ############################## |
| 041 | #【設定項目】 |
| 042 | #Version |
| 043 | set strVerSion to ("8.1.1") as text |
| 044 | #スナップショット=TRUE リリースビルド=FALSE |
| 045 | set boolSnap to true as boolean |
| 046 | # |
| 047 | set strURL_FFMPEG to ("https://ffmpeg.martin-riedl.de/download/macos/arm64/1778761665_8.1.1/ffmpeg.zip") as text |
| 048 | set strURL_FFPROBE to ("https://ffmpeg.martin-riedl.de/download/macos/arm64/1778761665_8.1.1/ffprobe.zip") as text |
| 049 | set strURL_FFPLAY to ("https://ffmpeg.martin-riedl.de/download/macos/arm64/1778761665_8.1.1/ffplay.zip") as text |
| 050 |
|
| 051 | ############################## |
| 052 | #メタ文字 |
| 053 | set strLF to ("" & linefeed & "") as text |
| 054 | set strCR to ("" & return & "") as text |
| 055 | set strCRLF to ("" & return & linefeed & "") as text |
| 056 | set strSpace to ("" & space & "") as text |
| 057 | set strTab to ("" & tab & "") as text |
| 058 | ############################## |
| 059 | #インストールチェック |
| 060 | set refResponse to doChkInstall("ffmpeg") |
| 061 | if refResponse is false then |
| 062 | log "未インストール" & strCR & "インストールします" |
| 063 | else |
| 064 | if refResponse contains "Support/bin/ffmpeg" then |
| 065 | log "インストール済みです" & strCR & "アップデートします" |
| 066 | else |
| 067 | log "ローカルインストール済みです" & strCR & "処理を中止します" |
| 068 | return false |
| 069 | end if |
| 070 | end if |
| 071 |
|
| 072 | ############################## |
| 073 | #ダウンロードサイト |
| 074 | set strURL to ("https://ffmpeg.martin-riedl.de") as text |
| 075 | set ocidURLString to refMe's NSString's stringWithString:(strURL) |
| 076 | set ocidURL to refMe's NSURL's alloc()'s initWithString:(ocidURLString) |
| 077 | #NSDATA |
| 078 | set ocidOption to (refMe's NSDataReadingMappedIfSafe) |
| 079 | set listResponse to refMe's NSData's alloc()'s initWithContentsOfURL:(ocidURL) options:(ocidOption) |error|:(reference) |
| 080 | if (last item of listResponse) = (missing value) then |
| 081 | log "initWithContentsOfURL 正常処理" |
| 082 | set ocidReadData to (first item of listResponse) |
| 083 | else if (item 2 of listResponse) ≠ (missing value) then |
| 084 | set strErrorNO to (last item of listResponse)'s code() as text |
| 085 | set strErrorMes to (last item of listResponse)'s localizedDescription() as text |
| 086 | refMe's NSLog("■:" & strErrorNO & strErrorMes) |
| 087 | return "initWithContentsOfURL エラーしました" & strErrorNO & strErrorMes |
| 088 | end if |
| 089 | #XML |
| 090 | set ocidOption to (refMe's NSXMLNodePreserveAll) + (refMe's NSXMLDocumentTidyHTML) |
| 091 | set listResponse to refMe's NSXMLDocument's alloc()'s initWithData:(ocidReadData) options:(ocidOption) |error|:(reference) |
| 092 | set ocidReadXML to (first item of listResponse) |
| 093 |
|
| 094 | set ocidRoorElement to ocidReadXML's rootElement() |
| 095 | ######################################## |
| 096 | #インストールURL (Apple Silicon/arm64) |
| 097 | ######################################## |
| 098 | set listResponse to (ocidRoorElement's nodesForXPath:("//a[contains(@href, 'macos/arm64') and contains(text(), 'FFmpeg (ZIP)')]") |error|:(reference)) |
| 099 | if boolSnap is true then |
| 100 | set ocidAelement to (first item of listResponse)'s firstObject() |
| 101 | else |
| 102 | set ocidAelement to (first item of listResponse)'s lastObject() |
| 103 | end if |
| 104 | if ocidAelement = (missing value) then |
| 105 | set strZip1 to (strURL_FFMPEG) as text |
| 106 | else |
| 107 | set ocidAttar to ocidAelement's attributeForName:("href") |
| 108 | set ocidZip1 to ocidAttar's stringValue() |
| 109 | set ocidZip1URL to ocidURLString's stringByAppendingString:(ocidZip1) |
| 110 | set strZip1 to ocidZip1URL as text |
| 111 | end if |
| 112 | log strZip1 as text |
| 113 | ################################### |
| 114 | set listResponse to (ocidRoorElement's nodesForXPath:("//a[contains(@href, 'macos/arm64') and contains(text(), 'FFprobe (ZIP)')]") |error|:(reference)) |
| 115 | if boolSnap is true then |
| 116 | set ocidAelement to (first item of listResponse)'s firstObject() |
| 117 | else |
| 118 | set ocidAelement to (first item of listResponse)'s lastObject() |
| 119 | end if |
| 120 | if ocidAelement = (missing value) then |
| 121 | set strZip2 to (strURL_FFPROBE) as text |
| 122 | else |
| 123 | set ocidAttar to ocidAelement's attributeForName:("href") |
| 124 | set ocidZip2 to ocidAttar's stringValue() |
| 125 | set ocidZip2URL to ocidURLString's stringByAppendingString:(ocidZip2) |
| 126 | set strZip2 to ocidZip2URL as text |
| 127 | end if |
| 128 | log strZip2 as text |
| 129 | ################################### |
| 130 | set listResponse to (ocidRoorElement's nodesForXPath:("//a[contains(@href, 'macos/arm64') and contains(text(), 'FFplay (ZIP)')]") |error|:(reference)) |
| 131 | if boolSnap is true then |
| 132 | set ocidAelement to (first item of listResponse)'s firstObject() |
| 133 | else |
| 134 | set ocidAelement to (first item of listResponse)'s lastObject() |
| 135 | end if |
| 136 | if ocidAelement = (missing value) then |
| 137 | set strZip3 to (strURL_FFPLAY) as text |
| 138 | else |
| 139 | set ocidAttar to ocidAelement's attributeForName:("href") |
| 140 | set ocidZip3 to ocidAttar's stringValue() |
| 141 | set ocidZip3URL to ocidURLString's stringByAppendingString:(ocidZip3) |
| 142 | set strZip3 to ocidZip3URL as text |
| 143 | end if |
| 144 | log strZip3 as text |
| 145 |
|
| 146 |
|
| 147 | ################################### |
| 148 | #リストに |
| 149 | set listURL to {strZip1, strZip2, strZip3} as list |
| 150 |
|
| 151 | #保存先 |
| 152 | set ocidHomeDirURL to appFileManager's homeDirectoryForCurrentUser() |
| 153 | set strSetSubePath to ("Library/Application Support/bin/ffmpeg/" & strVerSion & "") as text |
| 154 | set ocidDistDirPathURL to ocidHomeDirURL's URLByAppendingPathComponent:(strSetSubePath) isDirectory:(true) |
| 155 | set ocidAttrDict to refMe's NSMutableDictionary's alloc()'s init() |
| 156 | ocidAttrDict's setValue:(448) forKey:(refMe's NSFilePosixPermissions) |
| 157 | set ocidDistDirPath to ocidDistDirPathURL's |path|() |
| 158 | set boolDirExists to appFileManager's fileExistsAtPath:(ocidDistDirPath) isDirectory:(true) |
| 159 | if boolDirExists is false then |
| 160 | set listDone to appFileManager's createDirectoryAtURL:(ocidDistDirPathURL) withIntermediateDirectories:(true) attributes:(ocidAttrDict) |error|:(reference) |
| 161 | if (item 1 of listDone) is false then |
| 162 | set strErrorNO to (item 2 of listDone)'s code() as text |
| 163 | set strErrorMes to (item 2 of listDone)'s localizedDescription() as text |
| 164 | refMe's NSLog("■:" & strErrorNO & strErrorMes) |
| 165 | log "createDirectoryAtURL エラーしました" & strErrorNO & strErrorMes |
| 166 | return false |
| 167 | end if |
| 168 | end if |
| 169 | #解凍するためのレンポラリー(再起動時に自動で削除されます) |
| 170 | set ocidTempDirURL to appFileManager's temporaryDirectory() |
| 171 | set ocidUUID to refMe's NSUUID's alloc()'s init() |
| 172 | set ocidUUIDString to ocidUUID's UUIDString |
| 173 | set ocidSaveDirPathURL to ocidTempDirURL's URLByAppendingPathComponent:(ocidUUIDString) isDirectory:(true) |
| 174 | set ocidAttrDict to refMe's NSMutableDictionary's alloc()'s init() |
| 175 | ocidAttrDict's setValue:(511) forKey:(refMe's NSFilePosixPermissions) |
| 176 | set ocidMakeDirPath to ocidSaveDirPathURL's |path|() |
| 177 | set boolDirExists to appFileManager's fileExistsAtPath:(ocidMakeDirPath) isDirectory:(true) |
| 178 | if boolDirExists is false then |
| 179 | set listDone to appFileManager's createDirectoryAtURL:(ocidSaveDirPathURL) withIntermediateDirectories:(true) attributes:(ocidAttrDict) |error|:(reference) |
| 180 | if (item 1 of listDone) is false then |
| 181 | set strErrorNO to (item 2 of listDone)'s code() as text |
| 182 | set strErrorMes to (item 2 of listDone)'s localizedDescription() as text |
| 183 | refMe's NSLog("■:" & strErrorNO & strErrorMes) |
| 184 | log "createDirectoryAtURL エラーしました" & strErrorNO & strErrorMes |
| 185 | return false |
| 186 | end if |
| 187 | end if |
| 188 |
|
| 189 | #順番に処理 |
| 190 | repeat with itemURL in listURL |
| 191 | # |
| 192 | set ocidSourceFilePath to (refMe's NSString's stringWithString:(itemURL)) |
| 193 | set ocidSourceFilePathURL to (refMe's NSURL's alloc()'s initWithString:(ocidSourceFilePath)) |
| 194 | set ocidFileName to ocidSourceFilePathURL's lastPathComponent() |
| 195 | # |
| 196 | set ocidZipFilePathURL to (ocidSaveDirPathURL's URLByAppendingPathComponent:(ocidFileName) isDirectory:(false)) |
| 197 | set ocidZipFilePath to ocidZipFilePathURL's |path|() |
| 198 | ##NSDATAダウンロード |
| 199 | set ocidOption to (refMe's NSDataReadingMappedIfSafe) |
| 200 | set listResponse to (refMe's NSData's alloc()'s initWithContentsOfURL:(ocidSourceFilePathURL) options:(ocidOption) |error|:(reference)) |
| 201 | if (item 2 of listResponse) = (missing value) then |
| 202 | log "initWithContentsOfURL 正常処理" |
| 203 | set ocidSaveData to (item 1 of listResponse) |
| 204 | else if (item 2 of listResponse) ≠ (missing value) then |
| 205 | set strErrorNO to (item 2 of listResponse)'s code() as text |
| 206 | set strErrorMes to (item 2 of listResponse)'s localizedDescription() as text |
| 207 | refMe's NSLog("■:" & strErrorNO & strErrorMes) |
| 208 | log ocidSource1FilePathURL's |path|() as text |
| 209 | return "initWithContentsOfURL エラーしました" & strErrorNO & strErrorMes |
| 210 | end if |
| 211 | |
| 212 | ##NSDATAテンポラリーへ保存 |
| 213 | set ocidOption to (refMe's NSDataWritingAtomic) |
| 214 | set listDone to (ocidSaveData's writeToURL:(ocidZipFilePathURL) options:(ocidOption) |error|:(reference)) |
| 215 | if (item 1 of listDone) is true then |
| 216 | log "writeToURL 正常処理" |
| 217 | else if (item 2 of listDone) ≠ (missing value) then |
| 218 | set strErrorNO to (item 2 of listDone)'s code() as text |
| 219 | set strErrorMes to (item 2 of listDone)'s localizedDescription() as text |
| 220 | refMe's NSLog("■:" & strErrorNO & strErrorMes) |
| 221 | log ocidZip1FilePathURL's |path|() as text |
| 222 | return "writeToURL エラーしました" & strErrorNO & strErrorMes |
| 223 | end if |
| 224 | #古いバイナリーはゴミ箱に |
| 225 | set ocidFileName to ocidFileName's stringByDeletingPathExtension() |
| 226 | set ocidDistFilePathURL to (ocidDistDirPathURL's URLByAppendingPathComponent:(ocidFileName) isDirectory:(false)) |
| 227 | set listDone to (appFileManager's trashItemAtURL:(ocidDistFilePathURL) resultingItemURL:(missing value) |error|:(reference)) |
| 228 | ######################## |
| 229 | #解凍 |
| 230 | set strCmd to ("/usr/bin/unzip \"" & ocidZipFilePath & "\" -d \"" & ocidDistDirPath & "\"") as text |
| 231 | set strResponse to doZshShellScript(strCmd) |
| 232 | if strResponse is false then |
| 233 | return "解凍に失敗しました" |
| 234 | end if |
| 235 | end repeat |
| 236 |
|
| 237 | ######################## |
| 238 | ##シンボリックリンク処理 |
| 239 | set ocidURLsArray to (appFileManager's URLsForDirectory:(refMe's NSApplicationSupportDirectory) inDomains:(refMe's NSUserDomainMask)) |
| 240 | set ocidAppSupportDirPathURL to ocidURLsArray's firstObject() |
| 241 | set ocidExecDirPathURL to ocidAppSupportDirPathURL's URLByAppendingPathComponent:("bin/ffmpeg") isDirectory:(true) |
| 242 |
|
| 243 | set listBinName to {"ffmpeg", "ffplay", "ffprobe"} as list |
| 244 |
|
| 245 | repeat with itemBinName in listBinName |
| 246 | #シンボリックリンクを |
| 247 | set ocidLinkFilePathURL to (ocidExecDirPathURL's URLByAppendingPathComponent:(itemBinName) isDirectory:(false)) |
| 248 | #ゴミ箱に入れて |
| 249 | set listDone to (appFileManager's trashItemAtURL:(ocidLinkFilePathURL) resultingItemURL:(ocidLinkFilePathURL) |error|:(reference)) |
| 250 | #バイナリー |
| 251 | set strSubePath to ("" & strVerSion & "/" & itemBinName & "") as text |
| 252 | set ocidBinFilePathURL to (ocidExecDirPathURL's URLByAppendingPathComponent:(strSubePath) isDirectory:(false)) |
| 253 | set listDone to (appFileManager's createSymbolicLinkAtURL:(ocidLinkFilePathURL) withDestinationURL:(ocidBinFilePathURL) |error|:(reference)) |
| 254 | set strBinFilePath to ocidBinFilePathURL's |path|() as text |
| 255 | do shell script ("/usr/bin/xattr -c '" & strBinFilePath & "'") |
| 256 | end repeat |
| 257 |
|
| 258 | ######################## |
| 259 | #解凍前のファイルをゴミ箱へ |
| 260 | set listDone to (appFileManager's trashItemAtURL:(ocidSaveDirPathURL) resultingItemURL:(ocidSaveDirPathURL) |error|:(reference)) |
| 261 |
|
| 262 |
|
| 263 | ######################## |
| 264 | #保存先を開く |
| 265 | set appSharedWorkspace to refMe's NSWorkspace's sharedWorkspace() |
| 266 | set boolDone to appSharedWorkspace's openURL:(ocidDistDirPathURL) |
| 267 | log "処理終了" |
| 268 | return boolDone |
| 269 |
|
| 270 |
|
| 271 |
|
| 272 | ########################## |
| 273 | # 【N】ZSH 実行 |
| 274 | to doZshShellScript(argCommandText) |
| 275 | set strCommandText to argCommandText as text |
| 276 | log "コマンド開始" & return & strCommandText & return |
| 277 | set strExec to ("/bin/zsh -c '" & strCommandText & "'") as text |
| 278 | ########## |
| 279 | #コマンド実行 |
| 280 | try |
| 281 | set strResnponse to (do shell script strExec) as text |
| 282 | log "コマンド終了" |
| 283 | on error |
| 284 | return false |
| 285 | end try |
| 286 | return strResnponse |
| 287 | end doZshShellScript |
| 288 |
|
| 289 |
|
| 290 | ############################## |
| 291 | #すでにインストール済みで |
| 292 | #ユーザーがRC設定している場合は |
| 293 | #パスが通るのでこれがエラーにならない |
| 294 | to doChkInstall(argBinName) |
| 295 | try |
| 296 | #ランチD |
| 297 | set strCmd to ("/bin/zsh -c '/usr/bin/which " & argBinName & "'") |
| 298 | set strResponse to (do shell script strCmd) as text |
| 299 | return strResponse |
| 300 | on error strErrMsg number numErrNo |
| 301 | set strCmd to ("/bin/zsh -c \"[[ -f '/usr/local/bin/" & argBinName & "' ]] && echo \\\"TRUE\\\" || echo \\\"FALSE\\\"\"") |
| 302 | set strResponse to (do shell script strCmd) as text |
| 303 | if strResponse is "FALSE" then |
| 304 | return false |
| 305 | else if strResponse is "TRUE" then |
| 306 | set strResponse to ("/usr/local/bin/" & argBinName & "") |
| 307 | end if |
| 308 | end try |
| 309 | end doChkInstall |