feat(preferences): section langues a telecharger (defaut fr+en), transcripts filtres cote serveur et watch
CI / build-and-test (push) Successful in 13m50s
CI / build-and-test (push) Successful in 13m50s
This commit is contained in:
+61
-5
@@ -123,6 +123,7 @@ if (schemaFile && fs.existsSync(schemaFile)) {
|
||||
const colsPrefs = db.prepare(`PRAGMA table_info(user_preferences)`).all();
|
||||
const have = new Set(colsPrefs.map(c => c.name));
|
||||
if (!have.has('default_providers')) db.exec(`ALTER TABLE user_preferences ADD COLUMN default_providers TEXT`);
|
||||
if (!have.has('download_languages')) db.exec(`ALTER TABLE user_preferences ADD COLUMN download_languages TEXT`);
|
||||
} catch {}
|
||||
})();
|
||||
|
||||
@@ -144,11 +145,45 @@ export function insertUser({ id, username, email, passwordHash }) {
|
||||
db.prepare(`INSERT INTO users (id, username, email, password_hash, is_active, created_at, updated_at)
|
||||
VALUES (@id, @username, @email, @passwordHash, 1, @ts, @ts)`).run({ id, username, email, passwordHash, ts });
|
||||
// Default preferences row (default_providers NULL = no multi-provider preference yet)
|
||||
db.prepare(`INSERT INTO user_preferences (user_id, language, default_provider, default_providers, theme, video_quality, region, version, updated_at)
|
||||
VALUES (@id, 'en', 'youtube', NULL, 'system', 'auto', 'US', 1, @ts)`).run({ id, ts });
|
||||
db.prepare(`INSERT INTO user_preferences (user_id, language, default_provider, default_providers, download_languages, theme, video_quality, region, version, updated_at)
|
||||
VALUES (@id, 'en', 'youtube', NULL, @dl, 'system', 'auto', 'US', 1, @ts)`).run({ id, ts, dl: JSON.stringify(DEFAULT_DOWNLOAD_LANGUAGES) });
|
||||
}// Sanitize a defaultProviders list: keep only known provider ids.
|
||||
const KNOWN_PROVIDER_IDS = ['yt', 'dm', 'tw', 'pt', 'od', 'ru'];
|
||||
|
||||
// Langues de sous-titres/transcripts proposées dans les préférences.
|
||||
// Défaut : français + anglais uniquement.
|
||||
export const DEFAULT_DOWNLOAD_LANGUAGES = ['fr', 'en'];
|
||||
export const SUPPORTED_DOWNLOAD_LANGUAGES = [
|
||||
'fr', 'en', 'es', 'de', 'it', 'pt', 'nl', 'pl',
|
||||
'ru', 'ar', 'zh', 'ja', 'hi', 'ko',
|
||||
];
|
||||
|
||||
function sanitizeDownloadLanguages(value) {
|
||||
let arr = value;
|
||||
// Accept JSON string (from DB) or array (from API patch)
|
||||
if (typeof arr === 'string') {
|
||||
try { arr = JSON.parse(arr); } catch { arr = []; }
|
||||
}
|
||||
if (!Array.isArray(arr)) arr = [];
|
||||
const cleaned = arr
|
||||
.map(v => String(v || '').trim().toLowerCase().replace(/_/g, '-').split('-')[0])
|
||||
.filter(v => /^[a-z]{2,3}$/.test(v) && SUPPORTED_DOWNLOAD_LANGUAGES.includes(v));
|
||||
// Deduplicate, preserve order
|
||||
return Array.from(new Set(cleaned));
|
||||
}
|
||||
|
||||
function parseDownloadLanguagesForApi(stored) {
|
||||
// NULL (ancienne ligne) = défaut fr+en ; '[]' explicite = aucune langue.
|
||||
if (stored == null) return [...DEFAULT_DOWNLOAD_LANGUAGES];
|
||||
try {
|
||||
const parsed = JSON.parse(stored);
|
||||
if (!Array.isArray(parsed)) return [...DEFAULT_DOWNLOAD_LANGUAGES];
|
||||
return sanitizeDownloadLanguages(parsed);
|
||||
} catch {
|
||||
return [...DEFAULT_DOWNLOAD_LANGUAGES];
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeDefaultProviders(value) {
|
||||
let arr = value;
|
||||
// Accept JSON string (from DB) or array (from API patch)
|
||||
@@ -169,6 +204,11 @@ export function upsertPreferences(userId, patch) {
|
||||
if (patch.defaultProviders !== undefined) {
|
||||
defaultProviders = sanitizeDefaultProviders(patch.defaultProviders);
|
||||
}
|
||||
// Normalize the download/subtitle languages if provided (fr+en par défaut)
|
||||
let downloadLanguages = undefined;
|
||||
if (patch.downloadLanguages !== undefined) {
|
||||
downloadLanguages = sanitizeDownloadLanguages(patch.downloadLanguages);
|
||||
}
|
||||
|
||||
const current = db.prepare('SELECT * FROM user_preferences WHERE user_id = ?').get(userId);
|
||||
if (!current) {
|
||||
@@ -178,23 +218,33 @@ export function upsertPreferences(userId, patch) {
|
||||
default_providers: defaultProviders !== undefined
|
||||
? (defaultProviders.length ? JSON.stringify(defaultProviders) : null)
|
||||
: null,
|
||||
download_languages: downloadLanguages !== undefined
|
||||
? JSON.stringify(downloadLanguages)
|
||||
: JSON.stringify(DEFAULT_DOWNLOAD_LANGUAGES),
|
||||
theme: patch.theme ?? 'system',
|
||||
video_quality: patch.videoQuality ?? 'auto',
|
||||
region: patch.region ?? 'US',
|
||||
version: 1,
|
||||
};
|
||||
db.prepare(`INSERT INTO user_preferences (user_id, language, default_provider, default_providers, theme, video_quality, region, version, updated_at)
|
||||
VALUES (@userId, @language, @default_provider, @default_providers, @theme, @video_quality, @region, @version, @updated_at)`)
|
||||
db.prepare(`INSERT INTO user_preferences (user_id, language, default_provider, default_providers, download_languages, theme, video_quality, region, version, updated_at)
|
||||
VALUES (@userId, @language, @default_provider, @default_providers, @download_languages, @theme, @video_quality, @region, @version, @updated_at)`)
|
||||
.run({ userId, ...merged, updated_at: nowIso() });
|
||||
} else {
|
||||
const storedList = current.default_providers;
|
||||
const nextList = defaultProviders !== undefined
|
||||
? (defaultProviders.length ? JSON.stringify(defaultProviders) : null)
|
||||
: storedList;
|
||||
const storedDl = Object.prototype.hasOwnProperty.call(current, 'download_languages')
|
||||
? current.download_languages
|
||||
: undefined;
|
||||
const nextDl = downloadLanguages !== undefined
|
||||
? JSON.stringify(downloadLanguages)
|
||||
: (storedDl !== undefined ? storedDl : JSON.stringify(DEFAULT_DOWNLOAD_LANGUAGES));
|
||||
const merged = {
|
||||
language: patch.language ?? current.language,
|
||||
default_provider: patch.defaultProvider ?? current.default_provider,
|
||||
default_providers: nextList,
|
||||
download_languages: nextDl,
|
||||
theme: patch.theme ?? current.theme,
|
||||
video_quality: patch.videoQuality ?? current.video_quality,
|
||||
region: patch.region ?? current.region,
|
||||
@@ -203,6 +253,7 @@ export function upsertPreferences(userId, patch) {
|
||||
};
|
||||
db.prepare(`UPDATE user_preferences
|
||||
SET language=@language, default_provider=@default_provider, default_providers=@default_providers,
|
||||
download_languages=@download_languages,
|
||||
theme=@theme, video_quality=@video_quality, region=@region, version=@version, updated_at=@updated_at
|
||||
WHERE user_id=@userId`)
|
||||
.run({ userId, ...merged });
|
||||
@@ -211,6 +262,7 @@ export function upsertPreferences(userId, patch) {
|
||||
|
||||
export function getPreferences(userId) {
|
||||
return db.prepare(`SELECT language, default_provider AS defaultProvider, default_providers AS defaultProviders,
|
||||
download_languages AS downloadLanguages,
|
||||
theme, video_quality AS videoQuality, region, version, updated_at
|
||||
FROM user_preferences WHERE user_id = ?`).get(userId);
|
||||
}
|
||||
@@ -222,7 +274,11 @@ export function getPreferencesForApi(userId) {
|
||||
if (prefs.defaultProviders) {
|
||||
try { list = JSON.parse(prefs.defaultProviders); } catch { list = null; }
|
||||
}
|
||||
return { ...prefs, defaultProviders: Array.isArray(list) ? list : null };
|
||||
return {
|
||||
...prefs,
|
||||
defaultProviders: Array.isArray(list) ? list : null,
|
||||
downloadLanguages: parseDownloadLanguagesForApi(prefs.downloadLanguages),
|
||||
};
|
||||
}
|
||||
|
||||
export function insertSession({ id, userId, refreshTokenHash, isRemember, userAgent, deviceInfo, ip, expiresAt }) {
|
||||
|
||||
Reference in New Issue
Block a user