diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..dc5bf8b3 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +server/src/migrations/**/*.ts \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index c5003a61..f84dfea0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,6 +4,7 @@ "overrides": [ { "files": ["*.ts", "*.mts", "*.tsx"], + "excludeFiles": ["server/src/migrations/**/*.ts"], "options": { "parser": "typescript" } diff --git a/server/mikro-orm.base.config.ts b/server/mikro-orm.base.config.ts index a860372a..b62e1f1f 100644 --- a/server/mikro-orm.base.config.ts +++ b/server/mikro-orm.base.config.ts @@ -30,6 +30,7 @@ import { Migration20240618005544 } from './src/migrations/Migration2024061800554 import { Migration20240719145409 } from './src/migrations/Migration20240719145409.js'; import { Migration20240805185042 } from './src/migrations/Migration20240805185042.js'; import { Migration20240917191535 } from './src/migrations/Migration20240917191535.js'; +import { Migration20241014205231 } from './src/migrations/Migration20241014205231.js'; import { DATABASE_LOCATION_ENV_VAR } from './src/util/constants.js'; import { getDefaultDatabaseDirectory } from './src/util/defaults.js'; @@ -143,6 +144,10 @@ export default defineConfig({ name: 'add_channel_stream_mode', class: Migration20240917191535, }, + { + name: 'cascade_channel_filler_show_deletes', + class: Migration20241014205231, + }, ], }, extensions: [Migrator], diff --git a/server/package.json b/server/package.json index a5c18656..80f61034 100644 --- a/server/package.json +++ b/server/package.json @@ -23,6 +23,7 @@ "make-exec:macos-arm64": "tsx scripts/makeScriptPackage.ts --target macos-arm64", "make-exec:windows": "tsx scripts/makeScriptPackage.ts --target windows-x64", "generate-db-migration": "dotenv -e .env.development -- tsx --tsconfig ./tsconfig.build.json src/index.ts db generate-migration", + "generate-db-migration2": "dotenv -e .env.development -- tsx --tsconfig ./tsconfig.build.json scripts/generateDbMigration.ts", "generate-db-cache": "dotenv -e .env.development -- tsx scripts/generateDbCache.ts", "generate-db-types": "dotenv -e .env.development -- tsx scripts/genDbTypes.ts", "mikro-orm": "mikro-orm-esm", diff --git a/server/scripts/generateDbMigration.ts b/server/scripts/generateDbMigration.ts new file mode 100644 index 00000000..bdf5773f --- /dev/null +++ b/server/scripts/generateDbMigration.ts @@ -0,0 +1,27 @@ +import path from 'path'; +import tmp from 'tmp'; +import dbConfig from '../mikro-orm.config.js'; +import { initOrm } from '../src/dao/dataSource'; + +const tmpdir = tmp.dirSync({ + unsafeCleanup: true, +}); + +const dbName = path.join(tmpdir.name, 'db.db'); + +console.log(`using temporary database at ${dbName}`); + +const orm = await initOrm({ + ...dbConfig, + dbName, +}); + +const migrationResult = await orm.migrator.createMigration('./src/migrations'); + +console.log(`Succcesfully created migration ${migrationResult.fileName}`); + +console.debug(migrationResult.diff); + +await orm.close(); + +tmpdir.removeCallback(); diff --git a/server/src/dao/channelDb.ts b/server/src/dao/channelDb.ts index 4985dbae..43865020 100644 --- a/server/src/dao/channelDb.ts +++ b/server/src/dao/channelDb.ts @@ -478,6 +478,7 @@ export class ChannelDB { try { await this.markLineupFileForDeletion(channelId); marked = true; + const ref = em.getReference(Channel, channelId); await em.remove(ref).flush(); // Best effort remove references to this channel @@ -505,6 +506,8 @@ export class ChannelDB { channelId, e, ); + + throw e; } } diff --git a/server/src/dao/entities/Channel.ts b/server/src/dao/entities/Channel.ts index bf611642..f261a8e3 100644 --- a/server/src/dao/entities/Channel.ts +++ b/server/src/dao/entities/Channel.ts @@ -1,5 +1,4 @@ import { - Cascade, Collection, Entity, Enum, @@ -191,7 +190,7 @@ export class Channel extends BaseEntity { entity: () => FillerShow, pivotEntity: () => ChannelFillerShow, mappedBy: (filler) => filler.channels, - cascade: [Cascade.PERSIST, Cascade.REMOVE], + deleteRule: 'cascade', }) fillers = new Collection(this); @@ -204,7 +203,10 @@ export class Channel extends BaseEntity { @Property({ nullable: true }) fillerRepeatCooldown?: number; // Seconds - @ManyToMany(() => CustomShow) + @ManyToMany({ + entity: () => CustomShow, + deleteRule: 'cascade', + }) customShows = new Collection(this); // Watermark @@ -219,7 +221,10 @@ export class Channel extends BaseEntity { }) transcoding?: IType; - @ManyToMany(() => Program) + @ManyToMany({ + entity: () => Program, + deleteRule: 'cascade', + }) fallback = new Collection(this); @Enum({ items: () => ChannelStreamModes, default: 'hls' }) diff --git a/server/src/dao/entities/ChannelFillerShow.ts b/server/src/dao/entities/ChannelFillerShow.ts index 7c208861..1d5598d7 100644 --- a/server/src/dao/entities/ChannelFillerShow.ts +++ b/server/src/dao/entities/ChannelFillerShow.ts @@ -8,7 +8,11 @@ export class ChannelFillerShow { @ManyToOne({ primary: true, entity: () => FillerShow }) fillerShow!: Ref; - @ManyToOne({ primary: true, entity: () => Channel }) + @ManyToOne({ + primary: true, + entity: () => Channel, + deleteRule: 'cascade', + }) channel!: Ref; @Property() diff --git a/server/src/migrations/.snapshot-db.db.json b/server/src/migrations/.snapshot-db.db.json index 5531bac3..eb736cb2 100644 --- a/server/src/migrations/.snapshot-db.db.json +++ b/server/src/migrations/.snapshot-db.db.json @@ -237,7 +237,8 @@ "default": "'hls'", "enumItems": [ "hls", - "hls_slower" + "hls_slower", + "mpegts" ], "mappedType": "enum" } @@ -578,6 +579,7 @@ "uuid" ], "referencedTableName": "channel", + "deleteRule": "cascade", "updateRule": "cascade" } }, diff --git a/server/src/migrations/Migration20241014205231.ts b/server/src/migrations/Migration20241014205231.ts new file mode 100644 index 00000000..a3ef1f4c --- /dev/null +++ b/server/src/migrations/Migration20241014205231.ts @@ -0,0 +1,18 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20241014205231 extends Migration { + + async up(): Promise { + this.addSql('pragma foreign_keys = off;'); + this.addSql('PRAGMA defer_foreign_keys = ON;'); + this.addSql('create table `channel_filler_show__temp_alter` (`filler_show_uuid` text not null, `channel_uuid` text not null, `weight` integer not null, `cooldown` integer not null, constraint `channel_filler_show_filler_show_uuid_foreign` foreign key(`filler_show_uuid`) references `filler_show`(`uuid`) on update cascade, constraint `channel_filler_show_channel_uuid_foreign` foreign key(`channel_uuid`) references `channel`(`uuid`) on delete cascade on update cascade, primary key (`filler_show_uuid`, `channel_uuid`));'); + this.addSql('insert into `channel_filler_show__temp_alter` select * from `channel_filler_show`;'); + this.addSql('drop table `channel_filler_show`;'); + this.addSql('alter table `channel_filler_show__temp_alter` rename to `channel_filler_show`;'); + this.addSql('create index `channel_filler_show_filler_show_uuid_index` on `channel_filler_show` (`filler_show_uuid`);'); + this.addSql('create index `channel_filler_show_channel_uuid_index` on `channel_filler_show` (`channel_uuid`);'); + this.addSql('pragma foreign_keys = on;'); + this.addSql('PRAGMA defer_foreign_keys = OFF;'); + } + +} diff --git a/server/src/services/tvGuideService.ts b/server/src/services/tvGuideService.ts index e21188c1..18c8ce56 100644 --- a/server/src/services/tvGuideService.ts +++ b/server/src/services/tvGuideService.ts @@ -144,16 +144,6 @@ export class TVGuideService { this.currentUpdateTime = now; this.currentEndTime = now + guideDuration.asMilliseconds(); - this.eventService.push({ - type: 'xmltv', - message: `Started building tv-guide at = ${dayjs(now).format()}`, - module: 'xmltv', - detail: { - time: now, - }, - level: 'info', - }); - this.currentChannels = values( mapValues( await this.channelDb.loadAllLineups(), @@ -799,11 +789,10 @@ export class TVGuideService { } private async refreshXML() { - // const xmltvSettings = (await getSettings()).xmlTvSettings(); await this.xmltv.write(values(this.cachedGuide)); this.eventService.push({ type: 'xmltv', - message: `XMLTV updated at server time = ${new Date().toISOString()}`, + message: `XMLTV updated at server time = ${dayjs().format()}`, module: 'xmltv', detail: { time: new Date().getTime(),