From a853ffc394f955133703d08249a49c94b3c74ead Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Fri, 15 May 2020 15:33:22 +0200 Subject: [PATCH] Cleanup data model to allow graphql server backend to handle this better --- src/api/imdb_api.js | 4 +- src/metadata_loader.js | 84 ++++++++++++--------- src/storage.js | 166 +++++++++++++++++++++++++++++++---------- 3 files changed, 174 insertions(+), 80 deletions(-) diff --git a/src/api/imdb_api.js b/src/api/imdb_api.js index db88097..ac641b6 100644 --- a/src/api/imdb_api.js +++ b/src/api/imdb_api.js @@ -69,7 +69,7 @@ class ImdbApi { 'isAdult', json(case when title.isAdult = 0 then 'false' else 'true' end), 'startYear', title.startYear, 'endYear', title.endYear, - 'runtimeMinutes', title.runtimeMinutes, + 'runtime', title.runtimeMinutes, 'genres', json('["' || replace(title.genres, ',', '","') || '"]'), 'rating', json_object( 'averageRating', title_ratings.averageRating, @@ -133,7 +133,7 @@ class ImdbApi { 'id', title.tconst, 'primaryTitle', title.primaryTitle, 'originalTitle', title.originalTitle, - 'runtimeMinutes', title.runtimeMinutes + 'runtime', title.runtimeMinutes ) AS json FROM title_episode JOIN title ON title_episode.tconst = title.tconst diff --git a/src/metadata_loader.js b/src/metadata_loader.js index 9e08464..b04b9d5 100644 --- a/src/metadata_loader.js +++ b/src/metadata_loader.js @@ -50,16 +50,16 @@ class MetadataLoader { }) const primaryTitleName = await TitleName.build({ region: null, - languages: null, - original: false, + languages: [], + kind: "primary", name: imdbResult.primaryTitle, }); await primaryTitleName.setTitle(title.id, {save: false}); await primaryTitleName.save(); const originalTitleName = await TitleName.build({ region: null, - languages: null, - original: true, + languages: [], + kind: "original", name: imdbResult.originalTitle, }); await originalTitleName.setTitle(title.id, {save: false}); @@ -68,7 +68,7 @@ class MetadataLoader { const titleName = await TitleName.build({ region: el.region, languages: el.languages ? el.languages.split(",") : [], - original: false, + kind: "localized", name: el.title, }) await titleName.setTitle(title.id, {save: false}); @@ -81,7 +81,8 @@ class MetadataLoader { }) const originalTitleDescription = await TitleDescription.build({ region: null, - languages: null, + languages: [], + kind: "original", overview: tmdbResult.overview, tagline: tmdbResult.tagline, }); @@ -91,6 +92,7 @@ class MetadataLoader { const titleDescription = await TitleDescription.build({ region: el.iso_3166_1, languages: el.iso_639_1 ? el.iso_639_1.split(",") : [], + kind: "localized", overview: el.data.overview, tagline: el.data.tagline, }) @@ -110,7 +112,7 @@ class MetadataLoader { const titleCast = await TitleCast.build({ category: el.category, - characters: el.characters, + characters: el.characters || [], job: el.job, }); await titleCast.setTitle(title.id, {save: false}); @@ -165,7 +167,7 @@ class MetadataLoader { const showTitle = await Title.findByPk(ids.uuid); const [mapping] = await TitleEpisode.findOrBuild({ where: { - show_id: showTitle.id, + parent_id: showTitle.id, season_number: episodeIdentifier.season, episode_number: episodeIdentifier.episode, }, @@ -179,11 +181,13 @@ class MetadataLoader { tmdb_id: tmdbResult.id, tvdb_id: null, original_language: showTitle.original_language, + runtime: imdbResult.runtime, }, {returning: true}); mapping.air_date = tmdbResult.air_date; - await mapping.setShow(showTitle.id, {save: false}); - await mapping.setEpisode(episodeTitle, {save: false}); + await mapping.setParent(showTitle, {save: false}); await mapping.save(); + await episodeTitle.setParent(mapping, { save: false}); + await episodeTitle.save(); await TitleName.destroy({ where: { title_id: episodeTitle.id, @@ -191,29 +195,31 @@ class MetadataLoader { }) const primaryTitleName = await TitleName.build({ region: null, - languages: null, - original: false, + languages: [], + kind: "primary", name: imdbResult.primaryTitle, }); await primaryTitleName.setTitle(episodeTitle.id, {save: false}); await primaryTitleName.save(); const originalTitleName = await TitleName.build({ region: null, - languages: null, - original: true, + languages: [], + kind: "original", name: imdbResult.originalTitle, }); await originalTitleName.setTitle(episodeTitle.id, {save: false}); await originalTitleName.save(); for (let el of tmdbTranslations.translations) { - const titleName = await TitleName.build({ - region: el.iso_3166_1, - languages: el.iso_639_1 ? el.iso_639_1.split(",") : [], - original: false, - name: el.data.name, - }) - await titleName.setTitle(episodeTitle.id, {save: false}); - await titleName.save(); + if (el.data.name) { + const titleName = await TitleName.build({ + region: el.iso_3166_1, + languages: el.iso_639_1 ? el.iso_639_1.split(",") : [], + kind: "localized", + name: el.data.name, + }) + await titleName.setTitle(episodeTitle.id, {save: false}); + await titleName.save(); + } } await TitleDescription.destroy({ where: { @@ -222,21 +228,25 @@ class MetadataLoader { }) const originalTitleDescription = await TitleDescription.build({ region: null, - languages: null, + languages: [], + kind: "original", overview: tmdbResult.overview, tagline: tmdbResult.tagline, }); await originalTitleDescription.setTitle(episodeTitle.id, {save: false}); await originalTitleDescription.save(); for (let el of tmdbTranslations.translations) { - const titleDescription = await TitleDescription.build({ - region: el.iso_3166_1, - languages: el.iso_639_1 ? el.iso_639_1.split(",") : [], - overview: el.data.overview, - tagline: el.data.tagline, - }) - await titleDescription.setTitle(episodeTitle.id, {save: false}); - await titleDescription.save(); + if (el.data.overview) { + const titleDescription = await TitleDescription.build({ + region: el.iso_3166_1, + languages: el.iso_639_1 ? el.iso_639_1.split(",") : [], + kind: "localized", + overview: el.data.overview, + tagline: el.data.tagline, + }) + await titleDescription.setTitle(episodeTitle.id, {save: false}); + await titleDescription.save(); + } } return episodeTitle; @@ -349,29 +359,29 @@ class MetadataLoader { async processImages(basePath, filePath, images) { const imageData = !images ? [] : [ !images.logo ? null : !images.logo.url ? null : { - type: "logo", + kind: "logo", url: images.logo.url, src: encodePath(path.relative(basePath, path.join(filePath, "metadata", `logo${path.extname(images.logo.url)}`))) }, !images.poster ? null : !images.poster.file_path ? null : { - type: "poster", + kind: "poster", url: this.tmdb.getImageUrl(images.poster.file_path), src: encodePath(path.relative(basePath, path.join(filePath, "metadata", `poster${path.extname(images.poster.file_path)}`))) }, !images.backdrop ? null : !images.backdrop.file_path ? null : { - type: "backdrop", + kind: "backdrop", url: this.tmdb.getImageUrl(images.backdrop.file_path), src: encodePath(path.relative(basePath, path.join(filePath, "metadata", `backdrop${path.extname(images.backdrop.file_path)}`))) }, !images.still ? null : !images.still.file_path ? null : { - type: "still", + kind: "still", url: this.tmdb.getImageUrl(images.still.file_path), src: encodePath(path.relative(basePath, path.join(filePath, "metadata", `still${path.extname(images.still.file_path)}`))) } ].filter(el => el !== null); return await Promise.all(imageData.map(async img => { - const headers = await downloadFile(img.url, path.join(filePath, "metadata", img.type + path.extname(img.url))); + const headers = await downloadFile(img.url, path.join(filePath, "metadata", img.kind + path.extname(img.url))); return { mime: headers["content-type"], ...img, @@ -387,7 +397,7 @@ class MetadataLoader { }) for (let image of images) { const titleImage = await TitleImage.build({ - type: image.type, + kind: image.kind, mime: image.mime, src: image.src, }) diff --git a/src/storage.js b/src/storage.js index b2eb8c5..b9a6bb0 100644 --- a/src/storage.js +++ b/src/storage.js @@ -31,7 +31,10 @@ class Backend { primaryKey: true }, tmdb_id: sequelize.DataTypes.INTEGER, - name: sequelize.DataTypes.TEXT, + name: { + type: sequelize.DataTypes.TEXT, + allowNull: false, + }, }, { sequelize: this.db, underscored: true, @@ -45,8 +48,11 @@ class Backend { allowNull: false, primaryKey: true }, - imdb_id: sequelize.DataTypes.STRING(64), - name: sequelize.DataTypes.TEXT, + imdb_id: sequelize.DataTypes.TEXT, + name: { + type: sequelize.DataTypes.TEXT, + allowNull: false, + }, }, { sequelize: this.db, underscored: true, @@ -60,10 +66,10 @@ class Backend { allowNull: false, primaryKey: true }, - imdb_id: sequelize.DataTypes.STRING(64), + imdb_id: sequelize.DataTypes.TEXT, tmdb_id: sequelize.DataTypes.INTEGER, tvdb_id: sequelize.DataTypes.INTEGER, - original_language: sequelize.DataTypes.STRING(32), + original_language: sequelize.DataTypes.TEXT, runtime: sequelize.DataTypes.INTEGER, year_start: sequelize.DataTypes.INTEGER, year_end: sequelize.DataTypes.INTEGER, @@ -81,7 +87,10 @@ class Backend { primaryKey: true }, category: sequelize.DataTypes.TEXT, - characters: sequelize.DataTypes.ARRAY(sequelize.DataTypes.TEXT), + characters: { + type: sequelize.DataTypes.ARRAY(sequelize.DataTypes.TEXT), + allowNull: false, + }, job: sequelize.DataTypes.TEXT, }, { sequelize: this.db, @@ -89,9 +98,17 @@ class Backend { modelName: 'title_cast', indexes: [] }); - TitleCast.belongsTo(Title); + TitleCast.belongsTo(Title, { + foreignKey: { + allowNull: false, + } + }); Title.hasMany(TitleCast); - TitleCast.belongsTo(Person); + TitleCast.belongsTo(Person, { + foreignKey: { + allowNull: false, + } + }); Person.hasMany(TitleCast); TitleDescription.init({ @@ -101,9 +118,19 @@ class Backend { allowNull: false, primaryKey: true }, - region: sequelize.DataTypes.STRING(32), - languages: sequelize.DataTypes.ARRAY(sequelize.DataTypes.STRING(32)), - overview: sequelize.DataTypes.TEXT, + region: sequelize.DataTypes.TEXT, + languages: { + type: sequelize.DataTypes.ARRAY(sequelize.DataTypes.TEXT), + allowNull: false, + }, + kind: { + type: sequelize.DataTypes.TEXT, + allowNull: false, + }, + overview: { + type: sequelize.DataTypes.TEXT, + allowNull: false, + }, tagline: sequelize.DataTypes.TEXT, }, { sequelize: this.db, @@ -111,7 +138,11 @@ class Backend { modelName: 'title_description', indexes: [] }); - TitleDescription.belongsTo(Title); + TitleDescription.belongsTo(Title, { + foreignKey: { + allowNull: false, + } + }); Title.hasMany(TitleDescription); TitleEpisode.init({ @@ -121,8 +152,8 @@ class Backend { allowNull: false, primaryKey: true }, - season_number: sequelize.DataTypes.STRING(64), - episode_number: sequelize.DataTypes.STRING(64), + season_number: sequelize.DataTypes.TEXT, + episode_number: sequelize.DataTypes.TEXT, air_date: sequelize.DataTypes.DATEONLY, }, { sequelize: this.db, @@ -131,7 +162,7 @@ class Backend { indexes: [ { fields: [ - 'show_id', + 'parent_id', { attribute: 'season_number', collate: 'C', @@ -147,7 +178,7 @@ class Backend { { using: 'BTREE', fields: [ - 'show_id', + 'parent_id', { attribute: 'air_date', order: 'ASC', @@ -156,10 +187,20 @@ class Backend { } ] }); - TitleEpisode.belongsTo(Title, {as: "Show", foreignKey: "show_id"}); - TitleEpisode.belongsTo(Title, {as: "Episode", foreignKey: "episode_id"}); - Title.hasMany(TitleEpisode, { foreignKey: "show_id", as: 'Episodes'}); - Title.hasOne(TitleEpisode, { foreignKey: "episode_id", as: 'Show'}); + TitleEpisode.belongsTo(Title, { + as: "Parent", + foreignKey: { + name: "parent_id", + allowNull: false, + } + }) + Title.belongsTo(TitleEpisode, { + as: "Parent", + foreignKey: { + name: "parent_id", + allowNull: true, + } + }) TitleGenre.init({ id: { type: sequelize.DataTypes.UUID, @@ -175,9 +216,17 @@ class Backend { modelName: 'title_genre', indexes: [] }); - TitleGenre.belongsTo(Title); + TitleGenre.belongsTo(Title, { + foreignKey: { + allowNull: false, + } + }); Title.hasMany(TitleGenre); - TitleGenre.belongsTo(Genre); + TitleGenre.belongsTo(Genre, { + foreignKey: { + allowNull: false, + } + }); Genre.hasMany(TitleGenre); TitleImage.init({ id: { @@ -186,7 +235,7 @@ class Backend { allowNull: false, primaryKey: true }, - type: sequelize.DataTypes.STRING(64), + kind: sequelize.DataTypes.TEXT, mime: sequelize.DataTypes.TEXT, src: sequelize.DataTypes.TEXT, }, { @@ -195,7 +244,11 @@ class Backend { modelName: 'title_image', indexes: [] }); - TitleImage.belongsTo(Title); + TitleImage.belongsTo(Title, { + foreignKey: { + allowNull: false, + } + }); Title.hasMany(TitleImage); TitleMedia.init({ id: { @@ -204,9 +257,15 @@ class Backend { allowNull: false, primaryKey: true }, - mime: sequelize.DataTypes.STRING(64), - codecs: sequelize.DataTypes.ARRAY(sequelize.DataTypes.STRING(64)), - languages: sequelize.DataTypes.ARRAY(sequelize.DataTypes.STRING(32)), + mime: sequelize.DataTypes.TEXT, + codecs: { + type: sequelize.DataTypes.ARRAY(sequelize.DataTypes.TEXT), + allowNull: false, + }, + languages: { + type: sequelize.DataTypes.ARRAY(sequelize.DataTypes.TEXT), + allowNull: false, + }, src: sequelize.DataTypes.TEXT, }, { sequelize: this.db, @@ -214,7 +273,11 @@ class Backend { modelName: 'title_media', indexes: [] }); - TitleMedia.belongsTo(Title); + TitleMedia.belongsTo(Title, { + foreignKey: { + allowNull: false, + } + }); Title.hasMany(TitleMedia); TitleName.init({ id: { @@ -223,17 +286,30 @@ class Backend { allowNull: false, primaryKey: true }, - region: sequelize.DataTypes.STRING(32), - languages: sequelize.DataTypes.ARRAY(sequelize.DataTypes.STRING(32)), - original: sequelize.DataTypes.BOOLEAN, - name: sequelize.DataTypes.TEXT, + region: sequelize.DataTypes.TEXT, + languages: { + type: sequelize.DataTypes.ARRAY(sequelize.DataTypes.TEXT), + allowNull: false, + }, + kind: { + type: sequelize.DataTypes.TEXT, + allowNull: false, + }, + name: { + type: sequelize.DataTypes.TEXT, + allowNull: false, + }, }, { sequelize: this.db, underscored: true, modelName: 'title_name', indexes: [] }); - TitleName.belongsTo(Title); + TitleName.belongsTo(Title, { + foreignKey: { + allowNull: false, + } + }); Title.hasMany(TitleName); TitleRating.init({ id: { @@ -242,15 +318,19 @@ class Backend { allowNull: false, primaryKey: true }, - region: sequelize.DataTypes.STRING(32), - certification: sequelize.DataTypes.STRING(32), + region: sequelize.DataTypes.TEXT, + certification: sequelize.DataTypes.TEXT, }, { sequelize: this.db, underscored: true, modelName: 'title_rating', indexes: [] }); - TitleRating.belongsTo(Title); + TitleRating.belongsTo(Title, { + foreignKey: { + allowNull: false, + } + }); Title.hasMany(TitleRating); TitleSubtitles.init({ id: { @@ -259,10 +339,10 @@ class Backend { allowNull: false, primaryKey: true }, - format: sequelize.DataTypes.STRING(64), - language: sequelize.DataTypes.STRING(64), - region: sequelize.DataTypes.STRING(64), - specifier: sequelize.DataTypes.STRING(64), + format: sequelize.DataTypes.TEXT, + language: sequelize.DataTypes.TEXT, + region: sequelize.DataTypes.TEXT, + specifier: sequelize.DataTypes.TEXT, src: sequelize.DataTypes.TEXT, }, { sequelize: this.db, @@ -270,7 +350,11 @@ class Backend { modelName: 'title_subtitles', indexes: [] }); - TitleSubtitles.belongsTo(Title); + TitleSubtitles.belongsTo(Title, { + foreignKey: { + allowNull: false, + } + }); Title.hasMany(TitleSubtitles); } -- GitLab