202 lines
4.8 KiB
JavaScript
202 lines
4.8 KiB
JavaScript
import { IStorage } from "./implement";
|
|
|
|
export class AndroidStorage extends IStorage {
|
|
constructor() {
|
|
super();
|
|
this.memoryCache = new Map(); // 内存缓存
|
|
}
|
|
|
|
// 检查 Android 接口是否可用
|
|
isAvailable() {
|
|
return typeof window.AndroidAsset !== "undefined";
|
|
}
|
|
|
|
// 调用原生接口
|
|
async callNative(method, ...args) {
|
|
if (!this.isAvailable()) {
|
|
throw new Error("AndroidAsset not available");
|
|
}
|
|
return window.AndroidAsset[method](...args);
|
|
}
|
|
|
|
// 基础操作 (使用 localStorage 作为后备)
|
|
async get(key) {
|
|
try {
|
|
const value = await this.callNative("getItem", key);
|
|
return value ? JSON.parse(value) : null;
|
|
} catch {
|
|
return this.memoryCache.get(key) || null;
|
|
}
|
|
}
|
|
|
|
async set(key, value) {
|
|
this.memoryCache.set(key, value);
|
|
try {
|
|
await this.callNative("setItem", key, JSON.stringify(value));
|
|
} catch {
|
|
// 忽略原生存储失败
|
|
}
|
|
}
|
|
|
|
async remove(key) {
|
|
this.memoryCache.delete(key);
|
|
try {
|
|
await this.callNative("removeItem", key);
|
|
} catch {}
|
|
}
|
|
|
|
async clear() {
|
|
this.memoryCache.clear();
|
|
try {
|
|
await this.callNative("clear");
|
|
} catch {}
|
|
}
|
|
|
|
// 频道数据 (使用专用接口)
|
|
async getChannels() {
|
|
try {
|
|
const data = await this.callNative("readChannelData");
|
|
if (data && !data.startsWith("ERROR:")) {
|
|
// 解析频道数据并缓存
|
|
const channels = this.parseChannelData(data);
|
|
this.memoryCache.set("channels", channels);
|
|
return channels;
|
|
}
|
|
} catch {}
|
|
return this.memoryCache.get("channels") || [];
|
|
}
|
|
|
|
async setChannels(channels) {
|
|
this.memoryCache.set("channels", channels);
|
|
// 原生端通过文件存储,这里只更新内存缓存
|
|
await this.setCacheMeta(
|
|
"channels",
|
|
new CacheMeta({
|
|
key: "channels",
|
|
updatedAt: Date.now(),
|
|
size: channels.length,
|
|
}),
|
|
);
|
|
}
|
|
|
|
parseChannelData(text) {
|
|
// 简单的 TXT 格式解析
|
|
const channels = [];
|
|
const lines = text.split("\n");
|
|
let currentGroup = "";
|
|
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed) continue;
|
|
|
|
if (trimmed.includes("#genre#")) {
|
|
currentGroup = trimmed.split(",")[0];
|
|
} else if (trimmed.includes(",")) {
|
|
const [name, url] = trimmed.split(",").map((s) => s.trim());
|
|
if (name && url) {
|
|
const existing = channels.find(
|
|
(c) => c.name === name && c.group === currentGroup,
|
|
);
|
|
if (existing) {
|
|
existing.urls.push(url);
|
|
} else {
|
|
channels.push(
|
|
new Channel({
|
|
id: `${currentGroup}_${name}`,
|
|
name,
|
|
group: currentGroup,
|
|
urls: [url],
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return channels;
|
|
}
|
|
|
|
async getGroups() {
|
|
const channels = await this.getChannels();
|
|
const groups = new Set(channels.map((c) => c.group));
|
|
return Array.from(groups);
|
|
}
|
|
|
|
// 订阅源
|
|
async getSubscriptions() {
|
|
return (await this.get("subscriptions")) || [];
|
|
}
|
|
|
|
async setSubscriptions(subs) {
|
|
await this.set("subscriptions", subs);
|
|
}
|
|
|
|
// 线路有效性 (使用 SharedPreferences)
|
|
async getValidity(url) {
|
|
const all = await this.getAllValidity();
|
|
return all.find((v) => v.url === url);
|
|
}
|
|
|
|
async setValidity(url, validity) {
|
|
const all = await this.getAllValidity();
|
|
const index = all.findIndex((v) => v.url === url);
|
|
if (index >= 0) {
|
|
all[index] = { ...validity, url };
|
|
} else {
|
|
all.push({ ...validity, url });
|
|
}
|
|
await this.set("validity", all);
|
|
}
|
|
|
|
async getAllValidity() {
|
|
return (await this.get("validity")) || [];
|
|
}
|
|
|
|
// 用户偏好
|
|
async getPreferences() {
|
|
return (await this.get("preferences")) || new Preferences();
|
|
}
|
|
|
|
async setPreferences(prefs) {
|
|
await this.set("preferences", prefs);
|
|
}
|
|
|
|
// 播放历史
|
|
async getHistory(limit = 50) {
|
|
const all = (await this.get("history")) || [];
|
|
return all.slice(-limit).reverse();
|
|
}
|
|
|
|
async addHistory(item) {
|
|
const all = (await this.get("history")) || [];
|
|
all.push(item);
|
|
// 保留最近 500 条
|
|
if (all.length > 500) {
|
|
all.splice(0, all.length - 500);
|
|
}
|
|
await this.set("history", all);
|
|
}
|
|
|
|
async clearHistory() {
|
|
await this.remove("history");
|
|
}
|
|
|
|
// 缓存元数据
|
|
async getCacheMeta(key) {
|
|
const all = (await this.get("cacheMeta")) || {};
|
|
return all[key];
|
|
}
|
|
|
|
async setCacheMeta(key, meta) {
|
|
const all = (await this.get("cacheMeta")) || {};
|
|
all[key] = { ...meta, key };
|
|
await this.set("cacheMeta", all);
|
|
}
|
|
|
|
async isCacheValid(key, ttl) {
|
|
const meta = await this.getCacheMeta(key);
|
|
if (!meta || !meta.updatedAt) return false;
|
|
return Date.now() - meta.updatedAt < ttl;
|
|
}
|
|
}
|