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

日本語入力のライブ変換の状態をBooleanで取得する

Posted on 10月 28, 2018 by Takaaki Naganoya

日本語入力Input Methodの「ライブ変換」のOn/Offの状態を取得するAppleScriptです。

本Scriptは状態を取得する「だけ」で、強制設定などは行いません。

個人的には、この「ライブ変換」機能はまったく信用に値しないので、macOS環境で真っ先にオフにする不要な機能です。

AppleScript名:ライブ変換の状態をBooleanで取得する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/10/28
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
–   http://www.mindto01s.com/2017/03/27/9f85c2fb_9f90_4e1b_8e5e_8719f57900eb.html

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set imRes to getLiveHenkanStatus() of me
–> true : 「ライブ変換」On
–> false : 「ライブ変換」Off

on getLiveHenkanStatus()
  set theUD to current application’s NSUserDefaults’s alloc()’s initWithSuiteName:"com.apple.inputmethod.Kotoeri"
  
set imRes to (theUD’s valueForKey:"JIMPrefLiveConversionKey") as boolean
  
return imRes
end getLiveHenkanStatus

★Click Here to Open This Script 

Posted in Input Method System | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSUserDefaults | Leave a comment

アプリケーションの各種記法からBundle IDを求める

Posted on 10月 26, 2018 by Takaaki Naganoya

各種記法で記述したアプリケーション名から、Bundle IDを求めるAppleScriptです。

アプリケーション名(本当の名前)、Bundle ID、アプリケーションのフルパス(POSIX path)の3形式で記述したアプリケーション名から、Bundle IDを求めます。

ただし、Localized Name(Reminders–>「リマインダー」、Notes–>「メモ」)からBundle IDの取得は本Scriptではサポートしていません。

AppleScript名:アプリケーションの各種記法からBundle IDを求める
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set apList to {"Finder", "System Events", "com.apple.Finder", "/Applications/Safari"}
set bIDList to retAppBundleID(apList) of me
–> {"com.apple.finder", "com.apple.systemevents", "com.apple.Finder", "com.apple.Safari"}

–Application name list –> Bundle ID list
on retAppBundleID(appNameList)
  set resList to {}
  
  
repeat with i in appNameList
    set j to contents of i
    
    
if j starts with "/Applications/" then
      –POSIX path
      
if j does not end with ".app" then
        set j to j & ".app"
      end if
      
set aRes to getBundleIDFromPath(j) of me
      
    else if j contains "." then
      –Bundle ID (maybe)
      
copy j to aRes
    else
      –Application Name
      
set aRes to getBundleIDFromAppName(j) of me
    end if
    
    
set the end of resList to aRes
  end repeat
  
  
return resList
end retAppBundleID

–Application path –> Bundle ID
on getBundleIDFromPath(aPOSIXpath)
  set aURL to current application’s |NSURL|’s fileURLWithPath:aPOSIXpath
  
set aWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set appURL to aWorkspace’s URLForApplicationToOpenURL:aURL
  
set aBundle to current application’s NSBundle’s bundleWithURL:appURL
  
set anID to aBundle’s bundleIdentifier()
  
return anID as string
end getBundleIDFromPath

–Application Name –> path –> Bundle ID
on getBundleIDFromAppName(appName)
  try
    –Localized Name will require choose application dialog
    
set appPath to POSIX path of (path to application appName)
  on error
    return ""
  end try
  
return getBundleIDFromPath(appPath) of me
end getBundleIDFromAppName

★Click Here to Open This Script 

Posted in file File path System | 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

マウント中のドライブをアンマウント

Posted on 10月 7, 2018 by Takaaki Naganoya

マウント中のドライブのうち、起動ドライブを除いたものをアンマウントします。

NSWorkspace経由でアンマウントするものと、OLD Style AppleScriptでFinder経由でアンマウントするものを掲載しておきます。

ただし、Time Machineのバックアップドライブなど、NSWorkspace経由ではアンマウントできないものもあり、「どちらがいい」とも言い切れない様子です。

–> Download Blank Disk Image for test

AppleScript名:NSWorkSpace経由でアンマウント.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/10/07
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set aPath to retDrivePosixPath() of me
if aPath = false then return

set aWS to current application’s NSWorkspace’s sharedWorkspace()
set aRes to aWS’s unmountAndEjectDeviceAtPath:aPath

on retDrivePosixPath()
  tell application "Finder"
    set dList to properties of every disk whose startup is false and size > 0
    
    
if length of dList > 1 then
      –複数の対象ドライブがある場合にはユーザーに選択させる
      
set dnList to name of every disk whose startup is false and size > 0
      
–set dnList to name of every disk whose startup is false
      
set dRes to choose from list dnList
      
set driveProp to (properties of disk dRes)
      
set driveURL to URL of driveProp
    else if dList = {} then
      return false
    else
      set driveURL to URL of first item of dList
    end if
    
  end tell
  
  
set dPath to ((current application’s |NSURL|’s URLWithString:driveURL)’s |path|()) as string
  
return dPath
end retDrivePosixPath

★Click Here to Open This Script 

AppleScript名:OLD style AppleScriptでアンマウント可能なドライブをアンマウント
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/10/07
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
set dName to retEjectableDriveName() of me
if dName = false then return

tell application "Finder" to eject disk dName

on retEjectableDriveName()
  tell application "Finder"
    set dList to properties of every disk whose startup is false and size > 0
    
    
if length of dList > 1 then
      –複数の対象ドライブがある場合にはユーザーに選択させる
      
set dnList to name of every disk whose startup is false and size > 0
      
set dRes to choose from list dnList
    else if dList = {} then
      return false
    else
      set dRes to name of first item of dList
    end if
    
    
return dRes
  end tell
end retEjectableDriveName

★Click Here to Open This Script 

Posted in drive | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy Finder NSWorkspace | Leave a comment

Chris EspinosaがAppleScript 25周年を祝うTweetを投稿

Posted on 10月 5, 2018 by Takaaki Naganoya

On this day 25 years ago, Apple introduced AppleScript, a system and application automation system and language. It’s still shipping in Mojave and is one of the oldest code bases in continual use in macOS. Happy birthday, AppleScript! pic.twitter.com/ymijJ8mMa1

— Chris Espinosa (@cdespinosa) October 4, 2018

社員番号8番で、Developper Tools担当を長年務め、いまはHome Kitを手がけていると噂されている、AppleScript系の開発者にはおなじみのChris EspinosaがAppleScriptの生誕25周年を祝うTweetを投稿していました。

Posted in news | Leave a comment

Mojaveに合わせて「Mac OS XのバージョンとAppleScriptの動向の年表」を改定

Posted on 10月 4, 2018 by Takaaki Naganoya

電子書籍「AppleScript最新10大技術」に掲載の「Mac OS X/OS X/macOSのバージョンとAppleScriptの動向」の表を現在最新のmacOS 10.14に合わせて改定してみました。

macOS 10.12を歴史的な観点から再評価し、「セキュリティ至上主義期」のはじまりと位置づけた点が大きく異なります。

同バージョンでAppleScriptドロップレットへのファイル受け渡しがファイルの拡張属性(Xattr)「com.apple.quarantine」によって制御されるようになり、受け渡しの順番が変わったり、ユーザーの手によって確認されていないファイルはドロップレット処理時に無視されるなど、機能不全が目につくようになりました(10.13はOS自体が機能不全の塊なので評価に値しません)。

macOS 10.12自体にはその解消を行うための機能が用意されていないことから、この時期から「セキュリティを向上させることが最優先、既存機能に不具合が出ても整合性を維持するつもりがない」という現在のティム・クックCEO体制下における「動かないコンピュータ至上主義」(セキュリティ的には動かないコンピュータが最高だぜ!)ともいえる方針に沿った改変が行われていることを感じます。

ただし、最新のmacOS 10.14においてはシステム環境設定の「セキュリティ&プライバシー>オートメーション」の項目で、ホームディレクトリ以下のファイルにアクセスする権限を与える「フルディスクアクセス」の項目、およびアプリケーション間通信を許可する「オートメーション」の項目によって明示的に許可を行うようになった点は前進と言えるのかもしれません。

AppleScriptアプレットにおいてもこれらの制限機能による影響を受けるようになったわけですが、CodeSignすればいちいち「フルディスクアクセス」「オートメーション」項目の認証を必要としないため(CodeSignしても初回のみ聞かれる)、プロのAppleScript開発者はいままでどおりCodeSignすることで問題を回避できるはずです。

未確認ではあるものの、Terminal.appもMojaveの「フルディスクアクセス」管理下にあり、デフォルト状態ではホームディレクトリ下のファイルのcatやlsなども行えないとのこと(手動で登録すれば操作できるようになるとのこと)で、do shell scriptコマンドがどのような扱いになっているかが問題です。

なお、AppleはまだAppleScript Release Notesの「10.14 Changes」を公開しておらず、未完の作業が存在していることをうかがわせる(macOS 10.14.0はRelease版といいつつもGMビルドを公開しなかったなど、明らかにBetaレベルの仕上がり)状態です。
→ 後日、リリースノートが「macOS Mojave 10.14 Release Notes」に統合されていることが明らかになりました

Mac OS X/OS X/macOSのバージョンとAppleScriptの動向

OSバージョン 内容
10.1〜10.2 Classic Mac OSからの移行期(Classic Mac OSからMac OS Xへの移行)
10.3〜10.4 移行混乱期(Classic Mac OSからMac OS X、CarbonからCocoa、PowerPCからIntel x32)
10.5〜10.6 安定期(Mac OS Xとしての当初の目標レベルに到達?)
10.7〜10.8 制限化、64ビット(Intel x64)移行、iOS連携強化期。Carbon系の32ビット補助アプリケーションの切り捨て
10.9〜10.11 次世代準備期。「次の10年を担う機能」の実装開始。過去のバージョンとの互換性よりも、機能強化に舵をきった?
10.12〜10.14 セキュリティ至上主義期。別名、「アプレット迫害期」。セキュリティ向上のためなら、OS上の各種機能の不具合が起こることもやむなし、という方向性が強まった。アプレットやドロップレットにセキュリティ上の制限がかかり、解決する方法が提供されるまでに時間がかかるようになった。10.14でシステム環境設定の「セキュリティとプライバシー>オートメーション」項目に、「フルディスクアクセス」「オートメーション」の2項目を新設してアプレットへの制御権限のコントロールを行うようになった。OSAXの事実上の廃止。
10.15 32bitアプリケーションの起動禁止
11.0(10.16) iOSアプリケーションとの混在。ASからの操作が可能なものも?
Posted in History | Tagged 10.14savvy | Leave a comment

NSURLからBookmarkを作成するじっけん

Posted on 10月 3, 2018 by Takaaki Naganoya

NSURLからbookmarkDataを作成、あるいはbookmarkDataからNSURLを復元する実験を行うAppleScriptです。

POSIX pathのファイルパスをplistに保存するような場合に、元のファイルの場所が変わったり名前が変わったりすると、追跡できなくなってしまいます。

 「aliasをpropertyに保存していた時代には、自動で追跡してもらえたんだけどなー」

そのため、plistからPOSIX pathを取り出したあとは逐一「本当にそのパスにファイルが存在するか」をチェックしていたのですが、

 「もっといいものがあるよ」

と教えていただいたのが、このbookmarkDataです。

NSURL(filePathのほう)をbookmarkDataにすると、元のファイルの名前が変わったり場所(パス)が変わったりしても追跡してくれます。ファイルのノード番号をベースに追跡しているのでしょうか。とにかく、追跡してもらえるのはいいことです。

本AppleScriptでは、POSIX pathをNSURLを経由してboookmarkDataに変換し、オリジナルのファイルをリネームして、bookmarkDataからNSURL–> POSIX pathを取得。得られたパスがリネーム後のものと同じかどうかをチェックするものです。

テストを行った範囲では、想定どおりに使えているようです。

AppleScript名:POSIX path–>Bookmark & bookmark –> POSIX path.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/10/02
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSFileManager : a reference to current application’s NSFileManager
property NSURLBookmarkResolutionWithoutUI : a reference to current application’s NSURLBookmarkResolutionWithoutUI

set aFile to POSIX path of (choose file)
–> "/Users/me/Desktop/9E37BC8B-3920-4844-A6D1-0DCA183D744D.png"

–POSIX path –> URL –> Bookmark
set aURL to |NSURL|’s fileURLWithPath:aFile
set aBookmarkData to aURL’s bookmarkDataWithOptions:0 includingResourceValuesForKeys:(missing value) relativeToURL:(missing value) |error|:(missing value)

–Rename Original File
set fRes to renameFileItem(aFile, "test_test_test") of me
if fRes = false then error "Error: File already exists."

–Bookmark –> URL –> POSIX path
set bURL to |NSURL|’s URLByResolvingBookmarkData:aBookmarkData options:(NSURLBookmarkResolutionWithoutUI) relativeToURL:(missing value) bookmarkDataIsStale:(missing value) |error|:(missing value)
set bPath to (bURL’s |path|()) as string
–> "/Users/me/Desktop/test_test_test.png"

–File Rename Routine
on renameFileItem(aPOSIX, newName)
  set theNSFileManager to NSFileManager’s defaultManager()
  
set POSIXPathNSString to NSString’s stringWithString:(aPOSIX)
  
  
–Make New File Path
  
set anExtension to POSIXPathNSString’s pathExtension()
  
set newPath to (POSIXPathNSString’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:newName)’s stringByAppendingPathExtension:anExtension
  
  
–Rename
  
if theNSFileManager’s fileExistsAtPath:newPath then
    return false
  else
    set theResult to theNSFileManager’s moveItemAtPath:POSIXPathNSString toPath:newPath |error|:(missing value)
    
if (theResult as integer = 1) then
      return (newPath as string)
    else
      return false
    end if
  end if
end renameFileItem

★Click Here to Open This Script 

Posted in file URL | Tagged 10.11savvy 10.12savvy 10.13savvy NSFileManager NSString NSURL NSURLBookmarkResolutionWithoutUI | 1 Comment

ZipArchive Frameworkを使ってパスワード付きZipアーカイブを作成

Posted on 9月 30, 2018 by Takaaki Naganoya

オープンソースのSSZipArchiveを呼び出して、パスワード付きZipアーカイブを作成するAppleScriptです。

AppleScriptそのものにZipアーカイブの操作機能はないので、この手の操作は他のプログラムを操作することになります。

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

(1)Unix shellのzipコマンド
do shell scriptコマンド経由でUnix shellのzipコマンドを呼び出す方法です。データをファイルで扱っている場合にはよいのですが、そうでない場合には困ります(変数に入れたデータを圧縮したいとか)。以前に、なろう小説APIを呼び出したときに、GZIP.frameworkを用いて変数内のデータをZip展開しましたが、こういう用途には使えません。

(2)アーカイブユーティリティを呼び出す
/System/Library/CoreServices/Applications/Archive Utility.appを呼び出す方法です。Mac OS X 10.5までは「BOMArchiveHelper」という名前でした。その他、AppleScriptに対応しているZipアーカイバがあればそれを使ってみてもよいでしょう。これも、(1)同様にファイル単位で操作するものが普通なので、使える用途と使えない用途があります。

(3)各種Cocoa Frameworkを呼び出す
Objective-CのZipアーカイブ操作プログラムを見つけ、Frameworkになっていなくても、無理やりXcode上でCocoa Frameworkを作成し、必要なファイルをそこに突っ込んでFramework化して使っています。

今回使うのはSSZipArchiveです。

いろいろなZip操作のObjective-Cのプログラムをダウンロードしては分析してみると、各プログラムで作者の好みが色濃く反映されており、どれか1つで済むとは思えない状況です。

また、必要なメソッドの呼び出しにBlocks構文による記述が必要な場合にはAppleScriptから呼べません(さらにそのメソッドを呼び出すためのメソッドをObjective-C上で定義するとか、回避方法はいろいろありそうです)。

結局、いろいろGithub上で探してはビルドして試しています。

AppleScript名:ZipArchive Frameworkを使ってパスワード付きZipアーカイブを作成(ファイル指定).scptd
— Created 2018-09-28 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ZipArchive" –https://github.com/ZipArchive/ZipArchive

set aPassWord to "yourpassword"
set targFile to POSIX path of (choose file with prompt "Select a file to Zip")
set archiveTarg to targFile & ".zip"

set zipRes to current application’s SSZipArchive’s createZipFileAtPath:(archiveTarg) withFilesAtPaths:{targFile} withPassword:(aPassWord)
return zipRes as boolean

★Click Here to Open This Script 

Posted in file | Tagged 10.11savvy 10.12savvy 10.13savvy | 7 Comments

ZipZap frameworkを使ってZipアーカイブ内の情報を取得しファイルタイプごとに対応出力

Posted on 9月 29, 2018 by Takaaki Naganoya

オープンソースのZipZap frameworkを用いて、指定Zipアーカイブ内の情報を取得し、ファイルタイプごとに対応した出力を行うAppleScriptです。

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

客観的に動作内容を書くとわかりにくいですが、「目的」を書くと非常にわかりやすい部品です。

「バンドル内にパスワード付きのZipアーカイブ化したAppleScript書類を入れておいて、実行時にアーカイブからAppleScriptのソースを取り出して実行する」

というのが、その用途です。

プレーンテキストと画像を取り出せるような記述になっていますが、それらはあくまでも実験用で、AppleScript書類のソースを取り出すのが本来の目的です。

AppleScriptでGUIアプリケーションを作成したときに、GUIまわりは普通にXcode上で記述して(あるいは、外部エディタ上で記述して)おきますが、外部のアプリケーションをコントロールするコードは、実行専用の.scpt形式でかつ書き込み禁止状態にしてバンドル内に入れておいたりします(SandBox対応のため)。

ただ、さまざまな要求を満たすようにGUIアプリケーション操作用のScriptを呼び出そうとすると、load scriptで読み込んで実行させていたのでは、都合がよくない場合があります(何らかのキーを長押しすると実行を停止できるとか、システム内の別の要素……たとえばCPUの温度が上がりすぎたら停止させるとか)。

load scriptで実行すると、

(1)途中で実行停止しにくい
(2)セキュリティ上の制約がきつい(とくにGUI Scriptingまわり)
(3)スクリプトエディタ上で動かしていた時と挙動が変わる箇所がある(Finder上のselectionを取得していた場合とか)

といった問題がありますが、OSAScriptView上にAppleScriptのソースを展開して実行すると、これらの問題を解決できる一方、ソースが見える形式でアプリケーションバンドル内に入れるのはためらわれます。

その問題を解決するために書いたものです。展開したデータをファイル出力せず、すべて変数上で(オンメモリで)処理できるので都合がいいです。

Mac App Storeで販売するアプリケーションでこの部品を使ったことはありませんが、客先に納品するアプリケーションでは使えるといったところでしょうか。ここまで手の込んだプログラムだとリジェクトできないとは思っています(もっとレベルの低い指摘しか来ないので)。

AppleScript名:ZipZap frameworkを使ってZipアーカイブ内の情報を取得してファイルタイプごとに対応出力 v2
— Created 2018-09-28 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "OSAKit"
use framework "ZipZap" –https://github.com/pixelglow/zipzap
use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

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 NSImage : a reference to current application’s NSImage
property ZZArchive : a reference to current application’s ZZArchive
property OSAScript : a reference to current application’s OSAScript
property NSPredicate : a reference to current application’s NSPredicate
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey

set aPassword to "piyomarusoftware"
set aPath to POSIX path of (choose file of type {"public.zip-archive"})
set aList to extractArchive(aPath, aPassword) of me

on extractArchive(aPath, aPass)
  set oldArchive to ZZArchive’s archiveWithURL:(|NSURL|’s fileURLWithPath:aPath) |error|:(missing value)
  
set aList to oldArchive’s entries() as list
  
  
set outList to {}
  
  
repeat with i in aList
    set encF to i’s encrypted() as boolean
    
    
set aFileName to i’s fileName()
    
    
if (aFileName as string) does not start with "__MACOSX/" then
      set aExt to (aFileName’s pathExtension()) as string
      
set aUTI to my retFileFormatUTI(aExt)
      
set aData to (i’s newDataWithError:(missing value)) –Uncompressed raw data
      
      
if aData = missing value then
        set aData to (i’s newDataWithPassword:(aPass) |error|:(missing value))
        
if aData is equal to (missing value) then
          error "Internal archive error in extracting"
        end if
      end if
      
      
if aUTI = "public.plain-text" then
        –Plain Text
        
set aStr to (NSString’s alloc()’s initWithData:aData encoding:(NSUTF8StringEncoding)) as string
        
      else if aUTI = "com.apple.applescript.script" then
        –AppleScript .scpt file
        
set aScript to (OSAScript’s alloc()’s initWithCompiledData:aData |error|:(missing value))
        
set aStr to (aScript’s source()) as string
        
      else if (my filterUTIList({aUTI}, "public.image")) is not equal to {} then
        –Various Images
        
set aStr to (NSImage’s alloc()’s initWithData:aData)
        
      end if
      
      
set the end of outList to aStr
    end if
  end repeat
  
  
return outList
end extractArchive

on retFileFormatUTI(aExt as string)
  tell script "BridgePlus"
    load framework
    
return (current application’s SMSForder’s UTIForExtension:aExt) as string
  end tell
end retFileFormatUTI

–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

★Click Here to Open This Script 

Posted in file OSA Text URL UTI | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSImage NSPredicate NSString NSURL NSURLTypeIdentifierKey NSUTF8StringEncoding OSAScript ZZArchive | Leave a comment

easyJParse v3

Posted on 9月 27, 2018 by Takaaki Naganoya

簡易的な日本語テキストのParse(辞書なし)を行うAppleScriptです。

英語などの言語では、文章中の各単語の間にスペース(” “)を入れるようになっており、

My name is Takaaki Naganoya.

文章を単語ごとに分割することがきわめて容易です。

words of "My name is Takaaki Naganoya."
--> {"My", "name", "is", "Takaaki", "Naganoya"}

一方、日本語の文章において単語は続けて記述するため、

私の名前は長野谷です。

これを単語ごとに切り分けるのは大変です。そのため、単語の辞書を手掛かりに文章中の単語を切り分けるのが普通です。

辞書を使って単語単位の切り分けを行う日本語形態素解析器

日本語テキストを単語(形態素)ごとに区分けするソフトウェアは日本語形態素解析器と呼ばれます。Chasen、Juman、MeCabなどが有名です。形態素解析のための巨大な辞書を用いて、地名ぐらいの固有名詞なら問題なくParseできることが普通です。各単語がどの品詞なのか、活用形はどうなっているかといった文法的な情報も管理しています。

たとえば、ApitoreのREST API経由でKuromojiを呼び出して形態素解析を行うと、

"警告音「Basso」を最大音量で鳴らす"
-->{{startTime:"1538006762864", tokens:{{partOfSpeechLevel1:"名詞", baseForm:"警告", pronunciation:"ケイコク", position:0, partOfSpeechLevel3:"*", reading:"ケイコク", surface:"警告", known:true, allFeatures:"名詞,サ変接続,*,*,*,*,警告,ケイコク,ケイコク", conjugationType:"*", partOfSpeechLevel2:"サ変接続", conjugationForm:"*", allFeaturesArray:{"名詞", "サ変接続", "*", "*", "*", "*", "警告", "ケイコク", "ケイコク"}, partOfSpeechLevel4:"*"}, {partOfSpeechLevel1:"名詞", baseForm:"音", pronunciation:"オン", position:2, partOfSpeechLevel3:"一般", reading:"オン", surface:"音", known:true, allFeatures:"名詞,接尾,一般,*,*,*,音,オン,オン", conjugationType:"*", partOfSpeechLevel2:"接尾", conjugationForm:"*", allFeaturesArray:{"名詞", "接尾", "一般", "*", "*", "*", "音", "オン", "オン"}, partOfSpeechLevel4:"*"}, {partOfSpeechLevel1:"記号", baseForm:"「", pronunciation:"「", position:3, partOfSpeechLevel3:"*", reading:"「", surface:"「", known:true, allFeatures:"記号,括弧開,*,*,*,*,「,「,「", conjugationType:"*", partOfSpeechLevel2:"括弧開", conjugationForm:"*", allFeaturesArray:{"記号", "括弧開", "*", "*", "*", "*", "「", "「", "「"}, partOfSpeechLevel4:"*"}, {partOfSpeechLevel1:"名詞", baseForm:"Basso", pronunciation:"バッソ", position:4, partOfSpeechLevel3:"一般", reading:"バッソ", surface:"Basso", known:true, allFeatures:"名詞,固有名詞,一般,*,*,*,Basso,バッソ,バッソ", conjugationType:"*", partOfSpeechLevel2:"固有名詞", conjugationForm:"*", allFeaturesArray:{"名詞", "固有名詞", "一般", "*", "*", "*", "Basso", "バッソ", "バッソ"}, partOfSpeechLevel4:"*"}, {partOfSpeechLevel1:"記号", baseForm:"」", pronunciation:"」", position:9, partOfSpeechLevel3:"*", reading:"」", surface:"」", known:true, allFeatures:"記号,括弧閉,*,*,*,*,」,」,」", conjugationType:"*", partOfSpeechLevel2:"括弧閉", conjugationForm:"*", allFeaturesArray:{"記号", "括弧閉", "*", "*", "*", "*", "」", "」", "」"}, partOfSpeechLevel4:"*"}, {partOfSpeechLevel1:"助詞", baseForm:"を", pronunciation:"ヲ", position:10, partOfSpeechLevel3:"一般", reading:"ヲ", surface:"を", known:true, allFeatures:"助詞,格助詞,一般,*,*,*,を,ヲ,ヲ", conjugationType:"*", partOfSpeechLevel2:"格助詞", conjugationForm:"*", allFeaturesArray:{"助詞", "格助詞", "一般", "*", "*", "*", "を", "ヲ", "ヲ"}, partOfSpeechLevel4:"*"}, {partOfSpeechLevel1:"名詞", baseForm:"最大", pronunciation:"サイダイ", position:11, partOfSpeechLevel3:"*", reading:"サイダイ", surface:"最大", known:true, allFeatures:"名詞,一般,*,*,*,*,最大,サイダイ,サイダイ", conjugationType:"*", partOfSpeechLevel2:"一般", conjugationForm:"*", allFeaturesArray:{"名詞", "一般", "*", "*", "*", "*", "最大", "サイダイ", "サイダイ"}, partOfSpeechLevel4:"*"}, {partOfSpeechLevel1:"名詞", baseForm:"音量", pronunciation:"オンリョー", position:13, partOfSpeechLevel3:"*", reading:"オンリョウ", surface:"音量", known:true, allFeatures:"名詞,一般,*,*,*,*,音量,オンリョウ,オンリョー", conjugationType:"*", partOfSpeechLevel2:"一般", conjugationForm:"*", allFeaturesArray:{"名詞", "一般", "*", "*", "*", "*", "音量", "オンリョウ", "オンリョー"}, partOfSpeechLevel4:"*"}, {partOfSpeechLevel1:"助詞", baseForm:"で", pronunciation:"デ", position:15, partOfSpeechLevel3:"一般", reading:"デ", surface:"で", known:true, allFeatures:"助詞,格助詞,一般,*,*,*,で,デ,デ", conjugationType:"*", partOfSpeechLevel2:"格助詞", conjugationForm:"*", allFeaturesArray:{"助詞", "格助詞", "一般", "*", "*", "*", "で", "デ", "デ"}, partOfSpeechLevel4:"*"}, {partOfSpeechLevel1:"動詞", baseForm:"鳴らす", pronunciation:"ナラス", position:16, partOfSpeechLevel3:"*", reading:"ナラス", surface:"鳴らす", known:true, allFeatures:"動詞,自立,*,*,五段・サ行,基本形,鳴らす,ナラス,ナラス", conjugationType:"五段・サ行", partOfSpeechLevel2:"自立", conjugationForm:"基本形", allFeaturesArray:{"動詞", "自立", "*", "*", "五段・サ行", "基本形", "鳴らす", "ナラス", "ナラス"}, partOfSpeechLevel4:"*"}}, endTime:"1538006762864", |log|:"", processTime:"0"}}

のようになります。これらのデータのうち、surface項目を抽出すると、

--> {"警告", "音", "「", "Basso", "」", "を", "最大", "音量", "で", "鳴らす", "。"}

となります。

辞書を使わずにトリッキーな方法で単語単位の切り分けを行う日本語パーサー

一方で、これらの日本語形態素解析器ほどの大規模なデータや機能が必要ない場合もあります。形態素解析のための辞書を持たず、単にそれっぽく単語ごとに区切ることができればよいという、「割り切った用途」に用いるもので、便宜上「日本語パーサー」と呼びます。単語っぽいものに分割することが目的であり、品詞のデータなどは取得できないのが普通です。

この種類のソフトウェアは、工藤 拓さんのTinySegmenterがあり、これをObjective-Cに移植したSuper compact Japanese tokenizer 「Tiny Segmenter」をCocoa Framework化してAppleScriptから呼び出し、テストしています。正規表現を用いて助詞などをピックアップして、それを手掛かりに単語切り分けを行うもので、そのサイズからは想像できないぐらいまっとうに単語に切り分けてくれます。

このTiny Segmenter(Objective-C版)をコマンド解釈用に使ってみたのですが、

--> {"警告音", "「Basso", "」", "を", "最大", "音量", "で", "鳴ら", "す"}

記号などがきちんと分離されなかったため、いまひとつ。自分でコマンド解釈用のParserを作ってみることにしました。

words ofの不完全さを補う簡易日本語パーサーeasyJParse

AppleScriptの「words of」は、前述のように英文であればスペースを区切り子として、文章の単語への分解を行ってくれます。

一方、日本語テキストに対して「words of」で単語分解処理を行うと、ながらく「文字種別の切り替え箇所で区切る」という気の狂ったような使えない処理が行われていました。その無意味さと使えなさをAppleのエンジニアにことあるごとに説明してきたのですが、一向に理解されず、相手にされてきませんでした。

# 冗談抜きで、Appleのエンジニアとは「戦いの歴史」しかありません。そして、そうして戦って勝ち取っていかないと機能の改善もバグの修正も何もないのであります(本当)

風向きが変わってきたのは、OS X 10.6のころ。このころから日本語テキストのwords ofの実行結果が形態素解析を行なっているような気がする動作を行うようになっており、何かに使えるような気がするものの……

 words of "警告音「Basso」を最大音量で鳴らす。"
 --> {"警告", "音", "Basso", "を", "最大", "音量", "で", "鳴らす"}

なぜか記号類などをすべて無視してしまうので、いまひとつ実用性がありませんでした。

そこで、基本的にはこの「words of」の演算結果を活かしつつ、オリジナルの文章と比較を行なって、欠損した記号類を補うことで簡易日本語parserとして利用できるのでは? と考えました。

set aTargName to "警告音「Basso」を最大音量で鳴らす。"
set aList to parseJ(aTargName) of me
--> {"警告", "音", "「", "Basso", "」", "を", "最大", "音量", "で", "鳴らす", "。"}

実際に作ってテストしてみたところ、自分が必要なコマンド解析ぐらいの目的には十分に使えることがわかりました。むしろ、単語切り分けについてはKuromojiと同じ結果が得られています。

しかも、辞書を持たないためコンパクトであり、実行速度もたいへんに高速で、このeasyJParseを組み込んだプログラムはREST APIの日本語形態素解析器を呼んだバージョンよりも明らかに高速化され、ネットワーク接続のない環境でも実行可能になりました。いいことづくめです。

easyJParseの制約事項

なお、easyJParseはすでに文章単位で分割されたテキストをコマンド解釈用に分解するため「だけ」に作ったものであり、長文を文章ごとに分割する機能は持っていません。別のプログラムやルーチンで文章ごとに分割してからeasyJParseで処理してください。

easyJParseは、日本語ユーザー環境における日本語テキストに対する「words of」の演算結果を利用しており、言語環境が日本語に設定していない環境で同様に演算できることは保証していません。
→ 一応、英語ユーザー環境で実行してみたら期待どおりの動作を行いました

当然のことながら、macOS専用です。一部Cocoaの機能を呼び出しているため、macOS 10.10以降で動きます(10.10では動作確認していませんけれども)。

AppleScript名:easyJParse v3
— Created 2018-09-26 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.5" — El Capitan (10.11) or later
use framework "Foundation"
use scripting additions

property NSArray : a reference to current application’s NSArray
property NSSortDescriptor : a reference to current application’s NSSortDescriptor

set aTargName to "警告音「Basso」を最大音量で鳴らす。"
set aList to parseJ(aTargName) of me
–> {"警告", "音", "「", "Basso", "」", "を", "最大", "音量", "で", "鳴らす", "。"}

–set aTargName to "JPEGファイルを50%にリサイズして、デスクトップの「AAA」フォルダに出力"
–set aList to parseJ(aTargName) of me
–> {"JPEG", "ファイル", "を", "50", "%", "に", "リサイズ", "し", "て", "、", "デスクトップ", "の", "「", "AAA", "」", "フォルダ", "に", "出力"}

on parseJ(aTargStr as string)
  copy aTargStr to tStr
  
  
set cList to characters of tStr
  
set wList to words of tStr
  
  
set cLen to length of cList
  
  
set w2List to {}
  
set w3List to {}
  
set aCount to 0
  
  
set lastPos to 0
  
  
repeat with i in wList
    set j to contents of i
    
    
set anOffset to offset of j in tStr
    
    
if anOffset is not equal to 1 then
      set aChar to character (lastPos + 1) of aTargStr
      
      
set the end of w3List to {wordList:aChar, characterList:{aChar}, startPos:(lastPos + 1), endPos:(lastPos + 1)}
    end if
    
    
set aLen to length of j
    
    
set w2List to w2List & (characters of j)
    
set startPointer to (anOffset + aCount)
    
set endPointer to (anOffset + aCount + aLen – 1)
    
    
set the end of w3List to {wordList:j, characterList:(characters of j), startPos:startPointer, endPos:endPointer}
    
    
set trimStart to (anOffset + aLen)
    
    
if trimStart > (length of tStr) then
      set trimStart to 1
    end if
    
    
set tStr to text trimStart thru -1 of tStr
    
    
set aCount to aCount + anOffset + aLen – 1
    
copy endPointer to lastPos
  end repeat
  
  
–句読点など。文末の処理
  
if endPointer is not equal to cLen then
    set the end of w3List to {wordList:tStr, characterList:(characters of tStr), startPos:(lastPos + aCount), endPos:aLen}
  end if
  
  
set bArray to sortRecListByLabel((w3List), "startPos", true) of me
  
set cArray to (bArray’s valueForKeyPath:"wordList") as list
  
  
return cArray
end parseJ

–リストに入れたレコードを、指定の属性ラベルの値でソート
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
  
return sortedArray
end sortRecListByLabel

★Click Here to Open This Script 

Posted in Natural Language Processing Record Sort Text | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSSortDescriptor | 7 Comments

イベント「Think AppleScript」盛会のうちに終了

Posted on 9月 26, 2018 by Takaaki Naganoya

2018年9月26日、池袋で開催したイベント「Think AppleScript」、参加者12名で盛会のうちに終了しました。

お越しいただいた皆様、お忙しいところをありがとうございました。まずは、お礼までに。

電子書籍「AppleScript 10大最新技術」をベースにAppleScriptの歴史や成り立ち、これまでの経緯を説明し、Newt Onや最新のTanzaku、あるいはAppleScriptで作成したMac App Storeで販売中のアプリケーション「Double PDF」などについてデモを行いました。

電子書籍「AppleScript最新リファレンス」掲載のiOSデバイスとのAppleScriptによる連携をご紹介したところ、出席者から驚きの声が上がっていました。

Posted in news | 2 Comments

MOSAミーティングで新プロジェクト「Tanzaku」を発表

Posted on 9月 22, 2018 by Takaaki Naganoya

2018年9月21日にニュー新橋コワーキングにて行われた「新しいMOSA」スタート・ミーティングにおいて、Piyomaru Softwareの新プロジェクト「Tanzaku」を発表しました。

Tanzakuは「ファイル名に処理してほしい内容を書くと、そのとおりに動く」プログラムであり、まとまった操作を行うのにAppleScriptやshell scriptを書くことなく、実行したい内容をファイル名に書くだけで実行できるようにするものです。

2002年発表の「Newt On」(自然言語ユーザーインタフェース)や2003年発表の「符令韻投句」(日本語音声認識インタフェース)などと並ぶレベルの画期的なアイデアとして発表。開発中のバージョンの動作デモを行いました。

→ TanzakuのKeynote資料

Posted in 未分類 | 1 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

GUI Scripting的なUI Elementへの参照から所属するアプリケーション名を取得する

Posted on 9月 17, 2018 by Takaaki Naganoya

GUI Scripting的なUI Elementへの参照をもとに、そのUI Elementが所属するアプリケーション名を取得するAppleScriptです。

こんな、Safariの「ファイル」メニューの中にあるメニュー項目、

menu item “PDFとして書き出す…” of menu “ファイル” of menu bar item “ファイル” of menu bar 1 of application process “Safari”

があったとして、このオブジェクトが所属しているアプリケーション名を取得したいというケースがありました。上記の例では「Safari」がそれに該当します。

GUI Scripting的なお約束として、オブジェクト階層が上から下に向かって取得する場合には「entire contents」でまとめて取得できるのですが、下から上に向かってparentで取得できるわけでもなく、アプリケーションプロセス名をさかのぼって取得するといった処理ができていませんでした。

AppleScriptの「仕様」的には不可能です。

でも、不可能であることさえわかれば(正攻法では不可能なので)、いつものトリッキーかつ邪道な手口でなんとかなりそうです。

そうです、エラートラップ中で無理やりエラーを起こして、そのエラーメッセージの情報から取得してしまおうという、いつものやつです。

というわけで、GUI Elementの情報から所属アプリケーション名を取得できました。

最近は、さらなる「卑劣な手段」を用意しているので、そちらで華麗かつ卑劣に処理してもよかったのですが、とりあえず手短にまとめたかったのでいつものやつで処理しました。

AppleScript名:GUI Scripting的なUI Elementへの参照から所属するアプリケーション名を取得する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/09/17
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

tell application "System Events"
  set aRef to menu item "PDFとして書き出す…" of menu "ファイル" of menu bar item "ファイル" of menu bar 1 of application process "Safari"
end tell

set appName to retRootAppProcessNameFromGUIElementRef(aRef) of me
–> "Safari"

–GUI Scripting的なUI Elementへの参照から、rootのアプリケーションプロセス名を取得する
on retRootAppProcessNameFromGUIElementRef(aRef)
  try
    set aStr to aRef as string –force cause error
  on error erM
    set offsetA to offset of " of application \"System Events\"" in erM
    
set offsetB to offset of "«class pcap» " in erM
    
set appName to text (offsetB + (length of "«class pcap» ") + 1) thru (offsetA – 2) of erM
    
return appName as string
  end try
end retRootAppProcessNameFromGUIElementRef

★Click Here to Open This Script 

Posted in GUI GUI Scripting System | Tagged 10.11savvy 10.12savvy 10.13savvy System Events | Leave a comment

iCloud Driveフォルダへのパスを求める

Posted on 9月 16, 2018 by Takaaki Naganoya

iCloud Driveフォルダへのパスを求めるAppleScriptです。

ただ、これほどわかりにくいものも珍しいことでしょう。自分も避けておきたい仕様です。

パスについての仕様はあり、ファイルの読み書きなどはできそうですが、求めたパスをFinder上で指定すると不思議なことになります。

Finderのサイドバーから「iCloud Drive」アイコンをクリックすると、

のような表示になります。このときのFinder Windowのtargetを取得すると、~/Library/Mobile Documentsが返ってきます(形式はFinder Objectですけれども)。ただ、これと同じことをAppleScript側から行なっても、同じ結果が得られないというジレンマがあります。

~/Library/Mobile Documents

AppleScript名:iCloud Drivegフォルダへのパスを求める
set iCloudPath to ((path to library folder from user domain as string)) & "Mobile Documents:"
tell application "Finder"
  make new Finder window
  
set target of Finder window 1 to (iCloudPath as alias)
end tell

★Click Here to Open This Script 

これは、~/Library/Mobile Documentsをオープンするものですが、これを実行すると、

のようになります。

~/Library/Mobile Documents/com~apple~CloudDocs

これみたいですね。

AppleScript名:iCloud Drivegフォルダへのパスを求める(com~apple~CloudDocs フォルダ)
set iCloudPath to ((path to library folder from user domain as string)) & "Mobile Documents:com~apple~CloudDocs"
tell application "Finder"
  make new Finder window
  
set target of Finder window 1 to (iCloudPath as alias)
end tell

★Click Here to Open This Script 

これは、~/Library/iCloud Drive/com~apple~CloudDocsをオープンするものですが、これを実行すると、

となります。

Terminal上で当該フォルダを表示させてフォルダ名を再度確認してみたところ、きっとこれです。

Posted in File path System | Tagged 10.11savvy 10.12savvy Finder | Leave a comment

CotEditor 3.5でウィンドウの透明度コントロール機能がなくなる

Posted on 9月 15, 2018 by Takaaki Naganoya

CotEditor v3.5で機能に変更があり、「CotEditorでウィンドウの透明度を連続的に変更する」のようなScriptが実行できなくなりました。AppleScript側からウィンドウの透明度をコントロールするための予約語「view opacity」が廃止されました。

予約語が廃止されたときにどういう状態になるのか、という事例として掲載しておきます。

保存ずみの同Scriptをオープンすると、

のような表示になりました。この状態でコンパイル(構文確認)を行うと、

のようにエラーになります。

CotEditorのAppleScript用語辞書をHTMLに書き出してdiffで差分を計算すると、

たしかにview opacityが削除されていることが確認できます。

ただし、アプリケーションの機能としてウィンドウの透明度設定は行えるので、エディタとしての使い勝手は(あまり)変わりません。AppleScriptから透明度をコントロールできないと困ることがあるかと考えると、とくに困ることはありません。

Posted in 未分類 | Tagged 10.11savvy 10.12savvy 10.13savvy CotEditor | Leave a comment

front document

Posted on 9月 11, 2018 by Takaaki Naganoya

AppleScriptに対応しているGUIアプリケーションで、複数のドキュメントを管理するもの、テキストエディタや表計算ソフトなどはwindowとdocumentというクラスが定義されています。

documentは、オープンされた順にIDが振られます。このIDは、ウィンドウの重ね合わせ順が変更されても変わりません。保存したりクローズしたりする対象はdocumentです(原則。たまに例外があります)。

windowのIDは、画面上のウィンドウの重ね合わせ順を反映したものです。最前面のウィンドウがwindow 1です。その下にあるのがwindow 2、3、4……とカウントされます。別のウィンドウを最前面に移動させると、それがwindow 1になります。

windowに対して実行できるコマンドもあれば、documentに対して実行できるコマンドもあります。そのあたり、アプリケーションごとに実装が異なるので慣れが必要です。一番このあたりでdocumentとwindowの使い分けに頭を悩まされるのはFileMakerあたりでしょうか。「それ、document(database)に対してのコマンドじゃないんだ?」という例がズラズラと。

長いあいだ、「最前面のdocumentにアクセスするのって大変だ」と感じていました。window 1にアクセスして、そのpropertyを取得し、propertyの中に対応するdocumentの情報があれば、それを取得。

そんな中、front documentという書き方を見かけ、試してみたら「最前面のdocument」にアクセスできました。AppleScript用語辞書にも載っていないのに、なぜか使える用語です。

AppleScript名:window–>document
–window 1–> document
tell application "Safari"
  tell window 1
    properties
    
–> {zoomable:true, closeable:true, zoomed:true, class:window, index:1, visible:true, name:"AppleScriptの穴 – Useful & Practical AppleScript archive", miniaturizable:true, id:204098, miniaturized:false, resizable:true, bounds:{0, 22, 1233, 1200}, current tab:tab 1 of window id 204098 of application "Safari", document:document "AppleScriptの穴 – Useful & Practical AppleScript archive" of application "Safari"}
    
    
set aDoc to document of it
  end tell
  
  
tell aDoc
    set aURL to URL
    
–> "http://piyocast.com/as/"
  end tell
end tell

★Click Here to Open This Script 

AppleScript名:front document
–front document
tell application "Safari"
  tell front document
    set aURL to URL
    
–> "http://piyocast.com/as/"
  end tell
end tell

★Click Here to Open This Script 

こんな便利な機能、いつごろ実装されたのかと疑問に思って、macOS/OS X/Mac OS Xのバージョンをさかのぼり、Classic Macエミュレータ「SheepShaver」上のClassic Mac OS J1-8.6上のAppleScript J1-1.3.7で確認したところ、期待どおりの動作を行いました。

うわ、恥ずかしい〜。ただ、front documentについて書籍などで書かれているのを見た覚えがありません。

1999年に作成されたAppleScript Language Guide 1.3.7で「front document」を検索してみたら、けっこーたくさん出てきました。

Posted in How To | Tagged 10.12savvy JeditΩ Safari | Leave a comment

Post navigation

  • Older posts
  • Newer posts

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

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • macOS 15, Sequoia
  • 指定のWordファイルをPDFに書き出す
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Numbersで選択範囲のセルの前後の空白を削除
  • メキシカンハットの描画
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • AdobeがInDesign v19.4からPOSIX pathを採用
  • AppleScriptによる並列処理
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • macOS 15でも変化したText to Speech環境
  • Keynote/Pagesで選択中の表カラムの幅を均等割
  • デフォルトインストールされたフォント名を取得するAppleScript
  • macOS 15 リモートApple Eventsにバグ?
  • AppleScript入門① AppleScriptってなんだろう?
  • macOS 14で変更になったOSバージョン取得APIの返り値
  • Script Debuggerの開発と販売が2025年に終了
  • Keynoteで2階層のスライドのタイトルをまとめてテキスト化

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (194) 14.0savvy (147) 15.0savvy (132) CotEditor (66) Finder (51) iTunes (19) Keynote (117) 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) 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
  • 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年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