Skip to content

Commit ed92022

Browse files
committed
fix: handle ann_id being removed
1 parent fbcd882 commit ed92022

File tree

5 files changed

+103
-39
lines changed

5 files changed

+103
-39
lines changed

lib/handlers/MBNDSynchronizer.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const fs = require('fs');
22
const csv2json = require('csvtojson');
33
const { Op } = require('sequelize');
4+
const { randomUUID } = require('crypto');
45
const pLimit = require('p-limit');
56
const camelCase = require('camelcase');
67
const dayjs = require('dayjs');
@@ -39,7 +40,7 @@ class MBNDSynchronizer {
3940
dbFilePath: undefined
4041
};
4142
this.options = options;
42-
this.limit = pLimit(500);
43+
this.limit = pLimit(5000);
4344

4445
process.on('SIGINT', async () => await this.restoreDbFile());
4546
process.on('SIGTERM', async () => await this.restoreDbFile());
@@ -248,13 +249,17 @@ class MBNDSynchronizer {
248249

249250
let annotation = foundTrack?.trackAnnotation;
250251
if (!annotation) {
251-
annotation = Annotation.build({
252+
const record = {
252253
item_type: 'media_file',
253254
user_id: user.id,
254255
item_id: foundTrack.id,
255256
play_count: 0,
256257
starred: 0
257-
});
258+
};
259+
if (Annotation.getAttributes().ann_id) {
260+
record.ann_id = randomUUID();
261+
}
262+
annotation = Annotation.build(record);
258263
}
259264

260265
const update = {};
@@ -283,6 +288,7 @@ class MBNDSynchronizer {
283288
if (!Object.keys(update).length) {
284289
return;
285290
}
291+
286292
if (!foundTrack.trackAnnotation) {
287293
await annotation.save();
288294
}
@@ -361,13 +367,18 @@ class MBNDSynchronizer {
361367
}
362368
let annotation = album?.albumAnnotation;
363369
if (!annotation) {
364-
annotation = Annotation.build({
370+
const record = {
365371
item_type: 'album',
366372
user_id: user.id,
367373
item_id: album.id,
368374
play_count: 0,
369375
starred: 0
370-
});
376+
};
377+
378+
if (Annotation.getAttributes().ann_id) {
379+
record.ann_id = randomUUID();
380+
}
381+
annotation = Annotation.build(record);
371382
}
372383

373384
const update = {};
@@ -472,13 +483,18 @@ class MBNDSynchronizer {
472483

473484
let annotation = artist?.artistAnnotation;
474485
if (!annotation) {
475-
annotation = Annotation.build({
486+
const record = {
476487
item_type: 'artist',
477488
user_id: user.id,
478489
item_id: artist.id,
479490
play_count: 0,
480491
starred: 0
481-
});
492+
};
493+
494+
if (Annotation.getAttributes().ann_id) {
495+
record.ann_id = randomUUID();
496+
}
497+
annotation = Annotation.build(record);
482498
}
483499
const update = {};
484500
const totalPlaycount = artist.tracks.reduce((acc, current) => {

lib/handlers/dbManager.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,22 @@ exports.init = async dbFilePath => {
1919
throw error;
2020
}
2121

22-
createRelationships(sequelize);
22+
await createRelationships(sequelize);
2323

2424
return sequelize;
2525
};
2626

27-
const createRelationships = sequelize => {
27+
const createRelationships = async sequelize => {
28+
// handling legacy Annotation schema (ann_id removed from navidrome DB schema in this commit: https://github.com/navidrome/navidrome/commit/47378c68828861751b9d1a05d3fc9b29ce8dd9f0)
29+
const annotationTableSchema = await sequelize.getQueryInterface().describeTable('annotation');
30+
2831
const Track = sequelize.define('Track', TrackDefinition.attributes, TrackDefinition.options);
2932
const Album = sequelize.define('Album', AlbumDefinition.attributes, AlbumDefinition.options);
30-
const Annotation = sequelize.define('Annotation', AnnotationDefinition.attributes, AnnotationDefinition.options);
33+
const Annotation = sequelize.define(
34+
'Annotation',
35+
AnnotationDefinition.getAttributes(annotationTableSchema),
36+
AnnotationDefinition.options
37+
);
3138
const Artist = sequelize.define('Artist', ArtistDefinition.attributes, ArtistDefinition.options);
3239
const User = sequelize.define('User', UserDefinition.attributes, UserDefinition.options);
3340

@@ -44,7 +51,4 @@ const createRelationships = sequelize => {
4451
Annotation.belongsTo(Track, { as: 'track', foreignKey: 'item_id' });
4552
Annotation.belongsTo(Album, { as: 'album', foreignKey: 'item_id' });
4653
Annotation.belongsTo(User, { as: 'user', foreignKey: 'user_id' });
47-
// ann_id removed from navidrome DB schema in this commit: https://github.com/navidrome/navidrome/commit/47378c68828861751b9d1a05d3fc9b29ce8dd9f0
48-
// Sequelize sets an id attribute by default as primary key, which is not needed in this case
49-
Annotation.removeAttribute('id');
5054
};

lib/models/Annotation.js

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,45 @@
11
const { DataTypes } = require('sequelize');
22
const dayjs = require('dayjs');
33

4-
exports.attributes = {
5-
user_id: { type: DataTypes.STRING, allowNull: false, defaultValue: '' },
6-
item_id: { type: DataTypes.STRING, allowNull: false, defaultValue: '' },
7-
item_type: { type: DataTypes.STRING, allowNull: false, defaultValue: '' },
8-
play_count: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 },
9-
play_date: {
10-
type: DataTypes.STRING,
11-
defaultValue: null,
12-
set(value) {
13-
const date = dayjs(value);
14-
this.setDataValue('play_date', date.format('YYYY-MM-DD HH:mm:ss'));
15-
}
16-
},
17-
rating: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0, validate: { min: 0, max: 5 } },
18-
starred: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false },
19-
starred_at: {
20-
type: DataTypes.STRING,
21-
defaultValue: null,
22-
set(value) {
23-
const date = dayjs(value);
24-
this.setDataValue('starred_at', date.isValid() ? date.format('YYYY-MM-DD HH:mm:ss') : null);
4+
exports.getAttributes = existingSchema => {
5+
const defaultAttributes = {
6+
ann_id: { type: DataTypes.STRING, primaryKey: true, allowNull: false },
7+
user_id: { type: DataTypes.STRING, allowNull: false, defaultValue: '', primaryKey: true },
8+
item_id: { type: DataTypes.STRING, allowNull: false, defaultValue: '', primaryKey: true },
9+
item_type: { type: DataTypes.STRING, allowNull: false, defaultValue: '', primaryKey: true },
10+
play_count: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 },
11+
play_date: {
12+
type: DataTypes.STRING,
13+
defaultValue: null,
14+
set(value) {
15+
const date = dayjs(value);
16+
this.setDataValue('play_date', date.format('YYYY-MM-DD HH:mm:ss'));
17+
}
18+
},
19+
rating: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0, validate: { min: 0, max: 5 } },
20+
starred: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false },
21+
starred_at: {
22+
type: DataTypes.STRING,
23+
defaultValue: null,
24+
set(value) {
25+
const date = dayjs(value);
26+
this.setDataValue('starred_at', date.isValid() ? date.format('YYYY-MM-DD HH:mm:ss') : null);
27+
}
2528
}
29+
};
30+
if (!existingSchema.ann_id) {
31+
delete defaultAttributes.ann_id;
2632
}
33+
return defaultAttributes;
2734
};
2835

2936
exports.options = {
3037
tableName: 'annotation',
31-
timestamps: false
38+
timestamps: false,
39+
indexes: [
40+
{
41+
fields: ['user_id', 'item_id', 'item_type'],
42+
unique: true
43+
}
44+
]
3245
};

package-lock.json

Lines changed: 26 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,16 @@
2727
},
2828
"bin": "index.js",
2929
"pkg": {
30-
"assets": ["node_modules/**/*"],
31-
"targets": [ "node16-win-x64"],
30+
"assets": [
31+
"node_modules/**/*"
32+
],
33+
"targets": [
34+
"node16-win-x64"
35+
],
3236
"outputPath": "dist",
3337
"outputName": "musicbee-navidrome-sync"
38+
},
39+
"devDependencies": {
40+
"prettier": "^3.3.3"
3441
}
3542
}

0 commit comments

Comments
 (0)