tvbox支持根据用户细分

This commit is contained in:
mtvpls
2026-02-25 11:56:27 +08:00
parent 6a494f8a1b
commit 0402828c4c
16 changed files with 583 additions and 78 deletions
+45
View File
@@ -1346,6 +1346,51 @@ export class D1Storage implements IStorage {
}
}
// ==================== TVBox订阅token ====================
async getTvboxSubscribeToken?(userName: string): Promise<string | null> {
try {
const result = await this.db
.prepare('SELECT tvbox_subscribe_token FROM users WHERE username = ?')
.bind(userName)
.first();
return result?.tvbox_subscribe_token || null;
} catch (err) {
console.error('D1Storage.getTvboxSubscribeToken error:', err);
return null;
}
}
async setTvboxSubscribeToken?(userName: string, token: string): Promise<void> {
try {
await this.db
.prepare('UPDATE users SET tvbox_subscribe_token = ? WHERE username = ?')
.bind(token, userName)
.run();
// 清除缓存
userInfoCache?.delete(userName);
} catch (err) {
console.error('D1Storage.setTvboxSubscribeToken error:', err);
throw err;
}
}
async getUsernameByTvboxToken?(token: string): Promise<string | null> {
try {
const result = await this.db
.prepare('SELECT username FROM users WHERE tvbox_subscribe_token = ?')
.bind(token)
.first();
return result?.username || null;
} catch (err) {
console.error('D1Storage.getUsernameByTvboxToken error:', err);
return null;
}
}
// ==================== 搜索历史 ====================
async getSearchHistory(userName: string): Promise<string[]> {
+21
View File
@@ -482,6 +482,27 @@ export class DbManager {
return [];
}
// ---------- TVBox订阅token ----------
async getTvboxSubscribeToken(userName: string): Promise<string | null> {
if (typeof (this.storage as any).getTvboxSubscribeToken === 'function') {
return (this.storage as any).getTvboxSubscribeToken(userName);
}
return null;
}
async setTvboxSubscribeToken(userName: string, token: string): Promise<void> {
if (typeof (this.storage as any).setTvboxSubscribeToken === 'function') {
await (this.storage as any).setTvboxSubscribeToken(userName, token);
}
}
async getUsernameByTvboxToken(token: string): Promise<string | null> {
if (typeof (this.storage as any).getUsernameByTvboxToken === 'function') {
return (this.storage as any).getUsernameByTvboxToken(token);
}
return null;
}
// ---------- 播放记录迁移 ----------
async migratePlayRecords(userName: string): Promise<void> {
if (typeof (this.storage as any).migratePlayRecords === 'function') {
+46
View File
@@ -911,6 +911,52 @@ export class PostgresStorage implements IStorage {
}
}
// ==================== TVBox订阅token ====================
async getTvboxSubscribeToken(userName: string): Promise<string | null> {
try {
const result = await this.db
.prepare('SELECT tvbox_subscribe_token FROM users_v2 WHERE username = $1')
.bind(userName)
.first();
return result?.tvbox_subscribe_token || null;
} catch (err) {
console.error('PostgresStorage.getTvboxSubscribeToken error:', err);
return null;
}
}
async setTvboxSubscribeToken(userName: string, token: string): Promise<void> {
try {
await this.db
.prepare('UPDATE users_v2 SET tvbox_subscribe_token = $1 WHERE username = $2')
.bind(token, userName)
.run();
// 清除用户信息缓存
const { userInfoCache } = await import('./user-cache');
userInfoCache.delete(userName);
} catch (err) {
console.error('PostgresStorage.setTvboxSubscribeToken error:', err);
throw err;
}
}
async getUsernameByTvboxToken(token: string): Promise<string | null> {
try {
const result = await this.db
.prepare('SELECT username FROM users_v2 WHERE tvbox_subscribe_token = $1')
.bind(token)
.first();
return result?.username || null;
} catch (err) {
console.error('PostgresStorage.getUsernameByTvboxToken error:', err);
return null;
}
}
// ==================== 音乐播放记录 ====================
async getMusicPlayRecord(userName: string, key: string): Promise<any | null> {
+31
View File
@@ -1723,4 +1723,35 @@ export abstract class BaseRedisStorage implements IStorage {
// 清除缓存
userInfoCache?.delete(userName);
}
// ---------- TVBox订阅token相关 ----------
async getTvboxSubscribeToken(userName: string): Promise<string | null> {
// 直接从数据库读取,不使用缓存
const token = await this.withRetry(() =>
this.adapter.hGet(this.userInfoKey(userName), 'tvboxSubscribeToken')
);
return token || null;
}
async setTvboxSubscribeToken(userName: string, token: string): Promise<void> {
// 保存token到用户信息
await this.withRetry(() =>
this.adapter.hSet(this.userInfoKey(userName), 'tvboxSubscribeToken', token)
);
// 创建token到用户名的反向索引
await this.withRetry(() =>
this.adapter.set(`tvbox:token:${token}`, userName)
);
// 清除缓存
userInfoCache?.delete(userName);
}
async getUsernameByTvboxToken(token: string): Promise<string | null> {
const userName = await this.withRetry(() =>
this.adapter.get(`tvbox:token:${token}`)
);
return userName || null;
}
}
+9
View File
@@ -0,0 +1,9 @@
import { randomBytes } from 'crypto';
/**
* 生成TVBox订阅token
* 使用crypto生成32位随机hex字符串
*/
export function generateTvboxToken(): string {
return randomBytes(16).toString('hex');
}
+5
View File
@@ -161,6 +161,11 @@ export interface IStorage {
setUserEmail?(userName: string, email: string): Promise<void>;
getEmailNotificationPreference?(userName: string): Promise<boolean>;
setEmailNotificationPreference?(userName: string, enabled: boolean): Promise<void>;
// TVBox订阅token相关
getTvboxSubscribeToken?(userName: string): Promise<string | null>;
setTvboxSubscribeToken?(userName: string, token: string): Promise<void>;
getUsernameByTvboxToken?(token: string): Promise<string | null>;
}
// 搜索结果数据结构