feat(preferences): section langues a telecharger (defaut fr+en), transcripts filtres cote serveur et watch
CI / build-and-test (push) Successful in 13m50s

This commit is contained in:
2026-09-26 11:49:56 -04:00
parent 1b0a7eb3ce
commit 655061ba2c
46 changed files with 2018 additions and 380 deletions
+61 -5
View File
@@ -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 }) {