Compare commits
1 Commits
master
...
copilot/vs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c2c0273ff |
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# VS Code/ Copilot agent & skill 自定義配置
|
||||
.github/
|
||||
|
||||
# 備份文件
|
||||
*.bak
|
||||
491
README.md
491
README.md
@@ -1,445 +1,124 @@
|
||||
# Backup_script 數據備份腳本
|
||||
[](https://github.com/YAWAsau)
|
||||
[](https://github.com/YAWAsau/backup_script/releases)
|
||||
[](https://github.com/YAWAsau/backup_script/releases/latest)
|
||||
[](https://choosealicense.com/licenses/gpl-3.0)
|
||||
[](https://t.me/yawasau_script)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/YAWAsau/backup_script/stargazers"><img src="https://img.shields.io/github/stars/YAWAsau/backup_script?label=stars&style=flat-square" /></a>
|
||||
<a href="https://github.com/YAWAsau/backup_script/releases"><img src="https://img.shields.io/github/downloads/YAWAsau/backup_script/total?style=flat-square" /></a>
|
||||
<a href="https://github.com/YAWAsau/backup_script/releases/latest"><img src="https://img.shields.io/github/v/release/YAWAsau/backup_script?label=release&style=flat-square" /></a>
|
||||
<a href="https://choosealicense.com/licenses/gpl-3.0"><img src="https://img.shields.io/github/license/YAWAsau/backup_script?label=License&style=flat-square" /></a>
|
||||
<a href="https://t.me/yawasau_script"><img src="https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram&style=flat-square" /></a>
|
||||
</p>
|
||||
## 概述
|
||||
|
||||
---
|
||||
創作該腳本是為了使用戶能夠更加完整地**備份/恢復**應用數據,
|
||||
支援設備必須符合以下條件:`Android 8+`+`arm64`。
|
||||
|
||||
## 📖 概述
|
||||
由於本人是台灣人所以發布的版本為繁體版
|
||||
(CN系統將自動翻譯自身腳本為簡體中文)
|
||||
|
||||
一款專為 Android 設計的完整應用數據備份/恢復 Shell 腳本,支援 SSAID、運行時權限、OBB 數據包、WiFi 設定等完整備份,讓你換機換系統後能無縫還原所有應用狀態。
|
||||
|
||||
新版增加**完整的遠端備份系統**,支援 WebDAV / SMB 上傳到 NAS / 雲端 / 區網電腦,並可從遠端下載備份回手機直接恢復。支援**流式備份模式**,邊壓縮邊傳輸,數據完全不佔用本機空間。
|
||||
## 優勢
|
||||
|
||||
> 作者為台灣人,預設發布繁體版本。CN 系統環境下腳本將自動翻譯為簡體中文。
|
||||
- 數據完整:在更換系統之後,原有的數據全部保留,無需重新登陸或者下載額外數據包。
|
||||
- 支援備份SSAID 可完美備份LINE
|
||||
- 支援備份應用權限 可備份運行時權限與ops權限
|
||||
- 易操作:簡單几步即可備份應用完整數據!
|
||||
- 限制少:不限制機型,可跨安桌版本。
|
||||
- 功能強:可備份恢復`split apk`。
|
||||
- 算法多:目前支持的壓縮算法有 `tar(默認)`
|
||||
- `zstd`。
|
||||
- 速度快:即使使用`zstd`壓縮算法速率依舊快速(對比鈦備份 swift backup)。
|
||||
- 腳本自帶tools完整性效驗與壓縮包效驗
|
||||
## 如何使用
|
||||
`請認真閱讀以下說明,以減少不必要的問題`
|
||||
|
||||
**系統需求:** `Android 8+` · `arm64 架構` · `Root 權限(Magisk / KernelSU)`
|
||||
##### 推薦工具:[`MT管理器`](https://www.coolapk.com/apk/bin.mt.plus),若使用`Termux`,則請勿使用`tsu`。
|
||||
|
||||
---
|
||||
#### !!!以下操作皆須ROOT!!! ####
|
||||
|
||||
## ✨ 功能特色
|
||||
|
||||
| 功能 | 說明 |
|
||||
|------|------|
|
||||
| 📦 完整數據備份 | 換機換系統後原有數據完整保留,無需重新登入或下載額外數據包 |
|
||||
| 🔑 SSAID 備份 | 支援 SSAID 備份,可完美備份 LINE 等依賴設備識別碼的應用 |
|
||||
| 🛡️ 權限備份 | 支援備份運行時權限(Runtime Permission)與 ops 權限 |
|
||||
| 📂 Split APK | 支援備份與恢復 Split APK 格式 |
|
||||
| 🎮 OBB 數據包 | 可選備份外部 OBB 數據(如原神、王者榮耀等大型遊戲) |
|
||||
| 📡 WiFi 備份 | 支援備份與恢復 WiFi 設定 |
|
||||
| 📁 自定義資料夾備份 | 可備份 DCIM、Download、Music 等任意自定義目錄 |
|
||||
| 🗜️ 多種壓縮算法 | 支援 `tar`(僅打包)與 `zstd`(高壓縮率高速度) |
|
||||
| ⚡ 高速壓縮 | zstd 壓縮速率快速,優於鈦備份、Swift Backup |
|
||||
| 🔒 完整性校驗 | 內建 tools SHA-256 校驗與壓縮包完整性驗證 |
|
||||
| 🔄 增量備份 | 多維度比對(版本/大小/權限/SSAID),無變化則跳過 |
|
||||
| ✅ 最終計數核驗 | 備份結束後逐檔核對存在性,三態顯示結果 |
|
||||
| 🖥️ 後台執行 | 支援後台執行模式,可完全關閉終端,log 持續刷新 |
|
||||
| 💡 偽裝亮屏 | 備份/恢復期間可偽裝亮屏,避免 IO 因息屏降速 |
|
||||
| 🌐 自動更新 | 聯網偵測最新版本,支援 CDN 節點(適合中國大陸用戶) |
|
||||
| 🌏 多語言 | 自動識別系統語言環境,支援繁體中文/簡體中文自動切換 |
|
||||
| 👥 多用戶支援 | 支援多用戶環境(user 0、999 等),可手動或自動選擇用戶 |
|
||||
| ⬛ 黑名單模式 | 黑名單應用可選「完全忽略」或「僅備份安裝包」 |
|
||||
| ⬜ 白名單支援 | 支援預裝應用白名單與系統應用白名單,可指定備份範圍 |
|
||||
| 📱 進程偵測 | 可設定忽略正在運行中的應用,避免備份數據不一致 |
|
||||
| ☁️ 遠程備份上傳 | 支援 WebDAV / SMB 兩種協議,備份完成自動上傳,智能增量 |
|
||||
| 🚀 流式備份 | 邊壓縮邊傳輸,數據不落本機,節省本地空間 |
|
||||
| 📥 遠程下載恢復 | 可從遠端直接下載備份回手機,點 start.sh 即可恢復 |
|
||||
| 🔍 區網掃描 | 自動掃描區網內所有 SMB 主機,免去手動找 IP |
|
||||
| 🧪 連線測試 | 三層測試(TCP / 認證 / 路徑),設定不需備份就能驗證 |
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ 主選單功能
|
||||
|
||||
### 備份模式
|
||||
|
||||
| 選項 | 功能 |
|
||||
|------|------|
|
||||
| 生成應用列表 | 掃描已安裝的第三方應用並生成 `appList.txt` |
|
||||
| 備份應用 | 根據列表與設定完整備份應用數據 |
|
||||
| 備份已更新應用 | 僅備份自上次備份以來有版本更新的應用 |
|
||||
| 備份自定義資料夾 | 備份 `backup_settings.conf` 內設定的自定義目錄 |
|
||||
| 備份 WiFi | 備份當前設備的 WiFi 設定 |
|
||||
| 測試遠端連線 | 驗證 WebDAV / SMB 設定,三層測試(TCP / 認證 / 路徑) |
|
||||
| 單獨上傳當前備份 | 上傳現有本地備份到遠端,不重新跑備份流程 |
|
||||
| 列出遠端備份 | 連線遠端、產生 `appList_network.txt` 讓你勾選要下載哪些 app |
|
||||
| 從遠端下載備份 | 依清單下載備份到本地,可直接執行恢復 |
|
||||
| 殺死運行中腳本 | 安全終止正在執行的備份腳本(整棵進程樹) |
|
||||
|
||||
### 恢復模式
|
||||
|
||||
| 選項 | 功能 |
|
||||
|------|------|
|
||||
| 重新生成應用列表 | 刷新恢復資料夾內的 `appList.txt` |
|
||||
| 恢復備份 | 根據列表完整恢復應用與數據 |
|
||||
| 僅恢復包含 SSAID 應用(含數據) | 只恢復有 SSAID 的應用及其完整數據 |
|
||||
| 僅恢復包含 SSAID 應用(不含數據) | 只套用 SSAID,不覆蓋現有數據 |
|
||||
| 恢復自定義資料夾 | 恢復備份的自定義目錄 |
|
||||
| 恢復 WiFi | 恢復已備份的 WiFi 設定 |
|
||||
| 壓縮檔完整性檢查 | 驗證備份壓縮包是否完整無損 |
|
||||
| 轉換文件夾名稱 | 將備份資料夾名稱格式轉換(用於跨版本相容) |
|
||||
| 殺死運行中腳本 | 安全終止正在執行的恢復腳本 |
|
||||
|
||||
---
|
||||
|
||||
## 📁 目錄結構
|
||||
1. 首先將下載到的腳本解壓到任意目錄後,可以看到以下目錄結構 警告! 不論備份或是恢復都必須保證tools的存在與完整性 否則腳本失效或是二進制調用失敗。
|
||||
|
||||
`這是腳本結構與說明`
|
||||
```
|
||||
backup_script.zip
|
||||
│
|
||||
├── tools/
|
||||
│ ├── busybox # 核心工具集
|
||||
│ ├── zstd # zstd 壓縮工具
|
||||
│ ├── tar # tar 打包工具
|
||||
│ ├── curl # 遠程傳輸工具 (WebDAV)
|
||||
│ ├── smbclient # SMB 遠程傳輸
|
||||
│ ├── jq # JSON 處理
|
||||
│ ├── find # 文件搜索
|
||||
│ ├── keycheck # 音量鍵監聽
|
||||
│ ├── cmd # 系統指令橋接
|
||||
│ ├── classes.dex # Java 功能擴展(詳見下方說明)
|
||||
│ ├── soc.json # 處理器資料庫
|
||||
│ ├── Device_List # 設備型號資料庫
|
||||
│ └── tools.sh # 核心腳本
|
||||
│
|
||||
├── backup_settings.conf # 備份行為設定檔
|
||||
└── start.sh # 主執行腳本
|
||||
├── tools
|
||||
│ ├── Device_List
|
||||
│ ├── bc
|
||||
│ ├── busybox
|
||||
│ ├── classes.dex
|
||||
│ ├── cmd
|
||||
│ ├── jq
|
||||
│ ├── find
|
||||
│ ├── keycheck
|
||||
│ ├── soc.json
|
||||
│ ├── tar
|
||||
│ ├── tools.sh
|
||||
│ ├── zip
|
||||
│ └── zstd
|
||||
├── backup_settings.conf <--- 腳本默認行為設置
|
||||
└── start.sh <--- 執行腳本
|
||||
```
|
||||
|
||||
> ⚠️ **重要:** 無論備份或恢復,都必須確保 `tools/` 目錄完整存在,否則腳本將無法正常運作。
|
||||
2. 然後執行`start.sh`腳本音量鍵選擇生成應用列表,等待腳本輸出提示結束,此時會在當前目錄生成一個`appList.txt`,這就是你當前安裝的所有第三方應用(腳本會屏蔽預裝應用,可於backup_settings.conf設置需要備份包名)。
|
||||
|
||||
備份完成後,每個 app 子目錄會額外生成 `backup.sh` / `recover.sh` / `upload.sh`,可單獨備份、恢復或上傳單一應用。
|
||||
3. 現在打開生成的`appList.txt`,根據裏面的提示操作後保存,這樣你就設置好了需要備份的軟件。
|
||||
|
||||
---
|
||||
4. 最後找到`backup_settings.conf`打開後根據提示設置保存,再打開`start.sh`,音量鍵選擇備份應用,備份結束完成後會在當前目錄生成一個以`Backup_壓縮算法名`命名的資料夾,裡面就是你的軟件備份。把這個資料夾整個保持到其他位置,刷完機后複製回手機,直接執行`Backup_壓縮算法名/start.sh`即可恢復備份的所有數據,同樣道理,裡面也有個`appList.txt`,使用方法跟第3步驟一樣,不需要還原的刪除即可,另外進去備份好的資料夾找到單獨應用資料夾有個 backup.sh and recover.sh可以單獨備份與恢復腳本。
|
||||
|
||||
## ⚙️ 設定檔說明(backup_settings.conf)
|
||||
5. 腳本執行過程中請留意紅色字眼提示有無任何錯誤,並且使用恢復腳本時留意恢復結束後是否提示應用存在ssaid,假設提示存在ssaid請在恢復後立刻重啟已便套用ssaid,假設恢復ssaid後立刻打開應用會導致ssaid套用失敗,因為Android會產生一個新的saaid,如此會導致應用卡白屏或是提示需要登錄,ssaid是判斷應用是否換過環境與設備的判斷之一,保持一致可以減少諸如提示異地登錄或是需要重新登入驗證的方法。
|
||||
|
||||
| 設定項 | 說明 | 預設值 |
|
||||
|--------|------|--------|
|
||||
| `Lo` | 操作方式:`0` 音量鍵 / `1` 音量鍵(強制) / `2` 鍵盤輸入 | `0` |
|
||||
| `background_execution` | 後台執行:`1` 可關閉終端 / `0` 需保持終端開啟 | `0` |
|
||||
| `setDisplayPowerMode` | 備份期間偽裝亮屏防止 IO 降速 | `0` |
|
||||
| `Shell_LANG` | 語言:`0` 繁體中文 / `1` 簡體中文(留空自動偵測) | 自動 |
|
||||
| `Output_path` | 自定義備份輸出路徑(留空使用當前目錄) | 空 |
|
||||
| `list_location` | 自定義 appList.txt 位置(留空使用當前目錄) | 空 |
|
||||
| `update` | 自動更新:`1` 開啟 / `0` 關閉 | `1` |
|
||||
| `cdn` | 更新 CDN 節點:`0` 直連 / `1` ghfast.top / `2` workers.dev | `1` |
|
||||
| `mount_point` | 屏蔽外部掛載點,多個用 `\|` 分隔 | `rannki\|0000-1` |
|
||||
| `user` | 指定用戶 ID(留空自動選擇) | 空 |
|
||||
| `Backup_Mode` | 備份模式:`1` 應用+數據 / `0` 僅安裝包 | `1` |
|
||||
| `Backup_user_data` | 備份 user 數據:`1` 是 / `0` 否 | `1` |
|
||||
| `Backup_obb_data` | 備份 OBB 外部數據:`1` 是 / `0` 否 | `1` |
|
||||
| `backup_media` | 備份完成後一併備份自定義資料夾 | `0` |
|
||||
| `Background_apps_ignore` | 忽略正在運行中的應用:`1` 忽略 / `0` 備份 | `0` |
|
||||
| `Custom_path` | 自定義備份目錄列表(絕對路徑,每行一個) | DCIM / Download 等 |
|
||||
| `blacklist_mode` | 黑名單模式:`1` 完全忽略 / `0` 僅備份安裝包 | `0` |
|
||||
| `blacklist` | 黑名單應用包名列表 | 空 |
|
||||
| `whitelist` | 預裝應用白名單包名列表 | 小米系列預裝 |
|
||||
| `system` | 系統應用白名單包名列表 | Google 系列 |
|
||||
| `Compression_method` | 壓縮算法:`zstd` 或 `tar` | `zstd` |
|
||||
| `rgb_a` / `rgb_b` / `rgb_c` | 終端輸出主色/輔色1/輔色2(256 色代碼) | `220` / `51` / `213` |
|
||||
| `remote_type` | 遠程備份協議:`webdav` / `smb`(留空不啟用) | 空 |
|
||||
| `smb_url` | SMB 伺服器地址 | 空 |
|
||||
| `smb_remote_user` | SMB 認證用戶名 | 空 |
|
||||
| `smb_remote_pass` | SMB 認證密碼 | 空 |
|
||||
| `webdav_url` | WebDAV 伺服器地址 | 空 |
|
||||
| `webdav_remote_user` | WebDAV 認證用戶名 | 空 |
|
||||
| `webdav_remote_pass` | WebDAV 認證密碼 | 空 |
|
||||
| `remote_keep_local` | 上傳成功後本地檔案:`1` 保留 / `0` 刪除 | `0` |
|
||||
| `remote_stream` | 流式備份:`1` 開啟(邊壓邊傳,不佔本機) / `0` 關閉 | `0` |
|
||||
|
||||
---
|
||||
##### 附加說明:如何恢復 以下是關於恢復資料夾內的文件說明?
|
||||
|
||||
## 🚀 使用方式
|
||||
1. 找到恢復資料夾內的appList.txt打開 編輯列表 保存退出
|
||||
|
||||
> 推薦使用 [MT 管理器](https://www.coolapk.com/apk/bin.mt.plus) 執行腳本。若使用 Termux,請勿使用 `tsu`。
|
||||
2. 找到start.sh 給予root音量鍵選擇恢復備份後等待腳本結束即可
|
||||
|
||||
### 備份流程
|
||||
3. start.sh的重新生成應用列表功能可用於刷新appList.txt內的列表 使用時機為當你刪除列表內的任何應用備份時,抑或者是恢復備份提示列表錯誤時
|
||||
|
||||
**Step 1 — 生成應用列表**
|
||||
4. start.sh的終止腳本功能用於突然想要終止腳本或是意外操作時使用 同理備份也有一個,因為腳本無須後台特性不能使用常規手段終結,故此另外寫了一個終止
|
||||
|
||||
解壓腳本後執行 `start.sh`,選擇「**生成應用列表**」。執行完畢後,當前目錄會生成 `appList.txt`。
|
||||
|
||||
**Step 2 — 編輯應用列表**
|
||||
# 關於如何更新腳本?
|
||||
- 目前有三種更新方法,有下列方式
|
||||
- 1.手動將下載的備份腳本zip不解壓縮直接放到腳本任意目錄(不包括tools目錄內)的任意地方執行任何腳本即可更新,腳本將提示
|
||||
- 2.此備份的任何腳本在執行時均會聯網檢測腳本版本,當更新時會自己提示與下載,根據腳本提示操作的即可(conf update=1時生效),腳本聯網僅作為檢查更新用途,無任何非法操作亦或是下發格機
|
||||
- 3.將下載的壓縮包不解壓縮直接放在/storage/emulated/0/Download腳本自動檢測更新,並按照提示操作即可
|
||||
- 4.在QQ群內下載的腳本不解壓縮腳本會自己檢測更新
|
||||
|
||||
打開 `appList.txt`,根據需求調整:
|
||||
- 行首加 `#`:注釋掉該應用,不備份
|
||||
- 行首加 `!`:僅備份安裝包,不備份數據
|
||||
## 關於反饋
|
||||
- 如果使用過程中出現問題,請攜帶截圖並詳細說明問題,建立 [issues](https://github.com/YAWAsau/backup_script/issues)。
|
||||
- 酷安 @[落葉淒涼TEL](http://www.coolapk.com/u/2277637)
|
||||
- QQ組 976613477 很少上 盡量來TG
|
||||
- TG https://t.me/yawasau_script
|
||||
|
||||
**Step 3 — 設置備份選項**
|
||||
## 答疑
|
||||
- 一個shell腳本內為什麼有dex?
|
||||
- dex用來實現腳本難以實現的目的,目前saaid備份恢復,備份恢復運行時權限與ops權限,下載與訪問GitHub api來檢查腳本更新,列出使用者應用名稱與包名,繁體轉簡體均為dex的功能,感謝[Android-DataBackup](https://github.com/XayahSuSuSu/Android-DataBackup) by [XayahSuSuSu](https://github.com/XayahSuSuSu)
|
||||
|
||||
打開 `backup_settings.conf`,根據上方設定說明調整各選項後儲存。
|
||||
## 常見問題
|
||||
|
||||
**Step 4 — 執行備份**
|
||||
Q1:批量備份大量提示失敗怎麼辦?
|
||||
A1:退出腳本,刪除/data/backup_tools,再備份一次
|
||||
|
||||
執行 `start.sh`,選擇「**備份應用**」。備份完成後生成 `Backup_<壓縮算法>_<用戶ID>/` 資料夾。
|
||||
Q2:批量恢復大量提示失敗怎麼辦?
|
||||
A2:退出腳本,按照上面同樣操作。 如果還是錯誤,請建立issues,我幫你排除錯誤
|
||||
|
||||
---
|
||||
Q3:微信/QQ 能不能完美備份&恢復數據?
|
||||
A3:不能保證,有的人說不能有的人說能,所以備份會有提示。 建議用你信賴的備份軟件針對微信/QQ再備份一次,以防丟失重要數據
|
||||
|
||||
### 恢復流程
|
||||
Q4:為什麼部分應用備份很久? 例如王者榮耀、PUBG、原神、微信、QQ。
|
||||
A4:因為連同軟件數據包都給你備份了,例如原神數據包9GB+,當然久到裂開了,恢復也是同理,還要解壓縮數據包
|
||||
|
||||
**Step 1 — 編輯恢復列表**
|
||||
Q5:腳本每次備份都是全新備份嗎?
|
||||
A5;腳本備份時會比對上次備份時的備份SIZE大小 如果有差異就備份,反之忽略備份節省時間
|
||||
|
||||
進入備份資料夾,打開 `appList.txt`,刪除或注釋不需要恢復的應用行。
|
||||
備份腳本耗費了我大量時間與精力 如果你覺得好用,可以捐贈XD
|
||||
.(https://paypal.me/YAWAsau?country.x=TW&locale.x=zh_TW))
|
||||
|
||||
**Step 2 — 執行恢復**
|
||||
|
||||
執行備份資料夾內的 `start.sh`,選擇「**恢復備份**」,等待腳本完成。
|
||||
## 銘謝貢獻
|
||||
- 臭批老k([kmou424](https://github.com/kmou424)):提供部分與驗證函數思路
|
||||
- 屑老方([雄氏老方](http://www.coolapk.com/u/665894)):提供自動更新腳本方案
|
||||
- 胖子老陳(雨季騷年)
|
||||
- XayahSuSuSu([XayahSuSuSu](https://github.com/XayahSuSuSu)):提供App支持,dex支持
|
||||
|
||||
**Step 3 — 注意 SSAID**
|
||||
|
||||
若恢復結束後提示應用存在 SSAID,請**立刻重啟**後再開啟應用。若先開啟應用,Android 會生成新的 SSAID,導致應用白屏或需要重新登入。
|
||||
|
||||
> 💡 備份資料夾內每個應用子目錄都有獨立的 `backup.sh`、`recover.sh`、`upload.sh`,可單獨操作單一應用。
|
||||
|
||||
---
|
||||
|
||||
## ☁️ 遠程備份
|
||||
|
||||
### 設定方式
|
||||
|
||||
編輯 `backup_settings.conf`,SMB 與 WebDAV 地址分開設定,切換協議免重輸:
|
||||
|
||||
```conf
|
||||
remote_type=smb
|
||||
|
||||
smb_url=smb://192.168.1.100/Backup
|
||||
smb_remote_user=用戶名
|
||||
smb_remote_pass=密碼
|
||||
|
||||
webdav_url=http://192.168.1.100:8080/dav/
|
||||
webdav_remote_user=用戶名
|
||||
webdav_remote_pass=密碼
|
||||
|
||||
remote_keep_local=0
|
||||
remote_stream=0
|
||||
```
|
||||
|
||||
| 協議 | 地址格式 | 適用場景 |
|
||||
|------|----------|---------|
|
||||
| SMB | `smb://192.168.1.100/share/` | Windows 共享 / Samba / NAS |
|
||||
| WebDAV | `http://192.168.1.100:8080/dav/` | NAS / Nextcloud / rclone serve |
|
||||
|
||||
### 遠端目錄結構
|
||||
|
||||
腳本在 `remote_url` 下自動建立 `Backup_<壓縮算法>_<用戶ID>/`,與本地結構完全鏡像:
|
||||
|
||||
```
|
||||
smb://NAS/Backup/
|
||||
Backup_zstd_0/
|
||||
1DM+/
|
||||
LINE/
|
||||
Keep記事/
|
||||
wifi/wifi.json
|
||||
tools/
|
||||
start.sh
|
||||
restore_settings.conf
|
||||
appList.txt
|
||||
MT管理器.apk
|
||||
```
|
||||
|
||||
不同用戶(0、999)自動分開到 `Backup_zstd_0/`、`Backup_zstd_999/`,互不衝突。
|
||||
|
||||
### 特性
|
||||
|
||||
- **智能增量** — 多維度比對(版本/大小/權限/SSAID),只上傳本次有變更的 app
|
||||
- **遠端預掃** — 單次連線取全部列表 + 批量下載 json,主循環零網路開銷
|
||||
- **流式備份** — `remote_stream=1` 邊壓縮邊傳輸,數據完全不佔本機空間
|
||||
- **最終核驗** — 備份結束後逐檔核對存在性,缺失檔案列出清單
|
||||
- **連線預檢** — 沒網路時 3 秒內判斷並停用上傳,不卡死腳本
|
||||
- **失敗保護** — 流式上傳失敗不更新遠端 json,確保下輪整體重備
|
||||
|
||||
---
|
||||
|
||||
### 流式備份模式
|
||||
|
||||
`remote_stream=1` 啟用流式備份,數據直接 `tar | zstd | 傳輸`,本機不產生任何備份檔案:
|
||||
|
||||
```conf
|
||||
remote_type=smb
|
||||
smb_url=smb://192.168.1.100/Backup
|
||||
smb_remote_user=用戶名
|
||||
smb_remote_pass=密碼
|
||||
remote_stream=1
|
||||
```
|
||||
|
||||
> ⚠️ 流式模式為全量上傳(無本機校驗),建議搭配穩定的區網環境使用。
|
||||
|
||||
---
|
||||
|
||||
### 從遠端下載備份
|
||||
|
||||
**Step 1** — 主選單選「**列出遠端備份**」,產生 `appList_network.txt`
|
||||
|
||||
**Step 2** — 打開 `appList_network.txt`,用 `#` 註解掉不要下載的 app
|
||||
|
||||
**Step 3** — 主選單選「**從遠端下載備份**」,下載完成後直接執行內附 `start.sh` 恢復
|
||||
|
||||
---
|
||||
|
||||
### 連線測試
|
||||
|
||||
設定完 `backup_settings.conf` 後,主選單選「**測試遠端連線**」:
|
||||
|
||||
```
|
||||
—————— TCP 連線測試 ——————
|
||||
目標: 192.168.1.100:445
|
||||
TCP 連線通過
|
||||
—————— 認證與列目錄測試 ——————
|
||||
SMB 認證通過, share 可存取
|
||||
全部測試通過, 可以開始備份
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 腳本更新方式
|
||||
|
||||
1. **ZIP 放置更新**:將下載的 `.zip` 不解壓,直接放到腳本任意目錄(`tools/` 除外),執行任何腳本即自動更新
|
||||
2. **聯網自動更新**:腳本執行時自動連線 GitHub API 檢查版本(需設置 `update=1`)
|
||||
3. **Download 目錄**:將 `.zip` 放置於 `/storage/emulated/0/Download/`,腳本自動偵測並更新
|
||||
4. **QQ 群下載**:從 QQ 群下載的腳本不解壓,直接放置後執行即可自動更新
|
||||
|
||||
> 🔒 腳本聯網**僅用於檢查更新**,無任何資料收集或非法操作。
|
||||
|
||||
---
|
||||
|
||||
## ❓ 常見問題
|
||||
|
||||
<details>
|
||||
<summary><b>Q1:批量備份/恢復大量提示失敗?</b></summary>
|
||||
|
||||
退出腳本,刪除 `/data/backup_tools/` 目錄後重新執行。若問題持續,請建立 [Issue](https://github.com/YAWAsau/backup_script/issues) 並附上截圖與 log。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q2:微信/QQ 能完美備份恢復嗎?</b></summary>
|
||||
|
||||
無法保證。建議同時使用其他你信賴的備份工具針對微信/QQ 額外備份,以防丟失重要數據。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q3:為什麼部分應用備份很久?</b></summary>
|
||||
|
||||
腳本會一同備份應用的 OBB 數據包,例如原神數據包超過 9GB。可在 `backup_settings.conf` 設置 `Backup_obb_data=0` 跳過 OBB 備份。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q4:腳本每次都是全量備份嗎?</b></summary>
|
||||
|
||||
否。腳本多維度比對上次備份(版本號/數據大小/權限/SSAID),無差異則跳過,節省時間與空間。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q5:為什麼腳本內包含 .dex 檔案?</b></summary>
|
||||
|
||||
`classes.dex` 用於實現 Shell 腳本難以達成的功能:
|
||||
|
||||
- SSAID 備份與恢復
|
||||
- 運行時權限(Runtime Permission)與 ops 權限批量備份恢復
|
||||
- GitHub API 更新版本檢查與下載
|
||||
- 應用名稱與包名查詢
|
||||
- 繁體中文 ↔ 簡體中文自動翻譯
|
||||
- 後台執行模式的推送通知
|
||||
|
||||
感謝 [XayahSuSuSu](https://github.com/XayahSuSuSu) 的 [Android-DataBackup](https://github.com/XayahSuSuSu/Android-DataBackup) 提供 App 支持。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q6:息屏後備份速度變慢?</b></summary>
|
||||
|
||||
這是 Android 內核的 IO 節能機制。建議在 `backup_settings.conf` 設置 `setDisplayPowerMode=1` 開啟偽裝亮屏,或備份期間保持螢幕常亮。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q7:如何單獨備份/恢復/上傳單一應用?</b></summary>
|
||||
|
||||
進入備份資料夾內對應的應用子目錄,直接執行:
|
||||
- `backup.sh` — 單獨備份該 app
|
||||
- `recover.sh` — 單獨恢復該 app
|
||||
- `upload.sh` — 單獨上傳該 app 到遠端
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q8:WebDAV 上傳顯示 HTTP 423 Locked?</b></summary>
|
||||
|
||||
某些雲端網盤的 WebDAV 對大檔有大小限制。建議改用:
|
||||
- 自家 NAS / Windows SMB
|
||||
- rclone serve webdav
|
||||
- 群暉 / Nextcloud
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q9:WebDAV 上傳顯示 HTTP 404?</b></summary>
|
||||
|
||||
請檢查:
|
||||
- `webdav_url` 路徑是否含正確的 webdav 端點(例如 `/dav/` 或 `/remote.php/webdav/`)
|
||||
- 帳號是否有寫入權限
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q10:SMB 提示「找不到 share」?</b></summary>
|
||||
|
||||
- Windows 端確認 SMB 共享已開啟,且網路設成「私人」而非「公用」
|
||||
- 防火牆放行 445 port
|
||||
- 主選單啟動時的 `scan_smb` 會自動列出區網 SMB 主機與 share 名,可對照確認
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q11:沒網路會影響備份嗎?</b></summary>
|
||||
|
||||
不會。腳本啟動時做 TCP 預檢(3 秒內判斷),沒網路時自動停用遠端上傳但**完整保留本地備份**,流程繼續跑完。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q12:流式備份和一般備份有什麼差別?</b></summary>
|
||||
|
||||
| | 一般備份 | 流式備份 |
|
||||
|---|---|---|
|
||||
| 本機空間佔用 | 先壓縮到本機再上傳 | 不佔用(直接傳輸) |
|
||||
| 增量比對 | ✅ 支援 | ✅ 支援 |
|
||||
| 本機完整性校驗 | ✅ 支援 | ❌ 不支援(信任傳輸) |
|
||||
| 適合場景 | 本機空間充足 | 本機空間有限 |
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 📬 問題反饋
|
||||
|
||||
遇到問題請攜帶截圖與 log 檔,透過以下方式反饋:
|
||||
|
||||
- 🐛 [GitHub Issues](https://github.com/YAWAsau/backup_script/issues)
|
||||
- 💬 [Telegram 頻道](https://t.me/yawasau_script)
|
||||
- 🐧 QQ 群:`976613477`
|
||||
- 🧊 酷安:[@落葉淒涼TEL](http://www.coolapk.com/u/2277637)
|
||||
|
||||
---
|
||||
|
||||
## ☕ 支持作者
|
||||
|
||||
備份腳本耗費了大量時間與精力,如果你覺得好用,歡迎贊助支持!
|
||||
|
||||
[](https://paypal.me/YAWAsau?country.x=TW&locale.x=zh_TW)
|
||||
|
||||
---
|
||||
|
||||
## 🙏 銘謝貢獻
|
||||
|
||||
| 貢獻者 | 貢獻內容 |
|
||||
|--------|----------|
|
||||
| [kmou424](https://github.com/kmou424)(臭批老k) | 提供部分驗證函數思路 |
|
||||
| [雄氏老方](http://www.coolapk.com/u/665894)(屑老方) | 提供自動更新腳本方案 |
|
||||
| [sakuradairong](https://github.com/sakuradairong)(雨季騷年/胖子老陳) | 新增 WebDAV / SMB 功能與測試 |
|
||||
| [XayahSuSuSu](https://github.com/XayahSuSuSu) | 提供 App 支持與 dex 功能支持 |
|
||||
|
||||
`文檔編輯:Petit-Abba, YuKongA`
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<sub>GPL-3.0 Licensed · Made with ❤️ by <a href="https://github.com/YAWAsau">YAWAsau</a></sub>
|
||||
</p>
|
||||
`文檔編輯:Petit-Abba, YuKongA`
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
Lo=0
|
||||
|
||||
#後台執行腳本
|
||||
#0不能關閉當前終端,有壓縮速率
|
||||
#1終端有可能完全無顯示,但是log會持續刷新,可直接完全關閉終端
|
||||
0不能關閉當前終端,有壓縮速率
|
||||
1終端有可能完全無顯示,但是log會持續刷新,可直接完全關閉終端
|
||||
background_execution=0
|
||||
|
||||
#腳本語言設置 留空則自動識別系統語言環境並翻譯
|
||||
#1簡體中文 0繁體中文
|
||||
Shell_LANG=
|
||||
Shell_LANG=0
|
||||
|
||||
#備份開始後偽裝亮屏
|
||||
#1開啟 0關閉
|
||||
@@ -19,12 +19,6 @@ setDisplayPowerMode=0
|
||||
#自定義備份文件輸出位置 支持相對路徑(留空則默認當前路徑)
|
||||
Output_path=""
|
||||
|
||||
#自定義備份目錄後綴(留空則不添加後綴)
|
||||
#支持日期時間變量:%yyyymmdd %hhmmss %yyyymmddhhmmss %yyyy %mm %dd
|
||||
#例:_daily → Backup_zstd_0_daily
|
||||
#例:_%yyyymmdd → Backup_zstd_0_20260522
|
||||
Backup_suffix=""
|
||||
|
||||
#自定義applist.txt位置 支持相對路徑(留空則默認當前路徑)
|
||||
list_location=""
|
||||
|
||||
@@ -43,7 +37,7 @@ cdn=1
|
||||
mount_point="rannki|0000-1"
|
||||
|
||||
#使用者(如0 999等用戶,如存在多個用戶留空強制選擇,無多個用戶則默認用戶0不詢問)
|
||||
user=0
|
||||
user=
|
||||
|
||||
#備份模式
|
||||
#1包含數據+安裝包,0僅包安裝包
|
||||
@@ -65,19 +59,23 @@ backup_media=0
|
||||
Background_apps_ignore=0
|
||||
|
||||
#添加自定義備份路徑 例:Download DCIM等文件夾 請使用絕對路徑,請勿刪除""
|
||||
Custom_path=""
|
||||
Custom_path="
|
||||
/storage/emulated/0/Pictures/
|
||||
/storage/emulated/0/Download/
|
||||
/storage/emulated/0/Music
|
||||
/storage/emulated/0/DCIM/
|
||||
#/data/adb
|
||||
"
|
||||
|
||||
#黑名單模式(1完全忽略,不備份 0僅備份安裝包,注意!此選項Backup_Mode=1時黑名單模式才能使用)
|
||||
blacklist_mode=0
|
||||
|
||||
#備份黑名單(備份策略由「黑名單模式」控制,此處只作為黑名單應用列表)
|
||||
blacklist="
|
||||
com.facebook.orca
|
||||
com.facebook.katana
|
||||
com.esunbank
|
||||
com.instagram.barcelona
|
||||
com.instagram.android
|
||||
com.esunbank.ESUNWALLET
|
||||
com.esunbank
|
||||
com.facebook.katana
|
||||
#com.chailease.tw.app.android.ccfappcust"
|
||||
|
||||
#位於data的預裝應用白名單 例:相冊 錄音機 天氣 計算器等(默認屏蔽備份預裝應用,如需備份請添加預裝應用白名單)
|
||||
@@ -118,56 +116,9 @@ com.android.chrome"
|
||||
#zstd擁有良好的壓縮率與速度
|
||||
Compression_method=zstd
|
||||
|
||||
#色彩設定 (256 色 ANSI 編號)
|
||||
#常用值: 39藍 51青 82綠 196紅 208橘 213粉 220黃 165紫
|
||||
#主色 (一般資訊, 預設亮黃)
|
||||
rgb_a=220
|
||||
#輔色1 (提示/進度, 預設亮青)
|
||||
rgb_b=51
|
||||
#輔色2 (強調/變數值, 預設粉紅)
|
||||
rgb_c=213
|
||||
|
||||
#遠程備份類型 (留空不啟用)
|
||||
#推薦 webdav (穩定)
|
||||
#smb 支援 SMB2/SMB3 (本腳本拒絕 SMB1/CIFS, 會自動協商到伺服器支援的最高版本)
|
||||
remote_type=
|
||||
# 保存原始值, 供連線失敗後 (如中途開 WiFi) 重新檢測用
|
||||
_remote_type_orig=
|
||||
|
||||
#遠程地址 (兩種協議分開設定, 切換 remote_type 免重輸)
|
||||
#SMB例: smb://192.168.1.100/backup/
|
||||
smb_url=
|
||||
#認證用戶名
|
||||
smb_remote_user=
|
||||
#認證密碼
|
||||
smb_remote_pass=""
|
||||
#WebDAV例: http://192.168.1.100:8080/dav/
|
||||
webdav_url=
|
||||
#認證用戶名
|
||||
webdav_remote_user=
|
||||
#認證密碼
|
||||
webdav_remote_pass=""
|
||||
|
||||
#流式上傳 (邊壓邊傳, 不佔本機空間)
|
||||
#1 開啟流式: 數據直接壓縮→管道傳到遠端, 本機不留 tar (省空間, 全量上傳, 不做本機校驗/增量)
|
||||
#0 關閉(預設): 先壓到本機→校驗→再上傳 (保留本機檔案, 支援增量)
|
||||
#支援 smb / webdav 兩種 remote_type
|
||||
remote_stream=0
|
||||
|
||||
#流式上傳除錯 (1=失敗時印出 smbclient/curl 的具體錯誤, 用於排查流式失敗原因)
|
||||
_stream_debug=1
|
||||
|
||||
#遠程備份完成後是否保留本地檔案
|
||||
#1保留本地檔案(上傳後不刪除) 0上傳成功後刪除本地檔案
|
||||
remote_keep_local=0
|
||||
|
||||
#邊備份邊上傳 (每備份完一個應用立即上傳,然後刪除本機檔案再備份下一個,以節省本機空間)
|
||||
#1 開啟 0 關閉
|
||||
#開啟後:每個應用備份完成 → 立即上傳遠端 → 上傳成功後刪除本機檔案 → 繼續備份下一個
|
||||
#關閉後:先備份所有應用 → 全部備份完再統一上傳
|
||||
remote_upload_per_app=0
|
||||
|
||||
#log 目錄大小上限 (單位 MB), 達到上限會在啟動時自動清空 log/
|
||||
#留空或設 0 = 關閉自動清理
|
||||
log_max_size_mb=1
|
||||
#主色
|
||||
rgb_a=226
|
||||
#輔色
|
||||
rgb_b=123
|
||||
rgb_c=177
|
||||
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
# Remote Backup (WebDAV & SMB) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Add WebDAV upload and SMB mount backup support to the backup script using built-in busybox tools.
|
||||
|
||||
**Architecture:** Four config variables in `backup_settings.conf` control remote backup. Five new functions in `tools/tools.sh` handle SMB mount/unmount and WebDAV upload. A single hook in `backup_path()` triggers remote setup. The existing EXIT trap is extended to handle remote cleanup.
|
||||
|
||||
**Tech Stack:** Shell script (Android mksh compatible), busybox (wget, mount, umount)
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
| File | Action | Responsibility |
|
||||
|------|--------|---------------|
|
||||
| `backup_settings.conf` | Append | Remote backup configuration |
|
||||
| `tools/tools.sh` | Modify | Remote backup functions + hook + trap extension |
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add remote config to backup_settings.conf
|
||||
|
||||
**Files:**
|
||||
- Modify: `backup_settings.conf`
|
||||
|
||||
- [ ] **Step 1: Append remote backup configuration**
|
||||
|
||||
Append after line 123 (`rgb_c=177`), before the trailing newline at line 124:
|
||||
|
||||
```conf
|
||||
|
||||
#遠程備份類型 (留空不啟用)
|
||||
#webdav 或 smb
|
||||
remote_type=
|
||||
|
||||
#遠程地址
|
||||
#WebDAV例: http://192.168.1.100:8080/dav/
|
||||
#SMB例: //192.168.1.100/backup
|
||||
remote_url=
|
||||
|
||||
#遠程認證用戶名
|
||||
remote_user=
|
||||
|
||||
#遠程認證密碼
|
||||
remote_pass=
|
||||
```
|
||||
|
||||
Edit command: Use the Edit tool to append these lines after `rgb_c=177`.
|
||||
|
||||
- [ ] **Step 2: Verify config file**
|
||||
|
||||
Run: Read `backup_settings.conf` lines 120-140 to confirm the config lines are present.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add backup_settings.conf
|
||||
git commit -m "feat: add remote backup config (remote_type, remote_url, remote_user, remote_pass)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Add remote backup functions to tools.sh
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/tools.sh` (insert after line 465, before `Show_boottime` at line 466)
|
||||
|
||||
- [ ] **Step 1: Insert remote functions**
|
||||
|
||||
Insert after line 465 (`kill_Serve` call) and before line 466 (`Show_boottime()`):
|
||||
|
||||
```sh
|
||||
# -------- 遠程備份功能 --------
|
||||
mount_smb() {
|
||||
local mnt="$TMPDIR/smb_mount"
|
||||
mkdir -p "$mnt"
|
||||
if busybox mount -t cifs "$remote_url" "$mnt" -o "username=$remote_user,password=$remote_pass,iocharset=utf8,vers=2.0" 2>/dev/null; then
|
||||
SMB_MOUNT="$mnt"
|
||||
[[ $(mount | grep "$mnt") != "" ]] || { echoRgb "SMB掛載失敗: $remote_url" "0"; return 1; }
|
||||
echoRgb "SMB已掛載: $remote_url -> $mnt" "1"
|
||||
return 0
|
||||
fi
|
||||
echoRgb "SMB掛載失敗,回退本地備份" "0"
|
||||
return 1
|
||||
}
|
||||
|
||||
umount_smb() {
|
||||
[[ -n $SMB_MOUNT ]] && {
|
||||
busybox umount -l "$SMB_MOUNT" 2>/dev/null
|
||||
rm -rf "$SMB_MOUNT"
|
||||
unset SMB_MOUNT
|
||||
echoRgb "SMB已卸載" "2"
|
||||
}
|
||||
}
|
||||
|
||||
upload_webdav() {
|
||||
local base_url="${remote_url%/}"
|
||||
local auth=$(echo -n "$remote_user:$remote_pass" | busybox base64)
|
||||
local failed=0
|
||||
local list_file="$TMPDIR/.wdav_list"
|
||||
find "$Backup" -type f > "$list_file"
|
||||
while read -r f; do
|
||||
[[ -z $f ]] && continue
|
||||
local rel="${f#$Backup/}"
|
||||
echoRgb "上傳: $rel" "2"
|
||||
if busybox wget -q --method PUT --body-file="$f" --header "Authorization: Basic $auth" "$base_url/$rel" 2>/dev/null; then
|
||||
rm -f "$f"
|
||||
else
|
||||
failed=1
|
||||
break
|
||||
fi
|
||||
done < "$list_file"
|
||||
rm -f "$list_file"
|
||||
if [[ $failed -eq 0 ]]; then
|
||||
echoRgb "WebDAV上傳完成" "1"
|
||||
else
|
||||
echoRgb "WebDAV上傳失敗,本地檔案已保留" "0"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
remote_setup() {
|
||||
[[ -z $remote_type ]] && return
|
||||
|
||||
case $remote_type in
|
||||
smb)
|
||||
mount_smb || return
|
||||
Backup="$SMB_MOUNT/Backup_${Compression_method}_$user"
|
||||
mkdir -p "$Backup"
|
||||
Backup_path="${Backup%/*}"
|
||||
Output_path=""
|
||||
echoRgb "遠程備份目錄: $Backup" "3"
|
||||
;;
|
||||
webdav)
|
||||
echoRgb "WebDAV模式: 備份完成後將自動上傳" "3"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
remote_cleanup() {
|
||||
case $remote_type in
|
||||
smb) umount_smb ;;
|
||||
webdav) upload_webdav ;;
|
||||
esac
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify insertion**
|
||||
|
||||
Run: Read `tools/tools.sh` lines 464-550 to confirm functions are inserted correctly and `Show_boottime()` follows.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/tools.sh
|
||||
git commit -m "feat: add remote backup functions (mount_smb, umount_smb, upload_webdav, remote_setup, remote_cleanup)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Wire hook and trap
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/tools.sh` (line 462 and line 991-993)
|
||||
|
||||
- [ ] **Step 1: Modify EXIT trap in kill_Serve**
|
||||
|
||||
Change line 462 from:
|
||||
```sh
|
||||
trap "rm -rf '$LOCK_DIR'" EXIT
|
||||
```
|
||||
to:
|
||||
```sh
|
||||
trap "rm -rf '$LOCK_DIR'; remote_cleanup" EXIT
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add remote_setup call at end of backup_path()**
|
||||
|
||||
Change line 991-993 from:
|
||||
```sh
|
||||
echoRgb "$hx備份資料夾所使用分區統計如下↓\n -$(df -h "${Backup%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-3),$(NF-2),$(NF-1),$(NF)}' | awk 'END{print "總共:"$1"已用:"$2"剩餘:"$3"使用率:"$4}')檔案系統:$(df -T "$Backup_path" | sed -n 's|% /.*|%|p' | awk '{print $(NF-4)}')\n -備份目錄輸出位置↓\n -$Backup"
|
||||
echoRgb "$outshow" "2"
|
||||
}
|
||||
```
|
||||
to:
|
||||
```sh
|
||||
echoRgb "$hx備份資料夾所使用分區統計如下↓\n -$(df -h "${Backup%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-3),$(NF-2),$(NF-1),$(NF)}' | awk 'END{print "總共:"$1"已用:"$2"剩餘:"$3"使用率:"$4}')檔案系統:$(df -T "$Backup_path" | sed -n 's|% /.*|%|p' | awk '{print $(NF-4)}')\n -備份目錄輸出位置↓\n -$Backup"
|
||||
echoRgb "$outshow" "2"
|
||||
remote_setup
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Verify EXIT trap**
|
||||
|
||||
Run: Read `tools/tools.sh` line 462 to confirm the trap includes `remote_cleanup`.
|
||||
|
||||
- [ ] **Step 4: Verify hook**
|
||||
|
||||
Run: Read `tools/tools.sh` lines 989-994 to confirm `remote_setup` is inside `backup_path()` before the closing `}`.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/tools.sh
|
||||
git commit -m "feat: wire remote backup hook and EXIT trap"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Verify changes
|
||||
|
||||
**Files:**
|
||||
- Check: `tools/tools.sh`, `backup_settings.conf`
|
||||
|
||||
- [ ] **Step 1: Verify git diff**
|
||||
|
||||
```bash
|
||||
git diff master
|
||||
```
|
||||
|
||||
Expected: ~80 lines added across 2 files:
|
||||
- `backup_settings.conf`: +9 lines (remote config)
|
||||
- `tools/tools.sh`: +~70 lines (functions, trap modification, hook), 1 line modified (trap), 1 line added (hook call)
|
||||
|
||||
- [ ] **Step 2: Verify no syntax errors in shell script**
|
||||
|
||||
```bash
|
||||
bash -n tools/tools.sh
|
||||
```
|
||||
|
||||
Ignore "not found" warnings for Android-specific tools — we're only checking syntax.
|
||||
|
||||
- [ ] **Step 3: Verify function names don't collide**
|
||||
|
||||
```bash
|
||||
Select-String -Pattern "^(mount_smb|umount_smb|upload_webdav|remote_setup|remote_cleanup)\b" -Path tools/tools.sh
|
||||
```
|
||||
|
||||
Expected: Each function name appears once (definition only, not duplicated).
|
||||
|
||||
- [ ] **Step 4: Commit any fixes if needed**
|
||||
|
||||
```bash
|
||||
git add -A && git commit -m "fix: address verification issues"
|
||||
```
|
||||
@@ -1,136 +0,0 @@
|
||||
# Remote Backup (WebDAV & SMB) 设计文档
|
||||
|
||||
**日期:** 2026-05-09
|
||||
**目标:** 在不涉及大范围改动的前提下,为脚本增加备份到远程 WebDAV 和 SMB 的功能。
|
||||
|
||||
---
|
||||
|
||||
## 需求
|
||||
|
||||
- 支持直接备份到远程 WebDAV 和 SMB 设备
|
||||
- 使用脚本内置 busybox,不依赖额外工具
|
||||
- 配置通过 `backup_settings.conf`
|
||||
- 最小化对现有代码的改动
|
||||
|
||||
---
|
||||
|
||||
## 架构
|
||||
|
||||
### 配置
|
||||
|
||||
在 `backup_settings.conf` 末尾追加 4 个配置项:
|
||||
|
||||
```conf
|
||||
#远程备份类型 (留空不启用)
|
||||
#webdav 或 smb
|
||||
remote_type=
|
||||
|
||||
#远程地址
|
||||
#WebDAV例: http://192.168.1.100:8080/dav/
|
||||
#SMB例: //192.168.1.100/backup
|
||||
remote_url=
|
||||
|
||||
#远程认证用户名
|
||||
remote_user=
|
||||
|
||||
#远程认证密码
|
||||
remote_pass=
|
||||
```
|
||||
|
||||
### 新增函数(tools/tools.sh)
|
||||
|
||||
所有函数定义在 `tools/tools.sh` 中,靠近 `kill_Serve()` 之后的位置(~line 465)。
|
||||
|
||||
| 函数 | 行数 | 说明 |
|
||||
|------|------|------|
|
||||
| `mount_smb()` | ~10 | busybox `mount -t cifs` 挂载 SMB 到 `$TMPDIR/smb_mount` |
|
||||
| `umount_smb()` | ~5 | 安全卸载 SMB 挂载点 |
|
||||
| `upload_webdav()` | ~20 | 遍历备份目录,用 busybox `wget --method PUT` 逐个上传 |
|
||||
| `remote_setup()` | ~25 | 入口函数:SMB 挂载后设置 Output_path;WebDAV 注册 trap |
|
||||
| `remote_cleanup()` | ~15 | SMB 卸载 / WebDAV 上传+清理本地文件 |
|
||||
|
||||
### Hook 点
|
||||
|
||||
在 `backup_path()` 调用之后(line 991 附近)插入一行:
|
||||
|
||||
```sh
|
||||
remote_setup
|
||||
```
|
||||
|
||||
### 流程
|
||||
|
||||
```
|
||||
backup_path() # 确定本地备份路径 $Backup
|
||||
↓
|
||||
remote_setup() # 如启用远程,挂载 SMB 或准备 WebDAV uploader
|
||||
↓
|
||||
[SMB] [WebDAV]
|
||||
mount_smb() 正常本地备份
|
||||
$Backup → mount ↓
|
||||
正常备份 upload_webdav() (at EXIT)
|
||||
↓ rm -rf 本地副本
|
||||
umount_smb()
|
||||
```
|
||||
|
||||
### SMB 实现细节
|
||||
|
||||
```sh
|
||||
mount_smb() {
|
||||
local mnt="$TMPDIR/smb_mount"
|
||||
mkdir -p "$mnt"
|
||||
busybox mount -t cifs "$remote_url" "$mnt" -o "username=$remote_user,password=$remote_pass,iocharset=utf8"
|
||||
# 若 busybox mount 不支持 cifs,尝试系统 mount
|
||||
# 失败则返回非0,remote_setup 回退到本地备份
|
||||
}
|
||||
```
|
||||
|
||||
挂载成功后,`remote_setup()` 将 `$Backup` 覆盖为 `$mnt/Backup_${Compression_method}_$user`,后续备份流程无需修改。
|
||||
|
||||
### WebDAV 实现细节
|
||||
|
||||
```sh
|
||||
upload_webdav() {
|
||||
local base_url="${remote_url%/}"
|
||||
# 创建远程目录 (MKCOL)
|
||||
busybox wget -q --method MKCOL --header "Authorization: ..." "$base_url/..." 2>/dev/null
|
||||
# 遍历上传
|
||||
find "$Backup" -type f | while read f; do
|
||||
busybox wget -q --method PUT --body-file="$f" --header "Authorization: ..." "$base_url/..."
|
||||
# 上传成功才删除本地文件
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
备份先写到本地(正常流程),EXIT 时 `remote_cleanup()` 触发上传,成功后删除本地副本。
|
||||
|
||||
### 错误处理
|
||||
|
||||
- SMB 挂载失败:回退到本地备份(echo 警告,继续执行)
|
||||
- WebDAV 上传失败:保留本地文件不删除(保留数据,用户手动排查)
|
||||
- SMB 连接断开:trap EXIT 确保 `umount_smb()` 被调用
|
||||
- 网络异常:wget 自带超时,不会永久阻塞
|
||||
|
||||
### 恢复支持(后续迭代)
|
||||
|
||||
本次仅实现备份到远程。恢复从远程可在后续 PR 中补充,方向是对称实现:
|
||||
- SMB:挂载后从挂载点恢复
|
||||
- WebDAV:wget 下载到临时目录后恢复
|
||||
|
||||
---
|
||||
|
||||
## 改动范围
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `backup_settings.conf` | +9 行配置 |
|
||||
| `tools/tools.sh` | +75 行(5 个函数 + 1 个 hook 调用) |
|
||||
|
||||
---
|
||||
|
||||
## 测试要点
|
||||
|
||||
1. SMB 挂载成功 → 备份文件出现在远程共享目录
|
||||
2. SMB 挂载失败 → 回退到本地备份,有警告提示
|
||||
3. WebDAV 上传成功 → 远程有备份文件,本地副本已清理
|
||||
4. WebDAV 上传失败 → 本地文件保留,有错误提示
|
||||
5. EXIT 时 SMB 挂载点被正确卸载(无残留)
|
||||
7
start.sh
7
start.sh
@@ -2,11 +2,8 @@
|
||||
if [ -f "${0%/*}/tools/tools.sh" ]; then
|
||||
MODDIR="${0%/*}"
|
||||
conf_path="${0%/*}/backup_settings.conf"
|
||||
[ ! -f "${0%/*}/backup_settings.conf" ] && . "${0%/*}/tools/tools.sh"
|
||||
else
|
||||
echo "${0%/*}/tools/tools.sh遺失"
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "${0%/*}/log" 2>/dev/null
|
||||
logfile="${0%/*}/log/log_$(date +%Y-%m-%d_%H-%M).txt"
|
||||
. "${0%/*}/tools/tools.sh" | tee "$logfile"
|
||||
sed -i "$(printf 's/\[[0-9;]*m//g')" "$logfile"
|
||||
. "${0%/*}/tools/tools.sh" | tee "${0%/*}/log_$(date +%Y-%m-%d_%H-%M).txt"
|
||||
|
||||
@@ -158,13 +158,6 @@
|
||||
"2512BPNDAI" "Xiaomi 17 Ultra 印度版"
|
||||
"25128PNA1C" "Xiaomi 17 Ultra by Leica 國行版 (衛星通訊)"
|
||||
"25128PNA1G" "Leica Leitzphone powered by Xiaomi 國際版"
|
||||
"2605EPN8EC" "Xiaomi 17 Max"
|
||||
"M531DA" "Xiaomi 17T 國行版"
|
||||
"2602DPT53G" "Xiaomi 17T 國際版"
|
||||
"2602DPT53I" "Xiaomi 17T 印度版"
|
||||
"M025EC" "Xiaomi 17T Pro 國行版"
|
||||
"2602EPTC0G" "Xiaomi 17T Pro 國際版"
|
||||
"2602EPTC0R" "Xiaomi 17T Pro 日本版 (無鎖)"
|
||||
"2014616" "小米 Note 雙網通版"
|
||||
"2014619" "小米 Note 全網通版"
|
||||
"2014618" "小米 Note 移動合約版"
|
||||
@@ -434,7 +427,6 @@
|
||||
"2508CRN2BI" "REDMI 15C 5G 印度版"
|
||||
"2508CRN2BR" "REDMI 15C 5G 日本版"
|
||||
"25082RNC1C" "REDMI 15R 5G"
|
||||
"2602BRNA4I" "REDMI 15A 5G 印度版"
|
||||
"2014018" "紅米 Note 聯通 3G 標準版"
|
||||
"2013121" "紅米 Note 聯通 3G 增強版"
|
||||
"2014017" "紅米 Note 移動 3G 標準版"
|
||||
@@ -600,7 +592,7 @@
|
||||
"24117RN76E" "Redmi Note 14 歐洲版"
|
||||
"24117RN76L" "Redmi Note 14 拉美版"
|
||||
"24117RN76O" "Redmi Note 14 國際版 (NFC)"
|
||||
"24094RAD4C" "Redmi Note 14 5G 國行版 / REDMI Note 15R Pro 國行版"
|
||||
"24094RAD4C" "Redmi Note 14 5G 國行版"
|
||||
"24094RAD4I" "Redmi Note 14 5G 印度版 / Redmi Note 14 SE 5G 印度版"
|
||||
"24094RAD4G" "Redmi Note 14 5G 國際版"
|
||||
"2502FRA65G" "Redmi Note 14S 國際版"
|
||||
@@ -619,7 +611,6 @@
|
||||
"25098RA98E" "REDMI Note 15 5G 歐洲版"
|
||||
"25098RA98I" "REDMI Note 15 5G 印度版"
|
||||
"25098RA98T" "REDMI Note 15 5G 沙特版"
|
||||
"26022PCACI" "REDMI Note 15 SE 5G 印度版"
|
||||
"25100RA69G" "REDMI Note 15 Pro 國際版"
|
||||
"25080RABDC" "REDMI Note 15 Pro 國行版"
|
||||
"25080RABDG" "REDMI Note 15 Pro 5G 國際版"
|
||||
@@ -638,8 +629,7 @@
|
||||
"24069RA21C" "Redmi Turbo 3"
|
||||
"24129RT7CC" "REDMI Turbo 4"
|
||||
"25053RT47C" "REDMI Turbo 4 Pro"
|
||||
"2511FRT34C" "REDMI Turbo 5 國行版"
|
||||
"2606FRT34I" "REDMI Turbo 5 印度版"
|
||||
"2511FRT34C" "REDMI Turbo 5"
|
||||
"2602BRT18C" "REDMI Turbo 5 Max"
|
||||
"M1903F10A" "Redmi K20 全網通版"
|
||||
"M1903F10C" "Redmi K20 運營商全網通版"
|
||||
@@ -682,7 +672,6 @@
|
||||
"2510DRK44C" "REDMI K90"
|
||||
"25102RKBEC" "REDMI K90 Pro Max"
|
||||
"25102RK69C" "REDMI K90 Pro Max 冠軍版"
|
||||
"2604FRK1EC" "REDMI K90 Max"
|
||||
"2016020" "紅米 Pro 標準版"
|
||||
"2016021" "紅米 Pro 高配版 / 尊享版"
|
||||
"M1803E6E" "紅米 S2 全網通版"
|
||||
@@ -715,14 +704,8 @@
|
||||
"25028RN03Y" "REDMI A5 國際版"
|
||||
"25028RN03I" "REDMI A5 印度版"
|
||||
"25028RN03L" "REDMI A5 拉美版"
|
||||
"26020RNB4A" "REDMI A7 國際版"
|
||||
"26020RNB4I" "REDMI A7 印度版"
|
||||
"26020RNB4L" "REDMI A7 拉美版"
|
||||
"25128RN17Y" "REDMI A7 Pro 國際版"
|
||||
"25128RN17I" "REDMI A7 Pro 印度版"
|
||||
"25128RN17L" "REDMI A7 Pro 拉美版"
|
||||
"26020RN1AC" "REDMI R70 5G 國行版 / REDMI R70m 5G 國行版"
|
||||
"26020RN1AI" "REDMI A7 Pro 5G 印度版"
|
||||
"22081283C" "Redmi Pad 國行版"
|
||||
"22081283G" "Redmi Pad 國際版"
|
||||
"23073RPBFC" "Redmi Pad SE 國行版"
|
||||
@@ -753,13 +736,7 @@
|
||||
"2509BRP2DC" "REDMI Pad 2 Pro 5G 國行版"
|
||||
"2509BRP2DG" "REDMI Pad 2 Pro 5G 國際版"
|
||||
"2509BRP2DI" "REDMI Pad 2 Pro 5G 印度版"
|
||||
"2603ARP14C" "REDMI Pad 2 SE 國行版"
|
||||
"2603ARP14G" "REDMI Pad 2 9.7 國際版"
|
||||
"2604ERP4DC" "REDMI Pad 2 SE 4G 國行版"
|
||||
"2604ERP4DG" "REDMI Pad 2 9.7 4G 國際版"
|
||||
"2604ERP4DI" "REDMI Pad 2 9.7 4G 印度版"
|
||||
"25079RPDCC" "REDMI K Pad"
|
||||
"26048RP6AC" "REDMI K Pad 2"
|
||||
"M1805E10A" "POCO F1"
|
||||
"M2004J11G" "POCO F2 Pro 國際版"
|
||||
"M2012K11AG" "POCO F3 國際版"
|
||||
@@ -831,8 +808,6 @@
|
||||
"25062PC34E" "POCO M7 歐洲版"
|
||||
"24108PCE2I" "POCO M7 5G 印度版"
|
||||
"25057PC09I" "POCO M7 Plus 5G 印度版"
|
||||
"26067PC09G" "POCO M8s 5G 國際版"
|
||||
"26067PC09E" "POCO M8s 5G 歐洲版"
|
||||
"2409FPCC4G" "POCO M7 Pro 5G 國際版"
|
||||
"25118PC98G" "POCO M8 5G 國際版"
|
||||
"25118PC98I" "POCO M8 5G 印度版"
|
||||
@@ -856,10 +831,6 @@
|
||||
"2410FPCC5G" "POCO C75 國際版"
|
||||
"2410FPCC5I" "POCO C75 印度版"
|
||||
"24116PCC1I" "POCO C75 5G 印度版"
|
||||
"25128PC17Y" "POCO C81 Pro 國際版"
|
||||
"25128PC17L" "POCO C81 Pro 拉美版"
|
||||
"25128PC17I" "POCO C81 印度版"
|
||||
"26020PCB4I" "POCO C81x 印度版"
|
||||
"25078PC3EG" "POCO C85 國際版"
|
||||
"25078PC3EE" "POCO C85 歐洲版"
|
||||
"2508CPC2BI" "POCO C85 5G 印度版"
|
||||
@@ -868,12 +839,9 @@
|
||||
"24074PCD2I" "POCO Pad 5G 印度版"
|
||||
"25099RP08G" "POCO Pad X1 國際版"
|
||||
"2509ARPBDG" "POCO Pad M1 國際版"
|
||||
"2603APC14G" "POCO Pad C1 國際版"
|
||||
"FYJ01QP" "小米米家翻譯機"
|
||||
"21051191C" "CyberDog 仿生四足機器人"
|
||||
"2405AVPB7C" "小米澎湃智能座艙 (第一代 SU7/SU7 Ultra)"
|
||||
"25033VP3FC" "小米澎湃智能座艙 (YU7/新一代 SU7)"
|
||||
"2503CVPC6C" "小米汽車後排移動控制屏"
|
||||
"2503CVPC6C" "Xiaomi YU7 後排移動控制屏"
|
||||
"MI-ONE" "Mi 1 Lite"
|
||||
"2109119BC" "Xiaomi Civi China"
|
||||
"2109119BC" "Xiaomi Civi 1S China"
|
||||
@@ -1534,7 +1502,6 @@
|
||||
"SCG34" "Galaxy Z Fold7 Japan (au)"
|
||||
"SC-56F" "Galaxy Z Fold7 Japan (NTT Docomo)"
|
||||
"SM-F968B" "Galaxy Z TriFold Global"
|
||||
"SM-F968U1" "Galaxy Z TriFold US Unlocked"
|
||||
"SM-F968N" "Galaxy Z TriFold South Korea"
|
||||
"SM-F700F" "Galaxy Z Flip Global"
|
||||
"SM-F700U" "Galaxy Z Flip US Carrier"
|
||||
@@ -2626,13 +2593,8 @@
|
||||
"CPH2769" "一加 15R 歐洲版 / 國際版"
|
||||
"CPH2771" "一加 15R 北美版"
|
||||
"PLZ110" "一加 15T 國行版"
|
||||
"PMB110" "一加 Ace 6 至尊版 國行版"
|
||||
"PLU110" "一加 Turbo 6 國行版"
|
||||
"CPH2793" "一加 Nord 6 印度版"
|
||||
"CPH2795" "一加 Nord 6 國際版"
|
||||
"PLY110" "一加 Turbo 6V 國行版"
|
||||
"CPH2805" "一加 Nord CE 6 印度版"
|
||||
"CPH2807" "一加 Nord CE 6 國際版"
|
||||
"AC2001" "一加 Nord 印度版"
|
||||
"AC2003" "一加 Nord 歐洲版 / 國際版"
|
||||
"DN2101" "一加 Nord 2 印度版"
|
||||
@@ -2658,7 +2620,6 @@
|
||||
"CPH2621" "一加 Nord CE 4 Lite 歐洲版 / 國際版"
|
||||
"CPH2717" "一加 Nord CE 5 印度版"
|
||||
"CPH2719" "一加 Nord CE 5 國際版"
|
||||
"CPH2943" "一加 Nord CE 6 Lite 印度版"
|
||||
"BE2025" "一加 Nord N10 Metro 版"
|
||||
"BE2026" "一加 Nord N10 北美版"
|
||||
"BE2029" "一加 Nord N10 歐洲版 / 國際版"
|
||||
@@ -2688,8 +2649,6 @@
|
||||
"OPD2504" "一加平板 Go 2 Wi-Fi"
|
||||
"OPD2505" "一加平板 Go 2 5G"
|
||||
"OPD2508" "一加平板 2 (2025) 國行版"
|
||||
"OPD2513" "一加平板 3 Pro 國行版"
|
||||
"OPD2514" "一加平板 4 國際版"
|
||||
"W101IN" "一加 Band 印度版"
|
||||
"OPBBE221" "一加 Nord Watch"
|
||||
"W301CN" "一加手表 國行版 / Cyberpunk 2077 限定版"
|
||||
@@ -2701,7 +2660,6 @@
|
||||
"OPWW251" "一加手表 3 國行版~"
|
||||
"OPWWE251" "一加手表 3 國際版"
|
||||
"OPWE242" "一加手表 3 43mm 國際版"
|
||||
"OPWWE261" "一加手表 4 國際版"
|
||||
"E6683" "Xperia Z5 dual"
|
||||
"E6883" "Xperia Z5 Premium"
|
||||
"F8332" "Xperia XZ"
|
||||
@@ -2747,7 +2705,7 @@
|
||||
"RMX3310" "真我 GT2"
|
||||
"RMX3300" "真我 GT2 Pro"
|
||||
"RMX3551" "真我 GT2 大師探索版"
|
||||
"RMX3820" "真我 GT5"
|
||||
"RMX3820" "真我 GT5 150W"
|
||||
"RMX3823" "真我 GT5 240W"
|
||||
"RMX3888" "真我 GT5 Pro"
|
||||
"RMX3800" "真我 GT6"
|
||||
@@ -2760,15 +2718,15 @@
|
||||
"RMX3350" "真我 GT Neo 閃速版"
|
||||
"RMX3370" "真我 GT Neo2 / 真我 GT Neo2 龍珠定制版"
|
||||
"RMX3357" "真我 GT Neo2T"
|
||||
"RMX3560" "真我 GT Neo3"
|
||||
"RMX3560" "真我 GT Neo3 80W"
|
||||
"RMX3562" "真我 GT Neo3 150W / 真我 GT Neo3 火影限定版"
|
||||
"RMX3706" "真我 GT Neo5"
|
||||
"RMX3706" "真我 GT Neo5 150W"
|
||||
"RMX3708" "真我 GT Neo5 240W"
|
||||
"RMX3700" "真我 GT Neo5 SE"
|
||||
"RMX3852" "真我 GT Neo6 / 真我 GT Neo6 《完美世界》動畫雲曦限定禮盒"
|
||||
"RMX3850" "真我 GT Neo6 SE"
|
||||
"RMX5060" "真我 Neo7 / 真我 Neo7 《畫江湖之不良人》限定禮盒"
|
||||
"RMX5062" "真我 Neo7 Turbo / 真我 Neo7 Turbo AI版"
|
||||
"RMX5062" "真我 Neo7 Turbo"
|
||||
"RMX5080" "真我 Neo7 SE"
|
||||
"RMX5071" "真我 Neo7x"
|
||||
"RMX8899" "真我 Neo8"
|
||||
@@ -2863,9 +2821,9 @@
|
||||
"RMX3491" "realme 9i India / Global"
|
||||
"RMX3492" "realme 9i"
|
||||
"RMX3493" "realme 9i EU"
|
||||
"RMX3612" "realme 9i 5G"
|
||||
"RMX3388" "realme 9 5G India"
|
||||
"RMX3474" "realme 9 5G EU"
|
||||
"RMX3612" "realme 9i 5G"
|
||||
"RMX3471" "realme 9 Pro 5G India"
|
||||
"RMX3472" "realme 9 Pro 5G Global"
|
||||
"RMX3392" "realme 9 Pro+ 5G India"
|
||||
@@ -2905,7 +2863,6 @@
|
||||
"RMX5171" "realme 16 5G"
|
||||
"RMX5120" "realme 16 Pro 5G"
|
||||
"RMX5131" "realme 16 Pro+ 5G"
|
||||
"RMX5268" "realme 16T 5G"
|
||||
"RMX1921" "realme XT Global"
|
||||
"RMX1922" "realme XT India"
|
||||
"RMX1992" "realme X2 India"
|
||||
@@ -2975,11 +2932,8 @@
|
||||
"RMX5566" "realme C85"
|
||||
"RMX5253" "realme C85 5G"
|
||||
"RMX5555" "realme C85 Pro"
|
||||
"RMX5258" "realme C100 5G"
|
||||
"RMX5377" "realme C100i"
|
||||
"RMX3938" "realme Note 60x"
|
||||
"RMX5313" "realme Note 70 / realme Note 70T"
|
||||
"RMX5388" "realme Note 80"
|
||||
"RMX1833" "realme U1"
|
||||
"RMX3870" "realme P1 5G"
|
||||
"RMX3844" "realme P1 Pro 5G"
|
||||
@@ -3133,10 +3087,8 @@
|
||||
"V2430A" "vivo S20 Pro"
|
||||
"V2464A" "vivo S30"
|
||||
"V2465A" "vivo S30 Pro mini"
|
||||
"V2528A" "vivo S50 / vivo S50t"
|
||||
"V2528A" "vivo S50"
|
||||
"V2527A" "vivo S50 Pro mini"
|
||||
"V2571A" "vivo S60"
|
||||
"V2572A" "vivo S60 元氣版"
|
||||
"V1901A" "vivo Y3 全網通版"
|
||||
"V1901T" "vivo Y3 移動全網通版"
|
||||
"V1930A" "vivo Y3 標準版 全網通版"
|
||||
@@ -3180,9 +3132,8 @@
|
||||
"V2123A" "vivo Y53s (t2 版) 5G"
|
||||
"V2045A" "vivo Y54s 5G"
|
||||
"V2164A" "vivo Y55s 5G"
|
||||
"V2559A" "vivo Y60 5G / vivo Y6t 5G"
|
||||
"V2559A" "vivo Y60 5G"
|
||||
"V2559BA" "vivo Y60m 5G"
|
||||
"V2532BA" "vivo Y6m"
|
||||
"V1731CA" "vivo Y71s"
|
||||
"V2102A" "vivo Y71t 5G"
|
||||
"V2164PA" "vivo Y73t 5G"
|
||||
@@ -3217,9 +3168,7 @@
|
||||
"V2452GA" "vivo Y300 GT"
|
||||
"V2506A" "vivo Y500"
|
||||
"V2516A" "vivo Y500 Pro"
|
||||
"V2531A" "vivo Y500i / vivo Y500s / vivo Y6"
|
||||
"V2561A" "vivo Y600 Pro"
|
||||
"V2553A" "vivo Y600 Turbo"
|
||||
"V2531A" "vivo Y500i / vivo Y500s"
|
||||
"V2115A" "vivo T1"
|
||||
"V2199GA" "vivo T2"
|
||||
"V2188A" "vivo T2x"
|
||||
@@ -3264,7 +3213,6 @@
|
||||
"V2408A" "iQOO 13"
|
||||
"V2505A" "iQOO 15"
|
||||
"V2546A" "iQOO 15 Ultra"
|
||||
"V2564A" "iQOO 15T"
|
||||
"V1914A" "iQOO Neo 全網通版"
|
||||
"V1914T" "iQOO Neo 移動全網通版"
|
||||
"V1936A" "iQOO Neo 855 版 全網通版"
|
||||
@@ -3386,7 +3334,6 @@
|
||||
"V2515" "vivo X300"
|
||||
"V2514" "vivo X300 Pro"
|
||||
"V2542" "vivo X300 FE"
|
||||
"V2562" "vivo X300 Ultra"
|
||||
"vivo 1819" "vivo V15"
|
||||
"vivo 1920" "vivo V17"
|
||||
"vivo 1907" "vivo V17 Neo"
|
||||
@@ -3427,17 +3374,17 @@
|
||||
"V2418" "vivo V40 Lite 5G Indonesia"
|
||||
"V2337" "vivo V40 SE 5G"
|
||||
"V2451" "vivo V50"
|
||||
"V2504" "vivo V50 Pro"
|
||||
"V2428" "vivo V50e"
|
||||
"V2441" "vivo V50 Lite"
|
||||
"V2453" "vivo V50 Lite 5G"
|
||||
"V2512" "vivo V60"
|
||||
"V2549" "vivo V60 Lite"
|
||||
"V2530" "vivo V60 Lite"
|
||||
"V2529" "vivo V60 Lite 5G"
|
||||
"V2513" "vivo V60e"
|
||||
"V2540" "vivo V70"
|
||||
"V2548" "vivo V70 Elite"
|
||||
"V2558" "vivo V70 FE"
|
||||
"V2637" "vivo V70 Lite 5G"
|
||||
"vivo 1920_20" "vivo S1 Pro"
|
||||
"V2168" "vivo T1"
|
||||
"V2157" "vivo T1 5G"
|
||||
@@ -3457,11 +3404,8 @@
|
||||
"V2509" "vivo T4 Lite 5G"
|
||||
"V2518" "vivo T4R 5G"
|
||||
"V2510" "vivo T4 Pro"
|
||||
"V2504" "vivo T4 Ultra"
|
||||
"V2568" "vivo T5 Pro"
|
||||
"V2545" "vivo T5x 5G"
|
||||
"V2603" "vivo T5 Lite 5G"
|
||||
"V2557" "vivo T5 Lite 44W 5G"
|
||||
"vivo 2015_21" "vivo Y1s"
|
||||
"V2044" "vivo Y3s"
|
||||
"vivo 1902" "vivo Y5"
|
||||
@@ -3539,8 +3483,7 @@
|
||||
"V2036_21" "vivo Y31"
|
||||
"V2521" "vivo Y31 5G"
|
||||
"V2534" "vivo Y31 Pro 5G"
|
||||
"V2636" "vivo Y31d"
|
||||
"V2543" "vivo Y31d Pro"
|
||||
"V2611" "vivo Y31d"
|
||||
"V2533" "vivo Y31e 5G"
|
||||
"V2057" "vivo Y33"
|
||||
"V2109-EG" "vivo Y33A"
|
||||
@@ -3553,7 +3496,7 @@
|
||||
"V2447" "vivo Y39 5G"
|
||||
"V2035" "vivo Y51"
|
||||
"V2031_21" "vivo Y51s"
|
||||
"V2613" "vivo Y51 Pro 5G"
|
||||
"V2544" "vivo Y51 Pro 5G"
|
||||
"V2053" "vivo Y52 5G"
|
||||
"V2058" "vivo Y53s"
|
||||
"V2154" "vivo Y55"
|
||||
@@ -3623,19 +3566,12 @@
|
||||
"I2505" "iQOO Z10R 5G Global"
|
||||
"I2512" "iQOO Z11 5G"
|
||||
"I2507" "iQOO Z11x 5G"
|
||||
"I2510" "iQOO Z11 Lite 5G"
|
||||
"I2515" "iQOO Z11 Lite 44W 5G"
|
||||
"J2505" "JOVI X300 FE"
|
||||
"J2510" "JOVI X300 Ultra"
|
||||
"V2427" "JOVI V50"
|
||||
"V2440" "JOVI V50 Lite 5G"
|
||||
"J2507" "JOVI V70 5G"
|
||||
"V2454" "JOVI Y19s"
|
||||
"J2503" "JOVI Y21"
|
||||
"J2508" "JOVI Y21 5G"
|
||||
"V2445" "JOVI Y29"
|
||||
"V2459" "JOVI Y29s 5G"
|
||||
"J2506" "JOVI Y31"
|
||||
"V2444" "JOVI Y39 5G"
|
||||
"PAFM00" "OPPO Find X 標準版 全網通版"
|
||||
"PAFT00" "OPPO Find X 標準版 移動版"
|
||||
@@ -3731,8 +3667,6 @@
|
||||
"PLW110" "OPPO Reno15"
|
||||
"PLV110" "OPPO Reno15 Pro"
|
||||
"PMD110" "OPPO Reno15c"
|
||||
"PMM110" "OPPO Reno16"
|
||||
"PMK110" "OPPO Reno16 Pro"
|
||||
"PCLM10" "OPPO Reno Ace"
|
||||
"PDHM00" "OPPO Ace2"
|
||||
"PACM00" "OPPO R15 全網通版"
|
||||
@@ -3791,7 +3725,6 @@
|
||||
"PEHT00" "OPPO A93 移動版"
|
||||
"PFGM00" "OPPO A93s"
|
||||
"PELM00" "OPPO A95"
|
||||
"PFUM10" "OPPO A96"
|
||||
"PHA120" "OPPO A96"
|
||||
"PFTM10" "OPPO A97"
|
||||
"PHS110" "OPPO A1 5G (2023)"
|
||||
@@ -3815,9 +3748,8 @@
|
||||
"PLN110" "OPPO A6 Pro"
|
||||
"PKW120" "OPPO A6i"
|
||||
"PLT120" "OPPO A6s / OPPO A6i+ / OPPO A6k"
|
||||
"PMT110" "OPPO A6s Pro"
|
||||
"PLT130" "OPPO A6v"
|
||||
"PLT140" "OPPO A6x / OPPO A6m"
|
||||
"PLT140" "OPPO A6x"
|
||||
"PMC110" "OPPO A6c"
|
||||
"PBCM30" "OPPO K1"
|
||||
"PCGM00" "OPPO K3 全網通版"
|
||||
@@ -3840,8 +3772,6 @@
|
||||
"PLD110" "OPPO K12s"
|
||||
"PLM110" "OPPO K13 Turbo"
|
||||
"PLE110" "OPPO K13 Turbo Pro"
|
||||
"PMH110" "OPPO K15 Pro"
|
||||
"PMG110" "OPPO K15 Pro+"
|
||||
"OPD2101" "OPPO Pad"
|
||||
"OPD2102" "OPPO Pad Air"
|
||||
"OPD2201" "OPPO Pad 2"
|
||||
@@ -3852,9 +3782,6 @@
|
||||
"OPD2417" "OPPO Pad SE"
|
||||
"OPD2506" "OPPO Pad 5"
|
||||
"OPD2501" "OPPO Pad Air5"
|
||||
"OPD2511" "OPPO Pad 5 Pro"
|
||||
"OPD2515" "OPPO Pad Mini"
|
||||
"OPD2601" "OPPO Pad 6"
|
||||
"OB19O1" "OPPO Band 運動版"
|
||||
"OB19O3" "OPPO Band 運動版 (國際版)"
|
||||
"OB19O7" "OPPO Band 活力版"
|
||||
@@ -3883,7 +3810,6 @@
|
||||
"OWW251" "OPPO Watch X2"
|
||||
"OWW262" "OPPO Watch S"
|
||||
"OWW261" "OPPO Watch X3"
|
||||
"OWW263" "OPPO Watch X3 Mini"
|
||||
"CPH1875" "OPPO Find X"
|
||||
"CPH2023" "OPPO Find X2"
|
||||
"CPH2025" "OPPO Find X2 Pro"
|
||||
@@ -3902,8 +3828,6 @@
|
||||
"CPH2797" "OPPO Find X9"
|
||||
"OPG07" "OPPO Find X9 (KDDI)"
|
||||
"CPH2791" "OPPO Find X9 Pro"
|
||||
"CPH2873" "OPPO Find X9s"
|
||||
"CPH2841" "OPPO Find X9 Ultra"
|
||||
"CPH2439" "OPPO Find N2"
|
||||
"CPH2437" "OPPO Find N2 Flip"
|
||||
"CPH2499" "OPPO Find N3"
|
||||
@@ -3994,8 +3918,6 @@
|
||||
"CPH2781" "OPPO F31 5G"
|
||||
"CPH2763" "OPPO F31 Pro 5G"
|
||||
"CPH2757" "OPPO F31 Pro+ 5G"
|
||||
"CPH2777" "OPPO F33 5G"
|
||||
"CPH2835" "OPPO F33 Pro 5G"
|
||||
"CPH1835" "OPPO R15"
|
||||
"CPH1833" "OPPO R15 Pro"
|
||||
"CPH1879" "OPPO R17"
|
||||
@@ -4029,10 +3951,8 @@
|
||||
"CPH2831" "OPPO A6 5G Global"
|
||||
"CPH2785" "OPPO A6 5G India"
|
||||
"CPH2799" "OPPO A6 Pro"
|
||||
"CPH2827" "OPPO A6 Pro 5G India"
|
||||
"CPH2815" "OPPO A6s"
|
||||
"CPH2789" "OPPO A6s 5G Global"
|
||||
"CPH2889" "OPPO A6s 5G India"
|
||||
"CPH2789" "OPPO A6s 5G"
|
||||
"CPH2819" "OPPO A6x"
|
||||
"CPH2783" "OPPO A6x 5G Global"
|
||||
"CPH2823" "OPPO A6x 5G India"
|
||||
@@ -4040,7 +3960,6 @@
|
||||
"CPH2853" "OPPO A6t 5G"
|
||||
"CPH2849" "OPPO A6t Pro"
|
||||
"CPH2851" "OPPO A6t Pro 5G"
|
||||
"CPH2895" "OPPO A6c"
|
||||
"CPH1905" "OPPO A7"
|
||||
"CPH1903" "OPPO AX7"
|
||||
"CPH1938" "OPPO A9"
|
||||
@@ -4109,8 +4028,6 @@
|
||||
"OPD2303" "OPPO Pad Neo LTE"
|
||||
"OPD2406" "OPPO Pad 3"
|
||||
"OPD2402" "OPPO Pad 3 Pro"
|
||||
"OPD2502" "OPPO Pad 5 Wi-Fi"
|
||||
"OPD2503" "OPPO Pad 5 5G"
|
||||
"OPD2419" "OPPO Pad SE Wi-Fi"
|
||||
"OPD2420" "OPPO Pad SE LTE"
|
||||
"OBBE215" "OPPO Band 2 Japan"
|
||||
@@ -4120,6 +4037,3 @@
|
||||
"OW19W12" "OPPO Watch 46mm LTE"
|
||||
"OWWE231" "OPPO Watch X"
|
||||
"OWWE251" "OPPO Watch X2"
|
||||
"OWWE242" "OPPO Watch X2 Mini"
|
||||
"OWWE261" "OPPO Watch X3"
|
||||
"OWWE262" "OPPO Watch S"
|
||||
|
||||
Binary file not shown.
98
tools/core/app_utils.sh
Normal file
98
tools/core/app_utils.sh
Normal file
@@ -0,0 +1,98 @@
|
||||
#!/system/bin/sh
|
||||
# module: app_utils.sh
|
||||
|
||||
Process_Information() {
|
||||
dumpsys activity processes | awk -v key="$1" -v user="$user" 'function getUserFromUid(uid){return int(uid/100000)} /^ *user #[0-9]+ uid=/ {if($0 ~ /ISOLATED uid=[0-9]+/){uid="";pid="";pkg="";next} if(pkg!="" && uid!="" && pid!=""){if((key=="" || pkg==key) && (user=="" || getUserFromUid(uid)==user)){print pid}} uid="";pid="";pkg=""; if($0 ~ /uid=/ && uid==""){tmp=$0; sub(/^.*uid=/,"",tmp); sub(/ .*/,"",tmp); uid=tmp}} /packageList=\{/ {tmp=$0; sub(/^.*packageList=\{/,"",tmp); sub(/\}.*/,"",tmp); pkg=tmp} /pid=/ {tmp=$0; sub(/^.*pid=/,"",tmp); sub(/ .*/,"",tmp); pid=tmp} END {if(pkg!="" && uid!="" && pid!=""){if((key=="" || pkg==key) && (user=="" || getUserFromUid(uid)==user)){print pid}}}'
|
||||
}
|
||||
Background_application_list() {
|
||||
[[ $activity != false ]] && {
|
||||
if [[ $Background_apps_ignore = true || $1 = debug ]]; then
|
||||
unset Backstage
|
||||
#獲取後台
|
||||
Backstage="$(dumpsys activity activities | awk -v uid="$user" '/ActivityRecord\{/{split($4,a,"/"); user=$3; pkg=a[1]; if(user~/^u[0-9]+$/ && pkg!~/\//){sub(/^u/,"",user); if(uid=="" || user==uid) if(!seen[user","pkg]++) print pkg}}')"
|
||||
if [[ $Backstage = "" ]]; then
|
||||
Backstage="$(am stack list | awk -v uid="$user" '/taskId/&&!/unknown/{split($2,a,"/"); pkg=a[1]; user="unknown"; for(i=1;i<=NF;i++) if($i~/^userId=/){split($i,b,"="); user=b[2]; break} if(uid==""|user==uid) if(!seen[pkg]++) print pkg}')"
|
||||
[[ $Backstage = "" ]] && {
|
||||
echoRgb "獲取當前後台應用失敗" "0" && unset Backstage
|
||||
}
|
||||
fi
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
kill_app() {
|
||||
process_Information="$(Process_Information "$name2")"
|
||||
if [[ $name2 != bin.mt.plus && $name2 != com.termux && $name2 != bin.mt.plus.canary ]]; then
|
||||
if [[ $process_Information != "" ]]; then
|
||||
am force-stop --user "$user" "$name2" &>/dev/null
|
||||
echo "$process_Information" | xargs -r kill -9
|
||||
pkill -9 -f "$name2$|$name2[:/_]"
|
||||
#killall -9 "$name2" &>/dev/null
|
||||
#am kill "$name2" &>/dev/null
|
||||
echoRgb "殺死$name1進程"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
disable_verify() {
|
||||
#禁用apk驗證
|
||||
settings put global verifier_verify_adb_installs 0 2>/dev/null
|
||||
#禁用安裝包驗證
|
||||
settings put global package_verifier_enable 0 2>/dev/null
|
||||
#未知來源
|
||||
settings put secure install_non_market_apps 1 2>/dev/null
|
||||
#關閉play安全校驗
|
||||
if [[ $(settings get global package_verifier_user_consent 2>/dev/null) != -1 ]]; then
|
||||
settings put global package_verifier_user_consent -1 2>/dev/null
|
||||
settings put global upload_apk_enable 0 2>/dev/null
|
||||
echoRgb "PLAY安全驗證為開啟狀態已被腳本關閉防止apk安裝失敗" "3"
|
||||
fi
|
||||
# 設定檔案路徑
|
||||
FILE="/data/data/com.android.vending/shared_prefs/finsky.xml"
|
||||
if [[ -f $FILE ]]; then
|
||||
# 提取當前的 auto_update_enabled 值
|
||||
CURRENT_VALUE="$(sed -n '/<boolean name="auto_update_enabled" /s/.*value="\([^"]*\)".*/\1/p' "$FILE")"
|
||||
if [[ $CURRENT_VALUE = true ]]; then
|
||||
sed -i '/<boolean name="auto_update_enabled" /s/value="true"/value="false"/' "$FILE"
|
||||
[[ $(sed -n '/<boolean name="auto_update_enabled" /s/.*value="\([^"]*\)".*/\1/p' "$FILE") = false ]] && echoRgb "play自動更新已關閉" "3"
|
||||
echoRgb "殺死 Google Play 商店..."
|
||||
am force-stop com.android.vending
|
||||
else
|
||||
if [[ $CURRENT_VALUE = "" ]]; then
|
||||
sed -i '/<\/map>/i \ <boolean name="auto_update_enabled" value="false" />' "$FILE"
|
||||
[[ $(sed -n '/<boolean name="auto_update_enabled" /s/.*value="\([^"]*\)".*/\1/p' "$FILE") = false ]] && echoRgb "auto_update_enabled已插入false,play自動更新已關閉" "3"
|
||||
echoRgb "殺死 Google Play 商店..."
|
||||
am force-stop com.android.vending
|
||||
else
|
||||
[[ $CURRENT_VALUE != false ]] && echoRgb "無法識別play auto_update_enabled當前$CURRENT_VALUE值" "0"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
restore_permissions () {
|
||||
echoRgb "恢復權限"
|
||||
appops reset --user "$user" "$name2" &>/dev/null
|
||||
true_permissions="$(jq -r 'to_entries[] | select(.value.permissions != null) | .value.permissions | to_entries | map(select(.value | startswith("true")) | .key) | join(" ")' "$app_details")"
|
||||
false_permissions="$(jq -r 'to_entries[] | select(.value.permissions != null) | .value.permissions | to_entries | map(select(.value | startswith("false")) | .key) | join(" ")' "$app_details")"
|
||||
Set_Ops_permissions="$(jq -r '.[] | select(.permissions != null).permissions | to_entries | map(.value | split(" ")) | map(select(.[1] != "-1")) | map(.[1:]) | flatten | join(" ")' "$app_details")"
|
||||
[[ $true_permissions != "" ]] && {
|
||||
Set_true_Permissions "$name2" "$true_permissions"
|
||||
[[ $? != 0 ]] && echo_log "設置允許權限"
|
||||
}
|
||||
[[ $false_permissions != "" ]] && {
|
||||
Set_false_Permissions "$name2" "$false_permissions"
|
||||
[[ $? != 0 ]] && echo_log "設置拒絕權限"
|
||||
}
|
||||
[[ $Set_Ops_permissions != "" ]] && {
|
||||
Set_Ops "$name2" "$Set_Ops_permissions"
|
||||
[[ $? != 0 ]] && echo_log "設置ops權限"
|
||||
}
|
||||
}
|
||||
#分區佔用信息
|
||||
partition_info() {
|
||||
unset Skip
|
||||
Occupation_status="$(df -B1 "${1%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-1)}')"
|
||||
Filesize2="$(size "$Filesize")"
|
||||
echo " -$2大小:$Filesize2 剩餘大小:$(size "$Occupation_status")"
|
||||
[[ $Filesize != "" ]] && [[ $(echo "$Filesize > $Occupation_status" | bc) -eq 1 ]] && echoRgb "$2備份大小將超出rom可用大小" "0" && Skip=1
|
||||
Occupation_status="$(df -h "${Backup%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-1),$(NF)}')"
|
||||
}
|
||||
848
tools/core/backup_core.sh
Normal file
848
tools/core/backup_core.sh
Normal file
@@ -0,0 +1,848 @@
|
||||
#!/system/bin/sh
|
||||
# module: backup_core.sh
|
||||
|
||||
backup_path() {
|
||||
if [[ $Output_path != "" ]]; then
|
||||
[[ ${Output_path: -1} = / ]] && Output_path="${Output_path%?}"
|
||||
if [[ ${Output_path:0:1} != / ]]; then
|
||||
Directory_type="相對路徑"
|
||||
Backup="$MODDIR/$Output_path/Backup_${Compression_method}_$user"
|
||||
else
|
||||
Directory_type="絕對路徑"
|
||||
Backup="$Output_path/Backup_${Compression_method}_$user"
|
||||
fi
|
||||
outshow="使用自定義目錄($Directory_type)"
|
||||
else
|
||||
Backup="$MODDIR/Backup_${Compression_method}_$user"
|
||||
if [[ ! -f ${0%/*}/app_details.json ]]; then
|
||||
outshow="使用當前路徑作為備份目錄"
|
||||
else
|
||||
[[ -d $Backup ]] && outshow="使用上層路徑作為備份目錄" || echoRgb "$Backup目錄不存在" "0"
|
||||
fi
|
||||
fi
|
||||
PU=$(mount | awk '$3 ~ "/mnt/media_rw/[^/]+$" {print $3, $5}' | egrep -v "$mount_point")
|
||||
OTGPATH="$(echo "$PU" | cut -d' ' -f1)"
|
||||
OTGFormat="$(echo "$PU" | cut -d' ' -f2)"
|
||||
if [[ -d $OTGPATH ]]; then
|
||||
if [[ $(echo "$MODDIR" | egrep -o "^${OTGPATH}") != "" ]]; then
|
||||
hx="true"
|
||||
Backup="$MODDIR/Backup_${Compression_method}_$user"
|
||||
else
|
||||
case $Lo in
|
||||
0|1)
|
||||
echoRgb "檢測到隨身碟 是否在隨身碟備份\n -音量上是,音量下不是" "2"
|
||||
get_version "選擇了隨身碟備份" "選擇了本地備份" ;;
|
||||
2)
|
||||
Enter_options "檢測到隨身碟,輸入1使用隨身碟備份 0本地備份" "選擇了隨身碟備份" "本地備份" && isBoolean "$parameter" "branch" && branch="$nsx" ;;
|
||||
esac
|
||||
[[ $branch = true ]] && hx="$branch"
|
||||
[[ $hx = true ]] && Backup="$OTGPATH/Backup_${Compression_method}_$user"
|
||||
fi
|
||||
if [[ $hx = true ]]; then
|
||||
if [[ $OTGFormat = vfat ]]; then
|
||||
echoRgb "隨身碟檔案系統$OTGFormat不支持超過單檔4GB\n -請格式化為exfat" "0"
|
||||
exit 1
|
||||
fi
|
||||
outshow="於隨身碟備份" && hx=usb
|
||||
fi
|
||||
fi
|
||||
[[ ! -d $Backup ]] && mkdir -p "$Backup"
|
||||
#分區詳細
|
||||
if [[ $(echo "$Backup" | egrep -o "^/storage/emulated") != "" ]]; then
|
||||
Backup_path="/data"
|
||||
else
|
||||
Backup_path="${Backup%/*}"
|
||||
fi
|
||||
echoRgb "$hx備份資料夾所使用分區統計如下↓\n -$(df -h "${Backup%/*}" | sed -n 's|% /.*|%|p' | awk '{print $(NF-3),$(NF-2),$(NF-1),$(NF)}' | awk 'END{print "總共:"$1"已用:"$2"剩餘:"$3"使用率:"$4}')檔案系統:$(df -T "$Backup_path" | sed -n 's|% /.*|%|p' | awk '{print $(NF-4)}')\n -備份目錄輸出位置↓\n -$Backup"
|
||||
echoRgb "$outshow" "2"
|
||||
}
|
||||
Calculate_size() {
|
||||
#計算出備份大小跟差異性
|
||||
filesizee="$(find "$1" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')"
|
||||
if [[ $(echo "$filesizee > $filesize" | bc) -eq 1 ]]; then
|
||||
NJL="本次備份增加 $(size "$(echo "scale=2; $filesizee - $filesize" | bc)")"
|
||||
elif [[ $(echo "$filesizee < $filesize" | bc) -eq 1 ]]; then
|
||||
NJL="本次備份減少 $(size "$(echo "scale=2; $filesize - $filesizee" | bc)")"
|
||||
else
|
||||
NJL="文件大小未改變"
|
||||
fi
|
||||
echoRgb "備份資料夾路徑↓↓↓\n -$1"
|
||||
echoRgb "備份資料夾總體大小$(size "$filesizee")"
|
||||
echoRgb "$NJL"
|
||||
}
|
||||
Backup_apk() {
|
||||
#檢測apk狀態進行備份
|
||||
#創建APP備份文件夾
|
||||
[[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder"
|
||||
[[ ! -f $app_details ]] && echo "{\n}">"$app_details"
|
||||
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
|
||||
apk_version2="$(pm list packages --show-versioncode --user "$user" "$name2" 2>/dev/null | cut -f3 -d ':' | head -n 1)"
|
||||
if [[ $apk_version = $apk_version2 ]]; then
|
||||
[[ $(echo "$txt2" | sed -e '/^$/d' | cut -d' ' -f2 | awk -v pkg="$name2" '$1 == pkg {print $1}') = "" ]] && txt2="$txt2\n${Backup_folder##*/} $name2"
|
||||
unset xb
|
||||
osj=$((osj + 1))
|
||||
result=0
|
||||
echoRgb "Apk版本無更新 跳過備份" "2"
|
||||
else
|
||||
if [[ $nobackup = false ]]; then
|
||||
if [[ $apk_version != "" ]]; then
|
||||
osn=$((osn + 1))
|
||||
update_apk="$(echo "$name1 \"$name2\"")"
|
||||
update_apk2="$(echo "$update_apk\n$update_apk2")"
|
||||
echoRgb "版本:$apk_version>$apk_version2"
|
||||
else
|
||||
osk=$((osk + 1))
|
||||
add_app="$(echo "$name1 \"$name2\"")"
|
||||
add_app2="$(echo "$add_app\n$add_app2")"
|
||||
echoRgb "版本:$apk_version2"
|
||||
fi
|
||||
unset Filesize
|
||||
Filesize="$(find "$apk_path2" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')"
|
||||
rm -rf "$Backup_folder/apk.tar"*
|
||||
partition_info "$Backup" "$name1 apk"
|
||||
if [[ $Skip != 1 ]]; then
|
||||
#備份apk
|
||||
echoRgb "$1"
|
||||
echo "$apk_path" | sed -e '/^$/d' | while read; do
|
||||
echoRgb "${REPLY##*/} $(size "$REPLY")"
|
||||
done
|
||||
(
|
||||
cd "$apk_path2"
|
||||
case $Compression_method in
|
||||
tar | TAR | Tar) tar --checkpoint-action="ttyout=%T\r" -cf "$Backup_folder/apk.tar" *.apk ;;
|
||||
zstd | Zstd | ZSTD) tar --checkpoint-action="ttyout=%T\r" -cf - *.apk | zstd --ultra -3 -T0 -q --priority=rt >"$Backup_folder/apk.tar.zst" ;;
|
||||
esac
|
||||
)
|
||||
echo_log "備份$apk_number個Apk"
|
||||
if [[ $result = 0 ]]; then
|
||||
Validation_file "$Backup_folder/apk.tar"*
|
||||
if [[ $result = 0 ]]; then
|
||||
[[ $(echo "$txt2" | sed -e '/^$/d' | cut -d' ' -f2 | awk -v pkg="$name2" '$1 == pkg {print $1}') = "" ]] && txt2="$txt2\n${Backup_folder##*/} $name2"
|
||||
[[ $apk_version != "" ]] && {
|
||||
echoRgb "覆蓋app_details"
|
||||
jq --arg apk_version "$apk_version2" --arg software "$name1" '.[$software].apk_version = $apk_version' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
} || {
|
||||
echoRgb "新增app_details"
|
||||
extra_content="{
|
||||
\"$name1\": {
|
||||
\"PackageName\": \"$name2\",
|
||||
\"apk_version\": \"$apk_version2\"
|
||||
}
|
||||
}"
|
||||
jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
}
|
||||
else
|
||||
rm -rf "$Backup_folder"
|
||||
fi
|
||||
if [[ $name2 = com.android.chrome ]]; then
|
||||
#刪除所有舊apk ,保留一個最新apk進行備份
|
||||
ReservedNum=1
|
||||
FileNum="$(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null | wc -l)"
|
||||
while [[ $FileNum -gt $ReservedNum ]]; do
|
||||
OldFile="$(ls -rt /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null | head -1)"
|
||||
rm -rf "${OldFile%/*/*}" && echo "刪除文件:${OldFile%/*/*}"
|
||||
FileNum=$((FileNum - 1))
|
||||
done
|
||||
[[ -f $(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null) && $(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null | wc -l) = 1 ]] && cp -r "$(ls /data/app/*/com.google.android.trichromelibrary_*/base.apk 2>/dev/null)" "$Backup_folder/nmsl.apk"
|
||||
fi
|
||||
else
|
||||
rm -rf "$Backup_folder"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
osj=$((osj + 1))
|
||||
rm -rf "$Backup_folder"
|
||||
fi
|
||||
fi
|
||||
[[ $name2 = bin.mt.plus && ! -f $Backup/$name1.apk ]] && cp -r "$apk_path" "$Backup/$name1.apk"
|
||||
}
|
||||
Backup_ssaid() {
|
||||
Ssaid="$(jq -r '.[] | select(.Ssaid != null).Ssaid' "$app_details")"
|
||||
ssaid="$(awk -v pkg="$name2" '$1 == pkg {print $2}'<<<"$ssaid_info")"
|
||||
[[ $ssaid != null && $ssaid != "" ]] && echoRgb "SSAID:$ssaid"
|
||||
if [[ $ssaid != null && $ssaid != $Ssaid ]]; then
|
||||
echoRgb "備份ssaid"
|
||||
echoRgb "$Ssaid>$ssaid"
|
||||
SSAID_apk="$(echo "$name1 \"$name2\"")"
|
||||
SSAID_apk2="$(echo "$SSAID_apk\n$SSAID_apk2")"
|
||||
jq --arg entry "$name1" --arg new_value "$ssaid" '.[$entry].Ssaid |= $new_value' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
echo_log "備份ssaid"
|
||||
fi
|
||||
[[ $ssaid = null ]] && ssaid=
|
||||
}
|
||||
Backup_Permissions() {
|
||||
get_Permissions="$(jq -r '.[] | select(.permissions != null).permissions' "$app_details")"
|
||||
Get_Permissions="$(get_Permissions "$name2" | jq -nR '[inputs | select(. != "null" and length>0) | split(" ") | {(.[0]): (.[1:] | join(" "))}] | if length > 0 then add else empty end')"
|
||||
if [[ $Get_Permissions != "" && ($Get_Permissions = *true* || $Get_Permissions = *false*) ]]; then
|
||||
if [[ $get_Permissions = "" ]]; then
|
||||
echoRgb "備份權限"
|
||||
jq --arg packageName "$name1" --argjson permissions "$Get_Permissions" '.[$packageName].permissions |= $permissions' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
echo_log "備份權限"
|
||||
else
|
||||
if [[ $get_Permissions != "" && ($get_Permissions = *true* || $get_Permissions = *false*) ]]; then
|
||||
if [[ $get_Permissions != $Get_Permissions ]]; then
|
||||
echoRgb "權限變更"
|
||||
jq -n --argjson old "$get_Permissions" --argjson new "$Get_Permissions" '$new | to_entries | map(select(.key as $k | $old[$k] != null and $old[$k] != .value)) | .[].key' | sed 's/^/ /'
|
||||
jq --arg packageName "$name1" --argjson permissions "$Get_Permissions" '.[$packageName] |= . + {permissions: $permissions}' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
echo_log "備份權限"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
[[ $Get_Permissions != "" ]] && echoRgb "備份權限失敗$(get_Permissions "$name2")" "0"
|
||||
fi
|
||||
}
|
||||
#檢測數據位置進行備份
|
||||
Backup_data() {
|
||||
data_path="$path/$1/$name2"
|
||||
MODDIR_NAME="${data_path%/*}"
|
||||
MODDIR_NAME="${MODDIR_NAME##*/}"
|
||||
[[ -f $app_details ]] && Size="$(jq -r --arg entry "$1" '.[$entry] | select(.Size != null).Size' "$app_details" 2>/dev/null)"
|
||||
case $1 in
|
||||
user) data_path="$path2/$name2" ;;
|
||||
user_de) data_path="$path3/$name2" ;;
|
||||
data|obb) ;;
|
||||
*)
|
||||
data_path="$2"
|
||||
if [[ $1 != thanox ]]; then
|
||||
Compression_method1="$Compression_method"
|
||||
Compression_method=tar
|
||||
fi
|
||||
zsize=1
|
||||
zmediapath=1
|
||||
;;
|
||||
esac
|
||||
if [[ -d $data_path ]]; then
|
||||
unset Filesize ssaid Get_Permissions result Permissions
|
||||
Filesize="$(find "$data_path" -type f -printf "%s\n" 2>/dev/null | awk '{s+=$1} END {print s}')"
|
||||
[[ $Filesize != "" ]] && {
|
||||
if [[ $Size != $Filesize ]]; then
|
||||
case $1 in
|
||||
user)
|
||||
if [[ $(su "$(pm list packages -U --user "$user" </dev/null | awk -v pkg="$name2" -F'[ :]' '$2 == pkg {print $4}')" -c keystore_cli_v2 list | wc -l) -ge 2 ]]; then
|
||||
echoRgb "$name1包含keystore 恢復可能閃退" "0"
|
||||
jq --arg entry "$name1" '.[$entry].keystore |= "true"' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
else
|
||||
jq --arg entry "$name1" '.[$entry].keystore |= "false"' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
fi
|
||||
Backup_ssaid
|
||||
Backup_Permissions ;;
|
||||
esac
|
||||
#停止應用
|
||||
case $1 in
|
||||
user|data|obb|user_de) kill_app ;;
|
||||
esac
|
||||
rm -rf "$Backup_folder/$1.tar"*
|
||||
partition_info "$Backup" "$1"
|
||||
if [[ $Skip != 1 ]]; then
|
||||
echoRgb "備份$1數據"
|
||||
# 判斷是否超過指定大小
|
||||
if [[ $Filesize2 != *"bytes"* ]]; then
|
||||
if [[ $Filesize2 = *"KB"* ]]; then
|
||||
if [[ $(echo "${Filesize2% KB}" | bc) > 1 ]]; then
|
||||
Start_backup="true"
|
||||
else
|
||||
Start_backup="false"
|
||||
fi
|
||||
else
|
||||
Start_backup="true"
|
||||
fi
|
||||
else
|
||||
Start_backup="false"
|
||||
fi
|
||||
if [[ $Start_backup = true ]]; then
|
||||
case $1 in
|
||||
user|user_de)
|
||||
case $Compression_method in
|
||||
tar | Tar | TAR) tar --checkpoint-action="ttyout=%T\r" --exclude="${data_path##*/}/.ota" --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/lib" --exclude="${data_path##*/}/code_cache" --exclude="${data_path##*/}/no_backup" --warning=no-file-changed -cpf "$Backup_folder/$1.tar" -C "${data_path%/*}" "${data_path##*/}" 2>/dev/null ;;
|
||||
zstd | Zstd | ZSTD) tar --checkpoint-action="ttyout=%T\r" --exclude="${data_path##*/}/.ota" --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/lib" --exclude="${data_path##*/}/code_cache" --exclude="${data_path##*/}/no_backup" --warning=no-file-changed -cpf - -C "${data_path%/*}" "${data_path##*/}" | zstd --ultra -3 -T0 -q --priority=rt >"$Backup_folder/$1.tar.zst" 2>/dev/null ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
case $Compression_method in
|
||||
tar | Tar | TAR) tar --checkpoint-action="ttyout=%T\r" --exclude="Backup_"* --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/QQ" --exclude="${data_path##*/}/Telegram" --exclude="${data_path##*/}"/.* --warning=no-file-changed -cpf "$Backup_folder/$1.tar" -C "${data_path%/*}" "${data_path##*/}" ;;
|
||||
zstd | Zstd | ZSTD) tar --checkpoint-action="ttyout=%T\r" --exclude="Backup_"* --exclude="${data_path##*/}/cache" --exclude="${data_path##*/}/QQ" --exclude="${data_path##*/}/Telegram" --exclude="${data_path##*/}"/.* --warning=no-file-changed -cpf - -C "${data_path%/*}" "${data_path##*/}" | zstd --ultra -3 -T0 -q --priority=rt >"$Backup_folder/$1.tar.zst" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
echo_log "備份$1數據"
|
||||
else
|
||||
echoRgb "$1數據 $Filesize2太小" "0" && result=1
|
||||
fi
|
||||
if [[ $result = 0 ]]; then
|
||||
Validation_file "$Backup_folder/$1.tar"*
|
||||
if [[ $result = 0 ]]; then
|
||||
if [[ ! $Filesize -eq 0 ]]; then
|
||||
size2="$(stat -c %s "$Backup_folder/$1.tar"*)"
|
||||
rate="$(echo "scale=2; (1 - ($size2 / $Filesize)) * 100" | bc)"
|
||||
echoRgb "壓縮率${rate}% 大小$(size "$size2")"
|
||||
fi
|
||||
[[ ${Backup_folder##*/} = Media ]] && [[ $(sed -e '/^$/d' "$mediatxt" | grep -w "${REPLY##*/}.tar$" | head -1) = "" ]] && echo "$FILE_NAME" >> "$mediatxt"
|
||||
if [[ $zsize != "" ]]; then
|
||||
extra_content="{
|
||||
\"$1\": {
|
||||
\"path\": \"$2\",
|
||||
\"Size\": \"$Filesize\"
|
||||
},
|
||||
\"Backup time\": {
|
||||
\"date\": \"$(date "+%Y.%m.%d %H:%M:%S")\"
|
||||
}
|
||||
}"
|
||||
jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
else
|
||||
extra_content="{
|
||||
\"$1\": {
|
||||
\"Size\": \"$Filesize\"
|
||||
},
|
||||
\"Backup time\": {
|
||||
\"date\": \"$(date "+%Y.%m.%d %H:%M:%S")\"
|
||||
}
|
||||
}"
|
||||
jq --argjson new_content "$extra_content" '. += $new_content' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
fi
|
||||
else
|
||||
rm -rf "$Backup_folder/$1".tar.*
|
||||
fi
|
||||
fi
|
||||
[[ $Compression_method1 != "" ]] && Compression_method="$Compression_method1"
|
||||
unset Compression_method1
|
||||
fi
|
||||
else
|
||||
[[ $Size != "" ]] && echoRgb "$1數據無發生變化 跳過備份" "2"
|
||||
fi
|
||||
}
|
||||
else
|
||||
[[ -f $data_path ]] && echoRgb "$1是一個文件 不支持備份" "0"
|
||||
fi
|
||||
}
|
||||
# ===================== 子函數: 驗證備份參數 =====================
|
||||
validate_backup_params() {
|
||||
self_test
|
||||
case $MODDIR in
|
||||
/storage/emulated/0/Android/* | /data/media/0/Android/* | /sdcard/Android/*) echoRgb "請勿在$MODDIR內備份" "0" && exit 2 ;;
|
||||
esac
|
||||
case $Compression_method in
|
||||
zstd | Zstd | ZSTD | tar | Tar | TAR) ;;
|
||||
*) echoRgb "$Compression_method為不支持的壓縮算法" "0" && exit 2 ;;
|
||||
esac
|
||||
#校驗選填是否正確
|
||||
case $Lo in
|
||||
0)
|
||||
[[ $Backup_Mode != "" ]] && isBoolean "$Backup_Mode" "Backup_Mode" && Backup_Mode="$nsx" || {
|
||||
echoRgb "選擇備份模式\n -音量上備份應用+數據,音量下僅應用不包含數據" "2"
|
||||
get_version "應用+數據" "僅應用" && Backup_Mode="$branch"
|
||||
}
|
||||
if [[ $Backup_Mode = true ]]; then
|
||||
if [[ $(echo "$blacklist" | egrep -v '#|#' | wc -l) -gt 0 ]]; then
|
||||
if [[ $blacklist_mode != "" ]]; then
|
||||
isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
|
||||
else
|
||||
echoRgb "選擇黑名單模式\n -音量上不備份,音量下僅備份安裝檔\n -警告! " "2"
|
||||
get_version "不備份" "備份安裝檔" && blacklist_mode="$branch"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ $Backup_Mode = true ]]; then
|
||||
[[ $Backup_obb_data != "" ]] && isBoolean "$Backup_obb_data" "Backup_obb_data" && Backup_obb_data="$nsx" || {
|
||||
echoRgb "是否備份外部數據 即比如原神的數據包\n -音量上備份,音量下不備份" "2"
|
||||
get_version "備份" "不備份" && Backup_obb_data="$branch"
|
||||
}
|
||||
[[ $Backup_user_data != "" ]] && isBoolean "$Backup_user_data" "Backup_user_data" && Backup_user_data="$nsx" || {
|
||||
echoRgb "是否備份使用者數據\n -音量上備份,音量下不備份" "2"
|
||||
get_version "備份" "不備份" && Backup_user_data="$branch"
|
||||
}
|
||||
else
|
||||
Backup_user_data="false"
|
||||
Backup_obb_data="false"
|
||||
fi
|
||||
[[ $backup_media != "" ]] && isBoolean "$backup_media" "backup_media" && backup_media="$nsx" || {
|
||||
echoRgb "全部應用備份結束後是否備份自定義目錄\n -音量上備份,音量下不備份" "2"
|
||||
get_version "備份" "不備份" && backup_media="$branch"
|
||||
}
|
||||
[[ $setDisplayPowerMode != "" ]] && isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx" || {
|
||||
echoRgb "應用備份開始後關閉螢幕\n -音量上關閉,音量下不關閉" "2"
|
||||
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
|
||||
}
|
||||
[[ $Background_apps_ignore != "" ]] && isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx" || {
|
||||
echoRgb "存在進程忽略備份\n -音量上忽略,音量下備份" "2"
|
||||
get_version "忽略" "備份" && Background_apps_ignore="$branch"
|
||||
} ;;
|
||||
1)
|
||||
[[ $Backup_Mode = "" ]] && {
|
||||
echoRgb "選擇備份模式\n -音量上備份應用+數據,音量下僅應用不包含數據" "2"
|
||||
get_version "應用+數據" "僅應用" && Backup_Mode="$branch"
|
||||
} || isBoolean "$Backup_Mode" "Backup_Mode" && Backup_Mode="$nsx"
|
||||
if [[ $Backup_Mode = true ]]; then
|
||||
if [[ $(echo "$blacklist" | egrep -v '#|#' | wc -l) -gt 0 ]]; then
|
||||
[[ $blacklist_mode = "" ]] && {
|
||||
echoRgb "選擇黑名單模式\n -音量上不備份,音量下僅備份安裝檔" "2"
|
||||
get_version "不備份" "備份安裝檔" && blacklist_mode="$branch"
|
||||
} || isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
|
||||
fi
|
||||
[[ $Backup_obb_data = "" ]] && {
|
||||
echoRgb "是否備份外部數據 即比如原神的數據包\n -音量上備份,音量下不備份" "2"
|
||||
get_version "備份" "不備份" && Backup_obb_data="$branch"
|
||||
} || isBoolean "$Backup_obb_data" "Backup_obb_data" && Backup_obb_data="$nsx"
|
||||
[[ $Backup_user_data = "" ]] && {
|
||||
echoRgb "是否備份使用者數據\n -音量上備份,音量下不備份" "2"
|
||||
get_version "備份" "不備份" && Backup_user_data="$branch"
|
||||
} || isBoolean "$Backup_user_data" "Backup_user_data" && Backup_user_data="$nsx"
|
||||
fi
|
||||
[[ $backup_media = "" ]] && {
|
||||
echoRgb "全部應用備份結束後是否備份自定義目錄\n -音量上備份,音量下不備份" "2"
|
||||
get_version "備份" "不備份" && backup_media="$branch"
|
||||
} || isBoolean "$backup_media" "backup_media" && backup_media="$nsx"
|
||||
[[ $setDisplayPowerMode = "" ]] && {
|
||||
echoRgb "應用備份開始後關閉螢幕\n -音量上關閉,音量下不關閉" "2"
|
||||
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
|
||||
} || isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
|
||||
[[ $Background_apps_ignore = "" ]] && {
|
||||
echoRgb "存在進程忽略備份\n -音量上忽略,音量下備份" "2"
|
||||
get_version "忽略" "備份" && Background_apps_ignore="$branch"
|
||||
} || isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx"
|
||||
;;
|
||||
2)
|
||||
[[ $Backup_Mode = "" ]] && {
|
||||
Enter_options "輸入1備份應用+數據,輸入0僅應用不包含數據" "應用+數據" "僅應用" && isBoolean "$parameter" "Backup_Mode" && Backup_Mode="$nsx"
|
||||
} || {
|
||||
isBoolean "$Backup_Mode" "Backup_Mode" && Backup_Mode="$nsx"
|
||||
}
|
||||
if [[ $Backup_Mode = true ]]; then
|
||||
[[ $(echo "$blacklist" | egrep -v '#|#' | wc -l) -gt 0 ]] && {
|
||||
[[ $blacklist_mode = "" ]] && {
|
||||
Enter_options "選擇黑名單模式輸入1不備份,輸入0備份安裝檔" "不備份" "僅應用安裝檔" && isBoolean "$parameter" "blacklist_mode" && blacklist_mode="$nsx"
|
||||
} || {
|
||||
isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
|
||||
}
|
||||
}
|
||||
[[ $Backup_obb_data = "" ]] && {
|
||||
Enter_options "是否備份外部數據 即比如原神的數據包\n -輸入1備份,輸入0不備份" "備份" "不備份" && isBoolean "$parameter" "Backup_obb_data" && Backup_obb_data="$nsx"
|
||||
} || {
|
||||
isBoolean "$Backup_obb_data" "Backup_obb_data" && Backup_obb_data="$nsx"
|
||||
}
|
||||
[[ $Backup_user_data = "" ]] && {
|
||||
Enter_options "是否備份使用者數據,輸入1備份,輸入0不備份" "備份" "不備份" && isBoolean "$parameter" "Backup_user_data" && Backup_user_data="$nsx"
|
||||
} || {
|
||||
isBoolean "$Backup_user_data" "Backup_user_data" && Backup_user_data="$nsx"
|
||||
}
|
||||
fi
|
||||
[[ $backup_media = "" ]] && {
|
||||
Enter_options "全部應用備份結束後是否備份自定義目錄\n -輸入1備份,0不備份" "備份" "不備份" && isBoolean "$parameter" "backup_media" && backup_media="$nsx"
|
||||
} || {
|
||||
isBoolean "$backup_media" "backup_media" && backup_media="$nsx"
|
||||
}
|
||||
[[ $setDisplayPowerMode = "" ]] && {
|
||||
Enter_options "應用備份開始後關閉螢幕\n -輸入1關閉,0不關閉" "關閉" "不關閉" && isBoolean "$parameter" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
|
||||
} || {
|
||||
isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
|
||||
}
|
||||
[[ $Background_apps_ignore = "" ]] && {
|
||||
Enter_options "存在進程忽略備份\n -輸入1不備份,0備份" "忽略" "備份" && isBoolean "$parameter" "Background_apps_ignore" && Background_apps_ignore="$nsx"
|
||||
} || {
|
||||
isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx"
|
||||
} ;;
|
||||
*) echoRgb "$conf_path Lo=$Lo填寫錯誤,正確值0 1 2" "0" && exit 2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ===================== 子函數: 準備應用列表 =====================
|
||||
prepare_app_list() {
|
||||
i=1
|
||||
#數據目錄
|
||||
if [[ $list_location != "" ]]; then
|
||||
if [[ ${list_location:0:1} = / ]]; then
|
||||
txt="$list_location"
|
||||
else
|
||||
txt="$MODDIR/$list_location"
|
||||
fi
|
||||
else
|
||||
txt="$MODDIR/appList.txt"
|
||||
fi
|
||||
txt="${txt/'/storage/emulated/'/'/data/media/'}"
|
||||
txt_path="$txt"
|
||||
[[ ! -f $txt ]] && echoRgb "請執行start.sh獲取應用列表再來備份" "0" && exit 1
|
||||
TXT_NAME="${txt##*/}"
|
||||
case ${TXT_NAME##*.} in
|
||||
txt) ;;
|
||||
*) echoRgb "$txt不是腳本讀取格式" "0" && exit 2 ;;
|
||||
esac
|
||||
sort -u "$txt" -o "$txt" &>/dev/null
|
||||
data="$MODDIR"
|
||||
hx="本地"
|
||||
echoRgb "腳本受到內核機制影響 息屏後IO性能嚴重影響\n -請勿關閉終端或是息屏備份 如需終止腳本\n -請執行start.sh選擇終止腳本即可停止" "3"
|
||||
backup_path
|
||||
echoRgb "配置詳細:\n -壓縮方式:$Compression_method\n -音量鍵確認:$Lo\n -更新:$update\n -備份模式:$Backup_Mode\n -備份外部數據:$Backup_obb_data\n -備份user數據:$Backup_user_data\n -自定義目錄備份:$backup_media\n -存在進程忽略備份:$Background_apps_ignore\n -關閉螢幕:$setDisplayPowerMode"
|
||||
D="1"
|
||||
Apk_info="$(pm list packages --user "$user" | cut -f2 -d ':' | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
|
||||
if [[ $Apk_info != "" ]]; then
|
||||
[[ $Apk_info = *"Failure calling service package"* ]] && Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
|
||||
else
|
||||
Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
|
||||
fi
|
||||
[[ $Apk_info = "" ]] && echoRgb "Apk_info變量為空" "0" && exit
|
||||
[[ ! -f ${0%/*}/app_details.json ]] && {
|
||||
echoRgb "檢查備份列表中是否存在已經卸載應用" "3"
|
||||
while read -r ; do
|
||||
if [[ $(echo "$REPLY" | sed -E 's/^[ \t]*//; /^[ \t]*[##!]/d') != "" ]]; then
|
||||
app=($REPLY $REPLY)
|
||||
if [[ ${app[1]} != "" && ${app[2]} != "" ]]; then
|
||||
if [[ $(echo "$Apk_info" | awk -v pkg="${app[1]}" '$1 == pkg {print $1}') != "" ]]; then
|
||||
[[ $Tmplist = "" ]] && Tmplist='#不需要備份的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market(忽略安裝包和數據)\n#不需要備份數據的應用請在開頭使用!注釋 比如:!酷安 com.coolapk.market(僅忽略數據)'
|
||||
Tmplist="$Tmplist\n$REPLY"
|
||||
else
|
||||
echoRgb "$REPLY不存在系統,從列表中刪除" "0"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
Tmplist="$Tmplist\n$REPLY"
|
||||
fi
|
||||
done < "$txt"
|
||||
}
|
||||
[[ $Update_backup = true ]] && {
|
||||
echoRgb "檢查備份列表中已經更新應用" "3"
|
||||
while read apk; do
|
||||
Backup_folder="$Backup/$(echo "$apk" | cut -d':' -f1)"
|
||||
app_details="$Backup_folder/app_details.json"
|
||||
if [[ -d $Backup_folder ]]; then
|
||||
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
|
||||
apk_version2="$(pm list packages --show-versioncode --user "$user" "$(echo "$apk" | cut -d':' -f2)" </dev/null | cut -f3 -d ':' | head -n 1)"
|
||||
[[ $apk_version != $apk_version2 ]] && {
|
||||
[[ $Tmplist2 = "" ]] && Tmplist2="${apk/:/ }" || Tmplist2="$Tmplist2\n${apk/:/ }"
|
||||
}
|
||||
fi
|
||||
done<<<"$(grep -Ev '^[##!]' "$txt" | awk '{print $1 ":" $2}')"
|
||||
}
|
||||
[[ $Tmplist != "" ]] && echo "$Tmplist" | sed -e '/^$/d' | sort>"$txt"
|
||||
if [[ $Tmplist2 != "" ]]; then
|
||||
txt="$(echo "$Tmplist2" | sort)"
|
||||
else
|
||||
[[ $Update_backup != "" ]] && echoRgb "應用目前無更新" "0" && exit 0
|
||||
fi
|
||||
if [[ ! -f $txt ]]; then
|
||||
[[ $(echo "$txt") != "" ]] && txt="$(echo "$txt" | sed -e '/^$/d')"
|
||||
else
|
||||
txt="$(egrep -v '#|#' "$txt" | sed -e '/^$/d')"
|
||||
fi
|
||||
r="$(echo "$txt" | awk 'NF != 0 { count++ } END { print count }')"
|
||||
[[ -f ${0%/*}/app_details.json ]] && r=1
|
||||
[[ $r = "" && ! -f ${0%/*}/app_details.json ]] && echoRgb "$MODDIR_NAME/appList.txt是空的或是包名被注釋備份個鬼\n -檢查是否注釋亦或者執行$MODDIR_NAME/start.sh" "0" && exit 1
|
||||
}
|
||||
|
||||
# ===================== 子函數: 初始化備份存儲 =====================
|
||||
init_backup_storage() {
|
||||
if [[ $Backup_Mode = true ]]; then
|
||||
[[ $Backup_user_data = false ]] && echoRgb "當前$MODDIR_NAME/backup_settings.conf的\n -Backup_user_data=0將不備份user數據" "0"
|
||||
[[ $Backup_obb_data = false ]] && echoRgb "當前$MODDIR_NAME/backup_settings.conf的\n -Backup_obb_data=0將不備份外部數據" "0"
|
||||
fi
|
||||
[[ $backup_media = false ]] && echoRgb "當前$MODDIR_NAME/backup_settings.conf的\n -backup_media=0將不備份自定義資料夾" "0"
|
||||
txt2="$Backup/appList.txt"
|
||||
txt2="${txt2/'/storage/emulated/'/'/data/media/'}"
|
||||
txt_path2="$txt2"
|
||||
[[ ! -f $txt2 ]] && echo "#不需要恢復還原的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market">"$txt2"
|
||||
txt2="$(cat "$txt2")"
|
||||
[[ ! -d $Backup/tools ]] && cp -r "$tools_path" "$Backup"
|
||||
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
|
||||
[[ ! -f $Backup/restore_settings.conf ]] && update_Restore_settings_conf>"$Backup/restore_settings.conf"
|
||||
if [[ -d $Backup/tools ]]; then
|
||||
find "$Backup/tools" -maxdepth 1 -type f | while read; do
|
||||
Tools_FILE_NAME="${REPLY##*/}"
|
||||
if [[ -f $tools_path/$Tools_FILE_NAME ]]; then
|
||||
filesha256="$(sha256sum "$tools_path/$Tools_FILE_NAME" 2>/dev/null | cut -d" " -f1)"
|
||||
filesha256_1="$(sha256sum "$REPLY" 2>/dev/null | cut -d" " -f1)"
|
||||
if [[ $filesha256 != $filesha256_1 ]]; then
|
||||
cp -r "$tools_path/$Tools_FILE_NAME" "$REPLY"
|
||||
echoRgb "更新$REPLY"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
filesize="$(find "$Backup" -type f -printf "%s\n" | awk '{s+=$1} END {print s}')"
|
||||
Quantity=0
|
||||
#開始循環$txt內的資料進行備份
|
||||
#記錄開始時間
|
||||
en=118 # ANSI 256色起始編號,用於通知欄圖標顏色
|
||||
osn=0; osj=0; osk=0
|
||||
#獲取已經開啟的無障礙
|
||||
var="$(settings get secure enabled_accessibility_services 2>/dev/null)"
|
||||
#獲取預設鍵盤
|
||||
keyboard="$(settings get secure default_input_method 2>/dev/null)"
|
||||
Set_screen_pause_seconds on
|
||||
[[ $txt != "" ]] && [[ $(echo "$txt" | cut -d' ' -f2 | grep -w "^${keyboard%/*}$") != ${keyboard%/*} ]] && unset keyboard
|
||||
if [[ -f ${0%/*}/app_details.json ]]; then
|
||||
ssaid_info="$(get_ssaid "$(jq -r '.[] | select(.PackageName != null).PackageName' "${0%/*}/app_details.json")")"
|
||||
else
|
||||
ssaid_info="$(get_ssaid "$(echo "$txt" | awk '{printf "%s ", $2}')")"
|
||||
fi
|
||||
starttime1="$(date -u "+%s")"
|
||||
TIME="$starttime1"
|
||||
notification "101" "開始備份"
|
||||
}
|
||||
|
||||
# ===================== 子函數: 應用循環備份 =====================
|
||||
backup_applications_loop() {
|
||||
while [[ $i -le $r ]]; do
|
||||
[[ $en -ge 229 ]] && en=118 # 超過229重置,避免顏色越界
|
||||
unset name1 name2 apk_path apk_path2
|
||||
if [[ ! -f ${0%/*}/app_details.json ]]; then
|
||||
name1="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f1)"
|
||||
name2="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f2)"
|
||||
else
|
||||
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "${0%/*}/app_details.json" | head -n 1)"
|
||||
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "${0%/*}/app_details.json")"
|
||||
name1="$ChineseName"
|
||||
name2="$PackageName"
|
||||
fi
|
||||
[[ $name2 = "" || $name1 = "" ]] && echoRgb "警告! appList.txt應用包名獲取失敗,可能修改有問題" "0" && exit 1
|
||||
apk_path="$(pm path --user "$user" "$name2" 2>/dev/null | cut -f2 -d ':')"
|
||||
apk_path2="$(echo "$apk_path" | head -1)"
|
||||
apk_path2="${apk_path2%/*}"
|
||||
if [[ -d $apk_path2 ]]; then
|
||||
echoRgb "備份第$i/$r個應用 剩下$((r - i))個" "3"
|
||||
echoRgb "備份 $name1" "2"
|
||||
notification "101" "備份第$i/$r個應用 剩下$((r - i))個
|
||||
備份 $name1"
|
||||
unset Backup_folder ChineseName PackageName nobackup No_backupdata result apk_version apk_version2 zsize zmediapath Size data_path Ssaid ssaid Permissions
|
||||
nobackup="false"
|
||||
Background_application_list
|
||||
[[ $Backstage != "" && $(echo "$Backstage" | egrep -w "^$name2$") != "" ]] && echoRgb "$name1存在後台 忽略備份" "0" && nobackup="true"
|
||||
if [[ $Backup_Mode = true ]]; then
|
||||
if [[ $name1 = !* || $name1 = !* ]]; then
|
||||
name1="$(echo "$name1" | sed 's/!//g ; s/!//g')"
|
||||
echoRgb "跳過備份所有數據" "0"
|
||||
No_backupdata=1
|
||||
fi
|
||||
if [[ $(echo "$blacklist" | grep -w "^$name2$") = $name2 ]]; then
|
||||
if [[ $blacklist_mode = true ]]; then
|
||||
echoRgb "黑名單應用跳過備份" "0"
|
||||
nobackup="true"
|
||||
else
|
||||
echoRgb "黑名單應用跳過備份所有數據" "0"
|
||||
fi
|
||||
No_backupdata=1
|
||||
fi
|
||||
fi
|
||||
Backup_folder="$Backup/$name1"
|
||||
app_details="$Backup_folder/app_details.json"
|
||||
if [[ -f $app_details ]]; then
|
||||
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$app_details")"
|
||||
[[ $PackageName != $name2 ]] && jq --arg name2 "$name2" 'walk(if type == "object" and .PackageName then .PackageName = $name2 else . end)' "$app_details" > "$TMPDIR/temp.json" && mv "$TMPDIR/temp.json" "$app_details"
|
||||
echoRgb "上次備份時間$(jq -r --arg entry "Backup time" '.[$entry] | select(.date != null).date' "$app_details" 2>/dev/null)"
|
||||
fi
|
||||
[[ $hx = USB && $PT = "" ]] && echoRgb "隨身碟意外斷開 請檢查穩定性" "0" && exit 1
|
||||
starttime2="$(date -u "+%s")"
|
||||
[[ $name2 = com.tencent.mobileqq ]] && echoRgb "QQ可能恢復備份失敗或是丟失聊天記錄,請自行用你信賴的應用備份" "0"
|
||||
[[ $name2 = com.tencent.mm ]] && echoRgb "WX可能恢復備份失敗或是丟失聊天記錄,請自行用你信賴的應用備份" "0"
|
||||
apk_number="$(echo "$apk_path" | wc -l)"
|
||||
if [[ $nobackup != true ]]; then
|
||||
if [[ $apk_number = 1 ]]; then
|
||||
Backup_apk "非Split Apk" "3"
|
||||
else
|
||||
Backup_apk "Split Apk支持備份" "3"
|
||||
fi
|
||||
if [[ $result = 0 && $No_backupdata = "" ]]; then
|
||||
if [[ $Backup_Mode = true ]]; then
|
||||
if [[ $Backup_obb_data = true ]]; then
|
||||
if [[ $name2 != bin.mt.plus ]]; then
|
||||
#備份data數據
|
||||
[[ $name1 = Nekogram ]] && rm -rf /data/media/0/Android/data/tw.nekomimi.nekogram/files/Telegram/Telegram\ {Video,Stories,Documents,Images}/{*,.*} 2>/dev/null
|
||||
Backup_data "data"
|
||||
#備份obb數據
|
||||
Backup_data "obb"
|
||||
else
|
||||
echoRgb "$name1無法備份" "0"
|
||||
fi
|
||||
fi
|
||||
#備份user數據
|
||||
[[ $name2 != bin.mt.plus ]] && {
|
||||
[[ $Backup_user_data = true ]] && {
|
||||
Backup_data "user"
|
||||
Backup_data "user_de"
|
||||
}
|
||||
}
|
||||
[[ $name2 = github.tornaco.android.thanos ]] && Backup_data "thanox" "$(find "/data/system" -name "thanos"* -maxdepth 1 -type d 2>/dev/null)"
|
||||
fi
|
||||
fi
|
||||
[[ -f $Backup_folder/${name2}.sh ]] && rm -rf "$Backup_folder/${name2}.sh"
|
||||
[[ ! -f $Backup_folder/recover.sh ]] && touch_shell "3" "$Backup_folder/recover.sh"
|
||||
[[ ! -f $Backup_folder/backup.sh ]] && touch_shell "1" "$Backup_folder/backup.sh"
|
||||
fi
|
||||
endtime 2 "$name1 備份" "3"
|
||||
lxj="$(echo "$Occupation_status" | awk '{print $3}' | sed 's/%//g')"
|
||||
echoRgb "完成$((i * 100 / r))% $hx$(echo "$Occupation_status" | awk 'END{print "剩餘:"$1"使用率:"$2}')" "3"
|
||||
rgb_d="$rgb_a"
|
||||
rgb_a=188 # ANSI 256色: 淺灰色分隔線
|
||||
echoRgb "_________________$(endtime 1 "已經")___________________"
|
||||
rgb_a="$rgb_d"
|
||||
else
|
||||
echoRgb "$name1[$name2] 不在安裝列表,備份個寂寞?" "0"
|
||||
fi
|
||||
if [[ $i = $r ]]; then
|
||||
endtime 1 "應用備份" "3"
|
||||
#設置無障礙開關
|
||||
if [[ $var != "" ]]; then
|
||||
if [[ $var != null ]]; then
|
||||
settings put secure enabled_accessibility_services "$var" &>/dev/null
|
||||
echo_log "設置無障礙"
|
||||
settings put secure accessibility_enabled 1 &>/dev/null
|
||||
echo_log "打開無障礙開關"
|
||||
fi
|
||||
fi
|
||||
#設置鍵盤
|
||||
if [[ $keyboard != "" ]]; then
|
||||
ime enable "$keyboard" &>/dev/null
|
||||
ime set "$keyboard" &>/dev/null
|
||||
settings put secure default_input_method "$keyboard" &>/dev/null
|
||||
echo_log "設置鍵盤$(appinfo2 "${keyboard%/*}" 2>/dev/null)"
|
||||
fi
|
||||
update_apk2="${update_apk2:="暫無更新"}"
|
||||
add_app2="${add_app2:="暫無更新"}"
|
||||
echoRgb "\n -已更新的apk=\"$osn\"\n -已新增的備份=\"$osk\"\n -apk版本號無變化=\"$osj\"\n -下列為版本號已變更的應用\n$update_apk2\n -新增的備份....\n$add_app2\n -包含SSAID的應用\n$SSAID_apk2" "3"
|
||||
notification "101" "app備份完成 $(endtime 1 "應用備份" "3")"
|
||||
[[ $txt2 != "" ]] && {
|
||||
echo "$txt2" | sort | sed '/^$/d'>"$txt_path2"
|
||||
}
|
||||
if [[ $backup_media = true && ! -f ${0%/*}/app_details.json ]]; then
|
||||
A=1
|
||||
B="$(echo "$Custom_path" | egrep -v '#|#' | awk 'NF != 0 { count++ } END { print count }')"
|
||||
if [[ $B != "" ]]; then
|
||||
echoRgb "備份結束,備份多媒體" "1"
|
||||
notification "102" "Media備份開始"
|
||||
starttime1="$(date -u "+%s")"
|
||||
Backup_folder="$Backup/Media"
|
||||
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
|
||||
[[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder"
|
||||
app_details="$Backup_folder/app_details.json"
|
||||
[[ ! -f $app_details ]] && echo "{\n}">"$app_details"
|
||||
mediatxt="$Backup/mediaList.txt"
|
||||
[[ ! -f $mediatxt ]] && echo "#不需要恢復的資料夾請在開頭使用#注釋 比如:#Download" > "$mediatxt"
|
||||
echo "$Custom_path" | sed -e '/^#/d; /^$/d; s/\/$//' > "$TMPDIR/custom_paths"
|
||||
while read; do
|
||||
echoRgb "備份第$A/$B個資料夾 剩下$((B - A))個" "3"
|
||||
notification "102" "備份第$A/$B個資料夾 剩下$((B - A))個"
|
||||
starttime2="$(date -u "+%s")"
|
||||
if [[ ${REPLY##*/} = adb ]]; then
|
||||
if [[ $ksu != ksu ]]; then
|
||||
echoRgb "Magisk adb"
|
||||
Backup_data "${REPLY##*/}" "$REPLY"
|
||||
else
|
||||
echoRgb "KernelSU adb不支持備份" "0"
|
||||
Set_back_0
|
||||
fi
|
||||
else
|
||||
Backup_data "${REPLY##*/}" "$REPLY"
|
||||
fi
|
||||
endtime 2 "${REPLY##*/}備份" "1"
|
||||
echoRgb "完成$((A * 100 / B))% $hx$(echo "$Occupation_status" | awk 'END{print "剩餘:"$1"使用率:"$2}')" "2"
|
||||
rgb_d="$rgb_a"
|
||||
rgb_a=188 # ANSI 256色: 淺灰色分隔線
|
||||
echoRgb "_________________$(endtime 1 "已經")___________________"
|
||||
rgb_a="$rgb_d" && A=$((A + 1))
|
||||
done < "$TMPDIR/custom_paths"
|
||||
echoRgb "目錄↓↓↓\n -$Backup_folder"
|
||||
notification "102" "Media備份完成 $(endtime 1 "自定義備份")"
|
||||
endtime 1 "自定義備份"
|
||||
else
|
||||
echoRgb "自定義路徑為空 無法備份" "0"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
i=$((i + 1)); en=$((en + 1)); nskg=$((nskg + 1))
|
||||
done
|
||||
}
|
||||
|
||||
# ===================== 子函數: 清理與統計 =====================
|
||||
finalize_backup() {
|
||||
backup_wifi "$Backup/wifi"
|
||||
Set_screen_pause_seconds off
|
||||
[[ $user != 0 ]] && am stop-user "$user"
|
||||
Calculate_size "$Backup"
|
||||
echoRgb "批量備份完成"
|
||||
echoRgb "備份結束時間$(date +"%Y-%m-%d %H:%M:%S")"
|
||||
starttime1="$TIME"
|
||||
endtime 1 "批量備份開始到結束"
|
||||
notification "105" "備份完成 $(endtime 1 "批量備份開始到結束")"
|
||||
[[ -f $txt_path ]] && chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$txt_path"
|
||||
[[ -f $txt_path2 ]] && chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$txt_path2"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ===================== 重構後的 backup() 入口 =====================
|
||||
backup() {
|
||||
validate_backup_params
|
||||
prepare_app_list
|
||||
init_backup_storage
|
||||
backup_applications_loop
|
||||
finalize_backup
|
||||
}
|
||||
backup_update_apk() {
|
||||
Update_backup='true'
|
||||
backup
|
||||
}
|
||||
backup_media() {
|
||||
self_test
|
||||
backup_path
|
||||
echoRgb "假設反悔了要終止腳本請儘速離開此腳本點擊start.sh選擇終止腳本,否則腳本將繼續執行直到結束" "0"
|
||||
A=1
|
||||
B="$(echo "$Custom_path" | egrep -v '#|#' | awk 'NF != 0 { count++ } END { print count }')"
|
||||
if [[ $B != "" ]]; then
|
||||
starttime1="$(date -u "+%s")"
|
||||
Backup_folder="$Backup/Media"
|
||||
[[ ! -d $Backup_folder ]] && mkdir -p "$Backup_folder"
|
||||
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
|
||||
[[ ! -d $Backup/tools ]] && cp -r "$tools_path" "$Backup"
|
||||
[[ ! -f $Backup/restore_settings.conf ]] && update_Restore_settings_conf>"$Backup/restore_settings.conf"
|
||||
app_details="$Backup_folder/app_details.json"
|
||||
[[ ! -f $app_details ]] && echo "{\n}">"$app_details"
|
||||
filesize="$(find "$Backup_folder" -type f -printf "%s\n" 2>/dev/null | awk '{s+=$1} END {print s}')"
|
||||
mediatxt="$Backup/mediaList.txt"
|
||||
[[ ! -f $mediatxt ]] && echo "#不需要恢復的資料夾請在開頭使用#注釋 比如:#Download" > "$mediatxt"
|
||||
Set_screen_pause_seconds on
|
||||
notification "109" "Media備份開始"
|
||||
echo "$Custom_path" | sed -e '/^#/d; /^$/d; s/\/$//' > "$TMPDIR/custom_paths"
|
||||
while read; do
|
||||
echoRgb "備份第$A/$B個資料夾 剩下$((B - A))個" "3"
|
||||
starttime2="$(date -u "+%s")"
|
||||
if [[ ${REPLY##*/} = adb ]]; then
|
||||
if [[ $ksu != ksu ]]; then
|
||||
echoRgb "Magisk adb"
|
||||
Backup_data "${REPLY##*/}" "$REPLY"
|
||||
fi
|
||||
else
|
||||
Backup_data "${REPLY##*/}" "$REPLY"
|
||||
fi
|
||||
endtime 2 "${REPLY##*/}備份" "1"
|
||||
echoRgb "完成$((A * 100 / B))% $hx$(echo "$Occupation_status" | awk 'END{print "剩餘:"$1"使用率:"$2}')" "2" && echoRgb "____________________________________" && A=$((A + 1))
|
||||
done < "$TMPDIR/custom_paths"
|
||||
Calculate_size "$Backup_folder"
|
||||
Set_screen_pause_seconds off
|
||||
endtime 1 "自定義備份"
|
||||
notification "109" "Media備份完成 $(endtime 1 "自定義備份")"
|
||||
else
|
||||
echoRgb "自定義路徑為空 無法備份" "0"
|
||||
fi
|
||||
}
|
||||
Set_screen_pause_seconds () {
|
||||
if [[ $1 = on ]]; then
|
||||
#獲取系統設置的無操作息屏秒數
|
||||
if [[ $Get_dark_screen_seconds = "" ]]; then
|
||||
Get_dark_screen_seconds="$(settings get system screen_off_timeout)"
|
||||
#設置30分鐘後息屏
|
||||
settings put system screen_off_timeout 1800000
|
||||
echo_log "設置無操作息屏時間30分鐘"
|
||||
fi
|
||||
[[ $setDisplayPowerMode = true ]] && {
|
||||
setDisplay 0
|
||||
echo_log "設置螢幕狀態false"
|
||||
}
|
||||
elif [[ $1 = off ]]; then
|
||||
if [[ $Get_dark_screen_seconds != "" ]]; then
|
||||
settings put system screen_off_timeout "$Get_dark_screen_seconds"
|
||||
echo_log "設置無操作息屏時間為$Get_dark_screen_seconds"
|
||||
input keyevent 224
|
||||
fi
|
||||
[[ $setDisplayPowerMode = true ]] && {
|
||||
setDisplay 2
|
||||
echo_log "設置螢幕狀態true"
|
||||
}
|
||||
fi
|
||||
}
|
||||
172
tools/core/base_utils.sh
Normal file
172
tools/core/base_utils.sh
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/system/bin/sh
|
||||
# module: base_utils.sh
|
||||
|
||||
echoRgb() {
|
||||
#轉換echo顏色提高可讀性
|
||||
if [[ $2 = 0 ]]; then
|
||||
echo -e "\e[38;5;197m -!$1\e[0m"
|
||||
elif [[ $2 = 1 ]]; then
|
||||
echo -e "\e[38;5;121m -$1\e[0m"
|
||||
elif [[ $2 = 2 ]]; then
|
||||
echo -e "\e[38;5;${rgb_c}m -$1\e[0m"
|
||||
elif [[ $2 = 3 ]]; then
|
||||
echo -e "\e[38;5;${rgb_b}m -$1\e[0m"
|
||||
else
|
||||
echo -e "\e[38;5;${rgb_a}m -$1\e[0m"
|
||||
fi
|
||||
}
|
||||
Set_back_0() {
|
||||
return 0
|
||||
}
|
||||
Set_back_1() {
|
||||
return 1
|
||||
}
|
||||
endtime() {
|
||||
#計算總體切換時長耗費
|
||||
case $1 in
|
||||
1) starttime="${starttime1:-$(date -u "+%s")}" ;;
|
||||
2) starttime="${starttime2:-$(date -u "+%s")}" ;;
|
||||
*) starttime="$(date -u "+%s")" ;;
|
||||
esac
|
||||
endtime="$(date -u "+%s")"
|
||||
duration="$(echo $((endtime - starttime)) | awk '{t=split("60 秒 60 分 24 時 999 天",a);for(n=1;n<t;n+=2){if($1==0)break;s=$1%a[n]a[n+1]s;$1=int($1/a[n])}print s}')"
|
||||
[[ $duration != "" ]] && echo " -$2用時:$duration" || echo " -$2用時:0秒"
|
||||
}
|
||||
get_version() {
|
||||
while :; do
|
||||
keycheck
|
||||
case $? in
|
||||
42)
|
||||
[[ $Select_user = true ]] && branch="$1" || branch=true
|
||||
echoRgb "$1" "1"
|
||||
;;
|
||||
41)
|
||||
[[ $Select_user = true ]] && branch="$2" || branch=false
|
||||
echoRgb "$2" "0"
|
||||
;;
|
||||
*)
|
||||
echoRgb "keycheck錯誤" "0"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
sleep 0.3
|
||||
break
|
||||
done
|
||||
}
|
||||
isBoolean() {
|
||||
nsx="$1"
|
||||
if [[ $1 = 1 ]]; then
|
||||
nsx=true
|
||||
elif [[ $1 = 0 ]]; then
|
||||
nsx=false
|
||||
else
|
||||
echoRgb "$conf_path $2=$1填寫錯誤,正確值1or0" "0"
|
||||
Set_back_1
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
echo_log() {
|
||||
if [[ $? = 0 ]]; then
|
||||
[[ $2 = "" ]] && echoRgb "$1成功" "1"
|
||||
result=0
|
||||
Set_back_0
|
||||
else
|
||||
echoRgb "$1失敗,過世了" "0"
|
||||
notification "$RANDOM" "$name1: $1失敗,過世"
|
||||
result=1
|
||||
Set_back_1
|
||||
fi
|
||||
}
|
||||
kill_Serve() {
|
||||
LOCK_FILE="/tmp/backup_script_$$.lock"
|
||||
if [[ -f $LOCK_FILE ]]; then
|
||||
OLD_PID="$(cat "$LOCK_FILE")"
|
||||
if kill -0 "$OLD_PID" 2>/dev/null; then
|
||||
echo "發現先前的備份程序 (PID=$OLD_PID),將其終止"
|
||||
kill -KILL "$OLD_PID"
|
||||
echo "結束自身,避免重複執行"
|
||||
exit 1
|
||||
else
|
||||
echo "發現 lock 檔但程序已不存在,視為殘留 lock"
|
||||
fi
|
||||
fi
|
||||
echo $$ > "$LOCK_FILE"
|
||||
trap "rm -f '$LOCK_FILE'" EXIT
|
||||
}
|
||||
Show_boottime() {
|
||||
awk -F '.' '{run_days=$1 / 86400;run_hour=($1 % 86400)/3600;run_minute=($1 % 3600)/60;run_second=$1 % 60;printf("%d天%d時%d分%d秒",run_days,run_hour,run_minute,run_second)}' /proc/uptime 2>/dev/null
|
||||
}
|
||||
Enter_options() {
|
||||
echoRgb "$1" "2"
|
||||
unset option parameter
|
||||
while true ;do
|
||||
if [[ $option != "" ]]; then
|
||||
case $option in
|
||||
0|1)
|
||||
parameter="$option"
|
||||
[[ $option = 1 ]] && echoRgb "$2" "2" || echoRgb "$3" "2"
|
||||
break ;;
|
||||
*)
|
||||
echoRgb "$option參數錯誤 只能是0或1" "0"
|
||||
read option ;;
|
||||
esac
|
||||
else
|
||||
read option
|
||||
fi
|
||||
done
|
||||
}
|
||||
add_entry() {
|
||||
app_name="$1"
|
||||
package_name="$2"
|
||||
# 檢查是否已經存在同樣的應用名稱
|
||||
if [[ $(echo "$3" | cut -d' ' -f1 | grep -w "^$app_name$") = $app_name ]]; then
|
||||
if [[ $(echo "$3" | cut -d' ' -f2 | grep -w "^$package_name$") != $package_name ]]; then
|
||||
# 如果應用名稱存在但包名不同,則需要添加數字後綴
|
||||
count=1
|
||||
new_app_name="${app_name}_${count}"
|
||||
while echo "$3" | grep -q "$new_app_name"; do
|
||||
count=$((count + 1))
|
||||
new_app_name="${app_name}_${count}"
|
||||
done
|
||||
app_name="$new_app_name"
|
||||
fi
|
||||
fi
|
||||
REPLY="$app_name $package_name"
|
||||
}
|
||||
size() {
|
||||
local b_size get_size
|
||||
varr="$(echo "$1" | bc 2>/dev/null)"
|
||||
if [[ $varr != $1 ]]; then
|
||||
b_size="$(ls -l "$1" 2>/dev/null | awk '{print $5}')"
|
||||
else
|
||||
b_size="$1"
|
||||
fi
|
||||
if [[ $b_size -eq 0 ]]; then
|
||||
get_size="0 bytes"
|
||||
elif [[ $(echo "$b_size < 1024" | bc) -eq 1 ]]; then
|
||||
get_size="${b_size} bytes"
|
||||
elif [[ $(echo "$b_size < 1048576" | bc) -eq 1 ]]; then
|
||||
get_size="$(echo "scale=2; $b_size / 1024" | bc) KB"
|
||||
elif [[ $(echo "$b_size < 1073741824" | bc) -eq 1 ]]; then
|
||||
get_size="$(echo "scale=2; $b_size / 1048576" | bc) MB"
|
||||
else
|
||||
get_size="$(echo "scale=2; $b_size / 1073741824" | bc) GB"
|
||||
fi
|
||||
echo "$get_size"
|
||||
}
|
||||
self_test() {
|
||||
if [[ $(dumpsys deviceidle get charging) = false && $(dumpsys battery | awk '/level/{print $2}' | egrep -o '[0-9]+') -le 15 ]]; then
|
||||
echoRgb "電量$(dumpsys battery | awk '/level/{print $2}' | egrep -o '[0-9]+')%太低且未充電\n -為防止備份檔案或是恢復因低電量強制關機導致檔案損毀\n -請連接充電器後備份" "0" && exit 2
|
||||
fi
|
||||
}
|
||||
Validation_file() {
|
||||
MODDIR_NAME="${1%/*}"
|
||||
MODDIR_NAME="${MODDIR_NAME##*/}"
|
||||
FILE_NAME="${1##*/}"
|
||||
echoRgb "校驗$FILE_NAME"
|
||||
case ${FILE_NAME##*.} in
|
||||
zst) zstd -t "$1" 2>/dev/null ;;
|
||||
tar) tar -tf "$1" &>/dev/null ;;
|
||||
esac
|
||||
echo_log "${FILE_NAME##*.}校驗"
|
||||
}
|
||||
171
tools/core/config_mgmt.sh
Normal file
171
tools/core/config_mgmt.sh
Normal file
@@ -0,0 +1,171 @@
|
||||
#!/system/bin/sh
|
||||
# module: config_mgmt.sh
|
||||
|
||||
update_backup_settings_conf() {
|
||||
echo "#0關閉音量鍵選擇 (如選項未設置,則強制使用音量鍵選擇)
|
||||
#1開啟音量鍵選擇 (如選項已設置,則跳過該選項提示)
|
||||
#2使用鍵盤輸入,適用於無音量鍵可用設備選擇 (如選項未設置,則強制使用鍵盤輸入)
|
||||
Lo="${Lo:-0}"
|
||||
|
||||
#後台執行腳本
|
||||
0不能關閉當前終端,有壓縮速率
|
||||
1終端有可能完全無顯示,但是log會持續刷新,可直接完全關閉終端
|
||||
background_execution="${background_execution:-0}"
|
||||
|
||||
#腳本語言設置 留空則自動識別系統語言環境並翻譯
|
||||
#1簡體中文 0繁體中文
|
||||
Shell_LANG="$Shell_LANG"
|
||||
|
||||
#備份開始後偽裝亮屏
|
||||
#1開啟 0關閉
|
||||
setDisplayPowerMode="${setDisplayPowerMode:-0}"
|
||||
|
||||
#自定義備份文件輸出位置 支持相對路徑(留空則默認當前路徑)
|
||||
Output_path=\""$Output_path"\"
|
||||
|
||||
#自定義applist.txt位置 支持相對路徑(留空則默認當前路徑)
|
||||
list_location=\""$list_location"\"
|
||||
|
||||
#自動更新腳本(留空強制選擇)
|
||||
#1開啟 0關閉
|
||||
update="${update:-1}"
|
||||
|
||||
#自動更新的cdn節點,針對國內用戶使用,無牆或是使用VPN請設置0
|
||||
#0 直鏈下載
|
||||
#1 https://ghfast.top
|
||||
#2 https://shrill-pond-3e81.hunsh.workers.dev
|
||||
cdn=${cdn:-1}
|
||||
|
||||
#自定義屏蔽外部掛載點 例:OTG 虛擬SD等 多個掛載點請使用 | 區隔
|
||||
#屏蔽後不會提示音量鍵選擇,不影響Output_path指定外置存儲位置
|
||||
mount_point=\""${mount_point:-rannki|0000-1}"\"
|
||||
|
||||
#使用者(如0 999等用戶,如存在多個用戶留空強制選擇,無多個用戶則默認用戶0不詢問)
|
||||
user="$user"
|
||||
|
||||
#備份模式
|
||||
#1包含數據+安裝包,0僅包安裝包
|
||||
#此選項設置1時Backup_obb_data,Backup_user_data,blacklist_mode將可設置 0時Backup_user_data,Backup_obb_data,blacklist_mode選項不生效
|
||||
#此外設置0時將同時忽略appList.txt的!與任何黑名單設置(包括黑名單列表)
|
||||
Backup_Mode="${Backup_Mode:-1}"
|
||||
|
||||
#是否備份使用者數據 (1備份 0不備份 留空強制選擇)
|
||||
Backup_user_data="${Backup_user_data:-1}"
|
||||
|
||||
#是否備份外部數據 例:原神的數據包(1備份 0不備份 留空強制選擇)
|
||||
Backup_obb_data="${Backup_obb_data:-1}"
|
||||
|
||||
#是否在應用數據備份完成後備份自定義目錄
|
||||
#1開啟 0關閉
|
||||
backup_media="${backup_media:-0}"
|
||||
|
||||
#存在進程忽略備份(1忽略0備份)
|
||||
Background_apps_ignore="${Background_apps_ignore:-0}"
|
||||
|
||||
#添加自定義備份路徑 例:Download DCIM等文件夾 請使用絕對路徑,請勿刪除\"\"
|
||||
Custom_path=\""${Custom_path:-
|
||||
/storage/emulated/0/Pictures/
|
||||
/storage/emulated/0/Download/
|
||||
/storage/emulated/0/Music
|
||||
/storage/emulated/0/DCIM/
|
||||
/data/adb
|
||||
}"\"
|
||||
|
||||
#黑名單模式(1完全忽略,不備份 0僅備份安裝包,注意!此選項Backup_Mode=1時黑名單模式才能使用)
|
||||
blacklist_mode="${blacklist_mode:-0}"
|
||||
|
||||
#備份黑名單(備份策略由「黑名單模式」控制,此處只作為黑名單應用列表)
|
||||
blacklist=\""${blacklist:-
|
||||
#com.esunbank
|
||||
#com.chailease.tw.app.android.ccfappcust}"\"
|
||||
|
||||
#位於data的預裝應用白名單 例:相冊 錄音機 天氣 計算器等(默認屏蔽備份預裝應用,如需備份請添加預裝應用白名單)
|
||||
whitelist=\""${whitelist:-
|
||||
com.xiaomi.xmsf
|
||||
com.xiaomi.xiaoailite
|
||||
com.xiaomi.hm.health
|
||||
com.duokan.phone.remotecontroller
|
||||
com.miui.weather2
|
||||
com.milink.service
|
||||
com.android.soundrecorder
|
||||
com.miui.virtualsim
|
||||
com.xiaomi.vipaccount
|
||||
com.miui.fm
|
||||
com.xiaomi.shop
|
||||
com.xiaomi.smarthome
|
||||
com.miui.notes
|
||||
com.xiaomi.router
|
||||
com.xiaomi.mico
|
||||
dev.miuiicons.pedroz}"\"
|
||||
|
||||
#可被備份的系統應用白名單(默認屏蔽備份系統應用,如需備份請添加系統應用白名單)
|
||||
system=\""${system:-
|
||||
com.google.android.calendar
|
||||
com.google.android.gm
|
||||
com.google.android.googlequicksearchbox
|
||||
com.google.android.tts
|
||||
com.google.android.apps.maps
|
||||
com.google.android.apps.messaging
|
||||
com.google.android.inputmethod.latin
|
||||
com.instagram.android
|
||||
com.facebook.orca
|
||||
sh.siava.AOSPMods
|
||||
com.facebook.katana
|
||||
com.android.chrome}"\"
|
||||
|
||||
#壓縮算法(可用zstd tar,tar為僅打包 有什麼好用的壓縮算法請聯系我
|
||||
#zstd擁有良好的壓縮率與速度
|
||||
Compression_method=${Compression_method:-zstd}
|
||||
|
||||
#主色
|
||||
rgb_a="${rgb_a:-226}"
|
||||
#輔色
|
||||
rgb_b="${rgb_b:-123}"
|
||||
rgb_c="${rgb_c:-177}"" | sed 's/true/1/g ; s/false/0/g'
|
||||
}
|
||||
update_Restore_settings_conf() {
|
||||
echo "#0關閉音量鍵選擇 (如選項未設置,則強制使用音量鍵選擇)
|
||||
#1開啟音量鍵選擇 (如選項已設置,則跳過該選項提示)
|
||||
#2使用鍵盤輸入,適用於無音量鍵可用設備選擇 (如選項未設置,則強制使用鍵盤輸入)
|
||||
Lo="${Lo:-0}"
|
||||
|
||||
#後台執行腳本
|
||||
0不能關閉當前終端,有壓縮速率
|
||||
1終端有可能完全無顯示,但是log會持續刷新,可直接完全關閉終端
|
||||
background_execution="${background_execution:-0}"
|
||||
|
||||
#恢復開始後偽裝亮屏
|
||||
#1開啟 0關閉
|
||||
setDisplayPowerMode="${setDisplayPowerMode:-0}"
|
||||
|
||||
#腳本語言設置 為空自動針對當前系統語言環境自動翻譯
|
||||
#1簡體中文 0繁體中文
|
||||
Shell_LANG="$Shell_LANG"
|
||||
|
||||
#自動更新腳本(留空強制選擇)
|
||||
update="${update:-1}"
|
||||
|
||||
#自動更新的cdn節點,針對國內用戶使用,無牆或是使用VPN請設置0
|
||||
#0 直鏈下載
|
||||
#1 https://ghfast.top
|
||||
#2 https://shrill-pond-3e81.hunsh.workers.dev
|
||||
cdn=${cdn:-1}
|
||||
|
||||
#恢復模式(1恢復未安裝應用 0全恢復)
|
||||
recovery_mode="${recovery_mode:-0}"
|
||||
|
||||
#恢復資料夾
|
||||
media_recovery="${media_recovery:-0}"
|
||||
|
||||
#存在進程忽略恢復(1忽略0恢復)
|
||||
Background_apps_ignore="${Background_apps_ignore:-0}"
|
||||
|
||||
#使用者(如0 999等用戶,留空如存在多個用戶強制音量鍵選擇,無多用戶則默認0不詢問)
|
||||
user="$user"
|
||||
|
||||
#主色
|
||||
rgb_a="${rgb_a:-226}"
|
||||
#輔色
|
||||
rgb_b="${rgb_b:-123}"
|
||||
rgb_c="${rgb_c:-177}"" | sed 's/true/1/g ; s/false/0/g'
|
||||
}
|
||||
587
tools/core/restore_core.sh
Normal file
587
tools/core/restore_core.sh
Normal file
@@ -0,0 +1,587 @@
|
||||
#!/system/bin/sh
|
||||
# module: restore_core.sh
|
||||
|
||||
Release_data() {
|
||||
tar_path="$1"
|
||||
X="$path2/$name2"
|
||||
MODDIR_NAME="${tar_path%/*}"
|
||||
MODDIR_NAME="${MODDIR_NAME##*/}"
|
||||
FILE_NAME="${tar_path##*/}"
|
||||
FILE_NAME2="${FILE_NAME%%.*}"
|
||||
case ${FILE_NAME##*.} in
|
||||
zst | tar)
|
||||
unset FILE_PATH Size Selinux_state
|
||||
[[ -f $app_details ]] && Size="$(jq -r --arg entry "$FILE_NAME2" '.[$entry] | select(.Size != null).Size' "$app_details" 2>/dev/null)"
|
||||
case $FILE_NAME2 in
|
||||
user)
|
||||
if [[ -d $X ]]; then
|
||||
[[ $(jq -r '.[] | select(.Ssaid != null).keystore' "$app_details") = true ]] && echoRgb "$name1存在keystore 恢復可能閃退" "0"
|
||||
FILE_PATH="$path2"
|
||||
Selinux_state="$(LS "$X" | awk 'NF>1{print $1}' | sed -e "s/system_data_file/app_data_file/g" 2>/dev/null)"
|
||||
else
|
||||
echoRgb "$X不存在 無法恢復$FILE_NAME2數據" "0"
|
||||
fi ;;
|
||||
user_de)
|
||||
X="$path3/$name2"
|
||||
if [[ -d $X ]]; then
|
||||
FILE_PATH="$path3"
|
||||
Selinux_state="$(LS "$X" | awk 'NF>1{print $1}' | sed -e "s/system_data_file/app_data_file/g" 2>/dev/null)"
|
||||
else
|
||||
echoRgb "$X不存在 無法恢復$FILE_NAME2數據" "0"
|
||||
fi ;;
|
||||
data) FILE_PATH="$path/data" Selinux_state="$(LS "$FILE_PATH" | awk 'NF>1{print $1}' | sed -e "s/system_data_file/app_data_file/g" 2>/dev/null)" ;;
|
||||
obb) FILE_PATH="$path/obb" Selinux_state="$(LS "$FILE_PATH" | awk 'NF>1{print $1}' | sed -e "s/system_data_file/app_data_file/g" 2>/dev/null)";;
|
||||
thanox) FILE_PATH="/data/system" && find "/data/system" -name "thanos"* -maxdepth 1 -type d -exec rm -rf {} \; 2>/dev/null ;;
|
||||
*)
|
||||
if [[ $A != "" ]]; then
|
||||
if [[ ${MODDIR_NAME##*/} = Media ]]; then
|
||||
FILE_PATH="$(jq -r --arg entry "${FILE_NAME2}" 'select(.[$entry].path != null).[$entry].path' "$app_details")"
|
||||
if [[ $FILE_PATH = "" ]]; then
|
||||
echoRgb "路徑獲取失敗" "0"
|
||||
else
|
||||
echoRgb "解壓路徑↓\n -$FILE_PATH" "2"
|
||||
FILE_PATH="${FILE_PATH%/*}"
|
||||
[[ ! -d $FILE_PATH ]] && mkdir -p "$FILE_PATH"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echoRgb "$tar_path名稱似乎有誤" "0"
|
||||
fi ;;
|
||||
esac
|
||||
echoRgb "恢復$FILE_NAME2數據 釋放$(size "$Size")" "3"
|
||||
if [[ $FILE_PATH != "" ]]; then
|
||||
[[ ${MODDIR_NAME##*/} != Media ]] && rm -rf "$FILE_PATH/$name2"
|
||||
case ${FILE_NAME##*.} in
|
||||
zst) tar --checkpoint-action="ttyout=%T\r" -I zstd -xmpf "$tar_path" -C "$FILE_PATH" ;;
|
||||
tar) [[ ${MODDIR_NAME##*/} = Media ]] && tar --checkpoint-action="ttyout=%T\r" -axf "$tar_path" -C "$FILE_PATH" || tar --checkpoint-action="ttyout=%T\r" -amxf "$tar_path" -C "$FILE_PATH" ;;
|
||||
esac
|
||||
else
|
||||
Set_back_1
|
||||
fi
|
||||
echo_log "解壓縮$FILE_NAME"
|
||||
if [[ $result = 0 ]]; then
|
||||
case $FILE_NAME2 in
|
||||
user|data|obb|user_de)
|
||||
G="$(pm list packages -U --user "$user" </dev/null | awk -v pkg="$name2" -F'[ :]' '$2 == pkg {print $4}')"
|
||||
if [[ $G = "" ]]; then
|
||||
G="$(dumpsys package "$name2" 2>/dev/null | awk -F'uid=' '{print $2}' | egrep -o '[0-9]+' | head -n 1)"
|
||||
[[ $(echo "$G" | egrep -o '[0-9]+') = "" ]] && G="$(get_uid "$name2" 2>/dev/null)"
|
||||
fi
|
||||
G="$(echo "$G" | egrep -o '[0-9]+')"
|
||||
if [[ $G != "" ]]; then
|
||||
if [[ -d $X ]]; then
|
||||
case ${#G} in
|
||||
5)
|
||||
if [[ $user = 0 ]]; then
|
||||
uid="$G:$G"
|
||||
else
|
||||
uid="$user$G:$user$G"
|
||||
fi ;;
|
||||
6|7|8|9|10)
|
||||
uid="$G:$G" ;;
|
||||
esac
|
||||
case $FILE_NAME2 in
|
||||
user|user_de)
|
||||
case $FILE_NAME2 in
|
||||
user) [[ $X = $path2/$name2 ]] && Validation_settings="true" || Validation_settings="false" ;;
|
||||
user_de) [[ $X = $path3/$name2 ]] && Validation_settings="true" || Validation_settings="false" ;;
|
||||
esac
|
||||
if [[ $Validation_settings = true ]]; then
|
||||
chown -hR "$uid" "$X/"
|
||||
echo_log "設置用戶組$uid"
|
||||
chcon -hR "$Selinux_state" "$X/" 2>/dev/null
|
||||
echo_log "selinux上下文設置" "E"
|
||||
else
|
||||
echoRgb "路徑:$X出現錯誤"
|
||||
fi ;;
|
||||
data|obb)
|
||||
chown -hR "$uid" "$FILE_PATH/$name2/"
|
||||
echo_log "設置用戶組$uid" "E"
|
||||
chcon -hR "$Selinux_state" "$FILE_PATH/$name2/" 2>/dev/null
|
||||
echo_log "selinux上下文設置" "E" ;;
|
||||
esac
|
||||
else
|
||||
echoRgb "$FILE_NAME2路徑$X不存在" "0"
|
||||
fi
|
||||
else
|
||||
echoRgb "uid獲取失敗" "0"
|
||||
fi
|
||||
;;
|
||||
thanox)
|
||||
restorecon -RF "$(find "/data/system" -name "thanos"* -maxdepth 1 -type d 2>/dev/null)/" 2>/dev/null
|
||||
echo_log "selinux上下文設置" && echoRgb "警告 thanox配置恢復後務必重啟\n -否則不生效" "0"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echoRgb "$FILE_NAME 壓縮包不支持解壓縮" "0"
|
||||
Set_back_1
|
||||
;;
|
||||
esac
|
||||
rm -rf "$TMPDIR"/*
|
||||
}
|
||||
installapk() {
|
||||
apkfile="$(find "$Backup_folder" -maxdepth 1 -name "apk.*" -type f 2>/dev/null)"
|
||||
if [[ $apkfile != "" ]]; then
|
||||
rm -rf "$TMPDIR"/*
|
||||
case ${apkfile##*.} in
|
||||
zst) tar --checkpoint-action="ttyout=%T\r" -I zstd -xmpf "$apkfile" -C "$TMPDIR" ;;
|
||||
tar) tar --checkpoint-action="ttyout=%T\r" -xmpf "$apkfile" -C "$TMPDIR" ;;
|
||||
*)
|
||||
echoRgb "${apkfile##*/} 壓縮包不支持解壓縮" "0"
|
||||
Set_back_1
|
||||
;;
|
||||
esac
|
||||
echo_log "${apkfile##*/}解壓縮" && [[ -f $Backup_folder/nmsl.apk ]] && cp -r "$Backup_folder/nmsl.apk" "$TMPDIR"
|
||||
else
|
||||
echoRgb "你的Apk壓縮包離家出走了,可能備份後移動過程遺失了\n -解決辦法手動安裝Apk後再執行恢復腳本" "0"
|
||||
fi
|
||||
if [[ $result = 0 ]]; then
|
||||
case $(find "$TMPDIR" -maxdepth 1 -name "*.apk" -type f 2>/dev/null | wc -l) in
|
||||
1)
|
||||
echoRgb "恢復普通apk" "2"
|
||||
INSTALL "$TMPDIR"/*.apk
|
||||
echo_log "Apk安裝"
|
||||
;;
|
||||
0)
|
||||
echoRgb "$TMPDIR中沒有apk" "0"
|
||||
;;
|
||||
*)
|
||||
echoRgb "恢復split apk" "2"
|
||||
b="$(create | egrep -o '[0-9]+')"
|
||||
if [[ -f $TMPDIR/nmsl.apk ]]; then
|
||||
INSTALL "$TMPDIR/nmsl.apk"
|
||||
echo_log "nmsl.apk安裝"
|
||||
fi
|
||||
find "$TMPDIR" -maxdepth 1 -name "*.apk" -type f | grep -v 'nmsl.apk' | while read apk; do
|
||||
pm install-write "$b" "${apk##*/}" "$apk" </dev/null >/dev/null
|
||||
echo_log "${apk##*/}安裝"
|
||||
done
|
||||
pm install-commit "$b" >/dev/null
|
||||
echo_log "split Apk安裝"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
# ===================== 子函數: 前置驗證 =====================
|
||||
Restore_PreCheck() {
|
||||
self_test
|
||||
disable_verify
|
||||
[[ ! -d $path2 ]] && echoRgb "設備不存在user目錄" "0" && exit 1
|
||||
}
|
||||
|
||||
# ===================== 子函數: 批量恢復配置 =====================
|
||||
Restore_LoadBatchConfig() {
|
||||
if [[ ! -f ${0%/*}/app_details.json ]]; then
|
||||
echoRgb "假設反悔了要終止腳本請儘速離開此腳本點擊$MODDIR_NAME/start.sh選擇終止腳本\n -否則腳本將繼續執行直到結束" "0"
|
||||
echoRgb "如果大量提示找不到資料夾請執行$MODDIR_NAME/start.sh選擇轉換資料夾名稱"
|
||||
txt="$MODDIR/appList.txt"
|
||||
[[ ! -f $txt ]] && echoRgb "請執行start.sh獲取應用列表再來恢復" "0" && exit 2
|
||||
sort -u "$txt" -o "$txt" 2>/dev/null
|
||||
i=1
|
||||
r="$(egrep -v '#|#' "$txt" 2>/dev/null | awk 'NF != 0 { count++ } END { print count }')"
|
||||
[[ $r = "" ]] && echoRgb "appList.txt包名為空或是被注釋了\n -請執行start.sh獲取應用列表再來恢復" "0" && exit 1
|
||||
Backup_folder2="$MODDIR/Media"
|
||||
#校驗選填是否正確
|
||||
case $Lo in
|
||||
0)
|
||||
[[ $recovery_mode != "" ]] && isBoolean "$recovery_mode" "recovery_mode" && recovery_mode="$nsx" || {
|
||||
echoRgb "選擇應用恢復模式\n -音量上僅恢復未安裝,下全恢復"
|
||||
get_version "恢復未安裝" "全恢復" && recovery_mode="$branch"
|
||||
}
|
||||
[[ $setDisplayPowerMode != "" ]] && isBoolean "$setDisplayPowerMode" "setDisplayPowerMode" && setDisplayPowerMode="$nsx" || {
|
||||
echoRgb "應用恢復時關閉螢幕\n -音量上關閉,下不關閉"
|
||||
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
|
||||
}
|
||||
Get_user="$(echo "$MODDIR" | rev | cut -d '/' -f1 | cut -d '_' -f1 | rev | egrep -o '[0-9]+')"
|
||||
if [[ $Get_user != $user ]]; then
|
||||
echoRgb "檢測當前用戶$user與恢復資料夾用戶:$Get_user不同,音量上繼續恢復,下不恢復並離開腳本"
|
||||
get_version "恢復安裝" "不恢復安裝" && recovery_mode2="$branch"
|
||||
fi
|
||||
if [[ -d $Backup_folder2 ]]; then
|
||||
[[ $media_recovery != "" ]] && isBoolean "$media_recovery" "media_recovery" && media_recovery="$nsx" || {
|
||||
echoRgb "是否恢復多媒體數據\n -音量上恢復,音量下不恢復" "2"
|
||||
get_version "恢復媒體數據" "跳過恢復媒體數據" && media_recovery="$branch"
|
||||
}
|
||||
fi
|
||||
[[ $Background_apps_ignore != "" ]] && isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx" || {
|
||||
echoRgb "存在進程忽略恢復\n -音量上忽略,音量下恢復" "2"
|
||||
get_version "忽略" "恢復" && Background_apps_ignore="$branch"
|
||||
} ;;
|
||||
1)
|
||||
echoRgb "選擇應用恢復模式\n -音量上僅恢復未安裝,下全恢復"
|
||||
get_version "恢復未安裝" "全恢復" && recovery_mode="$branch"
|
||||
echoRgb "應用恢復時關閉螢幕\n -音量上關閉,下不關閉"
|
||||
get_version "關閉" "不關閉" && setDisplayPowerMode="$branch"
|
||||
Get_user="$(echo "$MODDIR" | rev | cut -d '/' -f1 | cut -d '_' -f1 | rev | egrep -o '[0-9]+')"
|
||||
if [[ $Get_user != $user ]]; then
|
||||
echoRgb "檢測當前用戶$user與恢復資料夾用戶:$Get_user不同,音量上繼續恢復,下不恢復並離開腳本"
|
||||
get_version "恢復安裝" "不恢復安裝" && recovery_mode2="$branch"
|
||||
fi
|
||||
echoRgb "是否恢復多媒體數據\n -音量上恢復,音量下不恢復" "2"
|
||||
get_version "恢復媒體數據" "跳過恢復媒體數據" && media_recovery="$branch"
|
||||
echoRgb "存在進程忽略恢復\n -音量上忽略,音量下恢復" "2"
|
||||
get_version "忽略" "恢復" && Background_apps_ignore="$branch" ;;
|
||||
2)
|
||||
[[ $recovery_mode = "" ]] && {
|
||||
Enter_options "選擇應用恢復模式\n -輸入1僅恢復未安裝,0全恢復" "僅恢復未安裝" "全恢復" && isBoolean "$parameter" "recovery_mode" && recovery_mode="$nsx"
|
||||
} || {
|
||||
isBoolean "$recovery_mode" "recovery_mode" && recovery_mode="$nsx"
|
||||
}
|
||||
[[ $setDisplayPowerMode = "" ]] && {
|
||||
Enter_options "應用恢復時關閉螢幕\n -輸入1關閉,0不關閉" "關閉" "不關閉" && isBoolean "$parameter" "setDisplayPowerMode" && setDisplayPowerMode="$nsx"
|
||||
} || {
|
||||
isBoolean "$recovery_mode" "recovery_mode" && recovery_mode="$nsx"
|
||||
}
|
||||
Get_user="$(echo "$MODDIR" | rev | cut -d '/' -f1 | cut -d '_' -f1 | rev | egrep -o '[0-9]+')"
|
||||
[[ $Get_user != $user ]] && {
|
||||
[[ $recovery_mode2 = "" ]] && {
|
||||
Enter_options "檢測當前用戶$user與恢復資料夾用戶:$Get_user不同,輸入1繼續恢復,0不恢復並離開腳本" "恢復安裝" "離開腳本" && isBoolean "$parameter" "recovery_mode2" && recovery_mode2="$nsx"
|
||||
} || {
|
||||
isBoolean "$recovery_mode2" "recovery_mode2" && recovery_mode2="$nsx"
|
||||
}
|
||||
}
|
||||
[[ $media_recovery = "" ]] && {
|
||||
Enter_options "是否恢復多媒體\n -輸入1僅恢復,0不恢復" "恢復" "不恢復" && isBoolean "$parameter" "media_recovery" && media_recovery="$nsx"
|
||||
} || {
|
||||
isBoolean "$media_recovery" "media_recovery" && media_recovery="$nsx"
|
||||
}
|
||||
[[ $Background_apps_ignore = "" ]] && {
|
||||
Enter_options "存在進程忽略恢復\n -輸入1不恢復,0恢復" "忽略" "恢復" && isBoolean "$parameter" "Background_apps_ignore" && Background_apps_ignore="$nsx"
|
||||
} || {
|
||||
isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx"
|
||||
} ;;
|
||||
*) echoRgb "$conf_path Lo=$Lo填寫錯誤,正確值0 1 2" "0" && exit 2 ;;
|
||||
esac
|
||||
[[ $recovery_mode2 = false ]] && exit 2
|
||||
if [[ $recovery_mode = true && $ssaid_mode != true ]]; then
|
||||
echoRgb "獲取未安裝應用中"
|
||||
Apk_info="$(pm list packages --user "$user" | cut -f2 -d ':' | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
|
||||
if [[ $Apk_info != "" ]]; then
|
||||
[[ $Apk_info = *"Failure calling service package"* ]] && Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
|
||||
else
|
||||
Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
|
||||
fi
|
||||
[[ $Apk_info = "" ]] && echoRgb "Apk_info變量為空" "0" && exit
|
||||
while read -r ; do
|
||||
if [[ $(echo "$REPLY" | sed 's/^[ \t]*//') != \#* ]]; then
|
||||
app=($REPLY $REPLY)
|
||||
if [[ ${app[1]} != "" && ${app[2]} != "" ]]; then
|
||||
[[ $(echo "$Apk_info" | awk -v pkg="${app[1]}" '$1 == pkg {print $1}') = "" ]] && Tmplist="$Tmplist\n$REPLY"
|
||||
fi
|
||||
fi
|
||||
done < "$txt"
|
||||
if [[ $(echo "$Tmplist" | awk 'NF != 0 { count++ } END { print count }') != "" ]]; then
|
||||
echoRgb "獲取完成 預計安裝$(echo "$Tmplist" | awk 'NF != 0 { count++ } END { print count }')個應用"
|
||||
txt="$Tmplist"
|
||||
case $Lo in
|
||||
0|1)
|
||||
echoRgb "未安裝應用列表\n$txt\n確認無誤使用音量上繼續恢復,音量下退出腳本" "1"
|
||||
get_version "恢復安裝" "退出腳本" ;;
|
||||
2)
|
||||
Enter_options "未安裝應用列表\n$txt\n-輸入1退出腳本,0恢復" "退出腳本" "恢復安裝" isBoolean "$parameter" "branch" && branch="$nsx" ;;
|
||||
esac
|
||||
[[ $branch = false ]] && exit
|
||||
else
|
||||
echoRgb "獲取完成 但備份內應用都已安裝....正在退出腳本" "0" && exit 0
|
||||
fi
|
||||
fi
|
||||
if [[ $ssaid_mode = true ]]; then
|
||||
while read; do
|
||||
if [[ $(jq -r '.[] | select(.Ssaid != null).Ssaid' "$REPLY") != "" ]]; then
|
||||
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "$REPLY" | head -n 1)"
|
||||
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$REPLY")"
|
||||
if [[ $ssaid_name = "" ]]; then
|
||||
ssaid_name="$ChineseName $PackageName"
|
||||
else
|
||||
ssaid_name="$ssaid_name\n$ChineseName $PackageName"
|
||||
fi
|
||||
fi
|
||||
done<<<"$(find "$MODDIR" -maxdepth 2 -name "app_details.json" -type f 2>/dev/null | sort)"
|
||||
[[ $ssaid_name != "" ]] && txt="$ssaid_name"
|
||||
fi
|
||||
if [[ ! -f $txt ]]; then
|
||||
[[ $(echo "$txt") != "" ]] && txt="$(echo "$txt" | sed -e '/^$/d')"
|
||||
else
|
||||
txt="$(egrep -v '#|#' "$txt" | sed -e '/^$/d')"
|
||||
fi
|
||||
r="$(echo "$txt" | awk 'NF != 0 { count++ } END { print count }')"
|
||||
DX="批量恢復"
|
||||
else
|
||||
i=1
|
||||
r=1
|
||||
Backup_folder="$MODDIR"
|
||||
app_details="$Backup_folder/app_details.json"
|
||||
if [[ ! -f $app_details ]]; then
|
||||
echoRgb "$app_details遺失,無法獲取包名" "0" && exit 1
|
||||
else
|
||||
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "$app_details" | head -n 1)"
|
||||
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$app_details")"
|
||||
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
|
||||
fi
|
||||
name1="$ChineseName"
|
||||
name1="${name1:="${Backup_folder##*/}"}"
|
||||
[[ $name1 = "" ]] && echoRgb "應用名獲取失敗" "0" && exit 2
|
||||
name2="$PackageName"
|
||||
[[ $name2 = "" ]] && echoRgb "包名獲取失敗" "0" && exit 2
|
||||
DX="單獨恢復"
|
||||
[[ $Background_apps_ignore != "" ]] && isBoolean "$Background_apps_ignore" "Background_apps_ignore" && Background_apps_ignore="$nsx" || {
|
||||
echoRgb "存在進程忽略恢復\n -音量上忽略,音量下恢復" "2"
|
||||
get_version "忽略" "恢復" && Background_apps_ignore="$branch"
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
# ===================== 子函數: 恢復循環初始化 =====================
|
||||
Restore_InitializeLoop() {
|
||||
starttime1="$(date -u "+%s")"
|
||||
TIME="$starttime1"
|
||||
Set_screen_pause_seconds on
|
||||
en=118 # ANSI 256色起始編號,用於通知欄圖標顏色
|
||||
notification "105" "開始恢復app"
|
||||
}
|
||||
|
||||
# ===================== 子函數: 單應用恢復 =====================
|
||||
Restore_ProcessSingleApp() {
|
||||
while [[ $i -le $r ]]; do
|
||||
[[ $en -ge 229 ]] && en=118 # 超過229重置,避免顏色越界
|
||||
if [[ ! -f ${0%/*}/app_details.json ]]; then
|
||||
echoRgb "恢復第$i/$r個應用 剩下$((r - i))個" "3"
|
||||
notification "105" "恢復第$i/$r個應用 剩下$((r - i))個
|
||||
恢復 $name1"
|
||||
name1="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f1)"
|
||||
name2="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f2)"
|
||||
unset No_backupdata apk_version Permissions
|
||||
if [[ $name1 = *! || $name1 = *! ]]; then
|
||||
name1="$(echo "$name1" | sed 's/!//g ; s/!//g')"
|
||||
echoRgb "跳過恢復$name1 所有數據" "0"
|
||||
No_backupdata=1
|
||||
fi
|
||||
Backup_folder="$MODDIR/$name1"
|
||||
if [[ -f "$Backup_folder/app_details.json" ]]; then
|
||||
app_details="$Backup_folder/app_details.json"
|
||||
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
|
||||
else
|
||||
echoRgb "$Backup_folder/app_details.json不存在" "0"
|
||||
fi
|
||||
[[ $name2 = "" ]] && echoRgb "應用包名獲取失敗" "0" && exit 1
|
||||
fi
|
||||
if [[ -d $Backup_folder ]]; then
|
||||
echoRgb "恢復$name1" "2"
|
||||
Background_application_list
|
||||
restore="true"
|
||||
[[ $Backstage != "" && $(echo "$Backstage" | egrep -w "^$name2$") != "" ]] && echoRgb "$name1存在後台 忽略恢復" "0" && restore="false"
|
||||
[[ $restore = true ]] && {
|
||||
starttime2="$(date -u "+%s")"
|
||||
if [[ $(pm list packages --user "$user" | awk -v pkg="$name2" -F':' '$2 == pkg {print $2}') = "" ]]; then
|
||||
installapk
|
||||
else
|
||||
[[ $apk_version -gt $(pm list packages --show-versioncode --user "$user" "$name2" 2>/dev/null | cut -f3 -d ':' | head -n 1) ]] && installapk && [[ $? = 0 ]] && echoRgb "版本提升$(pm list packages --show-versioncode --user "$user" "$name2" 2>/dev/null | cut -f3 -d ':' | head -n 1)>$apk_version" "1"
|
||||
fi
|
||||
if [[ $(pm list packages --user "$user" | awk -v pkg="$name2" -F':' '$2 == pkg {print $2}') != "" ]]; then
|
||||
if [[ $No_backupdata = "" ]]; then
|
||||
[[ $name2 != *mt* ]] && {
|
||||
kill_app
|
||||
find "$Backup_folder" -maxdepth 1 ! -name "apk.*" -name "*.tar*" -type f 2>/dev/null | sort | while read; do
|
||||
Release_data "$REPLY"
|
||||
done
|
||||
unset G
|
||||
restore_permissions
|
||||
Ssaid="$(jq -r '.[] | select(.Ssaid != null).Ssaid' "$app_details")"
|
||||
if [[ $Ssaid != "" ]]; then
|
||||
SSAID_Package="$(echo "$name1 $name2 $Ssaid")"
|
||||
SSAID_Package2="$(echo "$SSAID_Package\n$SSAID_Package2")"
|
||||
unset Ssaid
|
||||
fi
|
||||
}
|
||||
fi
|
||||
else
|
||||
[[ $No_backupdata = "" ]]&& echoRgb "$name1沒有安裝無法恢復數據" "0"
|
||||
fi
|
||||
endtime 2 "$name1恢復" "2" && echoRgb "完成$((i * 100 / r))%" "3"
|
||||
rgb_d="$rgb_a"
|
||||
rgb_a=188 # ANSI 256色: 淺灰色分隔線
|
||||
echoRgb "_________________$(endtime 1 "已經")___________________"
|
||||
rgb_a="$rgb_d"
|
||||
}
|
||||
else
|
||||
echoRgb "$Backup_folder資料夾遺失,無法恢復" "0"
|
||||
fi
|
||||
Restore_ProcessSSAIDAndMedia
|
||||
i=$((i + 1)); en=$((en + 1)); nskg=$((nskg + 1))
|
||||
done
|
||||
}
|
||||
|
||||
# ===================== 子函數: SSAID 與媒體恢復 =====================
|
||||
Restore_ProcessSSAIDAndMedia() {
|
||||
if [[ $i = $r ]]; then
|
||||
endtime 1 "應用恢復" "2"
|
||||
[[ $SSAID_Package2 != "" ]] && {
|
||||
echoRgb "開始恢復saaid" "0"
|
||||
set_ssaid "$(echo "$SSAID_Package2" | awk '{printf "%s %s ", $2, $3}')"
|
||||
ssaid_info="$(get_ssaid "$(echo "$SSAID_Package2" | awk '{printf "%s ", $2}')")"
|
||||
echo "$SSAID_Package2" | while read; do
|
||||
Ssaid="$(echo "$REPLY" | cut -d' ' -f3)"
|
||||
name1="$(echo "$REPLY" | cut -d' ' -f1)"
|
||||
name2="$(echo "$REPLY" | cut -d' ' -f2)"
|
||||
if [[ $(awk -v pkg="$name2" '$1 == pkg {print $2}'<<<"$ssaid_info") = $Ssaid ]]; then
|
||||
echoRgb "$name1 SSAID恢復成功" "1"
|
||||
else
|
||||
echoRgb "$name1 SSAID恢復失敗" "0"
|
||||
fi
|
||||
unset Ssaid
|
||||
done
|
||||
echoRgb "SSAID恢復後必須重新開機套用,否則應用閃退,如果沒有應用恢復ssaid則無須重啟" "0"
|
||||
notification "107" "SSAID恢復後必須重新開機套用,否則應用閃退,如果沒有應用恢復ssaid則無須重啟"
|
||||
}
|
||||
notification "105" "app恢復完成 $(endtime 1 "應用恢復" "2")"
|
||||
[[ ! -f ${0%/*}/app_details.json ]] && {
|
||||
if [[ $media_recovery = true ]]; then
|
||||
starttime1="$(date -u "+%s")"
|
||||
app_details="$Backup_folder2/app_details.json"
|
||||
txt="$MODDIR/mediaList.txt"
|
||||
sort -u "$txt" -o "$txt" 2>/dev/null
|
||||
A=1
|
||||
B="$(egrep -v '#|#' "$txt" 2>/dev/null | awk 'NF != 0 { count++ } END { print count }')"
|
||||
[[ $B = "" ]] && echoRgb "mediaList.txt壓縮包名為空或是被注釋了\n -請執行start.sh獲取列表再來恢復" "0" && B=0
|
||||
notification "106" "Media恢復開始"
|
||||
while [[ $A -le $B ]]; do
|
||||
name1="$(egrep -v '#|#' "$txt" 2>/dev/null | sed -e '/^$/d' | sed -n "${A}p" | awk '{print $1}')"
|
||||
starttime2="$(date -u "+%s")"
|
||||
echoRgb "恢復第$A/$B個壓縮包 剩下$((B - A))個" "3"
|
||||
Release_data "$Backup_folder2/$name1"
|
||||
endtime 2 "$FILE_NAME2恢復" "2" && echoRgb "完成$((A * 100 / B))%" "3" && echoRgb "____________________________________" && A=$((A + 1))
|
||||
done
|
||||
endtime 1 "自定義恢復" "2"
|
||||
notification "106" "Media恢復完成 $(endtime 1 "Media恢復" "2")"
|
||||
fi
|
||||
recover_wifi "$MODDIR/wifi"
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
# ===================== 子函數: 清理 =====================
|
||||
Restore_Cleanup() {
|
||||
Set_screen_pause_seconds off
|
||||
[[ $user != 0 ]] && am stop-user "$user"
|
||||
starttime1="$TIME"
|
||||
echoRgb "$DX完成" && endtime 1 "$DX開始到結束"
|
||||
notification "109" "恢復完成 $(endtime 1 "$DX開始到結束")"
|
||||
rm -rf "$TMPDIR"/*
|
||||
}
|
||||
|
||||
# ===================== 重構後的 Restore() 入口 =====================
|
||||
Restore() {
|
||||
Restore_PreCheck
|
||||
Restore_LoadBatchConfig
|
||||
Restore_InitializeLoop
|
||||
Restore_ProcessSingleApp
|
||||
Restore_ProcessSSAIDAndMedia
|
||||
Restore_Cleanup
|
||||
}
|
||||
Restore3() {
|
||||
self_test
|
||||
case $Lo in
|
||||
0|1)
|
||||
echoRgb "點錯了?這是恢復自定義資料夾腳本 如果你是要恢復應用那你就點錯了\n -音量上繼續恢復自定義資料夾,音量下離開腳本" "2"
|
||||
echoRgb "假設反悔了要終止腳本請儘速離開此腳本點擊start.sh選擇終止腳本,否則腳本將繼續執行直到結束" "0"
|
||||
get_version "恢復自定義資料夾" "離開腳本" && [[ $branch = false ]] && exit 0 ;;
|
||||
2)
|
||||
Enter_options "點錯了?這是恢復自定義資料夾腳本 如果你是要恢復應用那你就點錯了\n -輸入1繼續恢復自定義資料夾,輸入0離開腳本" "恢復" "退出腳本" && isBoolean "$parameter" "branch" && branch="$nsx" && [[ $branch = false ]] && exit 0 ;;
|
||||
esac
|
||||
mediaDir="$MODDIR/Media"
|
||||
[[ -f "$mediaDir/app_details.json" ]] && app_details="$mediaDir/app_details.json"
|
||||
Backup_folder2="$mediaDir"
|
||||
[[ ! -d $mediaDir ]] && echoRgb "媒體資料夾不存在" "0" && exit 2
|
||||
txt="$MODDIR/mediaList.txt"
|
||||
[[ ! -f $txt ]] && echoRgb "請執行start.sh獲取媒體列表再來恢復" "0" && exit 2
|
||||
sort -u "$txt" -o "$txt" 2>/dev/null
|
||||
#記錄開始時間
|
||||
starttime1="$(date -u "+%s")"
|
||||
echo_log() {
|
||||
if [[ $? = 0 ]]; then
|
||||
echoRgb "$1成功" "1" && result=0
|
||||
else
|
||||
echoRgb "$1恢復失敗,過世了" "0" && result=1
|
||||
fi
|
||||
}
|
||||
starttime1="$(date -u "+%s")"
|
||||
A=1
|
||||
B="$(egrep -v '#|#' "$txt" 2>/dev/null | awk 'NF != 0 { count++ } END { print count }')"
|
||||
Set_screen_pause_seconds on
|
||||
[[ $B = "" ]] && echoRgb "mediaList.txt壓縮包名為空或是被注釋了\n -請執行start.sh獲取列表再來恢復" "0" && exit 1
|
||||
notification "108" "Media恢復開始"
|
||||
while [[ $A -le $B ]]; do
|
||||
name1="$(egrep -v '#|#' "$txt" 2>/dev/null | sed -e '/^$/d' | sed -n "${A}p" | awk '{print $1}')"
|
||||
starttime2="$(date -u "+%s")"
|
||||
echoRgb "恢復第$A/$B個壓縮包 剩下$((B - A))個" "3"
|
||||
Release_data "$mediaDir/$name1"
|
||||
endtime 2 "$FILE_NAME2恢復" "2" && echoRgb "完成$((A * 100 / B))%" "3" && echoRgb "____________________________________" && A=$((A + 1))
|
||||
done
|
||||
Set_screen_pause_seconds off
|
||||
endtime 1 "恢復結束"
|
||||
notification "108" "Media恢復完成 $(endtime 1 "Media恢復")"
|
||||
}
|
||||
Restore4() {
|
||||
if [[ $ssaid_mode_1 = true ]]; then
|
||||
while read; do
|
||||
if [[ $(jq -r '.[] | select(.Ssaid != null).Ssaid' "$REPLY") != "" ]]; then
|
||||
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "$REPLY" | head -n 1)"
|
||||
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$REPLY")"
|
||||
if [[ $ssaid_name = "" ]]; then
|
||||
ssaid_name="$ChineseName $PackageName"
|
||||
else
|
||||
ssaid_name="$ssaid_name\n$ChineseName $PackageName"
|
||||
fi
|
||||
fi
|
||||
done<<<"$(find "$MODDIR" -maxdepth 2 -name "app_details.json" -type f 2>/dev/null | sort)"
|
||||
[[ $ssaid_name != "" ]] && txt="$ssaid_name"
|
||||
i=1
|
||||
[[ $(echo "$txt") != "" ]] && txt="$(echo "$txt" | sed -e '/^$/d')"
|
||||
r="$(echo "$txt" | awk 'NF != 0 { count++ } END { print count }')"
|
||||
while [[ $i -le $r ]]; do
|
||||
name1="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f1)"
|
||||
name2="$(echo "$txt" | sed -n "${i}p" | cut -d' ' -f2)"
|
||||
Backup_folder="$MODDIR/$name1"
|
||||
if [[ -f "$Backup_folder/app_details.json" ]]; then
|
||||
app_details="$Backup_folder/app_details.json"
|
||||
apk_version="$(jq -r '.[] | select(.apk_version != null).apk_version' "$app_details")"
|
||||
else
|
||||
echoRgb "$Backup_folder/app_details.json不存在" "0"
|
||||
fi
|
||||
[[ $name2 = "" ]] && echoRgb "應用包名獲取失敗" "0" && exit 1
|
||||
if [[ $(pm list packages --user "$user" | awk -v pkg="$name2" -F':' '$2 == pkg {print $2}') != "" ]]; then
|
||||
[[ $name2 != *mt* ]] && {
|
||||
kill_app
|
||||
Ssaid="$(jq -r '.[] | select(.Ssaid != null).Ssaid' "$app_details")"
|
||||
if [[ $Ssaid != "" ]]; then
|
||||
SSAID_Package="$(echo "$name1 $name2 $Ssaid")"
|
||||
SSAID_Package2="$(echo "$SSAID_Package\n$SSAID_Package2")"
|
||||
unset Ssaid
|
||||
fi
|
||||
}
|
||||
fi
|
||||
if [[ $i = $r ]]; then
|
||||
[[ $SSAID_Package2 != "" ]] && {
|
||||
echoRgb "開始恢復saaid" "0"
|
||||
set_ssaid "$(echo "$SSAID_Package2" | awk '{printf "%s %s ", $2, $3}')"
|
||||
ssaid_info="$(get_ssaid "$(echo "$SSAID_Package2" | awk '{printf "%s ", $2}')")"
|
||||
echo "$SSAID_Package2" | while read; do
|
||||
Ssaid="$(echo "$REPLY" | cut -d' ' -f3)"
|
||||
name1="$(echo "$REPLY" | cut -d' ' -f1)"
|
||||
name2="$(echo "$REPLY" | cut -d' ' -f2)"
|
||||
if [[ $(awk -v pkg="$name2" '$1 == pkg {print $2}'<<<"$ssaid_info") = $Ssaid ]]; then
|
||||
echoRgb "$name1 SSAID恢復成功" "1"
|
||||
else
|
||||
echoRgb "$name1 SSAID恢復失敗" "0"
|
||||
fi
|
||||
unset Ssaid
|
||||
done
|
||||
echoRgb "SSAID恢復後必須重新開機套用,否則應用閃退,如果沒有應用恢復ssaid則無須重啟" "0"
|
||||
notification "107" "SSAID恢復後必須重新開機套用,否則應用閃退,如果沒有應用恢復ssaid則無須重啟"
|
||||
}
|
||||
fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
fi
|
||||
573
tools/core/updater.sh
Normal file
573
tools/core/updater.sh
Normal file
@@ -0,0 +1,573 @@
|
||||
#!/system/bin/sh
|
||||
# module: updater.sh
|
||||
|
||||
backup_wifi() {
|
||||
[[ ! -d $1 ]] && mkdir -p "$1"
|
||||
if [[ -d $1 ]]; then
|
||||
echoRgb "備份wifi密碼"
|
||||
rm -rf "$1"/*
|
||||
app_process /system/bin com.xayah.dex.NetworkUtil saveNetworks>"$1/wifi.json"
|
||||
echo_log "wifi備份"
|
||||
fi
|
||||
}
|
||||
recover_wifi() {
|
||||
if [[ -d $1 ]]; then
|
||||
if [[ -f $1/wifi.json ]]; then
|
||||
echoRgb "恢復wifi密碼"
|
||||
app_process /system/bin com.xayah.dex.NetworkUtil restoreNetworks "$1/wifi.json"
|
||||
echo_log "wifi恢復"
|
||||
else
|
||||
echoRgb "wifi.json遺失"
|
||||
fi
|
||||
else
|
||||
echoRgb "$1不存在 wifi無法恢復" "0"
|
||||
fi
|
||||
}
|
||||
Rename_script () {
|
||||
HT="${HT:=0}"
|
||||
find "$path_hierarchy" -maxdepth 3 -name "*.sh" -type f -not -name "tools.sh" | sort | while read ; do
|
||||
MODDIR_NAME="${REPLY%/*}"
|
||||
FILE_NAME="${REPLY##*/}"
|
||||
if [[ -f ${REPLY%/*}/app_details.json || -f ${REPLY%/*}/app_details ]]; then
|
||||
if [[ $FILE_NAME = backup.sh ]]; then
|
||||
touch_shell "1" "$REPLY"
|
||||
elif [[ $FILE_NAME = recover.sh ]]; then
|
||||
touch_shell "3" "$REPLY"
|
||||
fi
|
||||
else
|
||||
if [[ -d ${REPLY%/*}/tools ]]; then
|
||||
if [[ $FILE_NAME = start.sh ]]; then
|
||||
[[ -f ${REPLY%/*}/backup_settings.conf ]] && touch_shell "0" "$REPLY"
|
||||
[[ -f ${REPLY%/*}/restore_settings.conf ]] && touch_shell "2" "$REPLY"
|
||||
fi
|
||||
fi
|
||||
HT=$((HT + 1))
|
||||
fi
|
||||
done
|
||||
unset HT
|
||||
}
|
||||
touch_shell () {
|
||||
unset conf_path MODDIR_Path
|
||||
case $1 in
|
||||
0)
|
||||
MODDIR_Path='${0%/*}'
|
||||
MODDIR_Path1="$MODDIR_Path"
|
||||
conf_path='${0%/*}/backup_settings.conf' ;;
|
||||
1)
|
||||
MODDIR_Path='${0%/*/*/*}'
|
||||
MODDIR_Path1="$MODDIR_Path"
|
||||
conf_path='${0%/*/*/*}/backup_settings.conf' ;;
|
||||
2)
|
||||
MODDIR_Path='${0%/*}'
|
||||
MODDIR_Path1="$MODDIR_Path"
|
||||
conf_path='${0%/*}/restore_settings.conf' ;;
|
||||
3)
|
||||
MODDIR_Path='${0%/*/*}'
|
||||
MODDIR_Path1='${0%/*}'
|
||||
conf_path='${0%/*/*}/restore_settings.conf' ;;
|
||||
esac
|
||||
echo "#!/system/bin/sh
|
||||
if [ -f \"$MODDIR_Path/tools/tools.sh\" ]; then
|
||||
MODDIR=\"$MODDIR_Path1\"
|
||||
conf_path=\"$conf_path\"
|
||||
[ ! -f \"$conf_path\" ] && . \"$MODDIR_Path/tools/tools.sh\"
|
||||
else
|
||||
echo \"$MODDIR_Path/tools/tools.sh遺失\"
|
||||
fi
|
||||
. \"$MODDIR_Path/tools/tools.sh\" | tee \"\${0%/*}/log_\$(date +%Y-%m-%d_%H-%M).txt\""> "$2"
|
||||
}
|
||||
update_script() {
|
||||
[[ $zipFile = "" ]] && zipFile="$(find "$MODDIR" -maxdepth 1 -name "*.zip" -type f 2>/dev/null)"
|
||||
if [[ $zipFile != "" ]]; then
|
||||
case $(echo "$zipFile" | wc -l) in
|
||||
1)
|
||||
if [[ $(unzip -l "$zipFile" | awk '{print $4}' | egrep -o "^backup_settings.conf$") != "" ]]; then
|
||||
unzip -o "$zipFile" -j "tools/tools.sh" -d "$MODDIR" &>/dev/null
|
||||
if [[ -f $MODDIR/tools.sh ]]; then
|
||||
if [[ $(expr "$(echo "$backup_version" | tr -d "a-zA-Z")" \> "$(awk '/backup_version/{print $1}' "$MODDIR/tools.sh" | cut -f2 -d '=' | head -1 | sed 's/\"//g' | tr -d "a-zA-Z")") -eq 0 ]]; then
|
||||
shell_language="$(awk -F= '/^shell_language=/ {gsub(/"/, "", $2); print $2}' "$MODDIR/tools.sh")"
|
||||
case $MODDIR in
|
||||
*Backup_*)
|
||||
if [[ -f $MODDIR/app_details.json ]]; then
|
||||
echoRgb "請在${MODDIR%/*}更新腳本" "0"
|
||||
rm -rf "$MODDIR/tools.sh"
|
||||
exit 2
|
||||
fi ;;
|
||||
esac
|
||||
echoRgb "從$zipFile更新"
|
||||
if [[ -d $path_hierarchy/tools ]]; then
|
||||
mv "$path_hierarchy/tools" "$TMPDIR"
|
||||
[[ -d $TMPDIR/tools ]] && {
|
||||
unzip -o "$zipFile" tools/* -d "$path_hierarchy" | sed 's/inflating/釋放/g ; s/creating/創建/g ; s/Archive/解壓縮/g'
|
||||
echo_log "解壓縮${zipFile##*/}"
|
||||
if [[ $result = 0 ]]; then
|
||||
if [[ $shell_language != $Script_target_language ]]; then
|
||||
echoRgb "腳本語言為$shell_language....轉換為$Script_target_language中,請稍後等待轉換...."
|
||||
ts <"$path_hierarchy/tools/Device_List">temp && cp temp "$path_hierarchy/tools/Device_List" && rm temp
|
||||
echo_log "$path_hierarchy/tools/Device_List翻譯"
|
||||
ts <"$path_hierarchy/tools/tools.sh">temp && cp temp "$path_hierarchy/tools/tools.sh" && rm temp && sed "s/shell_language=\"$shell_language\"/shell_language=\"$Script_target_language\"/g" "$path_hierarchy/tools/tools.sh" > temp && cp temp "$path_hierarchy/tools/tools.sh" && rm temp
|
||||
echo_log "$path_hierarchy/tools/tools.sh翻譯"
|
||||
HT=1
|
||||
fi
|
||||
update_backup_settings_conf>"$path_hierarchy/backup_settings.conf"
|
||||
ts <"$path_hierarchy/backup_settings.conf">temp && cp temp "$path_hierarchy/backup_settings.conf" && rm temp
|
||||
echo_log "$path_hierarchy/backup_settings.conf翻譯"
|
||||
echo "$find_tools_path" | while read; do
|
||||
if [[ $REPLY != $path_hierarchy/tools ]]; then
|
||||
rm -rf "$REPLY"
|
||||
cp -r "$path_hierarchy/tools" "${REPLY%/*}"
|
||||
update_Restore_settings_conf>"${REPLY%/*}/restore_settings.conf"
|
||||
ts <"${REPLY%/*}/restore_settings.conf">temp && cp temp "${REPLY%/*}/restore_settings.conf" && rm temp
|
||||
echo_log "${REPLY%/*}/restore_settings.conf翻譯"
|
||||
fi
|
||||
done
|
||||
Rename_script
|
||||
if [[ $Output_path != "" ]]; then
|
||||
[[ ${Output_path: -1} = / ]] && Output_path="${Output_path%?}"
|
||||
if [[ ${Output_path:0:1} != / ]]; then
|
||||
update_path="$MODDIR/$Output_path/Backup_${Compression_method}_$user"
|
||||
else
|
||||
update_path="$Output_path/Backup_${Compression_method}_$user"
|
||||
fi
|
||||
rm -rf "$update_path/tools"
|
||||
cp -r "$path_hierarchy/tools" "$update_path"
|
||||
echoRgb "$update_path/tools已經更新完成"
|
||||
fi
|
||||
else
|
||||
mv "$TMPDIR/tools" "$MODDIR"
|
||||
fi
|
||||
rm -rf "$TMPDIR"/* "$zipFile" "$MODDIR/tools.sh"
|
||||
echoRgb "更新完成 請重新執行腳本" "2"
|
||||
exit 0
|
||||
} || echoRgb "tools移動到TMPDIR失敗" "0"
|
||||
fi
|
||||
else
|
||||
echoRgb "${zipFile##*/}版本低於當前版本,自動刪除" "0"
|
||||
rm -rf "$zipFile" "$path_hierarchy/tools.sh"
|
||||
fi
|
||||
else
|
||||
rm -rf "$zipFile"
|
||||
unset zipFile
|
||||
fi
|
||||
fi ;;
|
||||
*)
|
||||
echoRgb "錯誤 請刪除當前目錄多餘zip\n -保留一個最新的數據備份.zip\n -下列為當前目錄zip\n$zipFile" "0"
|
||||
exit 1 ;;
|
||||
esac
|
||||
fi
|
||||
unset NAME
|
||||
}
|
||||
Check_archive() {
|
||||
starttime1="$(date -u "+%s")"
|
||||
error_log="$TMPDIR/error_log"
|
||||
rm -rf "$error_log"
|
||||
FIND_PATH="$(find "$1" -maxdepth 3 -name "*.tar*" -type f 2>/dev/null | sort)"
|
||||
i=1
|
||||
r="$(find "$MODDIR" -maxdepth 2 -name "app_details.json" -type f 2>/dev/null | wc -l)"
|
||||
find "$MODDIR" -maxdepth 2 -name "app_details.json" -type f 2>/dev/null | sort | while read; do
|
||||
REPLY="${REPLY%/*}"
|
||||
echoRgb "校驗第$i/$r個資料夾 剩下$((r - i))個" "3"
|
||||
echoRgb "校驗:${REPLY##*/}"
|
||||
find "$REPLY" -maxdepth 1 -name "*.tar*" -type f 2>/dev/null | sort | while read; do
|
||||
Validation_file "$REPLY"
|
||||
[[ $result != 0 ]] && echo "$REPLY">>"$error_log"
|
||||
done
|
||||
echoRgb "$((i * 100 / r))%"
|
||||
i=$((i + 1)); nskg=$((nskg + 1))
|
||||
done
|
||||
endtime 1
|
||||
[[ -f $error_log ]] && echoRgb "以下為失敗的檔案\n $(cat "$error_log")" || echoRgb "恭喜~~全數校驗通過"
|
||||
rm -rf "$error_log"
|
||||
}
|
||||
get_name(){
|
||||
txt="$MODDIR/appList.txt"
|
||||
txt="${txt/'/storage/emulated/'/'/data/media/'}"
|
||||
txt2="$MODDIR/mediaList.txt"
|
||||
if [[ $1 = Apkname ]]; then
|
||||
rm -rf "$txt" "$txt2"
|
||||
echoRgb "列出全部資料夾內應用名與自定義目錄壓縮包名稱" "3"
|
||||
fi
|
||||
rgb_a=118
|
||||
user="$(echo "${0%}" | sed 's/.*\/Backup_zstd_\([0-9]*\).*/\1/')"
|
||||
Apk_info="$(pm list packages --user "$user" | cut -f2 -d ':' | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
|
||||
if [[ $Apk_info != "" ]]; then
|
||||
[[ $Apk_info = *"Failure calling service package"* ]] && Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
|
||||
else
|
||||
Apk_info="$(appinfo "user|system" "pkgName" 2>/dev/null | egrep -v 'ice.message|com.topjohnwu.magisk' | sort -u)"
|
||||
fi
|
||||
[[ $Apk_info = "" ]] && echoRgb "Apk_info變量為空" "0" && exit
|
||||
starttime1="$(date -u "+%s")"
|
||||
i=1
|
||||
while read; do
|
||||
Folder="${REPLY%/*}"
|
||||
[[ $rgb_a -ge 229 ]] && rgb_a=118
|
||||
unset PackageName NAME DUMPAPK ChineseName apk_version Ssaid dataSize userSize obbSize
|
||||
if [[ -f $Folder/app_details.json ]]; then
|
||||
ChineseName="$(jq -r 'to_entries[] | select(.key != null).key' "$Folder/app_details.json" | head -n 1)"
|
||||
PackageName="$(jq -r '.[] | select(.PackageName != null).PackageName' "$Folder/app_details.json")"
|
||||
if [[ -f $Folder/Permissions ]]; then
|
||||
unset Permissions
|
||||
. "$Folder/Permissions"
|
||||
jq --arg packageName "$ChineseName" --argjson permissions "$(echo "$Permissions" | jq -nR '[inputs | select(length>0) | split(" ") | {(.[0]): .[-1]}] | add')" '.[$packageName] |= . + {permissions: $permissions}' "$Folder/app_details.json" > "$TMPDIR/temp.json" && cp "$TMPDIR/temp.json" "$Folder/app_details.json" && rm "$Folder/Permissions" "$TMPDIR/temp.json" && echoRgb "更新$Folder/app_details.json"
|
||||
fi
|
||||
else
|
||||
if [[ -f $Folder/app_details ]]; then
|
||||
. "$Folder/app_details" &>/dev/null
|
||||
extra_content="{
|
||||
\"$ChineseName\": {
|
||||
\"PackageName\": \"$PackageName\",
|
||||
\"apk_version\": \"$apk_version\",
|
||||
\"Ssaid\": \"$Ssaid\"
|
||||
},
|
||||
\"data\": {
|
||||
\"Size\": \"$dataSize\"
|
||||
},
|
||||
\"obb\": {
|
||||
\"Size\": \"$obbSize\"
|
||||
},
|
||||
\"user\": {
|
||||
\"Size\": \"$userSize\"
|
||||
}
|
||||
}"
|
||||
echo "{\n}">"$Folder/app_details.json"
|
||||
jq --argjson new_content "$extra_content" '. += $new_content' "$Folder/app_details.json" > "$TMPDIR/temp.json" && cp "$TMPDIR/temp.json" "$Folder/app_details.json" && rm "$TMPDIR/temp.json" "$Folder/app_details"
|
||||
fi
|
||||
fi
|
||||
if [[ $PackageName = "" || $ChineseName = "" ]]; then
|
||||
echoRgb "${Folder##*/}包名獲取失敗,解壓縮獲取包名中..." "0"
|
||||
rm -rf "$TMPDIR"/*
|
||||
case ${REPLY##*.} in
|
||||
zst) tar -I zstd -xmpf "$REPLY" -C "$TMPDIR" --wildcards --no-anchored 'base.apk' ;;
|
||||
tar) tar -xmpf "$REPLY" -C "$TMPDIR" --wildcards --no-anchored 'base.apk' ;;
|
||||
*)
|
||||
echoRgb "${REPLY##*/} 壓縮包不支持解壓縮" "0"
|
||||
Set_back_1
|
||||
;;
|
||||
esac
|
||||
echo_log "${REPLY##*/}解壓縮"
|
||||
if [[ $result = 0 ]]; then
|
||||
if [[ -f $TMPDIR/base.apk ]]; then
|
||||
DUMPAPK="$(appinfo3 "$TMPDIR/base.apk")"
|
||||
if [[ $DUMPAPK != "" ]]; then
|
||||
app=($DUMPAPK $DUMPAPK)
|
||||
PackageName="${app[1]}"
|
||||
ChineseName="${app[2]}"
|
||||
rm -rf "$TMPDIR"/*
|
||||
else
|
||||
echoRgb "appinfo輸出失敗" "0"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ $PackageName != "" && $ChineseName != "" ]]; then
|
||||
if [[ $(echo "$Apk_info" | awk -v pkg="$PackageName" '$1 == pkg {print $1}') = "" ]]; then
|
||||
echoRgb "$ChineseName已經不存在$user使用者中"
|
||||
if [[ $delete_app = "" ]]; then
|
||||
delete_app="$ChineseName $PackageName"
|
||||
else
|
||||
delete_app="$delete_app\n$ChineseName $PackageName"
|
||||
fi
|
||||
fi
|
||||
case $1 in
|
||||
Apkname)
|
||||
[[ -f $Folder/${PackageName}.sh ]] && rm -rf "$Folder/${PackageName}.sh"
|
||||
[[ ! -f $Folder/recover.sh ]] && touch_shell "3" "$Folder/recover.sh"
|
||||
[[ ! -f $Folder/backup.sh ]] && touch_shell "1" "$Folder/backup.sh"
|
||||
echoRgb "$i:$ChineseName $PackageName"
|
||||
if [[ $TMPTXT = "" ]]; then
|
||||
TMPTXT="#不需要恢復還原的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market\n$ChineseName $PackageName"
|
||||
else
|
||||
TMPTXT="$TMPTXT\n$ChineseName $PackageName"
|
||||
fi
|
||||
i=$((i + 1)) ;;
|
||||
convert)
|
||||
if [[ ${Folder##*/} = $PackageName ]]; then
|
||||
DIR_NAME="${Folder%/*}/$ChineseName"
|
||||
echoRgb "${Folder##*/} > $ChineseName"
|
||||
else
|
||||
DIR_NAME="${Folder%/*}/$PackageName"
|
||||
echoRgb "${Folder##*/} > $PackageName"
|
||||
fi
|
||||
if [[ -d $DIR_NAME ]]; then
|
||||
i=1
|
||||
NEW_DIR_NAME="${DIR_NAME}_${i}"
|
||||
while [[ -d $NEW_DIR_NAME ]]; do
|
||||
i=$((i + 1))
|
||||
NEW_DIR_NAME="${DIR_NAME}_${i}"
|
||||
done
|
||||
DIR_NAME="$NEW_DIR_NAME"
|
||||
fi
|
||||
mv "$Folder" "$DIR_NAME" ;;
|
||||
esac
|
||||
fi
|
||||
rgb_a=$((rgb_a + 1))
|
||||
done<<<"$(find "$MODDIR" -maxdepth 2 -name "apk.*" -type f 2>/dev/null | sort)"
|
||||
[[ $TMPTXT != "" ]] && echo "$TMPTXT">"$txt"
|
||||
if [[ -d $MODDIR/Media ]]; then
|
||||
echoRgb "存在媒體資料夾" "2"
|
||||
[[ ! -f $txt2 ]] && echo "#不需要恢復的資料夾請在開頭使用#注釋 比如:#Download" > "$txt2"
|
||||
find "$MODDIR/Media" -maxdepth 1 -name "*.tar*" -type f 2>/dev/null | while read; do
|
||||
echoRgb "${REPLY##*/}" && echo "${REPLY##*/}" >> "$txt2"
|
||||
done
|
||||
echoRgb "$txt2重新生成" "1"
|
||||
fi
|
||||
if [[ $delete_app != "" ]]; then
|
||||
if [[ $(echo "$delete_app" | awk 'NF != 0 { count++ } END { print count }') != "" ]]; then
|
||||
echoRgb "列出需要刪除的應用中....\n -$delete_app"
|
||||
case $Lo in
|
||||
0|1)
|
||||
echoRgb "確認列表無誤後音量上刪除,音量下退出腳本編輯列表" "2"
|
||||
get_version "刪除" "退出腳本" && Delete_App="$branch" ;;
|
||||
2)
|
||||
Enter_options "確認列表無誤後輸入1刪除,輸入0退出腳本編輯列表" "刪除" "退出腳本" && isBoolean "$parameter" "Delete_App" && Delete_App="$nsx" ;;
|
||||
esac
|
||||
if [[ $Delete_App = true ]]; then
|
||||
echoRgb "警告 即將刪除未安裝應用資料夾,請再三確認後在執行" "0"
|
||||
i=1
|
||||
r="$(echo "$delete_app" | awk 'NF != 0 { count++ } END { print count }')"
|
||||
while [[ $i -le $r ]]; do
|
||||
name1="$(echo "$delete_app" | sed -e '/^$/d' | sed -n "${i}p" | cut -d' ' -f1)"
|
||||
name2="$(echo "$delete_app" | sed -e '/^$/d' | sed -n "${i}p" | cut -d' ' -f2)"
|
||||
Backup_folder="$MODDIR/$name1"
|
||||
[[ -d $Backup_folder ]] && rm -rf "$Backup_folder"
|
||||
echo "$(sed -e "s/$name1 $name2//g ; /^$/d" "$txt" 2>/dev/null)" >"$txt"
|
||||
i=$((i + 1))
|
||||
done
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$txt"
|
||||
endtime 1
|
||||
exit 0
|
||||
}
|
||||
Getlist() {
|
||||
case $MODDIR in
|
||||
/storage/emulated/0/Android/* | /data/media/0/Android/* | /sdcard/Android/*) echoRgb "請勿在$MODDIR內生成列表" "0" && exit 2 ;;
|
||||
esac
|
||||
#校驗選填是否正確
|
||||
case $Lo in
|
||||
0)
|
||||
[[ $blacklist_mode != "" ]] && isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx" || {
|
||||
echoRgb "選擇黑名單模式\n -音量上不輸出,音量下輸出應用列表" "2"
|
||||
get_version "不輸出" "輸出應用列表" && blacklist_mode="$branch"
|
||||
} ;;
|
||||
1)
|
||||
if [[ $(echo "$blacklist" | egrep -v '#|#' | wc -l) -gt 0 ]]; then
|
||||
[[ $blacklist_mode = "" ]] && {
|
||||
echoRgb "選擇黑名單模式\n -音量上不輸出,音量下輸出應用列表" "2"
|
||||
get_version "不輸出" "輸出應用列表" && blacklist_mode="$branch"
|
||||
} || isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
|
||||
fi ;;
|
||||
2)
|
||||
[[ $blacklist_mode = "" ]] && {
|
||||
Enter_options "選擇黑名單模式輸入1不輸出,輸入0輸出應用列表" "不輸出" "輸出應用列表" && isBoolean "$parameter" "blacklist_mode" && blacklist_mode="$nsx"
|
||||
} || {
|
||||
isBoolean "$blacklist_mode" "blacklist_mode" && blacklist_mode="$nsx"
|
||||
} ;;
|
||||
*) echoRgb "$conf_path Lo=$Lo填寫錯誤,正確值0 1 2" "0" && exit 2 ;;
|
||||
esac
|
||||
txt="$TMPDIR/appList"
|
||||
[[ -f "$MODDIR/appList.txt" ]] && cat "$MODDIR/appList.txt" >"$txt"
|
||||
[[ ! -f $txt ]] && echo '#不需要備份的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market(忽略安裝包和數據)\n#不需要備份數據的應用請在開頭使用!注釋 比如:!酷安 com.coolapk.market(僅忽略數據)' >"$txt"
|
||||
echoRgb "請勿關閉腳本,等待提示結束"
|
||||
rgb_a=118
|
||||
starttime1="$(date -u "+%s")"
|
||||
echoRgb "提示! 腳本默認會屏蔽預裝應用 如需備份請添加預裝應用白名單" "0"
|
||||
Apk_info="$(appinfo "system|user|xposed" "label|pkgName|flag" | egrep -v 'ice.message|com.topjohnwu.magisk' | tr '/:' '_')"
|
||||
xposed_name="$(echo "$Apk_info" | awk '$3 == "xposed" {print $2}')"
|
||||
TARGET_PACKAGES="$(echo "$system" | paste -sd'|' - | sed 's/^|//')"
|
||||
Pre_installed_apps="$(echo "$Apk_info" | awk '$3 == "system" {print $1, $2}' | egrep -w "$TARGET_PACKAGES")"
|
||||
Apk_info="$(echo "$(echo "$Apk_info" | awk '$3 != "system" {print $1, $2}')\n$Pre_installed_apps" | sort -u)"
|
||||
[[ $Apk_info = "" ]] && {
|
||||
echoRgb "appinfo輸出失敗,請截圖畫面回報作者" "0"
|
||||
exit 2 ; } || Apk_info2="$(echo "$Apk_info" | cut -d' ' -f2)"
|
||||
Apk_Quantity="$(echo "$Apk_info" | wc -l)"
|
||||
LR="1"
|
||||
echoRgb "列出第三方應用......." "2"
|
||||
i="0"
|
||||
rc="0"
|
||||
rd="0"
|
||||
Q="0"
|
||||
rb="0"
|
||||
Output_list() {
|
||||
if [[ $(cat "$txt" | cut -f2 -d ' ' | egrep -w "^${app_1[1]}$") != ${app_1[1]} ]]; then
|
||||
[[ $REPLY2 = "" ]] && add_entry "${app_1[2]}" "${app_1[1]}" "$(grep -w "${app_1[2]}" "$txt")" || add_entry "${app_1[2]}" "${app_1[1]}" "$REPLY2"
|
||||
case ${app_1[1]} in
|
||||
*oneplus*|*miui*|*xiaomi*|*oppo*|*flyme*|*meizu*|com.android.soundrecorder|com.mfashiongallery.emag|com.mi.health|*coloros*|com.android.soundrecorder|com.duokan.phone.remotecontroller|com.android.calendar|com.android.deskclock|com.android.calendar|com.android.deskclock|com.google.android.safetycore|com.google.android.contactkeys|com.google.android.apps.messaging|com.google.android.calendar)
|
||||
if [[ $(echo "$xposed_name" | egrep -w "${app_1[1]}$") = ${app_1[1]} ]]; then
|
||||
echoRgb "$((i+1)):$app_name為Xposed模塊 進行添加" "0"
|
||||
if [[ $REPLY2 = "" ]]; then
|
||||
REPLY2="$REPLY" && [[ $tmp = "" ]] && tmp="1"
|
||||
else
|
||||
REPLY2="$REPLY2\n$REPLY" && [[ $tmp = "" ]] && tmp="1"
|
||||
fi
|
||||
i=$((i + 1)); rd=$((rd + 1))
|
||||
else
|
||||
if [[ $(echo "$whitelist" | egrep -w "^${app_1[1]}$") = ${app_1[1]} ]]; then
|
||||
if [[ $REPLY2 = "" ]]; then
|
||||
REPLY2="$REPLY" && [[ $tmp = "" ]] && tmp="1"
|
||||
else
|
||||
REPLY2="$REPLY2\n$REPLY" && [[ $tmp = "" ]] && tmp="1"
|
||||
fi
|
||||
echoRgb "$((i+1)):$app_name ${app_1[1]}($rgb_a)"
|
||||
i=$((i + 1))
|
||||
else
|
||||
echoRgb "$app_name 預裝應用 忽略輸出" "0"
|
||||
if [[ $REPLY2 = "" ]]; then
|
||||
REPLY2="#$REPLY" && [[ $tmp = "" ]] && tmp="1"
|
||||
else
|
||||
REPLY2="$REPLY2\n#$REPLY" && [[ $tmp = "" ]] && tmp="1"
|
||||
fi
|
||||
rc=$((rc + 1))
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if [[ $REPLY2 = "" ]]; then
|
||||
REPLY2="$REPLY" && [[ $tmp = "" ]] && tmp="1"
|
||||
else
|
||||
REPLY2="$REPLY2\n$REPLY" && [[ $tmp = "" ]] && tmp="1"
|
||||
fi
|
||||
if [[ $(echo "$xposed_name" | egrep -w "${app_1[1]}$") = ${app_1[1]} ]]; then
|
||||
echoRgb "$((i+1)):Xposed: $app_name ${app_1[1]}($rgb_a)"
|
||||
rd=$((rd + 1))
|
||||
else
|
||||
echoRgb "$((i+1)):$app_name ${app_1[1]}($rgb_a)"
|
||||
fi
|
||||
i=$((i + 1))
|
||||
;;
|
||||
esac
|
||||
else
|
||||
Q=$((Q + 1))
|
||||
fi
|
||||
}
|
||||
[[ $(echo "$blacklist" | egrep -v '#|#') != "" ]] && NZK=1
|
||||
echo "$Apk_info" | sed 's/[\/:()\[\]\-!]//g' | while read; do
|
||||
[[ $rgb_a -ge 229 ]] && rgb_a=118
|
||||
app_1=($REPLY $REPLY)
|
||||
if [[ $NZK = 1 ]]; then
|
||||
if [[ $(echo "$blacklist" | egrep -w "^${app_1[1]}$") != ${app_1[1]} ]]; then
|
||||
Output_list
|
||||
else
|
||||
if [[ $blacklist_mode = false ]]; then
|
||||
Output_list
|
||||
rb=$((rb + 1))
|
||||
else
|
||||
echoRgb "${app_1[2]}黑名單應用 不輸出" "0"
|
||||
rb=$((rb + 1))
|
||||
fi
|
||||
fi
|
||||
else
|
||||
Output_list
|
||||
fi
|
||||
if [[ $LR = $Apk_Quantity ]]; then
|
||||
echo "$REPLY2">>"$txt"
|
||||
if [[ $(cat "$txt" | wc -l | awk '{print $1-2}') -lt $i ]]; then
|
||||
rm -rf "$txt"
|
||||
echoRgb "\n -輸出異常 請聯繫作者解決" "0"
|
||||
exit 1
|
||||
fi
|
||||
echoRgb "已經將預裝應用輸出至appList.txt並注釋# 需要備份則去掉#" "0"
|
||||
[[ $tmp != "" ]] && echoRgb "\n -第三方apk數量=\"$Apk_Quantity\"\n -已過濾=\"$rc\"\n -xposed=\"$rd\"\n -黑名單應用=\"$rb\"\n -存在列表中=\"$Q\"\n -輸出=\"$i\""
|
||||
fi
|
||||
rgb_a=$((rgb_a + 1)); LR=$((LR + 1))
|
||||
done
|
||||
if [[ -f $txt ]]; then
|
||||
while read -r ; do
|
||||
if [[ $(echo "$REPLY" | sed -E 's/^[ \t]*//; /^[ \t]*[##!]/d') != "" ]]; then
|
||||
app=($REPLY $REPLY)
|
||||
if [[ ${app[1]} != "" && ${app[2]} != "" ]]; then
|
||||
if [[ $(echo "$Apk_info2" | awk -v pkg="${app[1]}" '$1 == pkg {print $1}') != "" ]]; then
|
||||
[[ $Tmplist = "" ]] && Tmplist='#不需要備份的應用請在開頭使用#注釋 比如:#酷安 com.coolapk.market(忽略安裝包和數據\n#不需要備份數據的應用請在開頭使用!注釋 比如:!酷安 com.coolapk.market(僅忽略數據)'
|
||||
Tmplist="$Tmplist\n$REPLY"
|
||||
else
|
||||
echoRgb "$REPLY不存在系統,從列表中刪除" "0"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
Tmplist="$Tmplist\n$REPLY"
|
||||
fi
|
||||
done < "$txt"
|
||||
[[ $Tmplist != "" ]] && echo "$Tmplist" | sed -e '/^$/d' | sort>"$txt"
|
||||
fi
|
||||
wait
|
||||
endtime 1
|
||||
cat "$txt">"$MODDIR/appList.txt" && rm "$txt"
|
||||
chown "$(stat -c '%u:%g' '/data/media/0/Download')" "$MODDIR/appList.txt"
|
||||
echoRgb "輸出包名結束 請查看$MODDIR/appList.txt"
|
||||
}
|
||||
}
|
||||
Device_List() {
|
||||
URL="https://raw.githubusercontent.com/KHwang9883/MobileModels/refs/heads/master/brands"
|
||||
rm -rf "$tools_path/Device_List"
|
||||
for i in $(echo "xiaomi\nxiaomi_en\nsamsung\nsamsung_global\nasus\nBlack_Shark\nBlack_Shark_en\ngoogle\nLenovo\nMEIZU\nMEIZU_en\nMotorola\nNokia\nnothing\nnubia\nOnePlus\nOnePlus_en\nSony\nrealme\nrealme_en\nvivo\nvivo_en\noppo\noppo_en"); do
|
||||
echoRgb "獲取品牌$i"
|
||||
case $i in
|
||||
xiaomi) Brand_URL="$URL/xiaomi.md" ;;
|
||||
xiaomi_en) Brand_URL="$URL/xiaomi_en.md" ;;
|
||||
samsung) Brand_URL="$URL/samsung_cn.md" ;;
|
||||
samsung_global) Brand_URL="$URL/samsung_global_en.md" ;;
|
||||
asus) Brand_URL="$URL/asus.md" ;;
|
||||
Black_Shark) Brand_URL="$URL/blackshark.md" ;;
|
||||
Black_Shark_en) Brand_URL="$URL/blackshark_en.md" ;;
|
||||
google) Brand_URL="$URL/google.md" ;;
|
||||
Lenovo) Brand_URL="$URL/lenovo.md" ;;
|
||||
MEIZU) Brand_URL="$URL/meizu.md" ;;
|
||||
MEIZU_en) Brand_URL="$URL/meizu_en.md" ;;
|
||||
Motorola) Brand_URL="$URL/motorola.md" ;;
|
||||
Nokia) Brand_URL="$URL/nokia.md" ;;
|
||||
nothing) Brand_URL="$URL/nothing.md" ;;
|
||||
nubia) Brand_URL="$URL/nubia.md" ;;
|
||||
OnePlus) Brand_URL="$URL/oneplus.md" ;;
|
||||
OnePlus_en) Brand_URL="$URL/oneplus_en.md" ;;
|
||||
Sony) Brand_URL="$URL/sony_cn.md" ;;
|
||||
realme) Brand_URL="$URL/realme_cn.md" ;;
|
||||
realme_en) Brand_URL="$URL/realme_global_en.md" ;;
|
||||
vivo) Brand_URL="$URL/vivo_cn.md" ;;
|
||||
vivo_en) Brand_URL="$URL/vivo_global_en.md" ;;
|
||||
oppo) Brand_URL="$URL/oppo_cn.md" ;;
|
||||
oppo_en) Brand_URL="$URL/oppo_global_en.md" ;;
|
||||
esac
|
||||
if [[ ! -e $tools_path/Device_List ]]; then
|
||||
down "$Brand_URL" | grep -oE '`[^`]+`:[^`]*' | sed -E 's/: /:/g' | sed -E 's/`([^`]+)`:(.*)/"\1" "\2"/'>"$tools_path/Device_List"
|
||||
else
|
||||
down "$Brand_URL" | grep -oE '`[^`]+`:[^`]*' | sed -E 's/: /:/g' | sed -E 's/`([^`]+)`:(.*)/"\1" "\2"/' | while read ; do
|
||||
unset model
|
||||
model="$(echo "$REPLY" | awk -F'"' '{print $2}')"
|
||||
if [[ $(egrep -w "$model" "$tools_path/Device_List" | awk -F'"' '{print $2}') != $model ]]; then
|
||||
echo "$REPLY">>"$tools_path/Device_List"
|
||||
else
|
||||
echo "$(egrep -w "$model" "$tools_path/Device_List" | awk -F'"' '{print $2}') = $model"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
if [[ -e $tools_path/Device_List ]]; then
|
||||
if [[ $(ls -l "$tools_path/Device_List" | awk '{print $5}') -gt 1 ]]; then
|
||||
[[ $shell_language = zh-TW ]] && ts <"$tools_path/Device_List">temp && cp temp "$tools_path/Device_List" && rm temp
|
||||
echoRgb "已下載機型列表在$tools_path/Device_List"
|
||||
else
|
||||
echoRgb "下載機型失敗"
|
||||
fi
|
||||
else
|
||||
echoRgb "下載機型失敗"
|
||||
fi
|
||||
}
|
||||
wifi() {
|
||||
backup_path
|
||||
[[ ! -d $Backup/tools ]] && cp -r "$tools_path" "$Backup"
|
||||
[[ ! -f $Backup/start.sh ]] && touch_shell "2" "$Backup/start.sh"
|
||||
[[ ! -f $Backup/restore_settings.conf ]] && update_Restore_settings_conf>"$Backup/restore_settings.conf"
|
||||
backup_wifi "$Backup/wifi"
|
||||
}
|
||||
dumpname() {
|
||||
get_name "Apkname"
|
||||
}
|
||||
convert() {
|
||||
get_name "convert"
|
||||
}
|
||||
check_file() {
|
||||
Check_archive "$MODDIR"
|
||||
}
|
||||
BIN
tools/curl
BIN
tools/curl
Binary file not shown.
BIN
tools/smbclient
BIN
tools/smbclient
Binary file not shown.
@@ -66,7 +66,6 @@
|
||||
"MSM8930AB": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ 400","MEMORY":"","CHANNELS":""},
|
||||
"SXR2230P": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ XR2 Gen 2","MEMORY":"","CHANNELS":""},
|
||||
"QCM6490": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ QCM6490","MEMORY":"","CHANNELS":""},
|
||||
"sm-sip1": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ Sip 1","MEMORY":"","CHANNELS":""},
|
||||
"MSM8953": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ 625","MEMORY":"LPDDR3 933 MHz","CHANNELS":"single channel"},
|
||||
"SM4450": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ 4 Gen 2","MEMORY":"","CHANNELS":""},
|
||||
"SM4450A": {"VENDOR":"Qualcomm®","NAME":"Snapdragon™ 4 Gen 2 Accelerated Edition","MEMORY":"","CHANNELS":""},
|
||||
@@ -347,8 +346,6 @@
|
||||
"MT6985": {"VENDOR":"MediaTek","NAME":"Dimensity 9200+","MEMORY":"LPDDR5X","CHANNELS":""},
|
||||
"MT6993W/CZA": {"VENDOR":"MediaTek","NAME":"Dimensity 9500","MEMORY":"","CHANNELS":""},
|
||||
"MT6993": {"VENDOR":"MediaTek","NAME":"Dimensity 9500","MEMORY":"","CHANNELS":""},
|
||||
"MT6899Z_T/TZA": {"VENDOR":"MediaTek","NAME":"Dimensity 8550 SUPER","MEMORY":"","CHANNELS":""},
|
||||
"MT6881V/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7500","MEMORY":"","CHANNELS":""},
|
||||
"MT6991Z/ECZB": {"VENDOR":"MediaTek","NAME":"Dimensity 9500s","MEMORY":"","CHANNELS":""},
|
||||
"MT6991Z/SCZB": {"VENDOR":"MediaTek","NAME":"Dimensity 9500s","MEMORY":"","CHANNELS":""},
|
||||
"MT6991Z/TCZB": {"VENDOR":"MediaTek","NAME":"Dimensity 9400+","MEMORY":"","CHANNELS":""},
|
||||
@@ -450,11 +447,8 @@
|
||||
"MT6878V_H/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7360-Apex","MEMORY":"","CHANNELS":""},
|
||||
"MT6878V/FZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7300X","MEMORY":"","CHANNELS":""},
|
||||
"MT6878V_A/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7300-ENERGY","MEMORY":"","CHANNELS":""},
|
||||
"MT6878V_I/TZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7400-SUPER","MEMORY":"","CHANNELS":""},
|
||||
"MT6878V_I/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7360-SUPER","MEMORY":"","CHANNELS":""},
|
||||
"MT6878": {"VENDOR":"MediaTek","NAME":"Dimensity 7300 Ultra","MEMORY":"","CHANNELS":""},
|
||||
"MT6858V/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7100","MEMORY":"","CHANNELS":""},
|
||||
"MT6858V_A/ZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7100 Ultimate","MEMORY":"","CHANNELS":""},
|
||||
"MT6858V/TZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7300e","MEMORY":"","CHANNELS":""},
|
||||
"MT6855V/ATZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7025","MEMORY":"","CHANNELS":""},
|
||||
"MT6855V_A/ATZA": {"VENDOR":"MediaTek","NAME":"Dimensity 7025 Ultra","MEMORY":"","CHANNELS":""},
|
||||
@@ -549,7 +543,7 @@
|
||||
"MT6789": {"VENDOR":"MediaTek","NAME":"Helio G99","MEMORY":"","CHANNELS":""},
|
||||
"mt6789": {"VENDOR":"MediaTek","NAME":"Helio G99","MEMORY":"","CHANNELS":""},
|
||||
"mt5867": {"VENDOR":"MediaTek","NAME":"MT5867","MEMORY":"","CHANNELS":""},
|
||||
"MT6769I": {"VENDOR":"MediaTek","NAME":"Helio G92 Max","MEMORY":"","CHANNELS":""},
|
||||
"MT6769I": {"VENDOR":"MediaTek","NAME":"Helio G92","MEMORY":"","CHANNELS":""},
|
||||
"MT6769L": {"VENDOR":"MediaTek","NAME":"Helio G81 Extreme","MEMORY":"","CHANNELS":""},
|
||||
"MT6785": {"VENDOR":"MediaTek","NAME":"Helio G90 series","MEMORY":"LPDDR4x 2133MHz","CHANNELS":"Dual channel"},
|
||||
"MT6769H": {"VENDOR":"MediaTek","NAME":"Helio G88","MEMORY":"LPDDR4X 1800MHz","CHANNELS":"Dual channel"},
|
||||
@@ -631,7 +625,7 @@
|
||||
"MT8768CT": {"VENDOR":"MediaTek","NAME":"MT8768CT","MEMORY":"","CHANNELS":""},
|
||||
"MT6762": {"VENDOR":"MediaTek","NAME":"Helio P22","MEMORY":"","CHANNELS":""},
|
||||
"mt6762": {"VENDOR":"MediaTek","NAME":"Helio P22","MEMORY":"","CHANNELS":""},
|
||||
"MT8766B": {"VENDOR":"MediaTek","NAME":"Helio A22","MEMORY":"","CHANNELS":""},
|
||||
"MT8766B": {"VENDOR":"MediaTek","NAME":"MT8766B","MEMORY":"","CHANNELS":""},
|
||||
"MT8768WT": {"VENDOR":"MediaTek","NAME":"Helio P22T","MEMORY":"","CHANNELS":""},
|
||||
"MT8768T": {"VENDOR":"MediaTek","NAME":"Helio P22T","MEMORY":"","CHANNELS":""},
|
||||
"MT8735P": {"VENDOR":"MediaTek","NAME":"MT8735P","MEMORY":"LPDDR3","CHANNELS":""},
|
||||
@@ -875,11 +869,9 @@
|
||||
"T770": {"VENDOR":"Unisoc","NAME":"T770","MEMORY":"","CHANNELS":""},
|
||||
"T7200": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
|
||||
"ums512": {"VENDOR":"Unisoc","NAME":"T610","MEMORY":"","CHANNELS":""},
|
||||
"ums312": {"VENDOR":"Unisoc","NAME":"T310","MEMORY":"","CHANNELS":""},
|
||||
"UMS312": {"VENDOR":"Unisoc","NAME":"T310","MEMORY":"","CHANNELS":""},
|
||||
"UMS9230T": {"VENDOR":"Unisoc","NAME":"T7225","MEMORY":"","CHANNELS":""},
|
||||
"UMS9230E": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
|
||||
"ums312": {"VENDOR":"Unisoc","NAME":"","MEMORY":"","CHANNELS":""},
|
||||
"ums9230": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
|
||||
"UMS9230E": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
|
||||
"UMS9230": {"VENDOR":"Unisoc","NAME":"T7200","MEMORY":"","CHANNELS":""},
|
||||
"SC9832E": {"VENDOR":"Unisoc","NAME":"SC9832E","MEMORY":"","CHANNELS":""},
|
||||
"sc9832a": {"VENDOR":"Unisoc","NAME":"SC9832A","MEMORY":"","CHANNELS":""},
|
||||
|
||||
7571
tools/tools.sh
7571
tools/tools.sh
File diff suppressed because it is too large
Load Diff
BIN
tools/zstd
BIN
tools/zstd
Binary file not shown.
Reference in New Issue
Block a user