Amazon Web サービス

Livedoor の次は Amazon かよ...って所ですが。既に気づいた方もいらっしゃるかもしれませんが、このサイトの右側の下の方に Amazon の商品を表示しています。すっかり、アフィリエイトづいています。

これ、今読んでいたりする本や聴いている CD などの関連商品です。Amazon Web サービスでは関連商品を調べることができるのですね。ここに表示される商品を見ると、なにに興味を持っているかが分かるっていうスンポー。

Livedoor の天気も Amazon を使った関連商品の表示も PHP で作っているのですが、一番手こずった部分は、実は JavaScript の部分ということは秘密。

REST で XML が返ってくるという部分は Livedoor お天気と同じなので AppleScript でも処理を行うことができます。ということで、今聴いている iTunes の曲のアーティストを使って Amazon で検索してみるスクリプトなんかを。

Script Editor で開く

property tmpFolder : path to temporary items folder from user domain
property tmpFile : POSIX path of ((tmpFolder as Unicode text) & "request.xml")

property LF : ASCII character 10

property baseurl : "http://webservices.amazon.co.jp/onca/xml"
property service : "AWSECommerceService"
property accesskey : "your access key is here." -- ここにアクセスキー
property associateid : "your associate id is here." -- ここにアソシエイト ID
property operation : "ItemSearch"
property SearchIndex : "Music"
property responsegroup : "Request,Small,Images"
property AWSVersion : "2006-05-17"
property pagenumber : "1"
property sort : "salesrank"

tell application "iTunes"
    set theArtist to artist of current track
end tell

set encodedText to my encodeURL(theArtist)

set keywords to encodedText

set request to baseurl
set request to request & "?Service=" & service
set request to request & "&AWSAccessKeyId=" & accesskey
set request to request & "&AssociateTag=" & associateid
set request to request & "&Operation=" & operation
set request to request & "&Keywords=" & keywords
set request to request & "&SearchIndex=" & SearchIndex
set request to request & "&ResponseGroup=" & responsegroup
set request to request & "&Page=" & pagenumber
set request to request & "&Version=" & AWSVersion
set request to request & "&Sort=" & sort

do shell script "curl -o " & quoted form of tmpFile & " " & quoted form of request

tell application "System Events"
    set xmlFile to XML file tmpFile

    set root to XML element 1 of xmlFile
    set itemsElement to XML element "Items" of root
    set html to ""
    repeat with thisElement in (XML elements of itemsElement whose name is "Item")
        set theURL to value of XML element "DetailPageURL" of thisElement
        set itemTitle to value of XML element "Title" of XML element "ItemAttributes" of thisElement
        set html to html & "<a href=\"" & theURL & "\">" & itemTitle & "</a>" & LF
    end repeat
end tell

html

on encodeURL(theText)
    if not ((theText starts with "'") and (theText ends with "'")) then
        set theText to quoted form of theText
    end if

    try
        do shell script "echo " & theText & " | " & "php -r 'echo rawurlencode(`cat -`);'"
    on error eMessage number eNumber
        tell application (path to frontmost application as Unicode text)
            activate
            display dialog (eNumber & return & eMessage & return & return) as Unicode text buttons {"OK"} default button 1 with icon 0
        end tell
        return ""
    end try
end encodeURL

検索結果を使って商品へのリンクタグを作っています。実際に利用するには Amazon でデベロッパー登録をしてアクセスキーをもらう必要があります。アフィリエイト ID は特に必要ないですが、あると検索結果の商品の URL にアフィリエイト ID が埋め込まれます。

検索結果がなかったときと Amazon でエラーが起きた時の処理は行っていませんので、その辺りご注意を。

天気予報

気がつけば、6 月も半ばを過ぎている...。ちょっとした驚きですね。

6 月に入った頃から、ADSL モデムの調子が悪くてネットに接続できないようになっていました。これは、モデムの故障で交換したらなおったのですが。こういったこともあったのですが、少し前から Web アプリケーションのことを調べていてそれに時間を取られていました。って、今も取られているのだけど。

Web アプリケーション、Web API、Web サービス...といった関連ですね。マッシュアップなんてものが流行っている昨今、私も流行に感化されました。

しかし、ネットに接続したいというときにモデムが故障して...。そうなれば、Ajax や Web サービスといったってネットにつなげられなきゃ、役立たずというごく当然の結論。これは、なかなか興味深い発見でした。

...いま、カーネルパニックが起きました。おい。なんか、調子悪いな...。

なんとなく理解(こういう中途半端が一番危ないと思うのですが)したので手頃な Web API がないかなと探していたところ、思い出した。livedoorWeather Hacks というのを公開していました。公開されてすぐに oomori.com さんがこのサービスを使った LiveOtenki という Cocoa アプリケーションを配布していましたが、これが手頃です。

仕様を読んでみる...。REST で XML が返ってくるんだな。よし、まずは、AppleScript で検証してみよう。

Script Editor で開く

property baseurl : "http://weather.livedoor.com/forecast/webservice/rest/v1"
property areaList : {"北海道地方", "東北地方", "関東地方", "信越・北陸地方", "東海地方", "近畿地方", "中国地方", "四国地方", "九州地方", "南西諸島地方"}
property prefList : {{"道北", "道央", "道東", "道南"}, {"青森県", "秋田県", "岩手県", "宮城県", "山形県", "福島県"}, {"茨城県", "栃木県", "群馬県", "埼玉県", "東京都", "千葉県", "神奈川県", "山梨県"}, {"富山県", "石川県", "福井県", "新潟県", "長野県"}, {"静岡県", "愛知県", "岐阜県", "三重県"}, {"滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県"}, {"岡山県", "広島県", "島根県", "鳥取県", "山口県"}, {"徳島県", "香川県", "愛媛県", "高知県"}, {"福岡県", "大分県", "長崎県", "佐賀県", "熊本県", "宮崎県", "鹿児島県"}, {"沖縄県"}}
property cityList : {{{"稚内", "旭川", "留萌"}, {"札幌", "岩見沢", "倶知安"}, {"網走", "北見", "紋別", "根室", "釧路", "帯広"}, {"室蘭", "浦河", "函館", "江差"}}, {{"青森", "むつ", "八戸"}, {"秋田", "横手"}, {"盛岡", "宮古", "大船渡"}, {"仙台", "白石"}, {"山形", "米沢", "酒田", "新庄"}, {"福島", "小名浜", "若松"}}, {{"水戸", "土浦"}, {"宇都宮", "大田原"}, {"前橋", "みなかみ"}, {"さいたま", "熊谷", "秩父"}, {"東京", "大島", "八丈島", "父島"}, {"千葉", "銚子", "館山"}, {"横浜", "小田原"}, {"甲府", "河口湖"}}, {{"富山", "伏木"}, {"金沢", "輪島"}, {"福井", "敦賀"}, {"新潟", "長岡", "高田", "相川"}, {"長野", "松本", "飯田"}}, {{"静岡", "網代", "三島", "浜松"}, {"名古屋", "豊橋"}, {"岐阜", "高山"}, {"津", "尾鷲"}}, {{"大津", "彦根"}, {"京都", "舞鶴"}, {"大阪"}, {"神戸", "豊岡"}, {"奈良", "風屋"}, {"和歌山", "潮岬"}}, {{"岡山", "津山"}, {"広島", "庄原"}, {"松江", "浜田", "西郷"}, {"鳥取", "米子"}, {"下関", "山口", "柳井", "萩"}}, {{"徳島", "日和佐"}, "高松", {"松山", "新居浜", "宇和島"}, {"高知", "室戸", "清水"}}, {{"福岡", "八幡", "飯塚", "久留米"}, {"大分", "中津", "日田", "佐伯"}, {"長崎", "佐世保", "厳原", "福江"}, {"佐賀", "伊万里"}, {"熊本", "阿蘇乙姫", "牛深", "人吉"}, {"宮崎", "延岡", "都城", "高千穂"}, {"鹿児島", "鹿屋", "種子島", "名瀬"}}, {{"那覇", "名護", "久米島", "南大東島", "宮古島", "石垣島", "与那国島"}}}
property cityIDList : {{{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9", "10", "11", "12"}, {"13", "14", "15", "16"}}, {{"17", "18", "19"}, {"20", "21"}, {"22", "23", "24"}, {"25", "26"}, {"27", "28", "29", "30"}, {"31", "32", "33"}}, {{"54", "55"}, {"56", "57"}, {"58", "59"}, {"60", "61", "62"}, {"63", "64", "65", "66"}, {"67", "68", "69"}, {"70", "71"}, {"75", "76"}}, {{"44", "45"}, {"46", "47"}, {"48", "49"}, {"50", "51", "52", "53"}, {"72", "73", "74"}}, {{"34", "35", "36", "37"}, {"38", "39"}, {"40", "41"}, {"42", "43"}}, {{"77", "78"}, {"79", "80"}, {"81"}, {"82", "83"}, {"84", "85"}, {"86", "87"}}, {{"88", "89"}, {"90", "91"}, {"92", "93", "94"}, {"95", "96"}, {"97", "98", "99", "100"}}, {{"101", "102"}, {"103"}, {"104", "105", "106"}, {"107", "108", "109"}}, {{"110", "111", "112", "113"}, {"114", "115", "116", "117"}, {"118", "119", "120", "121"}, {"122", "123"}, {"124", "125", "126", "127"}, {"128", "129", "130", "131"}, {"132", "133", "134", "135"}}, {{"136", "137", "138", "139", "140", "141", "142"}}}

on run
    set theResult to chooseIt(areaList, item 1 of areaList)
    if theResult is false then return
    repeat with i from 1 to count areaList
        if item i of areaList is theResult then exit repeat
    end repeat

    set areaNum to i
    set thisPref to a reference to item areaNum of prefList
    set theResult to chooseIt(thisPref, item 1 of thisPref)
    if theResult is false then return
    repeat with i from 1 to count thisPref
        if item i of thisPref is theResult then exit repeat
    end repeat

    set prefNum to i
    set thisCity to a reference to item prefNum of item areaNum of cityList
    set theResult to chooseIt(thisCity, item 1 of thisCity)
    if theResult is false then return
    repeat with i from 1 to count thisCity
        if item i of thisCity is theResult then exit repeat
    end repeat

    set cityID to item i of item prefNum of item areaNum of cityIDList
    set tmp to path to desktop folder as Unicode text
    set tmp to POSIX path of (tmp & "tmp")
    weatherQuery("today", cityID, tmp)
    lwws((tmp as POSIX file) as Unicode text)
end run

on weatherQuery(when, cityID, theFile)
    set query to baseurl & "?city=" & cityID
    set query to query & "&day=" & when

    do shell script "curl " & quoted form of query & " > " & theFile
end weatherQuery

on chooseIt(theList, defaultItems)
    tell application (path to frontmost application as Unicode text)
        activate
        set thisItem to choose from list theList default items defaultItems
        if thisItem is false then return false
        set thisItem to thisItem as Unicode text
    end tell
end chooseIt

on lwws(theFile)
    tell application "System Events"
        set xmlFile to XML file theFile

        set root to XML element 1 of xmlFile

        set area to value of XML attribute "area" of XML element "location" of root
        set pref to value of XML attribute "pref" of XML element "location" of root
        set city to value of XML attribute "city" of XML element "location" of root

        set forecastday to value of XML element "forecastday" of root
        if forecastday is "today" then
            set forecastday to "今日"
        else if forecastday is "tomorrow" then
            set forecastday to "明日"
        else
            set forecastday to "明後日"
        end if

        set forecastdate to value of XML element "forecastdate" of root
        set publictime to value of XML element "publictime" of root
        set telop to value of XML element "telop" of root
        set desc to value of XML element "description" of root
        set temperature to XML element "temperature" of root
        set max to value of XML element "celsius" of XML element "max" of temperature as Unicode text
        if max is "" then set max to "--"
        set min to value of XML element "celsius" of XML element "min" of temperature as Unicode text
        if min is "" then set min to "--"
        set imageURL to value of XML element "url" of XML element "image" of root

        return {area, pref, city, forecastday, forecastdate, publictime, telop, desc, max, min, imageURL}
    end tell
end lwws

なるほど。このまま日記の今日の天気欄に挿入したりして使えますね。感じがつかめたところで PHP で Web 上で動くものを作ってみる。それが、これ(...なくなりました)。

うむ、天気予報が表示されたところでどうってことはないな。これだけでは面白くも何ともないということが確認できました。やっぱり、なにかと組み合わせないと...。

しかし、Web アプリケーションを作るときの問題はコーディングではなくて、インターフェイスに時間がかかるということ。これは、私の向き不向きなのでしょうが、デザインって苦手だ。HTML と CSS でああでもない、こうでもないと。簡単に済ますなら、フォームでポップアップメニューを使って市町村の名前をずらずらと並べればいいのですが、これは避けたかった。だって、150 近くの市の中から目的のものなんて探したくないですから。でも、よく使われていますね。都道府県をメニューから選んだりするやつとか。あれでも 50 近くありますね。誕生日の日の選択とか。

どうすれば、簡単に目的のものを選択できるかを考えた結果、3 段階に分けたメニューにしたのですが...こうすると、動くブラウザと動かないブラウザがでてくるんですよね。

そんなわけで、AppleScript から少し離れている今日この頃でした。

Keynote 3

相変わらず QuickTime 7.1 で手こずっています。

まず、dimensions が変更されない(実際は、再保存して再び開くと変更されている)。書き出しで設定ファイルを書き出せない。書き出しで設定を使って書き出すことができない。annotation が設定、変更できない(Automator を使って試してみても駄目)。System Events を使って annotation を取得するもその名前おかしいだろ、って結果が返ってくる。で、以前に書いた text track の日本語の件。

これ、ほんとにまともなソフトウェア?

以上は全て AppleScript から操作するときに出会った不具合です。普通に使っている分にはそこそこ使えます(が、以前より機能や使い勝手が悪くなっている気がするのは気のせい?)。

はやく、QuickTime 7 以前の使い勝手に戻ってほしいものです。

のっけから表題と関係のない話でしたが...Keynote 3。iWork '06 に入っているものです。AppleScript の対応度も少しよくなっています。しかし、なぜにスライドの中にあるテキストやアウトラインや図形や表を操作することができないのでしょう。Pages 2 ではできるのに。Pages 2 で使えるクラスが Keynote でも使えるように...と切に願います(Keynote のスクリプト機能を拡張するプラグインはありますが)。

Keynote の用語説明を見てみましたが、それほどいろんなことができるわけではないみたい。トランジションは設定することができるようになっていますね。そこで iPhoto で選択しているアルバムの中の画像を使ってスライドショーを作るスクリプトなんかを。それぞれの画像に適当にトランジションを設定します。

Script Editor で開く

tell application "iPhoto"
    set curAlbum to a reference to current album
    if (count photos of curAlbum) is 0 then return

    set photoList to image path of photos of curAlbum
    set photoListRef to a reference to photoList
end tell

tell application "Keynote"
    launch
    -- make new slideshow
    set themeList to name of appThemes
    set thisTheme to choose from list themeList
    if thisTheme is false then return
    set thisTheme to thisTheme as Unicode text
    -- other slide size...
    -- {800, 600},{1024,768},{1280,720},{1680,1050},{1920,1080},custom...
    set themeData to {theme:thisTheme, slideSize:{800, 600}}
    set thisSlideshow to make new slideshow at end of slideshows with data themeData

    -- get transitions
    set transitionList to name of appTransitions
    set transitionCount to count transitionList

    tell thisSlideshow
        -- make image slides
        make image slides paths photoListRef with set titles
        delete first slide of thisSlideshow
        set slideCount to count slides
    end tell

    -- apply transition
    repeat with i from 1 to slideCount
        set thisTransition to (some item of transitionList) as Unicode text
        set thisTransition to (a reference to (appTransition thisTransition))
        set appTransition of slideTransition of slide i of thisSlideshow to thisTransition
    end repeat

    -- play slideshow
    start thisSlideshow
end tell

実行すると少しの間なにも起きないのでエラーかと思いますが、出来上がるまで少し待ってください。しかし、Keynote で新規ドキュメントを作る方法の分かりにくいこと。結局のところ AppleScript だけでは全ては完成しません。Pages 2 でもそうなのですが、最終的には自分の手で見映えなどを調整しないといけない。

このような限定的な自動化しかできないので欲求不満に陥っていたり。

QuickTime 7.1

QuickTime 7.1 がでていた。

ファイルサイズが大きかったので期待していたのですが...「文字、化ける?」で書いた不具合(バグ?)は直っていませんでした。うーむ。いったいなんのバージョンアップだったんだろう?

それにしても世間が MacBook で盛り上がっているところになんて関連のない話題。しかも、QuickTime 7.1 がでてからだいぶ日が経っているし。どうも、世間様とずれているな。

続けます。QuickTime 7.1 は容量も大きかったですが、バグもまたでています。例えば、次のスクリプトはエラーになります。

Script Editor で開く

tell application "QuickTime Player"
    set theMovie to make new movie
    tell theMovie
        paste
    end tell
end tell

一見なんの変哲もないのですが、make new movie で新しくドキュメントを作成すると返ってくる結果が、

--> document "名称未設定 2" of application "QuickTime Player"

といったものになります。作られたドキュメントは「名称未設定」なのに...。つまり、一つ数が多いドキュメントの参照が返ってくるのです(なんてこった)。うちだけ?

同じようなもので新しく作った録音用のドキュメントの結果もそのまま使えなかったりする。その他にもいろいろとあるみたい。もう、全然使いやすくなっていないじゃないか。怒る気力もありません。

Pages 2

iWork '06の Pages 2 が面白い。

Pages 2 は、日本語環境に対応していない...という批判(?)はよく聞くけど、なんの。横書きしかできなくてもルビが使えなくても、数式を埋め込むのが面倒でも、それでも個人の環境で便利に使える場面というのは多々ある。

ちょっと Web 見ただけでもなんとたくさんのテンプレートが無料で配布されていることか。CanonEpson のサイトにいってみると横書きでもいいんだ、ということがよく分かる。なにかを紙に印刷するのが目的であれば、工夫次第で様々なことができる。Pages 2 があれば、もう無料のテンプレートいらないや。はがきだって(横書きだけど)できるんだぞぅ。

と、少々ワケの分からないことを宣いましたが。Pages 2 から AppleScript に対応しているんですね。まだまだできないことも多いけど。

ということで Pages を AppleScript で操ってカレンダーなんかを作ってみました。いろんな部分で手を抜いていますが、Pages を持っているならぜひ、お試しください。面白いですから(なんか変なことが起きても責任は持てませんが)。

Script Editor で開く

using terms from application "Pages"
    -- for A4 size
    property paperWidth : 21.0
    property paperHeight : 29.7
    property cellWidth : 2.8
    property cellHeight : 2.4

    property topMargin : 1.0
    property bottomMargin : 1.5
    property leftMargin : 0.5
    property rightMargin : 0.5

    -- calendar cells props
    property strokeWidth : 0.15
    property strokeColor : {26214, 26214, 26214}
    property textFont : "Georgia"
    property textSize : 14.0
    property textAlignment : right

    -- weekday cells props
    property wCellWidth : cellWidth
    property wCellHeight : 1.5
    property wTextFont : "Optima-Bold"
    property wTextSize : 18.0
    property wTextAlignment : center

    -- month cells props
    property mCellWidth : cellWidth * 7
    property mCellHeight : 2.0
    property mTextFont : "Optima-ExtraBlack"
    property mTextSize : 40.0
    property mTextAlignment : center
    property mTextColor : {46003, 46003, 46003}
    property mTextTracking : 3.0

    -- other props
    property weekdayList : {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
end using terms from

set theDate to "2006 4 1" -- for debug
set theDate to date theDate -- for debug
set theDate to current date

tell application "Pages"
    set ruler units to centimeters

    set theDocument to make new document
    tell theDocument
        set top margin to topMargin
        set bottom margin to bottomMargin
        set left margin to leftMargin
        set right margin to rightMargin

        -- 印刷可能領域の算出
        set contentsWidth to paperWidth - (right margin + left margin)
        set contentsHeight to paperHeight - (top margin + bottom margin)

        set tmp to my getCalendar(theDate)
        set dateList to {}
        repeat with thisItem in tmp
            set thisItem to contents of thisItem
            if thisItem is not {} then set end of dateList to thisItem
        end repeat

        -- カレンダーの出来上がりサイズ
        set calendarWidth to cellWidth * 7
        set calendarHeight to cellHeight * (count dateList)
        --印刷可能領域よりカレンダーサイズが大きい場合は処理しない
        if calendarWidth is greater than contentsWidth then return

        -- 余白の算出
        set blank to contentsWidth - calendarWidth
        -- 最初のセルの開始位置
        set x to left margin + (blank / 2)
        set y to (contentsHeight - calendarHeight) + top margin

        -- カレンダーの作成
        set num to 0
        repeat with i from 1 to (count dateList)
            set currentWeek to item i of dateList
            set dayCount to count currentWeek
            repeat with j from 1 to 7
                set num to num + 1
                if j is greater than dayCount then
                    set thisItem to ""
                else
                    set thisItem to (item j of currentWeek) as Unicode text
                end if
                set theProp to {width:cellWidth, height:cellHeight, horizontal position:x + (cellWidth * (j - 1)), vertical position:y, name:(num as Unicode text), object text:thisItem}
                set newShape to make new shape with properties theProp
                if (num mod 7) is 0 then
                    set thisColor to {0, 0, 65535}
                else if (num mod 7) is 1 then
                    set thisColor to {65535, 0, 0}
                else
                    set thisColor to {0, 0, 0}
                end if
                tell object text of newShape
                    set color of it to thisColor
                end tell
            end repeat
            set y to y + cellHeight
        end repeat

        -- カレンダーセルの見た目の変更
        tell shapes
            set stroke width to strokeWidth
            set stroke color to strokeColor
            set wrap to none
            set fill type to none
            tell object text
                set alignment to textAlignment
                set font name to textFont
                set font size to textSize
            end tell
        end tell

        -- 曜日セルの作成
        set weekdayPosition to (vertical position of first shape) - wCellHeight
        repeat with i from 1 to 7
            set newShape to make new shape
            tell newShape
                set horizontal position to x + (wCellWidth * (i - 1))
                set vertical position to weekdayPosition
                set width to wCellWidth
                set height to wCellHeight
                set wrap to none
                set stroke type to none
                set fill type to none
                set object text to item i of weekdayList
                set alignment of object text to wTextAlignment
                set font name of object text to wTextFont
                set font size of object text to wTextSize
            end tell
        end repeat

        -- 月のセルを作成
        set monthPosition to (vertical position of last shape) - mCellHeight
        make new text box with properties {vertical position:monthPosition, height:mCellHeight, width:mCellWidth, horizontal position:x, name:"month"}

        tell text box "month"
            set thisYear to year of (theDate) as Unicode text
            set thisMonth to month of (theDate) as Unicode text
            set object text to thisMonth & space & thisYear
            set alignment of object text to mTextAlignment
            set font name of object text to mTextFont
            set font size of object text to mTextSize
            set tracking of object text to mTextTracking
            set color of object text to mTextColor
        end tell
        say "Complete."
    end tell
end tell

on getCalendar(selectedDate)
    set theList to {{}, {}, {}, {}, {}, {}}
    set numDaysInMonth to {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

    set currentYear to year of selectedDate
    set currentMonth to month of selectedDate as number

    set day of selectedDate to 1
    set time of selectedDate to 0

    copy selectedDate to firstOfMonth
    set startOffset to weekday of firstOfMonth as number

    set daysInMonth to item currentMonth of numDaysInMonth
    if ((currentMonth is 2) and (isLeap(currentYear))) then set daysInMonth to daysInMonth + 1

    set dayLabel to 1
    set num to 0
    repeat with i from 1 to 6
        set currentWeek to item i of theList
        repeat with j from 1 to 7
            set num to num + 1
            if (num is less than startOffset) then
                set end of currentWeek to ""
            else if (num is greater than or equal to (daysInMonth + startOffset)) then
                exit repeat
            else
                set end of currentWeek to dayLabel
                set dayLabel to dayLabel + 1
            end if
        end repeat
    end repeat
    return theList
end getCalendar

on isLeap(theYear)
    return (((theYear mod 4) is 0 and ((theYear mod 100) is not 0)) or (theYear mod 400) is 0)
end isLeap

文字、化ける?

気がつけば、5 月。ゴールデンウィーク。みなさん、せっかくの連休。しっかり、AppleScript に打ち込みましょう!

しないって。せっかくの連休にそんなこと。

さて、RSS の配信を始めてから 1 年経ちました。案外、続きました。

文字が化けるんです。

なにが?

RSS ではありません。紛らわしいよ、この書き方。

QuickTime Player 7 です。テキストトラックです。TextEdit で作ったテキストファイルでテキストトラックを作ったら日本語のチャプターを作れるんです(保存した文字コードは 「日本語(Mac OS)」)。これはこれでいいのですが、わたくしといたしましては、AppleScript でテキストトラックを作りたい。で、しこしこしこしこと AppleScript を書いて実行...。駄目なんですね。日本語を含んでいると文字が化けてしまう。

Script Editor で開く

tell application "QuickTime Player"
    make new movie
    tell movie 1
        make new track with data "おはよう"
    end tell
end tell

文字コード SJIS のファイルを読み込んでテキストトラックを作る分には大丈夫なんですね。AppleScript から渡す文字コードが問題なのかな...と試してみるんですが、解決しない。

QuickTime が元々こういうものなのか、それとも QuickTime 7 になってからのことなのか。その辺も判然としない。テキストトラックで日本語がおかしくなってしまうということは、チャプタートラックも作れないんですよね。どうしたものか...。

PDF に変換する

味も素っ気もない記事名ですね。

/Library/Scripts/printing Scripts の中に Finder で選択している項目を PDF に変換するスクリプトがあります。これが動かない。

なんでかな、と試行錯誤してやっと分かりました。変換対象のファイルへのパスに日本語が含まれていると変換に失敗するんですね。もちろん、ファイル名に日本語を使っていてもだめ。ファイルの中身が日本語かどうかやエンコーディングが何かといったことは関係ないっぽい。

パスに日本語が入っているとだめということなら、それなりの対処をすればいいだけでなんとか使えるようになりました。

しかし、この変換を行っているコマンドって RTF かテキストファイル(他にもいくつかの画像タイプに対応しているようですが、書類関連ではということです)しか変換できないのですね。画像が入っている RTFD ファイルなんかは変換できないし、Safari で保存した Web アーカイブなんかも変換できない。せめて、RTFD だけでも PDF に変換してほしいものです。

property typeList : {"TEXT"}
property extensionList : {"rtf", "txt"}
property tmpFolder : path to temporary items folder from user domain

(*
Usage: /System/Library/Printers/Libraries/convert
    [-f <input filename>]
    [-o <output filename>]
    [-i <input mimetype>]
    [-j <output mimetype>]
    [-P <PPD filename>]
    [-u]
    [-a <attribute string>]
    [-U <username>]
    [-J <jobname>]
    [-c <copies>]
    [-D]
*)

tell application "Finder"
    set curSelection to selection

    set processFiles to {}
    repeat with thisItem in curSelection
        if not (class of thisItem is folder) then
            if (name extension of thisItem is in extensionList or file type of thisItem is in typeList) then
                set end of processFiles to thisItem as alias
            end if
        end if
    end repeat
    if processFiles is {} then return

    repeat with thisItem in processFiles
        set inFile to my duplicateFile(thisItem)
        set outFile to (tmpFolder as Unicode text) & "out.pdf"
        my convertPDF(POSIX path of inFile, POSIX path of outFile)
        set fileName to (my trimExtension(thisItem) & ".pdf") as Unicode text
        set name of (outFile as alias) to fileName
        set parentFolder to my dirName(thisItem)
        move (((tmpFolder as Unicode text) & fileName) as alias) to parentFolder replacing yes
    end repeat
end tell

on duplicateFile(theFile)
    set tmpFile to "process_file"

    tell application "Finder"
        if (file tmpFile of tmpFolder exists) then
            move file tmpFile of tmpFolder to trash
        end if
        set copiedFile to duplicate theFile to tmpFolder replacing yes
        set name of copiedFile to tmpFile
        return (file tmpFile of tmpFolder) as alias
    end tell
end duplicateFile

on convertPDF(inFile, outFile)
    set converter to "/System/Library/Printers/Libraries/convert "
    set mime to "'application/pdf'"

    set theCommand to converter & "-f " & (quoted form of inFile) & " -o " & (quoted form of outFile) & " -j " & mime
    try
        do shell script theCommand
    on error eMessage number enumber
        return {enumber, eMessage}
    end try
end convertPDF

on trimExtension(theFile)
    set {name:fileName, name extension:theExtension} to info for theFile
    if theExtension is missing value then set theExtension to ""
    set fileCharNum to count fileName
    set exCharNum to count theExtension
    if exCharNum is not 0 then
        return text 1 thru (fileCharNum - exCharNum - 1) of fileName
    end if
    return fileName
end trimExtension

on dirName(theFile)
    tell application "Finder" to ((container of file theFile) as alias) as Unicode text
end dirName