搶救 OneTab 資料 Save my OneTab data
昨晚在如往常按下 OneTab 的圖示後,Chrome 瀏覽器的 OneTab 分頁卻進入了無窮等待載入狀態, 再也顯示不出 OneTab 的頁面了。在網路上能查到 OneTab 在 Linux 上的資料目前並不多, 只好自力求生。
OneTab 是什麼?
OneTab 是一個瀏覽器外掛,它的功能是將瀏覽器的分頁們整批收起來儲存到一頁式的顯示頁面。它主打的訴求是 “節省記憶體”,但原理其實只是關掉分頁,從而釋放出其佔用的記憶體。因爲它提供了一個對整批分頁的 Save / Restore 的方便操作方式,對於瀏覽器常常有大量分頁的使用者來說,是個不錯的工具。
進一步可參考: OneTab extension for Google Chrome and Firefox - save up to 95% memory and reduce tab clutter
資料存在哪?
網路上找到的線索主要有:
C:/Users/《Username》/AppData/Local/Google/Chrome/User Data/Default/Local Storage/leveldb
chrome-extension_chphlpgkkbolifaimnlloiipkdnihall_0.localstorage
- 資料庫爲 LevelDB , 副檔名爲
.ldb
然而,實際在 Linux 系統下卻找不到目標檔案。
爲找出真實位置,我採取下述方法:
-
首先,建一個新的 Profile 。 方式可參考說明: Create Chrome browser profiles
-
在新的 Profile 下安裝 OneTab。可參考: OneTab - Chrome 線上應用程式商店
-
進到 Profile 的目錄下,持續偵測監看某個目標網址(ex:
6bcf7279.info
)。例如:``` cd $HOME/.config/google-chrome/《NEW PROFILE》/ watch 'grep -nHr "https://6bcf7279.info" . | cut -c-80' ```
-
開啓目標網址,並觀察底下檔案的動靜。 註: 此時異動的主要是瀏覽歷史記錄,還不是 OneTab 資料庫。
-
將目標網址加進 OneTab,並觀察此操作所觸發的新增異動。此時的異動,才是 OneTab 資料庫真正的位置。
透過這個方法,最終找出實際位置在:
$HOME/.config/google-chrome/《NEW PROFILE》/Local Extension Settings/chphlpgkkbolifaimnlloiipkdnihall/
如何讀取?
OneTab 的資料以 LevelDB 資料庫方式儲存。一開始試了幾種工具都沒成功,後來試成功的有兩個:
- ldbdump - dumps LevelDB keys/values
- 底層是 rjpower/py-leveldb: leveldb bindings for python
- Arch package: AUR (en) - python-leveldb
- Debian package: https://tracker.debian.org/pkg/python-leveldb
- LevelDB Dumper
- Arch package: leveldb-dumper
Method: ldbdump
在 Linux 系統下, 須先安裝 python-leveldb
套件。之後可執行下述指令存取 OneTab 的資料:
# 註: 將 "《NEW PROFILE》" 更換爲實際的目標名稱
# NOTE: Replace "《NEW PROFILE》" with your target profile
LDB_DIR="$HOME/.config/google-chrome/《NEW PROFILE》/Local Extension Settings/chphlpgkkbolifaimnlloiipkdnihall/"
LDBDUMP_SRC_URL="https://gist.github.com/mkorthof/412e3cb64785c4f136bbb7f6a9d3a71c/raw/e407eda812448e711a1a496cfd480c227eebacfb/ldbdump.py"
pushd "$LDB_DIR"
python <(curl -skL "$LDBDUMP_SRC_URL") -8j "$(pwd)"
popd
NOTE: 若出現下述失敗訊息,則建議先關閉 Chrome 後再執行。
Traceback (most recent call last):
File "/dev/fd/63", line 74, in <module>
leveldb.LevelDBError: IO error: lock /home/user/.config/google-chrome/Default/Local Extension Settings/chphlpgkkbolifaimnlloiipkdnihall/LOCK: Resource temporarily unavailable
執行成功的話,輸出的內容大致如下:
{
"installDate": 1674487233973.0, "lastSeenVersion": "1.58", "settings": "{\"oneTabTabState\":{\"index\":0,\"pinned\":true,\"active\":true,\"updateDate\":1675574927466,\"updateEvent\":\"onRemoved\"}}", "state": "{\"tabGroups\":[{\"id\":\"api8W1May_apwfeZc3Ulw3\",\"tabsMeta\":[...],\"createDate\":1675489094057},{\"id\":\"1eydQsFZj7QgGOoAQ6jt1c\",\"tabsMeta\":[...],\"createDate\":1675335420561},{\"id\":\"J2-OWp5GcgzhgZu7ZN6yqx\",\"tabsMeta\":[...],\"createDate\":1675264586285}]}"
}
Method: LevelDB Dumper
LevelDB Dumper 在 Arch Linux 有現成套件: leveldb-dumper
。 安裝此程式後, 可執行指令如下:
# 註: 將 "《NEW PROFILE》" 更換爲實際的目標名稱
# NOTE: Replace "《NEW PROFILE》" with your target profile
LDB_DIR="$HOME/.config/google-chrome/《NEW PROFILE》/Local Extension Settings/chphlpgkkbolifaimnlloiipkdnihall/"
TMPD=$(mktemp -d)
pushd "$LIB_DIR"
LevelDBDumper -d "$(pwd)" -o $TMPD -b --outputType json
cat $TMPD/LevelDBDumper.json
popd
rm -r "$TMPD"
NOTE:
LevelDBDumper
須加選項-o
, 否則寫入檔案不會成功- 輸出 JSON 格式時,檔案路徑
LevelDBDumper.json
是程式寫死的,使用選項-f
並無作用- source code here: LevelDBDumper/FileOutputs.go at master · mdawsonuk/LevelDBDumper
執行成功的話,輸出的內容大致如下:
[
{
"modified_timestamp": "2023-02-05T05:31:21 UTC",
"path": "/home/user/.config/google-chrome/Default/Local Extension Settings/chphlpgkkbolifaimnlloiipkdnihall",
"data": {
"installDate": "1.674487233973e+12",
"lastSeenVersion": "\"1.58\"",
"settings": "\"{\\\"oneTabTabState\\\":{\\\"id\\\":1392050038,\\\"index\\\":0,\\\"windowId\\\":1392049925,\\\"pinned\\\":true,\\\"active\\\":false,\\\"updateDate\\\":1675575512019,\\\"updateEvent\\\":\\\"onUpdated\\\"}}\"",
"state": "\"{\\\"tabGroups\\\":[{\\\"id\\\":\\\"api8W1May_apwfeZc3Ulw3\\\",\\\"tabsMeta\\\":[...],\\\"createDate\\\":1675489094057},{\\\"id\\\":\\\"1eydQsFZj7QgGOoAQ6jt1c\\\",\\\"tabsMeta\\\":[...],\\\"createDate\\\":1675335420561},{\\\"id\\\":\\\"J2-OWp5GcgzhgZu7ZN6yqx\\\",\\\"tabsMeta\\\":[...],\\\"createDate\\\":1675264586285}]}\""
}
}
]
NOTE: 輸出內容和 ldbdump 結構相同,差異在於 LevelDB Dumper 的內容多了 \\
的跳脫字元。
匯出 Tab 群組資料(in Markdown)
確定能讀取 OneTab 資料後,下一步是嘗試匯出每一筆 Tab 群組資料,打算依易讀性佳的 Markdown 格式輸出。先安裝 jq
程式,用於處理 JSON 格式。之後寫好的 Shell script 如下:
#!/usr/bin/env bash
# Method: ldbdump
dump_data_with_ldbdump() {
python <(curl -skL "$LDBDUMP_SRC_URL") -8j "$(pwd)" | jq -Mr .state
}
# Method: LevelDBDumper
dump_data_with_LevelDBDumper() {
# NOTE: Need to remove extra escape chars and quotes
cat $TMPD/LevelDBDumper.json | jq -Mr .[0].data.state |
sed -e 's/\\"/"/g' -e 's/\\\\/\\/g' -e 's/^"//g' -e 's/"$//g'
}
dump_data() {
# NOTE: Choose and uncomment one of following command
#dump_with_ldbdump
#dump_with_LevelDBDumper
}
dump_data | jq -c -Mr .tabGroups[] | while read -r json_TabGroup; do
var_time=$(echo "$json_TabGroup" | jq -Mr .createDate | sed -e 's/...$//g') # Get the tab group's create time
var_time=$(date --date="@${var_time}" --iso-8601=s) # Convert it to ISO 8601 time formate
# Add a heading level 2 section and use datetime as section name
echo -e "## $var_time\n"
echo "$json_TabGroup" | jq -c -Mr .tabsMeta[] | while read -r json_Tab; do
var_url=$(echo "$json_Tab" | jq -Mr .url)
var_title=$(echo "$json_Tab" | jq -Mr .title)
# Output the tab data with Markdown's link and unordered lists syntax
echo "- [$var_title]($var_url)"
done
echo -e "\n" # Add empty lines to end section
done
NOTE:
- 關於
jq -c -Mr
中選項-c
, 讓同一個 Tab group 的資料壓縮成同一行輸出,以便 shell script 進行處理-M
, monochrome, 避免輸出的內容夾雜 colorize code-r
, raw, 避開 JSON 輸出字串是多出"
額外的雙引號
輸出的結果示意如下:
## 2019-09-01T08:25:24+08:00
- [Link title 1](Link url 1)
- [Link title 2](Link url 2)
- [Link title 3](Link url 3)
## 2019-09-04T01:49:51+08:00
- [Link title 4](Link url 4)
- [Link title 5](Link url 5)
- [Link title 6](Link url 6)
重新檢視每天使用流程
在確定能匯出資料後,開始重新思考:”我想要作到什麼樣子?“
回想 OneTab 會累積到 3000 多筆資料,很大部分的原因是,自己一直把 OneTab 當作臨時安置的倉庫。只有把東西存進去,暫時卸下眼前的負擔,卻沒有考慮到後續 Review 消化的流程。這樣的行爲模式,必然導致今天這樣的結果。
反思了一下自己的使用場景, 得到一些想法:
Q: 這些資料是在那裏產生的?
產生地點主要是:
- 手機瀏覽器(Chrome)
- Laptop 瀏覽器(Chrome)
- RSS reader (Feedly)
- 加在 “Reader Later” 的清單
- 另開瀏覽器
- YouTube App
- 加在 “Watch Later” 的清單
- 其他,大多是以瀏覽器開啓連接而回到上述的地點
Q: 產生後的流向爲何?
- 手機瀏覽器 => 加 Bookmark => 無下文
- 手機瀏覽器 => 用 “Share” 功能傳到 Obsidian => 進到 Obsidian 筆記
- => 進到當天的 note => 每隔一陣子消化 => 進到對應主題項目的 note
- => 進到 Queue note => 每隔一陣子消化 => 進到對應主題項目的 note
- => 進到對應主題項目的 note
- => 未來查資料、研究、回憶時最容易用到。
- 手機瀏覽器 => 分頁同步到 Laptop 瀏覽器 => OneTab => 無下文
- 手機瀏覽器 => 分頁一直累積 => 受不了 => 同步到 Laptop 瀏覽器 => 之後關閉分頁
- Laptop 瀏覽器 => Copy-n-paste 到 Obsidian => 進到 Obsidian 筆記
- Laptop 瀏覽器 => 加 Bookmark => 無下文
- Laptop 瀏覽器 => OneTab => 無下文
- YouTube App => 進 “Watch Later” => 無下文
- 其他 => 以瀏覽器開啓連結 => 進到手機瀏覽器或 Laptop 瀏覽器
嘗試改善設計
觀察到:
- 進到 Obsidian 筆記的資料,會回頭再消化跟重複使用的比例較高
- 進到 OneTab 或是 Bookmark 的資料,則幾乎就沒有下文了
那麼,若能做到一鍵(或甚至自動)將 OneTab 的資料傳送到 Obsidian 的筆記區,是否就能改善這個問題呢?(示意圖如下)
依此想法初步列出目標 :
- 能將 OneTab 資料同步到
~/Notebook/Notes/Mobile/OneTab.md
- 每一個 OneTab 的 Tab group 對應一個 Markdown heading level 2 的 Section
- 以 ISO 8601 的日期時間格式作標題
- 網址以 Unordered Lists 列出
- 比對並跳過已匯出的 Tab group (註: 不用擔心重複執行會產生重複資料)
- 新進的項目會附加在頁面最下方
- 若無錯誤的執行完成,則順便一併將該檔案作
git commit
儲存記錄。 - 儲存完後,回 OneTab 將已匯出的內容清空
- 安排流程進 OneTab 消化 queued 的group
實作
整合上述的研究內容跟設計想法,實作 script 工具如下:
onetab.inc.sh:
#!/usr/bin/env bash
function murmur {
echo -e ">>>" $@ >&2
}
function die {
murmur $@
exit 1
}
function need_prog() {
:
}
function cmd_onetab2md() { # Export OneTab data and output with markdown format
need_prog "LevelDBDumper"
need_prog "jq"
need_prog "tac"
need_prog "git"
local profile_path="$1"
local output_path="$2"
local do_git_commit="$3"
local ldb_dpath="$profile_path/Local Extension Settings/chphlpgkkbolifaimnlloiipkdnihall"
[ -d "$ldb_dpath" ] || die "OneTab database directory not found. Path: ${ldb_dpath}/"
TMPD=$(mktemp -d)
[ -d ${TMPD} ] && (
LevelDBDumper -d "$ldb_dpath" -o $TMPD -b --outputType json
[ -e $TMPD/LevelDBDumper.json ] || die "Output json file not found"
[ -e "$output_path" ] || die "Output markdown file not found"
{
cat $TMPD/LevelDBDumper.json | jq -Mr .[0].data.state | sed -e 's/\\"/"/g' -e 's/\\\\/\\/g' -e 's/^"//g' -e 's/"$//g' |
jq -cMr .tabGroups[] |
tac |
while read -r line; do
var_id=$(echo "$line" | jq -Mr .id)
var_time=$(echo "$line" | jq -Mr .createDate | sed -e 's/...$//g')
var_time=$(date --date="@${var_time}" --iso-8601=s)
(grep -e "^## $var_time $var_id" "${output_path}" >&/dev/null) && {
murmur "[SKIP] $var_time $var_id section already exists."
continue
}
murmur "Processing $var_time $var_id section..."
(
echo "## $var_time $var_id"
echo ""
echo "$line" | jq -Mcr .tabsMeta[] | while read -r line2; do
var_url=$(echo "$line2" | jq -Mr .url)
var_title=$(echo "$line2" | jq -Mr .title)
echo "- [$var_title]($var_url)"
done
echo ""
) >>"${output_path}"
done
}
)
rm -r ${TMPD}
do_git_commit=$(echo $do_git_commit | tr 'A-Z' 'a-z' | sed -e 's/\s//g')
if [ "$do_git_commit" = "yes" ]; then
murmur "Check git changes and commit..."
local output_dpath=$(dirname "$output_path")
(
cd "$output_dpath" || exit
(git rev-parse --show-toplevel >&/dev/null) && {
local var_filename=$(basename "$output_path")
git diff -- "$var_filename"
git add -- "$var_filename"
git commit -s --message="Export and commit by onetab2md script" -- "$var_filename"
}
)
fi
}
使用方式如下:
source onetab.inc.sh
cmd_onetab2md 《Chrome's profile directory path》 《Output markdown file path》 《git commit or not(yes|no)》
例:
source onetab.inc.sh
cmd_onetab2md $HOME/.config/google-chrome/Default $HOME/Notebooks/Notes/Mobile/OneTab.md yes
後記
2023-01-12
在這篇文章 https://amrf000.github.io/blogs/116893 中,作者 amrf000 分析 OneTab 丟失資料的可能原因。另外, 也在另一篇 https://amrf000.github.io/blogs/112515 有提到:
环境里C盘一旦满了之后,onetab保存标签出错后会导致所有onetab旧数据丢失
2023-01-15
我後來發現 GitHub 上也有專案找到 Local Extension Settings/
的存放目錄:
專案的 README.md 的 Notes 寫道:
Took me a lot of time to figure out OneTab data is stored in the Local Extension Settings folder
可見資料存放位置並不是很多人知道,也不容易找。
另外,從另一個專案:
有看到 storage.js
的線索。storage.js
似乎是屬於 OneTab 在 Firefox 上存放資料的位置。另一處找到的片段:
也指向相同的結果。
2023-01-15
我以關鍵字: “OneTab”, “Local Extension Settings” 合併在 GitHub 搜尋時,意外發現有人已經做了 OneTab 的備份工具:
其使用了 Tauri, React + TypeScript, Elastic UI 來打造 GUI 應用程式, 相當值得參考。
2023-02-06
GitHub 跟 OneTab 相關的專案有: onetab · GitHub Topics