1
2

More than 3 years have passed since last update.

Chrome拡張"OneTab"から、ブックマークにエクスポートする

Last updated at Posted at 2021-04-13

経緯、タブをoneTabに貯め過ぎて、整理が不可能に。

気づいたら1000個ぐらい。縦に長過ぎて整理が不可能。折り畳めないしマージもできない。
じゃあ、ブックマークに戻そう。がきっかけ。

oneTabに使えるエクスポート機能は無い

  • まずChromeに取り込むには、特定形式のbookmarks.htmlにする必要ある。
    • Chromeからブックマークを書き出した時のファイル。
    • NETSCAPE-Bookmark-file-1形式?
  • oneTabに簡易の書き出し機能はあるが、これは単純なURL改行のリスト。
    • そもそも日付情報が無い。
    • 日付は重要なので捨てたくない。
  • まあ、便利なエクスポート機能あるとユーザー出て行きやすいから。
    • あえて付けなかったり(想像)、有料にしたりしてるのをよく見る。

表示部分からデータを解析する。

  • 表示部分には日付データがちゃんと残ってる。
  • ターゲットはChrome拡張なので、Bookmarkletは使えない。
  • ctrl+shift+I で開発者ツールのconsoleからやる。
    • 複数行のコードを弄るのは大変なので、ソース→スペニットを使って作る。
  • 試行錯誤して・・・出来た。

この記事投稿してから気づいた大失態

  • oneTabのローカルストレージにJSONでデータが保存してあった!
  • なので表示から解析する必要がなかった。
  • でもまあ、書き出したものは同じだし、面倒くさいからそのままでいいか。

スクリプトの使い方

  • 下のコードをコピーして、ontab開いて、開発者ツールのコンソールに貼り付けて実行。
    • ただそういう攻撃もあるらしいので、軽くソースを読んでからのほうがいいらしい。
  • 出来たhtmlファイルを、Chrome→ブックマークマネージャ→インポート
  • スクリプトの主な機能
    • Chromeにインポートできる形式のhtmlを書き出す。
    • ブックマークにoneTabでの作成日を付与。
    • faviconも書き出す。
/* eslint no-undef: "error" */
//便利関数
var log = console.log
  , qsa = (s, o = document) => [...o.querySelectorAll(s)]
  , qs = (s, o = document) => o.querySelector(s)

function saveTextToFile(fileName, text) {
  const blob = new Blob([text], {type: 'text/plan'})
  const link = document.createElement('a')
  link.href = URL.createObjectURL(blob)
  link.download = fileName
  link.click()
}
//dataURL化
function getBase64(img) {
  var cvs = document.createElement('canvas')
  cvs.width = img.naturalWidth
  cvs.height = img.naturalHeight
  var ctx = cvs.getContext('2d')
  ctx.drawImage(img, 0, 0)
  //枠つけ
  // ctx.lineWidth = cvs.width / 3
  // ctx.strokeStyle = '#0f0c'
  // ctx.strokeRect(0, 0, cvs.width, cvs.height)
  //canvas要素をBase64化する
  return cvs.toDataURL('image/png')
}

function oneTab2html() {
  //表示から解析してdataに入れる。
  var data = qsa('.tabGroup').map((v) => {
    let dateText = qsa('div', v).find(v => v.innerHTML.startsWith('作成日時')).textContent.slice(5)
    let unixtime = new Date(dateText).getTime() / 1000
    let obj = {
      tabs: null,
      title: (qs('.tabGroupTitleText', v).textContent.trim() + '_' || '') + dateText,
      time: unixtime,
    }
    obj.tabs = qsa('.tab', v).map(v => ({
      url: qs('a', v).href,
      title: qs('a', v).textContent,
      favicon: (() => {
        let el = qs('img:nth-child(1)', v)
        if (el && el.src.startsWith('chrome://'))
          return getBase64(el) //''//
        return ''
      })(),
    }))
    return obj
  })

  //dataからhtmlに書き出す
  var folders = ''
  data.forEach(v => {
    folders += `    <DT><H3 ADD_DATE="${v.time}" >${v.title}</H3>\n`
    folders += '    <DL>\n'
    v.tabs.forEach(a =>
      folders += `        <DT><A HREF="${a.url}" ADD_DATE="${v.time}" ICON="${a.favicon}">${a.title}</A>\n`
    )
    folders += '    </DL>\n'
  })

  var html = `<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
     It will be read and overwritten.
     DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>
${folders}
</DL><p>
`
  //ファイルとして保存
  saveTextToFile('OneTab→bookmarks.html', html)
  return true
}
oneTab2html() && '完了'

インポート後、日付が書き出せてるか、確認する方法

  • 素のChromeでは、ブックマークの作成日を確認する方法は見つからなかった。
  • なので以下の拡張機能を使いました。

自分用の雑記、試行錯誤のおおよそ時系列。

  • chromeから書き出したhtmlファイルを色々試す
    • aadd_date属性にUNIX時間を入れるらしい。
    • その他ブックマークのほうがネストが浅くて分かりやすい。
    • H3をフォルダと認識
      • フォルダにもadd_date時間を付けれる、LAST_MODIFIED
    • ICON="data:image/png;base64,
      • base64のiconデータも渡せる。
    • インポート
      • インポート時はブックマークツールバーに入れられる
      • ブックマークツールバーが空の時以外は、インポートしたブックマークフォルダが作られそこに収まる。
        • そのフォルダが存在する時は、同名のフォルダが作られる。
      • エクスポート時のフォルダ名が同じものは一つにマージされる、ブックマークは両方残る。
  • oneTabの表示から解析
    • googleとかoverflowとか一部のサイトはfaviconのsrcが無く、よくわからない。放置。
    • [{tabs:[{href,title,favicon},],time:UNIX時間,title:フォルダ名},]
    • みたいな形式にまず変換。
  • 出来上がったhtmlをctrl+s保存するために、window.open()を使う。
    • Chromeはブックマークのインポート形式がhtmlだから。
    • about:blankではctrl+sの保存できなかった。なぜぇ。
    • example.com開いて書き換えれば?→セキュリティー的に無理
    • もういいや、onetabの画面を直接書き換えてしまえ。
      • ctrl+sは可能だったが、
      • 保存した瞬間ファイルが削除される怪奇現象。chrome-ext://だからぽい。
    • about:blankにhtml表示させて、そのouterHTMLをコピーしよう
      • document.documentElement.outerHTMLをcopy()して、htmlに貼り付けた。
      • V1.html完成
  • V1.htmlインポートしみてる
    • インポート失敗。まあそうか・・・。
    • インポートしてみるも数個の登録されるだけでおかしい。
    • Chromeが書き出したファイルと見比べると、Chromeはタグが大文字。!?
    • Chrome書き出しファイルを小文字にしてインポートテスト→反応なし。
      • タグは大文字必須?!
    • 大文字にしてからhtmlにparseしてからコピーすると、タグが小文字になってしまう。
      • タグと属性だけ大文字にするの・・・めんどくさいからparseするのやめよう。
  • html→保存、でなく直接書き出すことにした。
    • Chrome書き出しファイルを雛形にして書き出してコピー→htmlファイルに貼り付け。
    • V2.html完成
    • インポートするも、反映されない。
    • href←これも大文字必須だった。疲れた。もう全部大文字にしよう。
    • 修正版を読み込ます→成功・・・日付がおかしい。
      • ブックマークの日付を調べるにはそれっぽい拡張が必要。
    • UNIX時間はミリ秒じゃなく秒だった。
    • インポート成功!!
  • とりあえず機能的に完成。
  • ここまで来たら、Chromeが書き出したファイルで最小化を調べてる
    • pタグを消してみる、問題なし、
    • フォルダ前のdl dt 削除、OK
    • <h3>のフォルダより前は全部不要。文字コードはutf8なら削除できる。
    • <DL><DT>は改行無いと読み込まない→まじかあ。HTMLのこと詳しくないけど。
    • タイトルやH1は必要無い。
    • なんでこんな不親切な仕様なんだろう、超かしこ集団が作ってるブラウザなのに。
    • ADD_DATEはミリ秒じゃないく秒。10桁ぐらい。
    • <!DOCTYPE NETSCAPE-Bookmark-file-1>て検索すると情報が少し出てくる。
  • favicon取り込み(やらなくてもいい)
    • ついでにfaviconに色つけてみようかな。
    • そしたら、リンク開く→アイコン更新で、visited的なことできそう。
    • そもそも更新されるのかな?
    • とりあえず取り込もう
    • canvas作ったにimg貼り付ける、naturalWidthを初めて知る。
    • base64化で、toData()→エラー
    • クロスドメイン問題にひっかかった・・・別オリジンのimg貼り付けがあかんらしい。
      • ダメ元で.setAttribute("crossorigin","anonymous")
      • 画像が壊れマーク。エラーは出なくなった。返り値は空に・・・。
      • DOMでスクリーンショット取れるとか聞いたことある、それはどうか?
    • そもそもクロスドメインで全部弾かれてないんじゃないか?
      • onetabオリジン→chrome-extension://burabura
      • icon1→chrome://burabrua
      • icon2→https://s2.googleusercontent.com
      • ちゃんと調べるとhttp系だけが弾かれてる。じゃあこっち捨てよう。
    • 完成、iconに色枠が付→見ると消えた。
      • この機能要らない。
    • 気づいたこと
      • ブックマークマネージャーだと同じfaviconは共通表示されてた。
      • ツールバーだと共通表示されない。初めて気づいた。
      • いままで後で読む、の区別にこの挙動を利用してた。
  • 別のQiitaのBookmarkletの記事を読んでたら、偶然、ファイル保存の仕方を知る。
  • Qiitaへ投稿
  • onetabのローカルストレージにJSON発見。
    • 個別のタブデータに日付情報は無し。
    • faviconもChromeURLでfavicon返すのに突っ込めばよさそう。
    • favicon返しに使えない例外の判定は作る必要ありそう。

bookmarks.htmlの最小構成を検証した結果

<H3 ADD_DATE="1617199649" LAST_MODIFIED="0">フォルダ1</H3>
    <DL>
        <DT><A HREF="https://news.google.com/" ADD_DATE="1606126600" ICON="data:image/png;base64,burabura=">ニュース</A>
    </DL>

手間取った点

  • Chrome開発者ツールのスペニットで作ってみた。
    • サジェスト効かないし、コーフォドーマットが汚すぎて読みづらい。
  • インポート形式の仕様がもっとゆるいと思い込み、試す→弾かれる、の繰り返しに疲れた。
    • 最初から完コピを目指すべきだった。
  • 軽い気持ちでワンライナーや、アロー関数オブジェクト返しを多様。
    • アイテムが多い上に例外多くて、そこでエラーでとまると、どこでエラーが起きたか分かりにくい。
    • console.assrtを使うべきかな。
  • テンプレートリテラルの中でforEachとかすると、凄く読みづらい。
    • join()ぐらいまでにすべき。

参考にさせてもらったページ

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2