VirtualSkySlideShow (VSSS) 用のスライドデータを作るのに、Flickr にアップロードした画像を使うのですが、画像URL を引っこ抜くのが面倒だったので Greasemonkey スクリプトを書いてみました。Firefox で閲覧中の Flicker の写真ページにボタンを追加して、ボタンを押すと、以下の情報を抽出して VSSS 用のJSON断片をクリップボードにコピーします。
// ==UserScript== // @name VSSS用データ取得 // @namespace https://rna.hatenablog.com/ // @version 1.0 // @include https://www.flickr.com/photos/* // @grant none // ==/UserScript== addEventListener("DOMContentLoaded", function(){ const TARGET_SIZE = 2048; const getImageAndLabel = function (context, photoId, obj) { let model = null; try { model = context.modelRegistries["photo-models"]._data[photoId]; } catch (e) { return null; } console.log("photo:model = " + model); if (!model) { return null; } const sizes = model.sizes; let dMin = TARGET_SIZE; let url; let sizeMax; for (const name in sizes) { // console.log("photo:size: " + name); const sz = sizes[name]; let size = Math.max(sz.width, sz.height); let d = TARGET_SIZE - size; if (d >= 0 && d < dMin) { dMin = d; url = sz.displayUrl; sizeMax = size; } } obj.image = 'https:' + url; obj.label = model.title; return obj; }; const getDate = function (context, photoId, obj) { let model = null; try { model = context.modelRegistries["photo-exif-models"]._data[photoId]; } catch (e) { return null; } console.log("exif:model = " + model); if (!model) { return null; } let date = null; let tz = null; for (let tag of model.data) { // console.log("exif:tag: " + tag.label); switch (tag.label) { case "Date and Time (Original)": let m = /^(\d+):(\d+):(\d+)\s+(\d+:\d+:\d+)$/.exec(tag.value); date = m[1] + "-" + m[2] + "-" + m[3] + "T" + m[4]; break; case "Offset Time": tz = tag.value; break; } if (date && tz) { break; } } obj.date = date + tz; return obj; }; const onClick = function () { const wait = function () { button.innerText = '[wait...]'; console.log("wait..."); setTimeout(onClick, 100); }; const photoId = /^https?:\/\/www\.flickr\.com\/photos\/[^\/]+\/(\d+)\//.exec(window.location.href)[1]; console.log("photo_id = " + photoId); const context = window.wrappedJSObject.appContext; console.log("context:" + context); if (!context) { wait(); return; } let obj = getDate(context, photoId, {}); if (!obj) { wait(); return; } console.log(obj.date); obj = getImageAndLabel(context, photoId, obj); if (!obj) { wait(); return; } console.log(obj.image); const json = JSON.stringify(obj, null, " "); console.log(json); navigator.clipboard.writeText(json); button.innerText = '[VSSS]'; }; const button = document.createElement('div'); button.innerText = '[VSSS]'; button.style.position = 'absolute'; button.style.padding = '0.2rem'; button.style.right = '0'; button.style.color = '#77e'; button.style.zIndex = '3000'; button.style.cursor = 'copy'; button.style.fontWeight = 'bold'; button.addEventListener('click', onClick); document.body.insertBefore(button, document.body.firstChild); })
たとえば https://www.flickr.com/photos/rnanba/48346830907/ にアクセスしてページ右上に表示される [VSSS] ボタンを押すと以下のようなテキストがクリップボードにコピーされます(ボタンの表示が [wait...] の間は待ってください)。
{ "date": "2019-01-01T21:54:51+09:00", "image": "https://live.staticflickr.com/65535/48346830907_20a9a09643_k.jpg", "label": "ばら星雲 (2019/1/1 21:54) (StarNet++使用(3))" }
これに "ra", "dec" (あるいは "planet")の情報を足せば VSSS のスライドオブジェクトになります。これでスライド作りがだいぶ楽になりました。
TARGET_SIZE はスライドショーの再生環境に応じて適当な値を設定してください。設定した値を越えない範囲で一番大きなサイズの画像が選択されます。
技術的なこと
Flickr のページ上で動く JavaScript アプリケーションが保持するデータにアクセスすることで実現しています。window.appContext.modelRegistries 以下に色んなデータがあるのでそこから引っこ抜くわけです。
そういう仕組みなので Flickr のサイトがリニューアルされると使えなくなったりする可能性が大です。また、元のデータが非同期に更新されるので、タイミングによってはデータの取得に失敗するかも。一応データがなかった場合はリトライするように作ってあります(リトライ中はボタン表示が [wait...] になる)。