Public Access
feat(import): read the exports of GOG Galaxy, Grouvee, HowLongToBeat and Playnite
This commit is contained in:
1 parent
89d0b29698
commit
8e31eafc06
14 files changed
+289
-26
No files matched your search
+34
-18
@@ -45,32 +45,48 @@ const COLUMNS = {
|
||||
*
|
||||
* @return {rows} with title, platform, playtimeMinutes and status where the file has them, or {error} as an i18n key
|
||||
*/
|
||||
export const parseCsv = (text) => {
|
||||
const clean = text.replace(/^\uFEFF/, '');
|
||||
const firstLine = clean.split(/\r?\n/, 1)[0];
|
||||
const delimiter = [',', ';', '\t'].reduce((best, next) =>
|
||||
firstLine.split(next).length > firstLine.split(best).length ? next : best);
|
||||
const [header, ...records] = csvRecords(clean, delimiter);
|
||||
const names = (header ?? []).map((name) => name.trim().toLowerCase());
|
||||
export const parseCsv = (text) => csvRows(readCsv(text));
|
||||
|
||||
export const csvRows = ({names, records}) => {
|
||||
const at = Object.fromEntries(Object.entries(COLUMNS)
|
||||
.map(([field, headers]) => [field, names.findIndex((name) => headers.includes(name))]));
|
||||
if (at.title < 0) return {error: 'IMPORT.CSV_NO_TITLE'};
|
||||
|
||||
const value = (record, field) => (at[field] >= 0 ? (record[at[field]] ?? '').trim() : '');
|
||||
const rows = records.filter((record) => value(record, 'title')).map((record) => {
|
||||
const platform = value(record, 'platform');
|
||||
const minutes = at.minutes >= 0 ? Math.round(Number(value(record, 'minutes'))) || 0
|
||||
: hoursToMinutes(value(record, 'hours'));
|
||||
return {
|
||||
title: value(record, 'title').slice(0, MAX_IMPORT_TITLE_LENGTH),
|
||||
platform: platform && platform.length <= MAX_PLATFORM_LENGTH ? platform : null,
|
||||
playtimeMinutes: minutes > 0 ? Math.min(minutes, MAX_PLAYTIME_MINUTES) : null,
|
||||
status: parseStatus(value(record, 'status').toUpperCase()),
|
||||
};
|
||||
});
|
||||
const rows = records.filter((record) => value(record, 'title')).map((record) => importRow({
|
||||
title: value(record, 'title'),
|
||||
platform: value(record, 'platform'),
|
||||
minutes: at.minutes >= 0 ? Number(value(record, 'minutes')) : hoursToMinutes(value(record, 'hours')),
|
||||
status: parseStatus(value(record, 'status').toUpperCase()),
|
||||
}));
|
||||
return {rows};
|
||||
};
|
||||
|
||||
/**
|
||||
* A row as Core takes it: within the limits, and null for what is unknown.
|
||||
*/
|
||||
export const importRow = ({title, platform, minutes, status}) => {
|
||||
const playtime = Math.round(minutes);
|
||||
return {
|
||||
title: title.trim().slice(0, MAX_IMPORT_TITLE_LENGTH),
|
||||
platform: typeof platform === 'string' && platform && platform.length <= MAX_PLATFORM_LENGTH ? platform : null,
|
||||
playtimeMinutes: playtime > 0 ? Math.min(playtime, MAX_PLAYTIME_MINUTES) : null,
|
||||
status: status ?? null,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {names, records}: the header names in lower case and the other records as arrays of fields
|
||||
*/
|
||||
export const readCsv = (text) => {
|
||||
const clean = text.replace(/^\uFEFF/, '');
|
||||
const firstLine = clean.split(/\r?\n/, 1)[0];
|
||||
const delimiter = [',', ';', '\t'].reduce((best, next) =>
|
||||
firstLine.split(next).length > firstLine.split(best).length ? next : best);
|
||||
const [header, ...records] = csvRecords(clean, delimiter);
|
||||
return {names: (header ?? []).map((name) => name.trim().toLowerCase()), records};
|
||||
};
|
||||
|
||||
function csvRecords(text, delimiter) {
|
||||
const records = [];
|
||||
let record = [];
|
||||
|
||||
Reference in new issue
Block a user