fix: robustify PocketBase JSON parsing with recovery for malformed strings
This commit is contained in:
parent
c40c991cbe
commit
63dbd9289b
1 changed files with 33 additions and 80 deletions
|
|
@ -55,35 +55,9 @@ const syncManager = {
|
||||||
const record = await this._getUserRecord(user.uid);
|
const record = await this._getUserRecord(user.uid);
|
||||||
if (!record) return null;
|
if (!record) return null;
|
||||||
|
|
||||||
let library = record.library || {};
|
const library = this.safeParseInternal(record.library, 'library', {});
|
||||||
if (typeof library === 'string') {
|
const history = this.safeParseInternal(record.history, 'history', []);
|
||||||
try {
|
const userPlaylists = this.safeParseInternal(record.user_playlists, 'user_playlists', {});
|
||||||
library = JSON.parse(library);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to parse library JSON:', e);
|
|
||||||
library = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let history = record.history || [];
|
|
||||||
if (typeof history === 'string') {
|
|
||||||
try {
|
|
||||||
history = JSON.parse(history);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to parse history JSON:', e);
|
|
||||||
history = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let userPlaylists = record.user_playlists || {};
|
|
||||||
if (typeof userPlaylists === 'string') {
|
|
||||||
try {
|
|
||||||
userPlaylists = JSON.parse(userPlaylists);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to parse user_playlists JSON:', e);
|
|
||||||
userPlaylists = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { library, history, userPlaylists };
|
return { library, history, userPlaylists };
|
||||||
},
|
},
|
||||||
|
|
@ -96,13 +70,35 @@ const syncManager = {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const updated = await this.pb.collection('DB_users').update(record.id, { [field]: data }, { f_id: uid });
|
const stringifiedData = typeof data === 'string' ? data : JSON.stringify(data);
|
||||||
|
const updated = await this.pb
|
||||||
|
.collection('DB_users')
|
||||||
|
.update(record.id, { [field]: stringifiedData }, { f_id: uid });
|
||||||
this._userRecordCache = updated;
|
this._userRecordCache = updated;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to sync ${field} to PocketBase:`, error);
|
console.error(`Failed to sync ${field} to PocketBase:`, error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
safeParseInternal(str, fieldName, fallback) {
|
||||||
|
if (!str) return fallback;
|
||||||
|
if (typeof str !== 'string') return str;
|
||||||
|
try {
|
||||||
|
return JSON.parse(str);
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
// Recovery attempt: replace illegal internal quotes in name/title fields
|
||||||
|
const recovered = str.replace(/(:\s*")(.+?)("(?=\s*[,}\n\r]))/g, (match, p1, p2, p3) => {
|
||||||
|
const escapedContent = p2.replace(/(?<!\\)"/g, '\\"');
|
||||||
|
return p1 + escapedContent + p3;
|
||||||
|
});
|
||||||
|
return JSON.parse(recovered);
|
||||||
|
} catch {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async syncLibraryItem(type, item, added) {
|
async syncLibraryItem(type, item, added) {
|
||||||
const user = authManager.user;
|
const user = authManager.user;
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
|
@ -110,16 +106,7 @@ const syncManager = {
|
||||||
const record = await this._getUserRecord(user.uid);
|
const record = await this._getUserRecord(user.uid);
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
|
|
||||||
let library = record.library || {};
|
let library = this.safeParseInternal(record.library, 'library', {});
|
||||||
|
|
||||||
if (typeof library === 'string') {
|
|
||||||
try {
|
|
||||||
library = JSON.parse(library);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Library field is not valid JSON', e);
|
|
||||||
library = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pluralType = type === 'mix' ? 'mixes' : `${type}s`;
|
const pluralType = type === 'mix' ? 'mixes' : `${type}s`;
|
||||||
const key = type === 'playlist' ? item.uuid : item.id;
|
const key = type === 'playlist' ? item.uuid : item.id;
|
||||||
|
|
@ -230,15 +217,7 @@ const syncManager = {
|
||||||
const record = await this._getUserRecord(user.uid);
|
const record = await this._getUserRecord(user.uid);
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
|
|
||||||
let history = record.history || [];
|
let history = this.safeParseInternal(record.history, 'history', []);
|
||||||
if (typeof history === 'string') {
|
|
||||||
try {
|
|
||||||
history = JSON.parse(history);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('History field is not valid JSON', e);
|
|
||||||
history = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const newHistory = [historyEntry, ...history].slice(0, 100);
|
const newHistory = [historyEntry, ...history].slice(0, 100);
|
||||||
await this._updateUserJSON(user.uid, 'history', newHistory);
|
await this._updateUserJSON(user.uid, 'history', newHistory);
|
||||||
|
|
@ -251,16 +230,7 @@ const syncManager = {
|
||||||
const record = await this._getUserRecord(user.uid);
|
const record = await this._getUserRecord(user.uid);
|
||||||
if (!record) return;
|
if (!record) return;
|
||||||
|
|
||||||
let userPlaylists = record.user_playlists || {};
|
let userPlaylists = this.safeParseInternal(record.user_playlists, 'user_playlists', {});
|
||||||
|
|
||||||
if (typeof userPlaylists === 'string') {
|
|
||||||
try {
|
|
||||||
userPlaylists = JSON.parse(userPlaylists);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('user_playlists field is not valid JSON', e);
|
|
||||||
userPlaylists = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action === 'delete') {
|
if (action === 'delete') {
|
||||||
delete userPlaylists[playlist.id];
|
delete userPlaylists[playlist.id];
|
||||||
|
|
@ -288,15 +258,7 @@ const syncManager = {
|
||||||
.getFirstListItem(`uuid="${uuid}"`, { p_id: uuid });
|
.getFirstListItem(`uuid="${uuid}"`, { p_id: uuid });
|
||||||
|
|
||||||
let rawCover = record.image || record.cover || record.playlist_cover || '';
|
let rawCover = record.image || record.cover || record.playlist_cover || '';
|
||||||
let extraData = record.data;
|
let extraData = this.safeParseInternal(record.data, 'data', {});
|
||||||
|
|
||||||
if (typeof extraData === 'string') {
|
|
||||||
try {
|
|
||||||
extraData = JSON.parse(extraData);
|
|
||||||
} catch {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rawCover && extraData && typeof extraData === 'object') {
|
if (!rawCover && extraData && typeof extraData === 'object') {
|
||||||
rawCover = extraData.cover || extraData.image || '';
|
rawCover = extraData.cover || extraData.image || '';
|
||||||
|
|
@ -308,16 +270,7 @@ const syncManager = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let images = [];
|
let images = [];
|
||||||
let tracks = record.tracks || [];
|
let tracks = this.safeParseInternal(record.tracks, 'tracks', []);
|
||||||
|
|
||||||
if (typeof tracks === 'string') {
|
|
||||||
try {
|
|
||||||
tracks = JSON.parse(tracks);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to parse tracks JSON:', e);
|
|
||||||
tracks = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!finalCover && tracks && tracks.length > 0) {
|
if (!finalCover && tracks && tracks.length > 0) {
|
||||||
const uniqueCovers = [];
|
const uniqueCovers = [];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue