Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

カテゴリー: list

Mail.appで選択中のフォルダ以下に存在する全メールの添付ファイルの拡張子のバリエーションを集計

Posted on 11月 25, 2018 by Takaaki Naganoya

Mail.appで選択中のフォルダ(メールボックス)以下に存在する全メールの添付ファイルの拡張子のバリエーションを集計するAppleScriptです。

階層フォルダで管理しているメールボックス(フォルダ)のうち、選択しているメールフォルダ以下のすべてのメッセージに添付されているファイルの拡張子を取得して結果を表示します。

何のために作ったとかいうことはなくて、ただ単に調査のために作ったものです。処理するメールの数にもよりますが、CPU負荷も大きく、時間もそれなりに(数分ぐらい?)かかります。

AppleScript名:Mail.appで選択中のフォルダ以下に存在する全メールの添付ファイルの拡張子のバリエーションを集計
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/11/25
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

script spd
  property fullSubPath : {}
  
property tmpMesList : {}
  
property outList : {}
end script

property aFullPath : ""
property tmpFullPath : ""
property extList : {}

–変数の初期化
set tmpFullPath to ""
set aFullPath to ""
set extList to {}
set (fullSubPath of spd) to {}
set (tmpMesList of spd) to {}
set (outList of spd) to {}

–選択中のメールボックス(フォルダ)のフルパスを文字列で取得(dir1/dir2/dir3)
set targMBObj to getSelectedOneMailBox() of me
extraxctTextFullPathOfMBObject(targMBObj) of me

copy aFullPath to targMB

if targMB = "" then
  display notification "Mail.app上で選択中のメールボックス(フォルダ)はありません。" –"No Mail.app folder selection" in Japanese
  
return
end if

–指定メールボックス(フォルダ)以下のすべてのメールボックス(フォルダ)を取得する
tell application "Mail"
  tell mailbox targMB
    set aList to name of every mailbox
  end tell
end tell

–指定フォルダ以下のMailboxをサブフォルダまで再帰ですべて取得する
repeat with i in aList
  set j to contents of i
  
set tmpPath to targMB & "/" & j
  
set the end of (fullSubPath of spd) to tmpPath
  
getEverySubMailbox(tmpPath) of me
end repeat

–全サブフォルダからメールを取得し、添付ファイルの確認を行なって、添付ファイルの拡張子を取得して集計
repeat with i in (fullSubPath of spd)
  set tmpBox to contents of i
  
  
tell application "Mail"
    tell mailbox tmpBox
      set mesList to (every message)
    end tell
    
    
–指定フォルダ中のMailでループ
    
repeat with ii in mesList
      set jj to contents of ii
      
      
try
        set attS to mail attachment of jj
      on error
        set attS to {}
      end try
      
      
if attS is not equal to {} then
        
        
–Mailの添付ファイルの拡張子を集計する
        
try
          repeat with iii in attS
            set jjj to contents of iii
            
set aName to name of jjj
            
            
set aExt to getPathExtension(aName) of me
            
            
if (aExt is not in extList) and (aExt is not equal to "") then
              set the end of extList to aExt
            end if
            
          end repeat
        end try
      end if
    end repeat
  end tell
end repeat

choose from list extList with prompt "選択中のメールフォルダ「" & targMB & "」中のメールに添付されているファイルの種別です" with title "添付ファイルの拡張子一覧"

–指定のメールボックス(フォルダ)以下にあるすべてのサブフォルダを(propertyに)追記して返す
on getEverySubMailbox(aPath)
  tell application "Mail"
    tell mailbox aPath
      set aaList to name of every mailbox
      
if aaList is not equal to {} then
        repeat with i in aaList
          set j to contents of i
          
set tmpFullPath to aPath & "/" & j
          
copy tmpFullPath to newPath
          
set the end of (fullSubPath of spd) to newPath
          
getEverySubMailbox(newPath) of me
        end repeat
      end if
    end tell
  end tell
end getEverySubMailbox

–Message Viewerで現在表示中のメールボックスの情報を1つのみ返す
on getSelectedOneMailBox()
  tell application "Mail"
    tell message viewer 1
      set mbList to selected mailboxes
    end tell
  end tell
  
if length of mbList is equal to 0 then
    return ""
  end if
  
  
set aMailBox to contents of (item 1 of mbList)
  
return aMailBox
end getSelectedOneMailBox

–Mail.appのメールボックスオブジェクトを渡すと、テキストのフルパスに変換
on extraxctTextFullPathOfMBObject(aPath)
  tell application "Mail"
    try
      set parentPath to container of aPath
    on error
      return
    end try
    
    
set meName to name of aPath
    
if aFullPath = "" then –1回目のみスラッシュを入れないで処理
      set aFullPath to meName
    else
      –通常処理はこちら
      
set aFullPath to meName & "/" & aFullPath
    end if
    
    
extraxctTextFullPathOfMBObject(parentPath) of me –再帰呼び出し
  end tell
end extraxctTextFullPathOfMBObject

on getPathExtension(aStr as string)
  set pathString to current application’s NSString’s stringWithString:aStr
  
return (pathString’s pathExtension()) as string
end getPathExtension

★Click Here to Open This Script 

Posted in file list recursive call | Tagged 10.11savvy 10.12savvy Mail | Leave a comment

Mail.appで選択中のフォルダ以下に存在する全メールの指定形式の添付ファイルを指定フォルダにコピー v2

Posted on 11月 25, 2018 by Takaaki Naganoya

Mail.appで選択中のフォルダ(メールボックス)以下に存在する全メールに添付されているファイルのうち、指定形式のファイルのみ指定フォルダに保存するAppleScriptです。

・Mail.appで選択中のフォルダを求め、フォルダの(Mail.app上の)フルパス文字列を再帰処理で取得
・書き出し先フォルダを指定(choose folder)
・指定のMail.app上の選択中のフォルダ以下の全メールフォルダ、およびそれらの中に入っているメールを再帰処理で取得
・取得した全メールをループで調査し、添付ファイルが存在するかチェック
・1メールあたりの添付ファイルはリストで返ってくるので、すべて求めてループで処理
・添付ファイルの拡張子を求め、書き出し許可リスト {“txt”, “gv”, “dot”} に入っていれば、指定フォルダ内に保存


▲書き出したファイル

たまーに、添付ファイルがよく出回っているメーリングリスト(QuartzComposer MLとかGraphViz MLとか)で、メール添付されているファイルを一括で取得するために作成しました。

AppleScript名:Mail.appで選択中のフォルダ以下に存在する全メールの指定形式の添付ファイルを指定フォルダにコピー v2
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

script spd
  property fullSubPath : {}
  
property tmpMesList : {}
end script

property aFullPath : ""
property tmpFullPath : ""

set targList to {"txt", "gv", "dot"} –書き出し対象拡張子

–変数の初期化
set tmpFullPath to ""
set aFullPath to ""
set (fullSubPath of spd) to {}
set (tmpMesList of spd) to {}

–選択中のメールボックス(フォルダ)のフルパスを文字列で取得(dir1/dir2/dir3)
set targMBObj to getSelectedOneMailBox() of me
extraxctTextFullPathOfMBObject(targMBObj) of me

copy aFullPath to targMB

if targMB = "" then
  display notification "Mail.app上で選択中のメールボックス(フォルダ)はありません。"
  
return
end if

tell current application
  set targFol to choose folder with prompt "添付ファイルの書き出し先フォルダを指定してください"
end tell

–指定メールボックス(フォルダ)以下のすべてのメールボックス(フォルダ)を取得する
tell application "Mail"
  tell mailbox targMB
    set aList to name of every mailbox
  end tell
end tell

–指定フォルダ以下のMailboxをサブフォルダまで再帰ですべて取得する
repeat with i in aList
  set j to contents of i
  
set tmpPath to targMB & "/" & j
  
set the end of (fullSubPath of spd) to tmpPath
  
getEverySubMailbox(tmpPath) of me
end repeat

–全サブフォルダから指定文字列をSubjectに含むものをすべて取得する
repeat with i in (fullSubPath of spd)
  set tmpBox to contents of i
  
  
tell application "Mail"
    tell mailbox tmpBox
      set mesList to (every message)
    end tell
    
    
–指定フォルダ中のMailでループ
    
repeat with ii in mesList
      set jj to contents of ii
      
set attS to mail attachment of jj
      
      
if attS is not equal to {} then
        
        
–Mailの添付ファイルを順次指定フォルダに保存する
        
repeat with iii in attS
          set jjj to contents of iii
          
set aName to name of jjj
          
          
set aExt to getPathExtension(aName) of me
          
if aExt is in targList then
            set savePath to (targFol as string) & aName
            
            
–指定パスに添付ファイルを保存
            
save jjj in file savePath
          end if
        end repeat
        
      end if
    end repeat
  end tell
  
end repeat

–指定のメールボックス(フォルダ)以下にあるすべてのサブフォルダを(propertyに)追記して返す
on getEverySubMailbox(aPath)
  tell application "Mail"
    tell mailbox aPath
      set aaList to name of every mailbox
      
if aaList is not equal to {} then
        repeat with i in aaList
          set j to contents of i
          
set tmpFullPath to aPath & "/" & j
          
copy tmpFullPath to newPath
          
set the end of (fullSubPath of spd) to newPath
          
getEverySubMailbox(newPath) of me
        end repeat
      end if
    end tell
  end tell
end getEverySubMailbox

–Message Viewerで現在表示中のメールボックスの情報を1つのみ返す
on getSelectedOneMailBox()
  tell application "Mail"
    tell message viewer 1
      set mbList to selected mailboxes
    end tell
  end tell
  
if length of mbList is equal to 0 then
    return ""
  end if
  
  
set aMailBox to contents of (item 1 of mbList)
  
return aMailBox
end getSelectedOneMailBox

–Mail.appのメールボックスオブジェクトを渡すと、テキストのフルパスに変換
on extraxctTextFullPathOfMBObject(aPath)
  tell application "Mail"
    try
      set parentPath to container of aPath
    on error
      return
    end try
    
    
set meName to name of aPath
    
if aFullPath = "" then –1回目のみスラッシュを入れないで処理
      set aFullPath to meName
    else
      –通常処理はこちら
      
set aFullPath to meName & "/" & aFullPath
    end if
    
    
extraxctTextFullPathOfMBObject(parentPath) of me –再帰呼び出し
  end tell
end extraxctTextFullPathOfMBObject

–ファイル名から拡張子を取得する
on getPathExtension(aStr as string)
  set pathString to current application’s NSString’s stringWithString:aStr
  
return (pathString’s pathExtension()) as string
end getPathExtension

★Click Here to Open This Script 

Posted in list | Tagged 10.11savvy 10.12savvy Mail NSString | Leave a comment

Mail.appで選択中のメールボックス(フォルダ)のパスを文字列で返す

Posted on 11月 25, 2018 by Takaaki Naganoya

Mail.app上で選択中のメールボックス(フォルダ)のパスをフルパスの文字列で求めるAppleScriptです。

Mail.app上ではメールボックス(フォルダ)を複数階層作成できます。ファイルシステム上のフォルダではないので、ファイル処理でメールのメールボックス(フォルダ)間の移動を行うことはできませんが、パスを指定してmoveコマンドでメールを移動させられます。

Mail.app上のパスは、

level1/level2

のように記述します。

のように選択している場合には、

"ML/Xojo, REALbasic"

と返ってきます。

tell application "Mail"
  tell mailbox "ML/Xojo, REALbasic"
    set aList to every message
  end tell
end tell

★Click Here to Open This Script 

のように指定すれば、指定フォルダ以下のメール(message)が返ってきます(サブフォルダ内のメールは対象外)。ファイルパスと異なり、空白文字が入ってくることに備えてquoted ofでパスをクォート処理しておく必要はありません。

フルパスを求めるのに、再帰処理を行なっています。

AppleScript名:Mail.appで選択中のメールボックス(フォルダ)のパスを文字列で返す
property aFullPath : ""

set aFullPath to ""

–選択中のメールボックス(フォルダ)を取得
set aMB to getSelectedOneMailBox() of me

–メールボックス(フォルダ)のオブジェクトのMail.app上のパスを取得して文字列化する
set pathStr to extraxctTextFullPathOfMBObject(aMB) of me
return aFullPath
–> "ML/Xojo, REALbasic"

–Message Viewerで現在表示中のメールボックスの情報を1つのみ返す
on getSelectedOneMailBox()
  tell application "Mail"
    tell message viewer 1
      set mbList to selected mailboxes
    end tell
  end tell
  
if length of mbList is equal to 0 then
    return ""
  end if
  
  
set aMailBox to contents of (item 1 of mbList)
  
return aMailBox
end getSelectedOneMailBox

–Mail.appのメールボックスオブジェクトを渡すと、テキストのフルパスに変換
on extraxctTextFullPathOfMBObject(aPath)
  tell application "Mail"
    try
      set parentPath to container of aPath
    on error
      return
    end try
    
    
set meName to name of aPath
    
if aFullPath = "" then –1回目のみスラッシュを入れないで処理
      set aFullPath to meName
    else
      –通常処理はこちら
      
set aFullPath to meName & "/" & aFullPath
    end if
    
    
extraxctTextFullPathOfMBObject(parentPath) of me –再帰呼び出し
  end tell
end extraxctTextFullPathOfMBObject

★Click Here to Open This Script 

Posted in list Text | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Mail | Leave a comment

機械学習で学習したMSの画像を連邦・ジオン軍判定してフォルダ分け v2

Posted on 11月 20, 2018 by Takaaki Naganoya

Create MLでガンダムのMS画像(連邦、ジオン区分け)を学習したCore ML Modelをもとに、画像をどちらの軍所属機体かを判定してフォルダ分けするAppleScriptです。

機械学習(深層学習)データをもとに判定を行って対話型処理ではなくバッチ処理的なフローでデータを「なんとなく」推論して区分けを行うサンプルです。非力なIOT機器が機械学習データをもとに推論処理できるぐらいなので、機械学習データを使ってAppleScriptで推論処理できないはずがありません。推論処理時間もさほどかからず、機械学習データを一括処理的なワークフローの中で利用することが可能です。

例によって「戦場の絆」の公式ページから落としてきたMSの画像を連邦軍とジオン軍、および両軍の鹵獲機体の4フォルダに分けてCreate MLで学習させてみました。

実行するとMS画像が入っているフォルダ(各自ご用意ください)と、連邦軍機体(efsf)およびジオン軍機体(zeon)を仕分けるフォルダを聞かれます。あとはプログラム側がなんとなく推論して仕分けを行います。

前回掲載のモデルもそうですが、きちんと学習後にテストデータで評価を行っています。だいたい8割ぐらいのヒット率ではあるものの、特定の画像については間違えることがあらかじめわかっています。

実行にあたっては、macOS 10.14上でCoreML Modelを組み込んだフレームワーク「msDetector.framework」をインストールして、本ScriptをScript Debugger上で動作させてください。

–> Download msDetector.framework (To ~/Library/Frameworks/)

–> Download msDetector (Code Signed executable AppleScript applet with Framework and libraries in its bundle)

Core MLで判定時に詳細なデータを出力させ、その可能性が0.8を上回るものだけを処理するようにしてみました。ただ、全力で間違った判定を行うケースもあるため、単なる「気休め」です。

テストデータでは、連邦軍側が72機体中間違いが9個(正解87.5%)、ジオン軍側が57機体中間違いが3個(正解95%)。鹵獲機体のデータを排除すると、もう少しいい値が出るかもしれません。ただ、学習させたデータとほぼ同じデータ(数が少ない)で再度判定を行っているだけなので、このぐらいの確度が出ないと逆に困ります。

また、確度が高くないものは排除するように処理したので、確度が低い機体の画像バリエーションを増やして学習画像数を足すと正解の確率が上がるのではないか、などと「なんとなく」考えています。

Create MLでは、もっときちんとした表データやタグ付き自然言語テキストデータ(日本語だと形態素への区分けはあらかじめやっておく必要アリ?)の学習が行えるようなので(自然言語テキストの学習例は英語のものしか見かけないけれども)いろいろやっておきたいところです。

今回は「アニメとかゲームに出てくるMSの画像」の判定という、実用性をまったく考えないで行なってみたものですが、自分の身の回りで有用性の高そうな処理といえば……アイコンやプレゼン用資料の作成画像を学習させておおまかなジャンル分けをしておき(メール系のアイコンとか)、未知のアイコン素材をダウンロードしてきたら機械学習データをもとに自動でフォルダ分けするとかでしょうか。

さすがにアイコン画像だとデフォルメされすぎていて、MicrosoftのAzure Computer Vision APIとかでも画像認識してくれなさそうなので。

AppleScript名:機械学習で学習したMSの画像の連邦軍、ジオン軍判定してフォルダ分け v2.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/11/20
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7" — Mojave (10.14) or later & Script Debugger
use framework "Foundation"
use framework "AppKit"
use framework "msDetector"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSPredicate : a reference to current application’s NSPredicate
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableArray : a reference to current application’s NSMutableArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey

set aFol to POSIX path of (choose folder with prompt "Choose MS Image folder to go!")
set efsfFol to POSIX path of (choose folder with prompt "EFSF folder")
set zeonFol to POSIX path of (choose folder with prompt "Zeon folder")

–指定フォルダ以下のファイルをすべて取得(再帰ですべて取得)
set aSel to retFullpathsWithinAFolderWithRecursive(aFol) of me

–取得したファイルリストを指定UTIでフィルタリング
set filRes2 to filterPOSIXpathListByUTI(aSel, "public.image") of me

–機械学習モデルを用いて区分けを行い、任意の画像の連邦軍(efsf)、ジオン軍(zeon)へのフォルダ分け(コピー)
set msClassifier to current application’s msClassify’s alloc()’s init()

repeat with i in filRes2
  set aFile to contents of i
  
set aImage to (current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile)
  
  
set resDict to (msClassifier’s ImageClassifierWithImageAndRetDict:aImage) –returns NSDictionary
  
  
–Dictinaryのvalueをソートして、最も値の大きいvalueのkeyを求める
  
set sortedArray to (current application’s NSMutableArray’s arrayWithArray:(resDict’s allValues()))
  (
sortedArray’s sortUsingSelector:"compare:")
  
set aRes to (last item of (sortedArray as list)) –末尾のアイテムが一番値の大きい数値
  
set resKey to first item of ((resDict’s allKeysForObject:aRes) as list) –最大値をもとにKeyを求める
  
  
–可能性の高いものだけ処理してみる(自信たっぷりに全力で間違えることもけっこうある)
  
if (aRes as real) > 0.8 then
    if resKey begins with "efsf" then
      –efsf & efsf_stolen
      
set aRes to (my copyFileAt:aFile toFolder:efsfFol)
    else
      –zeon & zeon_stolen
      
set aRes to (my copyFileAt:aFile toFolder:zeonFol)
    end if
  end if
end repeat

–POSIX path listから指定UTIに含まれるものをPOSIX pathのリストで返す
on filterPOSIXpathListByUTI(aList, targUTI)
  set newList to {}
  
repeat with i in aList
    set j to contents of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, targUTI)
    
if utiRes is not equal to {} then
      set the end of newList to j
    end if
  end repeat
  
return newList
end filterPOSIXpathListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

–指定フォルダ以下のすべてのファイルを再帰で取得
on retFullpathsWithinAFolderWithRecursive(aFol)
  set anArray to NSMutableArray’s array()
  
set aPath to NSString’s stringWithString:aFol
  
set dirEnum to NSFileManager’s defaultManager()’s enumeratorAtPath:aPath
  
  
repeat
    set aName to (dirEnum’s nextObject())
    
if aName = missing value then exit repeat
    
set aFullPath to aPath’s stringByAppendingPathComponent:aName
    
anArray’s addObject:(aFullPath as string)
  end repeat
  
  
return anArray as list
end retFullpathsWithinAFolderWithRecursive

on copyFileAt:POSIXPath toFolder:folderPath
  set POSIXPath to NSString’s stringWithString:POSIXPath
  
set folderPOSIXPath to NSString’s stringWithString:folderPath
  
set theName to POSIXPath’s lastPathComponent()
  
set newPath to folderPOSIXPath’s stringByAppendingPathComponent:theName
  
set fileManager to NSFileManager’s defaultManager()
  
set theResult to fileManager’s copyItemAtPath:POSIXPath toPath:newPath |error|:(missing value)
  
return (theResult as integer = 1)
end copyFileAt:toFolder:

★Click Here to Open This Script 

Posted in file File path Image list Machine Learning Record | Tagged 10.14savvy NSArray NSFileManager NSMutableArray NSPredicate NSSortDescriptor NSString NSURL NSURLTypeIdentifierKey | 1 Comment

値渡しハンドラと参照渡しハンドラ

Posted on 11月 16, 2018 by Takaaki Naganoya

AppleScriptにおけるハンドラ(メソッド、サブルーチン)呼び出し時の値渡し(call by value)と参照渡し(vall by reference)の記述例です。

一般的なプログラミング言語の世界の「お作法」で、ハンドラ呼び出し時に、値そのものを受けわたす「値渡し」(call by value)と、ポインタを受けわたす「参照渡し」(call by reference)の2つがあります。

AppleScriptでハンドラ呼び出し時に、パラメータを受け渡しするのは値渡しがほとんどです。これは、プログラムにバグを作りにくくするためとサブルーチン(ハンドラ)を明確に区分けするためです。

値渡ししか使っていないといっても、参照渡しができないわけではなく、パラメータとして受け取った変数の内容を変更すると参照渡しになる、という「暗黙の参照渡し」を行える仕様になっています。

ほかには、巨大なデータを値渡しするとメモリー消費がそれだけ大きくなるため、パラメータを参照渡しすることがあります。画像のRaw DataやRTFのデータ内容などがそれに該当します。

本Blog掲載サンプル(1,000本以上)のうち、参照渡しを使っているものは、

・迷路をRTFで作成して脱出経路を赤く着色する v3
・画像+文字作成テスト_v4
・Tag CloudっぽいRTFの作成

ぐらいです。

# Xcode上で記述するAppleScript Applicationでは、ARC環境ではあるものの消費したオブジェクトのメモリが解放されにくいので、割と切実に必要になってくるような

AppleScript名:値渡しハンドラ
set aList to {1, 2, 3}
set aRes to aHandler(aList) of me
–> {3, 2, 1}

aList
–> {1, 2, 3}

on aHandler(bList)
  return reverse of bList
end aHandler

★Click Here to Open This Script 

AppleScript名:参照渡しハンドラ
set aList to {1, 2, 3}
aHandler(aList) of me
aList
–> {1, 2, 3, -1}

on aHandler(aList)
  set the end of aList to -1
end aHandler

★Click Here to Open This Script 

Posted in list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy | Leave a comment

指定画像の指定座標の色情報をRGBで取得

Posted on 11月 15, 2018 by Takaaki Naganoya

指定画像の指定座標のピクセルの色を8ビットRGB値で返すAppleScriptです。

使用する座標系は左上が{0,0}です。

AppleScript名:指定画像の指定座標の色情報をRGBで取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/11/15
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Cocoa" –Foundation + AppKit
use scripting additions

property NSImage : a reference to current application’s NSImage
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep

set aFile to POSIX path of (choose file of type {"public.image"})
set {xNum, yNum} to {0, 0}

set cRes to getColorRGBNumFromImageByPosition(aFile, xNum, yNum) of me
–> {255, 0, 0}

on getColorRGBNumFromImageByPosition(aFilePath, xPos as integer, yPos as integer)
  set anNSImage to NSImage’s alloc()’s initWithContentsOfFile:aFilePath
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:(anNSImage’s TIFFRepresentation())
  
  
set aColor to (aRawimg’s colorAtX:xPos y:yPos)
  
set aRed to (aColor’s redComponent()) * 255
  
set aGreen to (aColor’s greenComponent()) * 255
  
set aBlue to (aColor’s blueComponent()) * 255
  
  
return {aRed as integer, aGreen as integer, aBlue as integer}
end getColorRGBNumFromImageByPosition

★Click Here to Open This Script 

Posted in Color Image list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSBitmapImageRep NSImage | Leave a comment

squeezeNetで画像のオブジェクト認識を行い、結果をCSV出力してNumbersでオープン

Posted on 11月 12, 2018 by Takaaki Naganoya

Core MLモデル「squeezeNet.mlmodel」を用いて画像のオブジェクト認識を行い、結果をデスクトップにCSV形式でデータ書き出しを行い、NumbersでオープンするAppleScriptです。

–> Download squeezeNetKit.framework (To ~/Library/Frameworks)

macOS 10.13上ではスクリプトエディタ、Script Debugger上で、macOS 10.14上ではScript Debugger上で動作します(AppleScript Appletでも動作)。

さまざまなCore MLモデルを用いて画像認識を行わせてはみたものの、最もスコアの高いもののラベルだけを返させても、結果がまったく内容にそぐわないケースが多く見られます。


▲機械学習の評価値が1.0に近いものは確度が高く、0.0に近いものは判断に迷った末に消極的に出した値。経験則でいえば、最高スコアの項目が0.7以上のスコアでないといまひとつのようです(0.7以上でもダメな時はダメなわけで、、、)

画像認識については、結果があまりに的外れで「なんじゃこら?」というケースが多いので、確認のためにCSVデータ出力させてNumbers上で確認させてみることにしてみました。そのためにありあわせのサブルーチンを組み合わせて作ったものです。

スコアが高い順に結果を並べて確認してみると、最も高いスコアの値であっても0.0に近い評価値であることがあり、「何も考えずに最高スコアのラベルだけ取ってくるのはものすごく危険」なことがよくわかります。


▲squeezeNet.mlmodelをNetronでビジュアライズしたところ(一部抜粋)

AppleScript名:SqueezeNetでオブジェクト判定して結果をCSV書き出ししてNumbersでオープン.scpt
–  Created by: Takaaki Naganoya
–  Created on: 2018/11/11
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
use AppleScript version "2.7" — High Sierra (10.13) or later
use framework "Foundation"
use framework "AppKit"
use framework "squeezeNetKit"
use scripting additions

property NSUUID : a reference to current application’s NSUUID
property NSArray : a reference to current application’s NSArray
property NSImage : a reference to current application’s NSImage
property NSMutableArray : a reference to current application’s NSMutableArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor

set aFile to POSIX path of (choose file of type {"public.image"})
set aImage to NSImage’s alloc()’s initWithContentsOfFile:aFile
set resDict to (current application’s imageClassifier’s alloc()’s init()’s ImageClassifierWithImageAndRetDict:aImage)

set kArray to (resDict’s allKeys())
set vArray to (resDict’s allValues())
set aLen to kArray’s |count|()

set anArray to NSMutableArray’s new()

repeat with i from 0 to (aLen – 1)
  set tmpKey to (kArray’s objectAtIndex:i) as string
  
set tmpVal to (vArray’s objectAtIndex:i) as real
  (
anArray’s addObject:{aKey:tmpKey, aVal:tmpVal})
end repeat

–Sort by value
set bList to sortRecListByLabel(anArray, "aVal", false) of me

–record in list –> 2D list
set cList to convRecInListTo2DList(bList) of me

–デスクトップにCSVファイルを書き出してNumbersでオープン
set aPath to (((path to desktop) as string) & (NSUUID’s UUID()’s UUIDString()) as string) & ".csv"
set fRes to saveAsCSV(cList, aPath) of me
tell application "Numbers"
  open (aPath as alias)
end tell

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList as list, aLabelStr as string, ascendF as boolean)
  set aArray to NSArray’s arrayWithArray:aRecList
  
set sortDesc to NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF
  
set sortDescArray to NSArray’s arrayWithObjects:sortDesc
  
set sortedArray to aArray’s sortedArrayUsingDescriptors:sortDescArray
  
set bList to (sortedArray) as anything
  
return bList
end sortRecListByLabel

on convRecInListTo2DList(aList)
  set anArray to NSMutableArray’s arrayWithArray:aList
  
set fItem to anArray’s objectAtIndex:0
  
set keyList to fItem’s allKeys() as list
  
set aCount to anArray’s |count|()
  
  
set allArray to NSMutableArray’s new()
  
  
repeat with i from 1 to aCount
    set anItem to (anArray’s objectAtIndex:(i – 1))
    
set tmpItem to {}
    
    
repeat with ii in keyList
      set tmpDat to (anItem’s valueForKeyPath:(contents of ii))
      
if tmpDat is not equal to missing value then
        set the end of tmpItem to tmpDat
      else
        set the end of tmpItem to ""
      end if
    end repeat
    (
allArray’s addObject:tmpItem)
  end repeat
  
  
set allList to allArray as list
  
set the beginning of allList to keyList
  
return allList
end convRecInListTo2DList

–CSV Kit

–2D List to CSV file
on saveAsCSV(aList, aPath)
  –set crlfChar to (ASCII character 13) & (ASCII character 10)
  
set crlfChar to (string id 13) & (string id 10)
  
set LF to (string id 10)
  
set wholeText to ""
  
  
repeat with i in aList
    set newLine to {}
    
    
–Sanitize (Double Quote)
    
repeat with ii in i
      set jj to ii as text
      
set kk to repChar(jj, string id 34, (string id 34) & (string id 34)) of me –Escape Double Quote
      
set the end of newLine to kk
    end repeat
    
    
–Change Delimiter
    
set aLineText to ""
    
set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to "\",\""
    
set aLineList to newLine as text
    
set AppleScript’s text item delimiters to curDelim
    
    
set aLineText to repChar(aLineList, return, "") of me –delete return
    
set aLineText to repChar(aLineText, LF, "") of me –delete lf
    
    
set wholeText to wholeText & "\"" & aLineText & "\"" & crlfChar –line terminator: CR+LF
  end repeat
  
  
if (aPath as string) does not end with ".csv" then
    set bPath to aPath & ".csv" as Unicode text
  else
    set bPath to aPath as Unicode text
  end if
  
  
write_to_file(wholeText, bPath, false) of me
  
end saveAsCSV

on write_to_file(this_data, target_file, append_data)
  tell current application
    try
      set the target_file to the target_file as text
      
set the open_target_file to open for access file target_file with write permission
      
if append_data is false then set eof of the open_target_file to 0
      
write this_data to the open_target_file starting at eof
      
close access the open_target_file
      
return true
    on error error_message
      try
        close access file target_file
      end try
      
return error_message
    end try
  end tell
end write_to_file

on repChar(origText as text, targChar as text, repChar as text)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

★Click Here to Open This Script 

Posted in file Image list Machine Learning Record Sort | Tagged 10.13savvy 10.14savvy NSArray NSImage NSMutableArray NSSortDescriptor NSUUID Numbers | Leave a comment

Numbersの表の選択範囲をHTML化 v2

Posted on 11月 1, 2018 by Takaaki Naganoya

Numbersの最前面の書類で選択中の表の範囲をHTML化してファイルに書き出すAppleScriptです。

Numbers v5.1+macOS 10.12.6、Numbers v5.2+macOS 10.13.6、Numbers v5.2+macOS 10.14.1で確認しています。

Numbers v5.xでは書類の書き出し(export)形式にPDF、Microsoft Excel、CSV、Numbers 09の4形式を指定できるようになっていますが、HTMLの表(table)に書き出してくれたりはしません。

GUI側(↑)にその機能がありませんし、AppleScript用語辞書(↓)にもそういう機能は見当たりません。

ただ、HTMLの表に書き出すという処理自体はとても初歩的で簡単な内容なので、「なければ作ればいい」ということになるでしょう。

そこで、ありもののルーチンを組み合わせて、選択範囲をHTMLの表(table)として書き出すScriptを書いて、OS標準装備のScript Menuに入れて利用しています。書き出すHTMLの内容は個人の趣味や用途に応じて大きく変わってきますので、そのあたりは自由に書き換えるとよいと思われます。

本Scriptは、電子書籍「AppleScript最新リファレンス」の正誤表(2018/1のBlog消失事件の影響で、書籍内で参照していたURLリンクが切れてしまったことへの対処)を作成するために、すでに存在していたものにほんの少しの修正を加えたものです。

データベースやデータからHTMLを作成するという処理は、自分がAppleScriptを覚えようと思ったきっかけでもあり(当時は、FileMaker Pro Server経由でLAN上でデータを共有して処理)、感慨深いものがあります。

AppleScript名:表の選択範囲をHTML化 v2
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use bPlus : script "BridgePlus"

script spd
  property uniqList : {}
end script

set (uniqList of spd) to {}

–Numbersからデータを取得する
set aList to get2DListFromNumbersSelection() of me
if aList = false then return

set colCount to (length of first item of first item of aList) + 1

set aHeader to "<!DOCTYPE html><html lang=\"ja\" ><head><meta charset=\"utf-8\"><title>First</title><style>table , td, table , tr, th{border:1px solid #333333;padding:1px;}</style></head><body><table >"
set aTable to "<tr><td>%@</td><td> </td><td>%@</td><td>%@</td><td>%@</td></tr>"
set aFooter to "</table></body></html>"

set aHTMLres to retTableHTML(aList, aHeader, aTable, aFooter) of me

set tFile to choose file name with prompt "Select Output File Name and Folder"
set fRes to writeToFileAsUTF8(aHTMLres, tFile, false) of me

on retTableHTML(aList, aHeaderHTML, aTableHTML, aFooterHTML)
  set allHTML to current application’s NSMutableString’s stringWithString:aHeaderHTML
  
  
set aCounter to 1
  
repeat with i in aList
    set j to contents of i
    
    
set aRowHTML to "<tr>"
    
repeat with ii in j
      set jj to contents of ii
      
if jj = missing value then
        set jj to " "
      else
        try
          set aURL to (current application’s |NSURL|’s URLWithString:jj)
          
if aURL is not equal to missing value then
            set jj to "<a href=\"" & jj & "\">" & jj & "</a>"
          end if
        end try
      end if
      
      
set aTmpHTML to "<td>" & jj & "</td>"
      
set aRowHTML to aRowHTML & aTmpHTML
    end repeat
    
set aRowHTML to aRowHTML & "</tr>"
    
    (
allHTML’s appendString:aRowHTML)
    
set aCounter to aCounter + 1
  end repeat
  
  (
allHTML’s appendString:aFooterHTML)
  
  
return allHTML as list of string or string
end retTableHTML

–テキストを指定デリミタでリスト化
on parseByDelim(aData, aDelim)
  set aText to current application’s NSString’s stringWithString:aData
  
set aList to aText’s componentsSeparatedByString:aDelim
  
return aList as list
end parseByDelim

on get2DListFromNumbersSelection()
  –Numbersで選択範囲を縦に区切ったリストを返す
  
tell application "Numbers"
    tell front document
      tell active sheet
        set theTable to first table whose class of selection range is range
        
        
tell theTable
          try
            set selList to value of every cell of selection range –選択範囲のデータを取得
          on error
            return "" –何も選択されてなかった場合
          end try
          
          
set selName to name of selection range –選択範囲のrange情報を取得
          
set {s1, s2} to parseByDelim(selName, ":") of me
          
          
–始点の情報を取得する
          
set s1Row to (address of row of range s1) as integer
          
set s1Column to (address of column of range s1) as integer
          
          
–終点の情報を取得する
          
set s2Row to (address of row of range s2) as integer
          
set s2Column to (address of column of range s2) as integer
          
          
–選択範囲の情報を取得する
          
set selHeight to s2Row – s1Row + 1 –高さ(Height of selection range)
          
set selWidth to s2Column – s1Column + 1 –幅(Width of selection range)
          
        end tell
      end tell
    end tell
  end tell
  
  
set aLen to length of selList
  
set aaLen to selHeight
  
  
set bList to {}
  
repeat with i from 1 to aaLen
    set aHoriList to {}
    
    
repeat with ii from 1 to selWidth
      set j1 to ii + (i – 1) * selWidth
      
set tmpCon to contents of item j1 of selList
      
      
set aClass to class of tmpCon
      
if aClass = number or aClass = integer or aClass = real then
        set tmpCon to tmpCon as integer
      end if
      
      
set the end of aHoriList to tmpCon
    end repeat
    
    
set the end of bList to aHoriList
  end repeat
  
  
return bList
  
end get2DListFromNumbersSelection

on writeToFileAsUTF8(this_data, target_file, append_data)
  tell current application
    try
      set the target_file to the target_file as text
      
set the open_target_file to open for access file target_file with write permission
      
if append_data is false then set eof of the open_target_file to 0
      
write this_data as «class utf8» to the open_target_file starting at eof
      
close access the open_target_file
      
return true
    on error error_message
      try
        close access file target_file
      end try
      
return error_message
    end try
  end tell
end writeToFileAsUTF8

★Click Here to Open This Script 

Posted in Internet list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Numbers | Leave a comment

Photosで選択中の写真が指定場所から50メートル以内の場合には書き出してFineReader OCR Proで処理 v2.1

Posted on 10月 30, 2018 by Takaaki Naganoya

Photos(写真).app上で選択中の写真が、指定場所から50メートル以内の場合にはファイルに書き出して、OCRアプリケーション「FineReader OCR Pro」でOCR処理するAppleScriptです。

–> Demo Movie

アーケードゲーム「戦場の絆」のリプレイムービーは、プレイ後の操作により1日に2プレイ分までYouTubeにアップロードされる仕様になっています。プレイ後、ゲーセンのターミナル上で操作してリプレイムービーにアクセスするためのアクセスコード(リプレイID)が表示されるようになっています。

この、ターミナル上のアクセスコードをiPhoneで写真撮影すると、写真に撮影場所のGPSデータが添付されます。写真.app経由でこのGPSデータを取得し、指定場所(ゲームセンター)から50メートル以内であればターミナルで撮影した写真と判定。

この写真をファイル書き出しして、OCRアプリケーションで認識しやすいようにCocoaの機能を用いて階調反転。一昔前ならPhotoshopで処理していましたが、いまならAppleScriptだけで高速に処理できます。デモムービーは実際の速度なので、その速さを体感していただけると思います。写真.appから選択中の写真を取得して反転するまで一瞬です。

反転した画像をMacのOCRアプリケーション「FineReader OCR Pro」でOCR処理し、YouTubeリプレイ再生用のコードを取得します。

あとは、再生用コードをリプレイムービー検索ページのフォームに入れて、実際のYouTube上のリプレイムービーのURLにアクセス。そのまま再生するなり、ダウンロードして保存しておくことになります。


■©創通・サンライズ

本サンプルでは、AppleScriptからコントロール可能なOCRアプリケーション「FineReader OCR Pro」を用いましたが、日本語の文字列を認識しないのであれば、Web APIのOCRサービス(MicrosoftのCognitive Serviceとか)を用いてみてもよいでしょう。

あとは、全国の「戦場の絆」が導入されているゲームセンターの住所情報および緯度経度情報がストックしてあるので、それらのGPSデータと写真撮影地点とのマッチングを行なってみてもよいかもしれません(700箇所程度なので、たいした演算ではありません)。

AppleScript名:Photosで選択中の写真が指定場所から50メートル以内の場合には書き出してFineReader OCR Proで処理 v2.1
— Created 2015-12-04 by Takaaki Naganoya –v2.1
— getDistanceBetweenTwoPlaces : Created 2015-03-03 by Shane Stanley
— convAsFilteredJPEG : Created 2013-12-29 by badcharan@Twitter
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "CoreLocation"
use framework "QuartzCore"

property CIFilter : a reference to current application’s CIFilter
property NSUUID : a reference to current application’s NSUUID
property CIImage : a reference to current application’s CIImage
property NSString : a reference to current application’s NSString
property NSScanner : a reference to current application’s NSScanner
property CLLocation : a reference to current application’s CLLocation
property NSFileManager : a reference to current application’s NSFileManager
property NSCharacterSet : a reference to current application’s NSCharacterSet
property NSJPEGFileType : a reference to current application’s NSJPEGFileType
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
property NSMutableCharacterSet : a reference to current application’s NSMutableCharacterSet
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch

set targPlace to {35.745769, 139.675565} –Game City Itabashi
set bLoc to getSelectionOnPhotos() of me –Get Location From Photo
set aDist to getDistanceBetweenTwoPlaces(targPlace, bLoc) of me

–指定地点から50メートル以内の距離で撮影された写真であれば、Exportして後続の処理を行う
if aDist is equal to false or aDist > 50 then return false

–選択中の写真のうち最初のものだけExport
set targPhotoAlias to exportSelectedPhotoOnPhotos() of me

–実験に用いた写真はそのままではOCR処理できなかったので、手っ取り早く階調反転を行う
set invertRes to convAsFilteredJPEG(targPhotoAlias, "CIColorInvert") of me
if invertRes = false then
  display dialog "Error in CIImage" with title "Error" buttons {"OK"} default button 1 with icon 2
  
return
end if
set invImage to (POSIX file invertRes) as alias

set outFileName to (NSUUID’s UUID()’s UUIDString() as text) & ".txt"
set outPath to ((path to desktop) as text) & outFileName –sandbox環境ではパスは無視される

tell application id "com.abbyy.FineReaderPro"
  activate
  
  
–Check Ready
  
set getReady to (is finereader controller active)
  
if getReady = false then return
  
  
–set idList to {English} –英語だとスペルチェックされて、"1"と"t"を誤認識するため英語の指定をやめた
  
–Caution: this command is executed asynchronously
  
–Caution: export command was changed in v12.1.4
  
  
set idList to {Basic, Java, Fortran, Cobol, Pascal} –認識ターゲット言語(C++の予約語はあるようだが、指定できないよ!)
  
open invImage — In this version ,we have to open image, at first.
  
export to txt outPath ocr languages enum idList retain layout (as plain text) encoding (utf8)
  
  
–Wait for finish
  
repeat 30 times
    set curStat to (is busy)
    
if curStat = false then exit repeat
    
delay 1
  end repeat
  
  
–sandbox環境(Mac App Store版)の場合にはファイル出力先を別途取得
  
set sandRes to (is sandboxed)
  
if sandRes = true then
    set outDir to (get output dir) as text
    
set outPath to (outDir & outFileName) as text
  end if
  
end tell

–OCR処理した結果のテキストを読み込む
tell current application
  set textRes to read file outPath
end tell

–数字ではじまる行のみを抽出して、リプレイIDを取得
set textList to paragraphs of textRes
set outList to {}
repeat with i in textList
  set j to contents of i
  
  
–1行あたりのテキストが8文字以上か?
  
if length of j > 8 then
    
    
–行頭の文字が数字か?
    
set firstChar to first character of j
    
set nRes to chkNumeric(firstChar) of me
    
if nRes = true then
      set tmp1 to text 2 thru -1 of j –最初の数字を除去
      
      
–数字とアルファベット以外の文字を削除(タブ、スペースなど)
      
set tmp2 to returnNumberAndAlphabetCharsOnly(tmp1) of me
      
if length of tmp2 ≥ 8 then
        set tmp3 to text 1 thru 8 of tmp2
        
–数字とアルファベットの混在の文字列であれば出力する
        
set tmp3Res to chkMixtureOfNumericAndAlphabet(tmp3) of me
        
if tmp3Res = true then
          set the end of outList to tmp3
        end if
      end if
    end if
  end if
end repeat

outList
–> {"4f73vg1v", "v3v32zt3", "yk1z371x", "52yzvn11", "k1ftfvvg"}–Version 1.x (3rd ID is wrong)
–> {"4f73vg1v", "v3v32zt3", "yk1z37tx", "52yzvn11", "k1ftfvvg"}– Version 2.0(Perfect!!!!)
–> {"4f73vg1v", "v3v32zt3", "yk1z37tx", "52yzvn11", "k1ftfvvg"}– Version 2.1(Perfect!!!!)
–> {"51n4gg1f", "2zxt41gg", "57k2txk9", "yy43f3gt", "4f73vg1v"} –Version 2.1(Perfect!!!!)

–Photosで選択中の写真の1枚目(複数時には無視)から緯度、経度情報を取得する
on getSelectionOnPhotos()
  tell application "Photos"
    set aa to selection
    
if aa = {} or aa = missing value then return false
    
set a to first item of aa
    
set aProp to properties of a
    
    
set aLoc to location of aProp
    
return aLoc
  end tell
end getSelectionOnPhotos

–2点間の距離を計算する
on getDistanceBetweenTwoPlaces(aPlaceLoc, bPlaceLoc)
  try
    set {aLat, aLong} to aPlaceLoc
    
set {bLat, bLong} to bPlaceLoc
  on error
    return false
  end try
  
  
set aPlace to CLLocation’s alloc()’s initWithLatitude:aLat longitude:aLong
  
set bPlace to CLLocation’s alloc()’s initWithLatitude:bLat longitude:bLong
  
set distanceInMetres to aPlace’s distanceFromLocation:bPlace
  
return (distanceInMetres as real)
end getDistanceBetweenTwoPlaces

–Photos上で選択中の写真をTemporary Folderに掘ったフォルダに書き出して、そのalias情報を返す
on exportSelectedPhotoOnPhotos()
  set dtPath to (path to temporary items) as text
  
set aUUID to NSUUID’s UUID()’s UUIDString() as text
  
  
set dirPath to ((POSIX path of dtPath) & aUUID)
  
set fileManager to NSFileManager’s defaultManager()
  
set aRes to (fileManager’s createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
  
set dtPath to dtPath & aUUID
  
  
tell application "Photos"
    set a to selection
    
if a = {} then return
    
set aRes to (export a to file dtPath)
  end tell
  
  
tell application "Finder"
    tell folder dtPath
      set fList to (every file) as alias list
    end tell
  end tell
  
  
if fList = {} then return false
  
return first item of fList
end exportSelectedPhotoOnPhotos

–CIFilterをかけたJPEG画像を生成
–参照:http://ashplanning.blogspot.jp/ のうちのどこか
on convAsFilteredJPEG(aPath, aFilterName)
  
  
–aliasをURL(input)とPOSIX path(output) に変換
  
set aURL to (current application’s |NSURL|’s fileURLWithPath:(POSIX path of aPath)) –Input
  
set aPOSIX to (POSIX path of aPath) & "_" & aFilterName & ".jpg" –Output
  
  
–CIImageを生成
  
set aCIImage to CIImage’s alloc()’s initWithContentsOfURL:aURL
  
  
— CIFilter をフィルタの名前で生成
  
set aFilter to CIFilter’s filterWithName:aFilterName
  
aFilter’s setDefaults() –各フィルタのパラメータはデフォルト
  
  
–Filterを実行
  
aFilter’s setValue:aCIImage forKey:"inputImage"
  
set aOutImage to aFilter’s valueForKey:"outputImage"
  
  
— NSBitmapImageRep を CIImage から生成
  
set aRep to NSBitmapImageRep’s alloc()’s initWithCIImage:aOutImage
  
  
— NSBitmapImageRep から JPEG データを取得
  
set jpegData to aRep’s representationUsingType:(NSJPEGFileType) |properties|:(missing value)
  
  
— ファイルに保存
  
set fsRes to jpegData’s writeToFile:aPOSIX atomically:true
  
if (fsRes as boolean) = false then return false –失敗した場合
  
return aPOSIX –成功した場合
  
end convAsFilteredJPEG

–数字とアルファベットの混在状態の時にtrueを返す
on chkMixtureOfNumericAndAlphabet(checkString)
  set a0Res to chkAlphabetAndNumeric(checkString) of me
  
set a1Res to chkNumeric(checkString) of me
  
set a2Res to chkAlphabet(checkString) of me
  
if {a0Res, a1Res, a2Res} = {true, false, false} then
    return true
  else
    return false
  end if
end chkMixtureOfNumericAndAlphabet

–数字のみかを調べて返す
on chkNumeric(checkString)
  set digitCharSet to NSCharacterSet’s characterSetWithCharactersInString:"0123456789"
  
set ret to my chkCompareString:checkString baseString:digitCharSet
  
return ret as boolean
end chkNumeric

— アルファベットのみか調べて返す
on chkAlphabet(checkString)
  set aStr to NSString’s stringWithString:checkString
  
set allCharSet to NSMutableCharacterSet’s alloc()’s init()
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "a", 26))
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "A", 26))
  
set aBool to my chkCompareString:aStr baseString:allCharSet
  
return aBool as boolean
end chkAlphabet

— アルファベットと数字のみか調べて返す
on chkAlphabetAndNumeric(checkString)
  set aStr to NSString’s stringWithString:checkString
  
set allCharSet to NSMutableCharacterSet’s alloc()’s init()
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "0", 10))
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "a", 26))
  
allCharSet’s addCharactersInRange:(current application’s NSMakeRange(ASCII number of "A", 26))
  
set aBool to my chkCompareString:aStr baseString:allCharSet
  
return aBool as boolean
end chkAlphabetAndNumeric

on chkCompareString:checkString baseString:baseString
  set aScanner to NSScanner’s localizedScannerWithString:checkString
  
aScanner’s setCharactersToBeSkipped:(missing value)
  
aScanner’s scanCharactersFromSet:baseString intoString:(missing value)
  
return (aScanner’s isAtEnd()) as boolean
end chkCompareString:baseString:

–文字置換
on repChar(aStr, targStr, repStr)
  set aString to NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:targStr withString:repStr
  
return bString as text
end repChar

–アルファベットと数字以外を削除して返す
on returnNumberAndAlphabetCharsOnly(aStr)
  set anNSString to NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:"[^0-9A-Za-z]" withString:"" options:(NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end returnNumberAndAlphabetCharsOnly

★Click Here to Open This Script 

Posted in file filter geolocation Image list OCR Sandbox | Tagged 10.11savvy 10.12savvy FineReader OCR Pro | Leave a comment

ハッシュ値から、メモリースティックに保存されたReplay Dataのステージ名とプレイ日時を求める v3

Posted on 10月 30, 2018 by Takaaki Naganoya

PSPのゲーム「戦場の絆ポータブル」のセーブデータについているステージ別のプレビュー画像のハッシュ値から対戦ステージと対戦日時を抽出してタブ区切りテキストで出力するAppleScriptです。

保存された対戦データの内容を集計するために作成したものです。

PSPでメモリースティックに保存された対戦データには、すべて同じファイル名(ICON0.PNG)で異なる画像が添付されていました。ファイル名で対戦ステージを判定しようにも同じファイル名なので区別できません。

そこで、Spotlightの機能を用いて各セーブデータの「ICON0.PNG」をピックアップし、画像の内容のハッシュ値(SHA-1)を計算して、対戦ステージ内容を判定しました。

直近の対戦データをメモリースティック経由でMacに読み込み、本Scriptで分析してみたところ、

ジャブロー地上	2017年9月18日月曜日 14:42:48
ジャブロー地上	2017年9月18日月曜日 14:50:02
ジャブロー地上	2017年9月18日月曜日 14:55:22
タクラマカン砂漠	2017年9月18日月曜日 15:02:32
サイド7	2017年9月18日月曜日 15:09:36
ジャブロー地下	2017年9月18日月曜日 15:17:06
ジャブロー地上	2017年9月18日月曜日 15:31:08
サイド7	2017年9月18日月曜日 15:38:28
ジャブロー地上	2017年9月18日月曜日 15:51:52
ジャブロー地上	2018年1月2日火曜日 16:13:30
サイド7	2018年1月2日火曜日 16:23:04
ジャブロー地下	2018年1月2日火曜日 16:57:22

のようになりました。正月と秋分の日に親戚で集まったときに甥っ子と対戦した様子がありありと記録されています。

本Scriptはたまたまゲームのセーブデータの集計を行っていますが、同様の形式のデータを集計したい場合には使えそうです。

–> md5Lib.framework(To ~/Library/Frameworks)

AppleScript名:チェックサム値から、メモリースティックに保存されたReplay Dataのステージ名とプレイ日時を求める v3.1
— Created 2015-04-17 by Takaaki Naganoya
— Modified 2018-10-29 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "md5Lib" –https://github.com/JoeKun/FileMD5Hash
use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html
use mdLib : script "Metadata Lib" version "2.0.0" –https://www.macosxautomation.com/applescript/apps/

property FileHash : a reference to current application’s FileHash
property SMSForder : a reference to current application’s SMSForder
property NSPredicate : a reference to current application’s NSPredicate
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableArray : a reference to current application’s NSMutableArray

–各SAVEDATAフォルダ中の「ICON0.PNG」のSHA-1ハッシュ値とステージ名の対応表
set shaArray to NSMutableArray’s arrayWithArray:{{stageName:"ニューヤーク", sha1:"dbb9e8e26e96dbb4dd3198e55b6cde41aba8e0a8"}, {stageName:"鉱山都市", sha1:"72f35456504b1957ec85fb6a1597ac1a2baa2ee9"}, {stageName:"グレートキャニオン", sha1:"9de93e8b853fe153bc73066fb07481c774499960"}, {stageName:"サイド7", sha1:"be22fa949bfd78b0cd97596929f07ce4ec501d7b"}, {stageName:"タクラマカン砂漠", sha1:"5284dc5f0f7a53ee5677908f66da1e00b80f76b6"}, {stageName:"トリントン・タワー", sha1:"9f080853dac45ecaf1672ff2230f2b9a80a00eb4"}, {stageName:"ジャブロー地下", sha1:"a93550099419f52444cf77366773192d0bf5f848"}, {stageName:"ヒマラヤ", sha1:"877f998d608dd267c380e59a17b2a95a139baef5"}, {stageName:"ジャブロー地上", sha1:"8c4ee44e8f2fbcbf061e6d5ea2b202b08f42c59a"}}

load framework

set apPath1 to choose folder with prompt "リプレイデータが入っているフォルダを選択してください"
set aRes to perform search in folders {apPath1} predicate string "kMDItemFSName == %@" search arguments {"ICON0.PNG"}

set outList to {}
set errorList to {}

repeat with i in aRes
  set j to contents of i
  
set sumRes to (FileHash’s sha1HashOfFileAtPath:(j)) as string
  
  
–チェックサムからステージ名を検索する
  
set aPredStr to "sha1 == ’" & sumRes & "’"
  
set aPredicate to (NSPredicate’s predicateWithFormat:aPredStr)
  
set filteredArray to (shaArray’s filteredArrayUsingPredicate:aPredicate)
  
  
if filteredArray as list = {} then
    set the end of errorList to j
  else
    set tmpStage to (filteredArray’s valueForKey:"stageName") as string
    
    
set fAttrib to (NSFileManager’s defaultManager()’s attributesOfItemAtPath:j |error|:(missing value))
    
set cDat to (fAttrib’s fileCreationDate()) as date
    
    
set tmpList to {tmpStage, cDat}
    
    
if tmpList is not in outList then
      set the end of outList to tmpList
    end if
  end if
end repeat

–2D Listのソート
set sortIndexes to {1} –Key Item id: begin from 0
set sortOrders to {true} –ascending = true
set sortTypes to {"compare:"}
set out2List to (SMSForder’s subarraysIn:(outList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list

–2D Listをタブ区切りテキストに変換して返す
set aText to retItemDelimedAndParagraphDelimedText(out2List, tab, return) of me

–入れ子のリストを、アイテム間のデリミタとパラグラフ間のデリミタを指定してテキスト化
–というか、入れ子のリストをタブ区切りテキストにするのが目的
on retItemDelimedAndParagraphDelimedText(aList, itemDelim, paragraphDelim)
  set aText to ""
  
  
repeat with i in aList
    set aStr to retDelimedText(i, itemDelim) of me
    
set aText to aText & aStr & paragraphDelim
  end repeat
  
  
return aText
end retItemDelimedAndParagraphDelimedText

on retDelimedText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retDelimedText

★Click Here to Open This Script 

Posted in file File path list Record Spotlight | Tagged 10.11savvy 10.12savvy NSFileManager NSMutableArray NSPredicate | Leave a comment

recordのlistでKeynoteに表を作成する v1.5

Posted on 10月 29, 2018 by Takaaki Naganoya

リスト(配列)に入ったレコード(dictionary)のデータをもとに、現在オープン中のKeynoteの書類にスライドを追加し、その上に表を作成するAppleScriptです。

Keynoteで書類をオープンしていない場合には、書類を新規作成します。

表のデータを順次埋めているのは、Keynoteの表作成機能がそのように実装されているからです。普通は、データ内容を指定して表の作成と内容を一気に指定するように実装するのが普通ですが、Keynoteのこの仕様はあまりいただけません。

そのため、各セルを順次データで埋めているわけですが(アプリケーションとの通信頻度が高くなるので本来は避けたい処理)、「ignoring application responses」〜「end ignoring」による非同期実行モードによりデータでセルを埋める箇所のみ倍速実行しています。

「ignoring application responses」を使うと、AppleScriptがアプリケーションに命令を出して、正常に実行されたかどうかの「実行結果」を待たないため、おおよそ2倍の速度で実行できます。ただし、命令が正常に実行できたかどうかを取得できないので、安全性が確認されているようなケースで返り値が不要な場合にのみ利用できます。

macOS 10.14, Mojaveで実行したところ、macOS 10.12, Sierraの倍ぐらいの速度で実行しました。なんだろうこれは(10.12:Keynote v8.1, 10.14:Keynote v8.2)、、、、ハードウェアの構成などはほぼ同じで、まったく同じScriptを実行したのですが、、、、

AppleScript名:recordのlistでKeynoteに表を作成する v1.5
— Created 2016-06-29 by Takaaki Naganoya
— 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

property aFontSize : 12

–This Date format is localized in Japanese. But this date string works only string. Don’t care about it.
set aList to {{pathStr:"book1_0.1.pdf", creationDate:"2016年6月17日金曜日", pageCount:222, countChars:127978, fileSize:"12027660"}, {pathStr:"book1_0.2.pdf", creationDate:"2016年6月17日金曜日", pageCount:230, countChars:129506, fileSize:"11109818"}, {pathStr:"book1_0.210.pdf", creationDate:"2016年6月18日土曜日", pageCount:254, countChars:147119, fileSize:"22832000"}, {pathStr:"book1_0.211.pdf", creationDate:"2016年6月18日土曜日", pageCount:254, countChars:147123, fileSize:"22831931"}, {pathStr:"book1_0.212.pdf", creationDate:"2016年6月18日土曜日", pageCount:241, countChars:134856, fileSize:"22273252"}, {pathStr:"book1_0.213.pdf", creationDate:"2016年6月18日土曜日", pageCount:241, countChars:134845, fileSize:"22271667"}, {pathStr:"book1_0.214.pdf", creationDate:"2016年6月18日土曜日", pageCount:241, countChars:134850, fileSize:"22270980"}, {pathStr:"book1_0.220.pdf", creationDate:"2016年6月18日土曜日", pageCount:242, countChars:134870, fileSize:"21098301"}, {pathStr:"book1_0.222.pdf", creationDate:"2016年6月18日土曜日", pageCount:243, countChars:135694, fileSize:"21146421"}, {pathStr:"book1_0.300.pdf", creationDate:"2016年6月20日月曜日", pageCount:251, countChars:142787, fileSize:"21427502"}, {pathStr:"book1_0.301.pdf", creationDate:"2016年6月20日月曜日", pageCount:251, countChars:142784, fileSize:"21421107"}, {pathStr:"book1_0.302.pdf", creationDate:"2016年6月20日月曜日", pageCount:256, countChars:142827, fileSize:"22593201"}, {pathStr:"book1_0.303.pdf", creationDate:"2016年6月20日月曜日", pageCount:257, countChars:142845, fileSize:"22616595"}, {pathStr:"book1_0.400.pdf", creationDate:"2016年6月22日水曜日", pageCount:281, countChars:162419, fileSize:"22430779"}, {pathStr:"book1_0.500.pdf", creationDate:"2016年6月23日木曜日", pageCount:309, countChars:178210, fileSize:"27611566"}, {pathStr:"book1_0.600.pdf", creationDate:"2016年6月24日金曜日", pageCount:326, countChars:194751, fileSize:"26820825"}, {pathStr:"book1_0.700.pdf", creationDate:"2016年6月24日金曜日", pageCount:310, countChars:195943, fileSize:"26408415"}, {pathStr:"book1_0.701.pdf", creationDate:"2016年6月24日金曜日", pageCount:310, countChars:195926, fileSize:"26406738"}, {pathStr:"book1_0.702.pdf", creationDate:"2016年6月24日金曜日", pageCount:310, countChars:195924, fileSize:"26406703"}, {pathStr:"book1_0.703.pdf", creationDate:"2016年6月24日金曜日", pageCount:311, countChars:196594, fileSize:"26416223"}, {pathStr:"book1_1.0.pdf", creationDate:"2016年6月25日土曜日", pageCount:311, countChars:196594, fileSize:"26075419"}}

set anItem to contents of first item of aList
set aDict to (current application’s NSMutableArray’s arrayWithObject:anItem)’s firstObject()
set aKeyList to aDict’s allKeys() as list
set aKeyCount to length of aKeyList
set aRowCount to length of aList

tell application "Keynote"
  set dCount to count every document
  
if dCount = 0 then
    set newDoc to make new document with properties {document theme:theme "ホワイト", width:1024, height:768} –This theme name is *localized* by stupid software maker in Cupertino. This means "White" in Japanese
  end if
  
  
  
tell document 1
    set aNewSlide to make new slide
    
set base slide of aNewSlide to master slide "空白" –This theme name is *localized* by stupid software maker in Cupertino. This means "Blank" in Japanese
    
    
tell aNewSlide
      set aTable to make new table with properties {column count:aKeyCount + 1, row count:aRowCount + 1}
      
      
tell aTable
        –ヘッダー行を作成(ラベルで埋める)
        
tell row 1
          repeat with i from 2 to aKeyCount + 1
            set aKey to item (i – 1) of aKeyList
            
            
–非同期実行ここから
            
ignoring application responses
              set value of cell i to aKey
              
set font size to aFontSize
            end ignoring
            
–非同期実行ここまで
            
          end repeat
        end tell
        
        
–各行のデータを埋める
        
repeat with ii from 2 to aRowCount + 1
          set aRecRow to item (ii – 1) of aList
          
tell row ii
            
            
repeat with iii from 2 to (aKeyCount + 1)
              
              
tell cell iii
                set aKey to item (iii – 1) of aKeyList
                
                
–非同期実行ここから
                
ignoring application responses
                  set value to retValueForKey(aRecRow, aKey) of me
                  
set font size to aFontSize
                end ignoring
                
–非同期実行ここまで
                
              end tell
            end repeat
          end tell
        end repeat
        
      end tell
    end tell
    
  end tell
end tell

on retValueForKey(aRec, aLabel)
  set tmpDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
return (tmpDic’s valueForKey:aLabel) as string
end retValueForKey

★Click Here to Open This Script 

Posted in list Record | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Keynote | Leave a comment

並び順をインデックスリストで指定してソート

Posted on 10月 29, 2018 by Takaaki Naganoya

並び順を定義したリスト(インデックスリスト)にもとづいてソートを行うAppleScriptです。

テストデータにあるように、都道府県別のリストを処理する際に、結果を北から南へと並べたいというニーズを満たすために、その場で作ったものでしかありません。都道府県別なので重複データが存在しないことが前提です。

一応、リストを扱うための高速化処理を行なっているので、速度についてはそれほど困ることはないと思われます。

# 書籍「AppleScript最新リファレンス」からのリンクURLがBlog消失後に修復されていなかったので、再掲載しました。書籍側もリンクURLを修正したものを近日中にアップします

AppleScript名:並び順をインデックスリストで指定してソート
–並び順を指定するインデックスリスト(この順番にソート対象データを並べ替える)
set indexList to {"北海道", "青森県", "岩手県", "秋田県", "宮城県", "山形県", "福島県", "新潟県", "富山県", "石川県", "福井県", "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県", "長野県", "山梨県", "岐阜県", "静岡県", "愛知県", "三重県", "滋賀県", "京都府", "兵庫県", "大阪府", "奈良県", "和歌山県", "島根県", "岡山県", "鳥取県", "広島県", "山口県", "香川県", "徳島県", "愛媛県", "高知県", "福岡県", "大分県", "佐賀県", "長崎県", "宮崎県", "熊本県", "鹿児島県", "沖縄県"}

–ソート対象データ
set aList to {{"東京都", 301}, {"神奈川県", 98}, {"大阪府", 90}, {"千葉県", 60}, {"埼玉県", 55}, {"北海道", 40}, {"愛知県", 40}, {"京都府", 40}, {"兵庫県", 40}, {"福岡県", 30}, {"静岡県", 20}, {"広島県", 20}, {"茨城県", 20}, {"宮城県", 16}, {"長野県", 15}, {"福島県", 12}, {"新潟県", 11}, {"滋賀県", 10}, {"岐阜県", 10}, {"群馬県", 10}, {"石川県", 10}, {"熊本県", 9}, {"岡山県", 9}, {"福井県", 7}, {"青森県", 7}, {"秋田県", 6}, {"愛媛県", 6}, {"和歌山県", 6}, {"奈良県", 5}, {"三重県", 5}, {"沖縄県", 5}, {"岩手県", 5}, {"大分県", 4}, {"鹿児島県", 4}, {"宮崎県", 4}, {"栃木県", 3}, {"香川県", 3}, {"山形県", 3}, {"富山県", 2}, {"山梨県", 2}, {"長崎県", 2}, {"徳島県", 2}, {"島根県", 2}, {"鳥取県", 1}, {"高知県", 1}, {"山口県", 1}}

set bList to sortItemByIndexList(aList, indexList, 1, 2) of me
–> {{"北海道", 40}, {"青森県", 7}, {"岩手県", 5}, {"秋田県", 6}, {"宮城県", 16}, {"山形県", 3}, {"福島県", 12}, {"新潟県", 11}, {"富山県", 2}, {"石川県", 10}, {"福井県", 7}, {"茨城県", 20}, {"栃木県", 3}, {"群馬県", 10}, {"埼玉県", 55}, {"千葉県", 60}, {"東京都", 301}, {"神奈川県", 98}, {"長野県", 15}, {"山梨県", 2}, {"岐阜県", 10}, {"静岡県", 20}, {"愛知県", 40}, {"三重県", 5}, {"滋賀県", 10}, {"京都府", 40}, {"兵庫県", 40}, {"大阪府", 90}, {"奈良県", 5}, {"和歌山県", 6}, {"島根県", 2}, {"岡山県", 9}, {"鳥取県", 1}, {"広島県", 20}, {"山口県", 1}, {"香川県", 3}, {"徳島県", 2}, {"愛媛県", 6}, {"高知県", 1}, {"福岡県", 30}, {"大分県", 4}, {"佐賀県", 0}, {"長崎県", 2}, {"宮崎県", 4}, {"熊本県", 9}, {"鹿児島県", 4}, {"沖縄県", 5}}

–並び順をインデックスリストで指定してソート
–パラメータ: (1) aList=ソート元リスト ({{"name1", number1}, {"name2", number2}……} )、
–      (2) indexList=ソート順指定用インデックスリスト、
–      (3) itemNo=並べ替え対象アイテム番号(ソート元リスト内の各リスト要素内のソートキーになるアイテムの番号)
–      (4) nohitItem=インデックス項目がソート元リストに対してヒットしなかった場合の、件数0を入れるアイテム番号
–エラー:    エラー時にはfalseを返す
on sortItemByIndexList(aList, indexList, itemNo, nohitItem)
  –処理高速化のためにハンドラ内部にScript Objectを宣言してデータアクセス
  
script sortItemByIndexListObj
    property indexListSpd : indexList
    
property aListSpd : aList
  end script
  
  
–パラメータのエラーチェック
  
set tmpItem to contents of first item of aList
  
set tmpLen to length of tmpItem
  
  
–itemNoのエラーチェック
  
if (itemNo < 1) or (itemNo > tmpLen) then return false
  
–nohitItemのエラーチェック
  
if (nohitItem < 1) or (nohitItem > tmpLen) then return false
  
–itemNoとnohitItemが衝突していないかチェック
  
if itemNo = nohitItem then return false
  
  
  
–処理本体
  
set outList to {}
  
repeat with i in indexListSpd of sortItemByIndexListObj
    set j to contents of i
    
    
set hitF to false
    
    
repeat with ii in aListSpd of sortItemByIndexListObj
      set targItem to contents of item itemNo of ii
      
if targItem = j then
        set the end of outList to contents of ii
        
set hitF to true
        
exit repeat
      end if
    end repeat
    
    
–合致するデータがなかった場合の対応
    
if hitF = false then
      set tmpList to {}
      
repeat nohitItem times
        set the end of tmpList to ""
      end repeat
      
set item itemNo of tmpList to j
      
set item nohitItem of tmpList to 0
      
set the end of outList to tmpList
    end if
    
  end repeat
  
  
outList
  
end sortItemByIndexList

★Click Here to Open This Script 

Posted in list Sort | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy | Leave a comment

iTunesライブラリ中の楽曲のみしぼりこんでtitle取得

Posted on 10月 24, 2018 by Takaaki Naganoya

iTunesライブラリ(+Apple Book)のメディアから楽曲(mediaKind=ITLibMediaItemMediaKindSong)のみ抽出してタイトルを取得するAppleScriptです。

iTunesLibrary.framework経由でメデイアアイテムの情報にアクセスするため、iTunes.appが起動していてもいなくても関係ありません。開発環境のマシン(MacBook Pro Retina 2012 Core i7 2.66GHz)でiTunesに6,871曲の楽曲が存在している状態で0.02秒程度です。

Scriptの末尾で配列の大きさを取得しているのは、タイトルのリストをそのままスクリプトエディタの「結果」欄に出力させると、数千項目にもなる結果データをスクリプトエディタが受信するのに余計に時間がかかるので、ダミーで計算させたものです。

iTunes.appとプロセス間通信していないため、macOS 10.14でSecurityダイアログが表示されることもありません。

AppleScript名:iTunesライブラリ中の楽曲のみしぼりこんでtitle取得.scptd
— Created 2018-10-16 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "iTunesLibrary"

property ITLibrary : a reference to current application’s ITLibrary
property NSPredicate : a reference to current application’s NSPredicate

set library to ITLibrary’s libraryWithAPIVersion:"1.0" |error|:(missing value)
if library is equal to missing value then return

set playLists to library’s allPlaylists()
set gArray to (library’s allMediaItems())

set aPredicate to NSPredicate’s predicateWithFormat:"self.mediaKind = 2" –ITLibMediaItemMediaKindSong
set filteredArray to gArray’s filteredArrayUsingPredicate:aPredicate
set tList to (filteredArray’s title) as list
–> {"Wait & See~リスク~", "Can You Keep A Secret?", "DISTANCE", "サングラス", "ドラマ", "Eternally", "Addicted To You (UP-IN-HEAVEN-MIX)"…}

set tLen to length of tList
–> 7010

★Click Here to Open This Script 

Posted in list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy ITLibrary iTunes NSPredicate | Leave a comment

iTunesライブラリ中の各trackの種別を集計(media種別)

Posted on 10月 24, 2018 by Takaaki Naganoya

iTunesライブラリ(+Apple Book)のメディアの種別(mediaKind)を集計するAppleScriptです。

iTunesLibrary.framework経由でメデイアアイテムの情報にアクセスするため、iTunes.appが起動していてもいなくても関係ありません。開発環境のマシン(MacBook Pro Retina 2012 Core i7 2.66GHz)でiTunesに6,871曲の楽曲が存在している状態で、集計に0.01秒程度です。

ITMediaItemのmediaKindを取得して種別情報を取り出しました。「楽曲のみ抽出」といった用途にバッチリ向いているようです。

iTunes.appとプロセス間通信していないため、macOS 10.14でSecurityダイアログが表示されることもありません。

AppleScript名:iTunesライブラリ中の各trackの種別を集計(media種別).scptd
— Created 2018-10-16 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version “2.4”
use scripting additions
use framework “Foundation”
use framework “iTunesLibrary”
(*
ITLibMediaItemMediaKindAlertTone:21
ITLibMediaItemMediaKindAudiobook:5
ITLibMediaItemMediaKindBook:19
ITLibMediaItemMediaKindDigitalBooklet:15
ITLibMediaItemMediaKindHomeVideo:12
ITLibMediaItemMediaKindIOSApplication:16
ITLibMediaItemMediaKindInteractiveBooklet:9
ITLibMediaItemMediaKindMovie:3
ITLibMediaItemMediaKindMusicVideo:7
ITLibMediaItemMediaKindPDFBook:20
ITLibMediaItemMediaKindPDFBooklet:6
ITLibMediaItemMediaKindPodcast:4
ITLibMediaItemMediaKindRingtone:14
ITLibMediaItemMediaKindSong:2
ITLibMediaItemMediaKindTVShow:8
ITLibMediaItemMediaKindUnknown:1
ITLibMediaItemMediaKindVoiceMemo:17
ITLibMediaItemMediaKindiTunesU:18
*)

property NSDictionary : a reference to current application’s NSDictionary
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor


set library to current application’s ITLibrary’s libraryWithAPIVersion:“1.0” |error|:(missing value)
if library is equal to missing value then return


set playLists to library’s allPlaylists()
set gList to (library’s allMediaItems())’s mediaKind
set aRes to countItemsByItsAppearance(gList) of me
–> {{theName:2, numberOfTimes:7010}, {theName:4, numberOfTimes:1422}, {theName:19, numberOfTimes:57}, {theName:3, numberOfTimes:25}, {theName:12, numberOfTimes:21}, {theName:5, numberOfTimes:5}, {theName:6, numberOfTimes:5}, {theName:7, numberOfTimes:2}, {theName:20, numberOfTimes:1}}


–出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to NSCountedSet’s alloc()’s initWithArray:aList
set bArray to NSMutableArray’s array()
set theEnumerator to aSet’s objectEnumerator()


  repeat
    set aValue to theEnumerator’s nextObject()
if aValue is missing value then exit repeat
bArray’s addObject:(NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat


  –出現回数(numberOfTimes)で降順ソート
set theDesc to NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
bArray’s sortUsingDescriptors:{theDesc}


  return bArray as list
end countItemsByItsAppearance


★Click Here to Open This Script 
Posted in list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy ITLibrary iTunes NSCountedSet NSDictionary NSMutableArray NSSortDescriptor | Leave a comment

iTunesライブラリ中の各trackの種別を集計(ファイル種別文字列)

Posted on 10月 24, 2018 by Takaaki Naganoya

iTunesライブラリ(+Apple Book)のメディアの種別(kind)を集計するAppleScriptです。

iTunesLibrary.framework経由でメデイアアイテムの情報にアクセスするため、iTunes.appが起動していてもいなくても関係ありません。開発環境のマシン(MacBook Pro Retina 2012 Core i7 2.66GHz)でiTunesに6,871曲の楽曲が存在している状態で、集計に0.01秒程度です。

ITMediaItemのkindを取得して種別情報を取り出してみたものの、この文字列情報がローカライズされているのと、あくまでファイル属性の情報を返すため、「楽曲のみ抽出」といった用途には向いていません。

iTunes.appとプロセス間通信していないため、macOS 10.14でSecurityダイアログが表示されることもありません。

AppleScript名:iTunesライブラリ中の各trackの種別を集計(ファイル種別文字列).scptd
— Created 2018-10-16 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version “2.4”
use scripting additions
use framework “Foundation”
use framework “iTunesLibrary”

 

 

 

set library to current application’s ITLibrary’s libraryWithAPIVersion:“1.0” |error|:(missing value)
if library is equal to missing value then return

set playLists to library’s allPlaylists()
set gList to (library’s allMediaItems())’s |kind| –kind is *localized*!!
set aRes to countItemsByItsAppearance(gList) of me
–> {{theName:”AAC オーディオファイル”, numberOfTimes:3260}, {theName:”MPEG オーディオファイル”, numberOfTimes:1760}, {theName:”購入した AAC オーディオファイル”, numberOfTimes:1302}, {theName:”保護された AAC オーディオファイル”, numberOfTimes:1102}, {theName:”MPEGオーディオファイル”, numberOfTimes:421}, {theName:”保護された MPEG-4 オーディオストリーム”, numberOfTimes:134}, {theName:”購入したAACオーディオファイル”, numberOfTimes:124}, {theName:missing value, numberOfTimes:111}, {theName:”MPEG オーディオストリーム”, numberOfTimes:88}, {theName:”MPEGオーディオストリーム”, numberOfTimes:40}, {theName:”Apple ロスレス・オーディオファイル”, numberOfTimes:36}, {theName:”QuickTime ムービーファイル”, numberOfTimes:34}, {theName:”保護されたブック”, numberOfTimes:32}, {theName:”AIFF オーディオファイル”, numberOfTimes:21}, {theName:”購入したブック”, numberOfTimes:21}, {theName:”MPEG-4 ビデオファイル”, numberOfTimes:16}, {theName:”保護された MPEG-4 ビデオファイル”, numberOfTimes:12}, {theName:”PDF 書類”, numberOfTimes:6}, {theName:”AACオーディオファイル”, numberOfTimes:5}, {theName:”ブック”, numberOfTimes:4}, {theName:”購入したMPEG-4ビデオファイル”, numberOfTimes:4}, {theName:”インターネットオーディオストリーム”, numberOfTimes:3}, {theName:”購入した MPEG-4 ビデオファイル”, numberOfTimes:3}, {theName:”WAV オーディオファイル”, numberOfTimes:2}, {theName:”iTunes Extras”, numberOfTimes:2}, {theName:”QuickTimeムービーURL”, numberOfTimes:2}, {theName:”QuickTime ムービー URL”, numberOfTimes:1}, {theName:”MPEG-4ビデオファイル”, numberOfTimes:1}, {theName:”Purchased AAC audio file”, numberOfTimes:1}}

–出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to current application’s NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()

  repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat

  –出現回数(numberOfTimes)で降順ソート
  
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}

  return bArray as list
end countItemsByItsAppearance

 

★Click Here to Open This Script 

 

Posted in list | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy iTunes NSCountedSet NSDictionary NSMutableArray NSSortDescriptor | Leave a comment

iTunesライブラリ中の各trackのartworkの種別を集計 v2

Posted on 10月 16, 2018 by Takaaki Naganoya

iTunesライブラリ中の各trackのアートワークの画像フォーマットの種別を集計するAppleScriptです。

iTunesライブラリ中の各trackのアートワークの画像フォーマットについては、iTunes.app上で楽曲を何曲か選択状態にした状態で、

tell application "iTunes"
  set aSel to selection
  
–> {file track id 50115 of user playlist id 43242 of source id 65 of application "iTunes"}
  
  
repeat with i in aSel
    set j to contents of i
    
    
set aFormat to (format of artwork 1 of j) as string
    
log aFormat
    
–>   (*JPEG picture*)
  end repeat
end tell

★Click Here to Open This Script 

のように、個別に取り出すことが可能です。

ただし、iTunes内の(数千とか数万)trackすべてについて集計を行いたい、といった場合にiTunes.appに対して問い合わせを行うと、Objective-CだろうがSwiftだろうがAppleScriptだろうが、どの言語を使っても膨大な処理時間がかかります。

そこで、iTunesLibrary.frameworkを介して、iTunesライブラリに直接アクセスしてアートワーク画像の集計を行なってみました。iTunes.appに対して数千個のtrackのアートワークの画像フォーマットを確認していては、処理に何時間かかるかわかりませんが、直接Frameworkの機能を呼び出せば、迅速に処理できます。

開発環境(MacBook Pro 2012 Core i7 2.66GHz)で、iTunesに音楽が6,800曲程度入っている環境で、集計処理に10秒程度かかりました。アートワークにアクセスすると楽曲情報よりも多めに処理時間がかかるようです。

アートワークの種別については、iTunesLibrary.frameworkが定めるところでは9種類ほど画像フォーマットが存在し、自分の環境ではJPEGが多く、PNGもありました。

macOS 10.14でも動かしてみましたが、とくに問題ありません。GUIアプリへの問い合わせではないので、「オートメーション」の許可ダイアログも表示されません。

AppleScript名:各trackのartworkの種別を集計 v2.scptd
— Created 2018-10-16 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "iTunesLibrary"

–https://developer.apple.com/documentation/ituneslibrary/itlibartwork/itlibartworkformat?language=objc
property ITLibArtworkFormatNone : 0
property ITLibArtworkFormatBitmap : 1
property ITLibArtworkFormatJPEG : 2
property ITLibArtworkFormatJPEG2000 : 3
property ITLibArtworkFormatGIF : 4
property ITLibArtworkFormatPNG : 5
property ITLibArtworkFormatBMP : 6
property ITLibArtworkFormatTIFF : 7
property ITLibArtworkFormatPICT : 8

set library to current application’s ITLibrary’s libraryWithAPIVersion:"1.0" |error|:(missing value)
if library is equal to missing value then return

set playLists to library’s allPlaylists()
set gArray to library’s allMediaItems()’s artwork

set imgFormatArray to gArray’s imageDataFormat

set aRes to countItemsByItsAppearance(imgFormatArray) of me
–>{{theName:2, numberOfTimes:4001}, {theName:missing value, numberOfTimes:2659}, {theName:5, numberOfTimes:1884}}

–出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to current application’s NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{"theName", "numberOfTimes"})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:"numberOfTimes" ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

★Click Here to Open This Script 

Posted in list System | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy iTunes | Leave a comment

offset of list in list

Posted on 9月 22, 2018 by Takaaki Naganoya

標準命令「offset of ① in ②」を拡張して、本来はテキスト同士の処理であったものを、リスト同士で処理するようにしたAppleScriptです。

サポートしているのは4つのパターンです。

(1)offset of string1 in string2

標準パターンですが、例によって標準命令よりも2.5倍速に高速化してあります。

(2)offset of list1 in list2

想定ターゲットのパターンです。1D Listと1D Listのoffsetを計算します。

(3)offset of list1 in string2

(2)を指定しようとして間違ったケースです。

(4)offset of string1 in list2

(2)を指定しようとして間違ったケース(2)です。ただし、成立する組み合わせがほとんどないので、エラーにしてもいいかもしれません。

1D List同士のoffset演算は普段であれば必要ないものですが、いざ必要になるとどこにも見つからなかったので、やむを得ず作成しました。

ASOC(use framework “Foundation”宣言時)では、そのままではoffset命令がだましにくくなるので(パラメータが文字列ではないとしてエラーになる)、using terms from scripting additionsで囲う必要がありました。

左から順に1番目:OLD Style AppleScript環境で、offset命令をのっとってstring/listともに処理できるようにした状態。構文確認/実行ともに問題なし。左から2番目:ASOC環境にした。左から3番目:ASOC環境にすると実行時にパラメータの型チェックでエラーになった。左から4番目:ASOC環境でもusing terms from句でoffsetコマンド実行部を囲むと構文確認/実行時にエラーにはならなくなった

AppleScript名:offset of list in list
— Created 2018-09-18 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

–case 1
using terms from scripting additions –ここ、AppleScriptObjC(use framework "Foundation"宣言時)では必要になる
  set bRes to offset of "a" in "bcdefa"
end using terms from
–> 6

–case 2 (The target case)
set aTargList to {"Safari", "の", "メニュー", "で", "「", "ファイル", "」", ">", "「", "PDF", "を", "書き出す", "」", "を", "実行"}
set bList to {"の", "メニュー", "で"}
using terms from scripting additions
  set aRes to offset of bList in aTargList
end using terms from
–> 2

–case 3 (Illegular case)
set aTargList to {"Safari", "の", "メニュー", "で", "「", "ファイル", "」", ">", "「", "PDF", "を", "書き出す", "」", "を", "実行"}
set bList to "で"
using terms from scripting additions
  set aRes to offset of bList in aTargList
end using terms from
–> 4

–case 4 (Illegular case)
set aTargList to "で"
set bList to {"で"}
using terms from scripting additions
  set aRes to offset of bList in aTargList
end using terms from
—> 1

on offset of bArg in anArg
  set aClass to class of anArg
  
set bClass to class of bArg
  
  
if {aClass, bClass} = {text, text} then –case 1
    return getOffset(anArg, bArg) of me
  else if {aClass, bClass} = {list, list} then –case 2 (The target case)
    return execOffsetList(bArg, anArg) of me
  else if {aClass, bClass} = {text, list} then –case 3 (Illegular case)
    return execOffsetList(bArg, {anArg}) of me
  else if {aClass, bClass} = {list, text} then –case 4 (Illegular case)
    return execOffsetList({bArg}, anArg) of me
  end if
end offset

–1D List同士のoffset演算を行うルーチンの本体
on execOffsetList(aList as list, bList as list)
  set resList to {}
  
repeat with i in aList
    set j to contents of i
    
set aCount to 1
    
    
repeat with ii in bList
      set jj to contents of ii
      
if jj = j then
        set the end of resList to aCount
        
exit repeat
      end if
      
set aCount to aCount + 1
    end repeat
  end repeat
  
  
–見つかったItem No.が連続値かどうかチェック
  
set sRes to chkSequential(resList) of me
  
if sRes = true then
    return contents of first item of resList
  else
    return false
  end if
end execOffsetList

–与えられた1D Listが連続値かどうかをチェックする
on chkSequential(aList)
  if length of aList = 1 then return true
  
  
set aFirst to first item of aList
  
set aList to rest of aList
  
  
repeat with i in aList
    set j to contents of i
    
if j is not equal to (aFirst + 1) then
      return false
    end if
    
copy j to aFirst
  end repeat
  
  
return true
end chkSequential

–テキスト同士のoffset ofを(2.5x fasterで)実行する
on getOffset(str, searchStr)
  set d to divideBy(str, searchStr)
  
if (count d) is less than 2 then return 0
  
return (length of item 1 of d) + 1
end getOffset

on divideBy(str, separator)
  set delSave to AppleScript’s text item delimiters
  
set the AppleScript’s text item delimiters to separator
  
set strItems to every text item of str
  
set the AppleScript’s text item delimiters to delSave
  
return strItems
end divideBy

★Click Here to Open This Script 

Posted in list | Tagged 10.11savvy 10.12savvy 10.13savvy | Leave a comment

iTunesライブラリの曲名をすべて取得

Posted on 9月 9, 2018 by Takaaki Naganoya

iTunesLibrary.framework経由でiTunesライブラリの曲名をすべて取得するAppleScriptです。

8,500曲の曲名を取得するのにおおよそ1.2秒程度です。

AppleScript名:iTunesライブラリの曲名をすべて取得
— Created 2016-11-02 by Takaaki Naganoya
— 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "iTunesLibrary"

set library to current application’s ITLibrary’s libraryWithAPIVersion:"1.0" |error|:(missing value)
if library is equal to missing value then return

set playLists to library’s allPlaylists()
set gArray to (library’s allMediaItems()’s title) as list

★Click Here to Open This Script 

Posted in list | Tagged 10.11savvy 10.12savvy 10.13savvy ITLibrary iTunes | Leave a comment

Keynoteで一番左上のshapeオブジェクトに他のshapeのサイズを合わせる

Posted on 8月 17, 2018 by Takaaki Naganoya

Keynoteでオープン中の最前面の書類で表示中のスライド(ページ)上の一番左上のshapeオブジェクトの縦横のサイズに他のshapeオブジェクトのサイズを合わせるAppleScriptです。

座標(position)の配列(2D Array)のソートにはBridgePlus Script Libraryを用いています。


▲初期状態


▲本AppleScriptで一番左上のshapeオブジェクトに他のshapeオブジェクトのサイズを合わせた


▲コマンドで適当に上ぞろえや左ぞろえを実行して整列(ここは手作業)

昔、Finder上のアイコンの整列AppleScriptを作ったことがあったので、その処理を使い回せば「いい感じの整列」はできそうです。

AppleScript名:Keynoteで一番左上のshapeオブジェクトに他のshapeのサイズを合わせる
— Created 2018-08-17 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

tell application "Keynote"
  tell front document
    tell (current slide)
      — すべてのshapeオブジェクトの座標{x,y}を返す
      
set pList to position of every shape
      
      
— shapeオブジェクトの全座標を昇順ソートして最もX座標値が小さいものを返す
      
set mostLeftPos to first item of sort2DList(pList) of me
      
      
— 一番X,Y座標値が小さい(=左上にある)オブジェクトを特定
      
set mostLeftObj to first item of (every shape whose position is equal to mostLeftPos)
      
set mostLeftProp to properties of mostLeftObj
      
–> {opacity:100, parent:slide 13 of document id "95E47D6C-C444-41BD-9E7E-61229486F370" of application "Keynote", class:shape, reflection showing:false, background fill type:color fill, position:{45, 229}, object text:"", width:144, rotation:0, reflection value:0, height:25, locked:false}
      
      
set mostLeftHeight to height of mostLeftProp
      
set mostLeftWidth to width of mostLeftProp
      
      
— 「一番左」以外のshapeオブジェクトへの参照を取得して一気にオブジェクトのwidthをそろえる
      
set otherShape to a reference to (every shape whose position is not equal to mostLeftPos)
      
set width of otherShape to mostLeftWidth
      
set height of otherShape to mostLeftHeight
      
    end tell
  end tell
end tell

on sort2DList(aList)
  load framework
  
set sortIndexes to {1, 0} –Key Item id: begin from 0
  
set sortOrders to {true, true} –ascending = true
  
set sortTypes to {"compare:", "compare:"}
  
set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
  
return resList
end sort2DList

★Click Here to Open This Script 

Posted in list Sort | Tagged 10.11savvy 10.12savvy 10.13savvy Keynote | Leave a comment

WordPressの指定IDの記事にリンクされているapplescriptからCocoa Classのproperty宣言を抽出 v3

Posted on 8月 5, 2018 by Takaaki Naganoya

本BlogのようなWordPressで運用されており、AppleScriptのURLリンクを記事に埋め込んでいるWordPressに対して、XML-RPC経由で指定IDの記事本文を取得し、埋め込まれているURLリンクからAppleScriptのソースコードを取得して、メモリー上でコンパイルして書式つきテキストに変換し、AppleScript構文書式をもとにpropertyラベルを抽出、そのうちCocoa Classのみをリストで取得するAppleScriptです。

本Blogに投稿した記事から宣言しているCocoa Classを抽出し、自動でタグ付けするために準備したものです。1記事に対して複数のAppleScriptが掲載されている場合にも対応しています。

HTMLReader.frameworkを用いてBlog本文からのリンク抽出、リンクURL抽出を行っています。

–> HTMLReader.framework(To ~/Library/Frameworks/)

本Sample Scriptで指定したIDの記事のproperty部分はこのようになっており、

本Scriptの実行によって抽出されたCocoa Class(単なる変数的なproperty項目や、Enumは対象外のため排除)は、

–> {“NSString”, “NSArray”, “OSAScript”, “NSPredicate”, “OSALanguage”, “NSDictionary”, “OSALanguageInstance”, “NSBundle”, “NSUnarchiver”}

のようになります。自分の環境でMacBook Proを有線ネットワーク接続した状態で、3.3〜3.4秒程度かかっています。大量の記事を処理する場合には本AppleScriptの並列処理を行うと処理時間の大幅な短縮が期待できます(MacのCPUがサーマルスロットリングで速度低下しなければ)。

また、負荷が集中している特定コアの動作周波数を上げ、他のコアの動作周波数を落とすTurbo Boostが有効な状態で並列処理を実行すると、並列処理を行う意義そのものが低下してしまうため、Turbo-Boost-Switcherのようなツールの併用が必要と思われます。

AppleScript名:WordPressの指定IDの記事にリンクされているapplescriptからCocoa Classのproperty宣言を抽出 v3
— Created 2018-07-30 by Takaaki Naganoya
— Modified 2018-08-05 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "OSAKit"
use framework "HTMLReader" –https://github.com/nolanw/HTMLReader

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSBundle : a reference to current application’s NSBundle
property NSThread : a reference to current application’s NSThread
property OSAScript : a reference to current application’s OSAScript
property NSPredicate : a reference to current application’s NSPredicate
property NSTextView : a reference to current application’s NSTextView
property NSDictionary : a reference to current application’s NSDictionary
property NSUnarchiver : a reference to current application’s NSUnarchiver
property OSALanguage : a reference to current application’s OSALanguage
property OSAScriptView : a reference to current application’s OSAScriptView
property NSMutableArray : a reference to current application’s NSMutableArray
property OSAScriptController : a reference to current application’s OSAScriptController
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property OSALanguageInstance : a reference to current application’s OSALanguageInstance
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

set postID to 3864
set {myUser, myPass} to getAcountData() of me
set aURL to "http://piyocast.com/as/xmlrpc.php"
set cocoaClassList to getCocoaPropListFromPost(aURL, postID, myUser, myPass) of me
–>  {"NSString", "NSBundle", "NSPredicate", "NSDictionary", "NSMutableArray", "NSMutableDictionary"}

–指定Blogの指定IDの記事にURLリンクされているAppleScriptから、Cocoa Classのpropertyのみ取得する
on getCocoaPropListFromPost(aURL, postID, myUser, myPass)
  –AppleScriptの構文色分け設定ファイルを読み込んで、重複色のチェックを実施  
  
set cList to getAppleScriptSourceColors() of me
  
set cRes to chkASLexicalFormatColorConfliction(cList) of me –構文色分けの重複色チェック
  
if cRes = false then error "There is some duplicate(s) color among AppleScript’s lexical color settings"
  
  
–WordPressの指定Post IDの記事を取得してリンクされているURLからURL Schemeでフィルタして、リンクされているAppleScriptのソースを取得
  
set aScheme to "applescript://"
  
set sourceList to getASSouceLinkedInWordPressPost(postID, aURL, aScheme, myUser, myPass) of me
  
  
–AppleScriptのソースをRAM上でコンパイル(構文確認)して、構文色分けしたRTFを取得。RTFの書式情報をparseしてattribute runsと同様のrecordを生成
  
–構文色分けをもとにproperty項目を抽出し、Cocoa Classに該当するもののみを抽出
  
set outList to {}
  
repeat with i in sourceList
    set j to contents of i
    
set anAttrStr to compileASandReturnAttributedString(j) of me
    
set attrRes to getAttributeRunsFromAttrString(anAttrStr) of me
    
set propNames to getPropertyNamesCocoaOnly(cList, attrRes) of me
    
    
if propNames is not equal to {} then
      set outList to outList & propNames
    end if
  end repeat
  
  
–1D Listのユニーク化(重複要素の排除)
  
set aArray to NSArray’s arrayWithArray:outList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
set bList to bArray as list of string or string –as anything
  
  
return bList
end getCocoaPropListFromPost

–Property名称を取得する
on getPropertyNamesCocoaOnly(cList, aRec)
  script spdHnd
    property aRec : {}
  end script
  
  
set (aRec of spdHnd) to aRec
  
  
set targAttr to contents of item 7 of cList –ハンドラあるいは変数
  
set tmpCoStr to ((redValue of targAttr) as string) & " " & ((greenValue of targAttr) as string) & " " & ((blueValue of targAttr) as string)
  
  
set ontoColItem to contents of item 3 of cList –スクリプティング予約語(on/to)
  
set ontoCoStr to ((redValue of ontoColItem) as string) & " " & ((greenValue of ontoColItem) as string) & " " & ((blueValue of ontoColItem) as string)
  
  
  
–変数あるいはハンドラ名称をリストアップ(variables & handler)
  
set tmp1Array to NSArray’s arrayWithArray:(aRec of spdHnd)
  
set thePred0 to NSPredicate’s predicateWithFormat_("colorStr == %@", tmpCoStr)
  
set dArray to (tmp1Array’s filteredArrayUsingPredicate:thePred0) as list
  
  
–改行を含むデータをリストアップ(text data contains return)
  
set thePred1 to NSPredicate’s predicateWithFormat_("stringVal CONTAINS %@", return)
  
set eArray to ((tmp1Array’s filteredArrayUsingPredicate:thePred1)’s valueForKeyPath:"itemIndex") as list
  
  
set the beginning of eArray to 0 –ハンドラ宣言部がTopに来る場合に備える
  
  
–"property"(プロパティ宣言)の項目をリストアップ 文字と色で抽出
  
set thePred2 to NSPredicate’s predicateWithFormat_("stringVal == %@ && colorStr == %@ ", "property", ontoCoStr)
  
set fArray to ((tmp1Array’s filteredArrayUsingPredicate:thePred2)’s valueForKeyPath:"itemIndex") as list
  
  
set handlerList to {}
  
  
–property ではじまるハンドラの抽出
  
repeat with i in eArray –改行を含むテキストのアイテム番号リスト
    set j to (contents of i) as integer
    
repeat with ii in fArray –"on"の項目リスト
      set jj to (contents of ii) as integer
      
      
set handlerStr to missing value
      
      
if (j + 1) = jj then
        set handlerStr to stringVal of (item (jj + 2) of ((aRec of spdHnd) as list))
      else if (j + 2) = jj then
        set handlerStr to stringVal of (item (jj + 2) of ((aRec of spdHnd) as list))
      end if
      
      
set tmpStr to repChar(handlerStr, "|", "") of me
      
      
if tmpStr is not in {"error", missing value} and tmpStr is not in handlerList then
        –抽出したProperty宣言がCocoa Classのものかどうか判定
        
if searchClassInFrameworks(tmpStr) of me is not equal to false then
          set the end of handlerList to tmpStr
        end if
      end if
      
    end repeat
  end repeat
  
  
return handlerList
end getPropertyNamesCocoaOnly

–RAM上にスクリプトエディタと同じ部品を組み立て(非表示)、AppleScriptのソーステキストからObjectを生成し、Attributed Stringデータを返す
on compileASandReturnAttributedString(theSource as string)
  set targX to 1024 –View Width
  
set targY to 2048 –View Height
  
  
set osaCon to current application’s OSAScriptController’s alloc()’s init()
  
set osaView to current application’s OSAScriptView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
  
set resView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
resView’s setRichText:true
  
resView’s useAllLigatures:true
  
  
osaCon’s setScriptView:osaView
  
osaCon’s setLanguage:(OSALanguage’s languageForName:"AppleScript")
  
osaCon’s setResultView:resView
  
  
osaView’s setString:theSource
  
osaCon’s compileScript:(missing value) –Compile(構文確認)
  
  
set aRes to (osaView’s attributedString())
  
  
return aRes
end compileASandReturnAttributedString

–Attributed StringをDictionary化
on getAttributeRunsFromAttrString(theStyledText)
  script aSpd
    property styleList : {}
  end script
  
  
set (styleList of aSpd) to {} —for output
  
  
set thePureString to theStyledText’s |string|() –pure string from theStyledText
  
  
set theLength to theStyledText’s |length|()
  
set startIndex to 0
  
set itemCount to 1
  
  
repeat until (startIndex = theLength)
    set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(reference) inRange:{startIndex, theLength – startIndex}
    
    
–String  
    
set aText to (thePureString’s substringWithRange:theRange) as string
    
    
–Color
    
set aColor to (theAtts’s valueForKeyPath:"NSColor")
    
if aColor is not equal to missing value then
      set aSpace to aColor’s colorSpace()
      
      
set aRed to (aColor’s redComponent()) * 255
      
set aGreen to (aColor’s greenComponent()) * 255
      
set aBlue to (aColor’s blueComponent()) * 255
      
      
set colList to {aRed as integer, aGreen as integer, aBlue as integer} –for comparison
      
set colStrForFind to (aRed as integer as string) & " " & (aGreen as integer as string) & " " & (aBlue as integer as string) –for filtering
    else
      set colList to {0, 0, 0}
      
set colStrForFind to "0 0 0"
    end if
    
    
–Font
    
set aFont to (theAtts’s valueForKeyPath:"NSFont")
    
if aFont is not equal to missing value then
      set aDFontName to aFont’s displayName()
      
set aDFontSize to aFont’s pointSize()
    end if
    
    
set the end of (styleList of aSpd) to {stringVal:aText, colorStr:colStrForFind, colorVal:colList, fontName:aDFontName as string, fontSize:aDFontSize, itemIndex:itemCount}
    
set startIndex to current application’s NSMaxRange(theRange)
    
    
set itemCount to itemCount + 1
  end repeat
  
  
return (styleList of aSpd)
  
end getAttributeRunsFromAttrString

–指定クラスがいずれかのCocoa Frameworkに所属しているかを検索
on searchClassInFrameworks(aTarget)
  set aClass to current application’s NSClassFromString(aTarget)
  
if aClass = missing value then return false
  
set theComponenents to (NSBundle’s bundleForClass:aClass)’s bundleURL’s pathComponents()
  
set thePred to NSPredicate’s predicateWithFormat:"pathExtension == ’framework’"
  
set aRes to (theComponenents’s filteredArrayUsingPredicate:thePred)’s firstObject() as list of string or string
  
return aRes
end searchClassInFrameworks

–指定Post IDのWordPress記事から、指定SchemeのURLを抽出し、AS Sourceをdecodeしてproperty行のみ抽出
on getASSouceLinkedInWordPressPost(postID, aURL, aScheme, myUser, myPass)
  –call xmlrpc命令に対するURLの間接指定を有効にするために、AppleScriptの構文解釈機能をダミーURLでだます
  
using terms from application "http://piyocast.com/as/xmlrpc.php" –URLと判定されればなんでもいい
    tell application aURL
      set wRes to (call xmlrpc {method name:"wp.getPost", parameters:{"1", myUser, myPass, postID as string}})
    end tell
  end using terms from
  
  
set aBody to post_content of wRes –Blog本文
  
  
–記事中でリンクしているURLを取得し、指定のURL Schemeでフィルタする
  
set urlList to filterURLLinksByScheme(aBody, aScheme) of me
  
  
set propList to {}
  
  
repeat with i in urlList
    set j to contents of i
    
set urlRec to parseQueryDictFromURLString(j) of me
    
set tmpScript to (urlRec’s |script|) as string –Get AppleScript Source
    
    
set propList to propList & tmpScript
  end repeat
  
  
return propList
end getASSouceLinkedInWordPressPost

on parseQueryDictFromURLString(aURLStr as string)
  if aURLStr = "" then error "No URL String"
  
  
set aURL to |NSURL|’s URLWithString:aURLStr
  
set aQuery to aURL’s query() –Get Query string part from URL
  
if aQuery’s |length|() = 0 then return false
  
  
set aDict to NSMutableDictionary’s alloc()’s init()
  
set aParamList to (aQuery’s componentsSeparatedByString:"&") as list
  
  
repeat with i in aParamList
    set j to contents of i
    
if length of j > 0 then
      set tmpStr to (NSString’s stringWithString:j)
      
set eList to (tmpStr’s componentsSeparatedByString:"=")
      
set anElement to (eList’s firstObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding))
      
set aValStr to (eList’s lastObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding))
      (
aDict’s setObject:aValStr forKey:anElement)
    end if
  end repeat
  
  
return aDict
end parseQueryDictFromURLString

–指定のHTML文字列から、Link URLを抽出し、schemeで再抽出する
on filterURLLinksByScheme(aBody, aScheme)
  set conType to "text/html"
  
  
–HTML文字列をいったんNSDataにしているのは、HTMLReader.frameworkの仕様のため
  
set aData to (current application’s NSString’s stringWithString:aBody)’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType
  
  
set aTextArray to ((aHTML’s nodesMatchingSelector:"a")’s textContent) as list –リンク文字
  
set aLinkList to ((aHTML’s nodesMatchingSelector:"a")’s attributes’s valueForKeyPath:"href") as list –URL文字列
  
  
set outList to {}
  
repeat with i in aLinkList
    set j to contents of i
    
if j begins with aScheme then
      set the end of outList to j
    end if
  end repeat
  
  
return outList
end filterURLLinksByScheme

–文字置換
on repChar(origText as string, targChar as string, repChar as string)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

–AppleScriptの構文色分けのカラー値をRGBで取得する
on getAppleScriptSourceColors()
  — get the plist info as a dictionary
  
set thePath to NSString’s stringWithString:"~/Library/Preferences/com.apple.applescript.plist"
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theInfo to NSDictionary’s dictionaryWithContentsOfFile:thePath
  
  
— extract relevant part and loop through
  
set theArray to (theInfo’s valueForKey:"AppleScriptSourceAttributes") as list
  
  
set colList to {}
  
  
repeat with i from 1 to count of theArray
    set anEntry to item i of theArray
    
    
set colorData to NSColor of anEntry
    
set theColor to (NSUnarchiver’s unarchiveObjectWithData:colorData)
    
    
set {rVal, gVal, bVal} to retColListFromNSColor(theColor, 255) of me
    
    
set fontData to NSFont of anEntry
    
set theFont to (NSUnarchiver’s unarchiveObjectWithData:fontData)
    
    
set aFontName to theFont’s displayName() as text
    
set aFontSize to theFont’s pointSize()
    
    
set aColRec to {redValue:rVal, greenValue:gVal, blueValue:bVal, fontName:aFontName, fontSize:aFontSize}
    
    
set the end of colList to aColRec
  end repeat
  
  
return colList
end getAppleScriptSourceColors

–NSColorからRGBの値を取り出す
on retColListFromNSColor(aCol, aMAX as integer)
  set aRed to round ((aCol’s redComponent()) * aMAX) rounding as taught in school
  
set aGreen to round ((aCol’s greenComponent()) * aMAX) rounding as taught in school
  
set aBlue to round ((aCol’s blueComponent()) * aMAX) rounding as taught in school
  
  
if aRed > aMAX then set aRed to aMAX
  
if aGreen > aMAX then set aGreen to aMAX
  
if aBlue > aMAX then set aBlue to aMAX
  
  
return {aRed, aGreen, aBlue}
end retColListFromNSColor

–AS書式で配色に重複がないかどうかチェック
on chkASLexicalFormatColorConfliction(aList)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set bList to (anArray’s valueForKeyPath:"redValue.stringValue") as list
  
set cList to (anArray’s valueForKeyPath:"greenValue.stringValue") as list
  
set dList to (anArray’s valueForKeyPath:"blueValue.stringValue") as list
  
  
set colStrList to {}
  
repeat with i from 1 to (length of bList)
    set bItem to contents of item i of bList
    
set cItem to contents of item i of cList
    
set dItem to contents of item i of dList
    
set the end of colStrList to bItem & " " & cItem & " " & dItem
  end repeat
  
  
set aRes to returnDuplicatesOnly(colStrList) of me
  
if aRes is equal to {} then
    return true –重複が存在しなかった場合
  else
    return false –重複があった場合
  end if
end chkASLexicalFormatColorConfliction

on returnDuplicatesOnly(aList as list)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bList to (aSet’s allObjects()) as list
  
  
set dupList to {}
  
repeat with i in bList
    set aRes to (aSet’s countForObject:i)
    
if aRes > 1 then
      set the end of dupList to (contents of i)
    end if
  end repeat
  
  
return dupList
end returnDuplicatesOnly

on getAcountData()
  return {"xxxxxxxx_xx", "XXXXXXXXXXXXXXXXXXXXXXXX"} –user name, password
end getAcountData

★Click Here to Open This Script 

Posted in list OSA RTF Text URL XML-RPC | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSBundle NSDictionary NSMutableArray NSMutableDictionary NSPredicate NSString NSTextView NSThread NSUnarchiver OSALanguage OSALanguageInstance OSAScript OSAScriptController OSAScriptView | Leave a comment

Post navigation

  • Older posts
  • Newer posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • macOS 15, Sequoia
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Numbersで選択範囲のセルの前後の空白を削除
  • メキシカンハットの描画
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • AppleScriptによる並列処理
  • macOS 15でも変化したText to Speech環境
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • デフォルトインストールされたフォント名を取得するAppleScript
  • 【続報】macOS 15.5で特定ファイル名パターンのfileをaliasにcastすると100%クラッシュするバグ
  • macOS 15 リモートApple Eventsにバグ?
  • Script Debuggerの開発と販売が2025年に終了
  • AppleScript入門① AppleScriptってなんだろう?
  • macOS 14で変更になったOSバージョン取得APIの返り値
  • NSObjectのクラス名を取得 v2.1
  • macOS 15:スクリプトエディタのAppleScript用語辞書を確認できない
  • 有害ではなくなっていたSpaces
  • AVSpeechSynthesizerで読み上げテスト

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (195) 14.0savvy (148) 15.0savvy (137) CotEditor (66) Finder (51) Keynote (119) NSAlert (61) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (53) NSDictionary (28) NSFileManager (23) NSFont (21) NSImage (41) NSJSONSerialization (21) NSMutableArray (63) NSMutableDictionary (22) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (119) NSURL (98) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (76) Pages (55) Pixelmator Pro (20) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • check sum
  • Clipboard
  • Cocoa-AppleScript Applet
  • Code Sign
  • Color
  • Custom Class
  • date
  • dialog
  • diff
  • drive
  • Droplet
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • process
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2025年7月
  • 2025年6月
  • 2025年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC