Skip to content

Commit 4fa6aa1

Browse files
committed
feat: add DeduplicatorService to handle channel merging and item deduplication
1 parent 21a310a commit 4fa6aa1

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ export * from './services/channel/channelValueRecipient';
143143

144144
export * from './services/clip';
145145

146+
export * from './services/deduplicator';
147+
146148
export * from './services/feed/feed';
147149
export * from './services/feed/feedFlagStatus';
148150
export * from './services/feed/feedLog';

src/services/deduplicator.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { EntityManager } from 'typeorm';
2+
import { AppDataSourceReadWrite } from '@orm/db';
3+
import { AccountFollowingChannel } from '@orm/entities/account/accountFollowingChannel';
4+
import { Clip } from '@orm/entities/clip';
5+
import { Item } from '@orm/entities/item/item';
6+
import { PlaylistResource } from '@orm/entities/playlist/playlistResource';
7+
8+
export class DeduplicatorService {
9+
protected readWriteEntityManager: EntityManager;
10+
11+
constructor() {
12+
this.readWriteEntityManager = AppDataSourceReadWrite.manager;
13+
}
14+
15+
async mergeChannels(id_to_remove: number, duplicate_id_to_keep: number): Promise<void> {
16+
await this.updateAccountFollowingChannel(id_to_remove, duplicate_id_to_keep);
17+
18+
const { itemsToRemove, duplicateItemsToKeep } = await this.getItemsForBothChannels(id_to_remove, duplicate_id_to_keep);
19+
const { guidMap, guidEnclosureUrlMap } = this.buildItemMaps(duplicateItemsToKeep);
20+
21+
for (const itemToRemove of itemsToRemove) {
22+
const duplicateItemToKeep = this.findDuplicateItem(itemToRemove, guidMap, guidEnclosureUrlMap);
23+
if (duplicateItemToKeep) {
24+
await this.updateClipAndPlaylistResource(itemToRemove.id, duplicateItemToKeep.id);
25+
}
26+
}
27+
}
28+
29+
private async updateAccountFollowingChannel(id_to_remove: number, duplicate_id_to_keep: number): Promise<void> {
30+
await this.readWriteEntityManager
31+
.createQueryBuilder()
32+
.update(AccountFollowingChannel)
33+
.set({ channel_id: duplicate_id_to_keep })
34+
.where("channel_id = :channel_to_remove", { channel_to_remove: id_to_remove })
35+
.execute();
36+
}
37+
38+
private buildItemMaps(items: Item[]): { guidMap: Map<string, Item>, guidEnclosureUrlMap: Map<string, Item> } {
39+
const guidMap = new Map<string, Item>();
40+
const guidEnclosureUrlMap = new Map<string, Item>();
41+
for (const item of items) {
42+
if (item.guid) guidMap.set(item.guid, item);
43+
if (item.guid_enclosure_url) guidEnclosureUrlMap.set(item.guid_enclosure_url, item);
44+
}
45+
return { guidMap, guidEnclosureUrlMap };
46+
}
47+
48+
private findDuplicateItem(
49+
itemToRemove: Item,
50+
guidMap: Map<string, Item>,
51+
guidEnclosureUrlMap: Map<string, Item>
52+
): Item | undefined {
53+
if (itemToRemove.guid && guidMap.has(itemToRemove.guid)) {
54+
return guidMap.get(itemToRemove.guid);
55+
}
56+
if (itemToRemove.guid_enclosure_url && guidEnclosureUrlMap.has(itemToRemove.guid_enclosure_url)) {
57+
return guidEnclosureUrlMap.get(itemToRemove.guid_enclosure_url);
58+
}
59+
return undefined;
60+
}
61+
62+
private async updateClipAndPlaylistResource(itemToRemoveId: number, duplicateItemToKeepId: number): Promise<void> {
63+
await this.readWriteEntityManager
64+
.createQueryBuilder()
65+
.update(Clip)
66+
.set({ item_id: duplicateItemToKeepId })
67+
.where("item_id = :itemToRemoveId", { itemToRemoveId })
68+
.execute();
69+
70+
await this.readWriteEntityManager
71+
.createQueryBuilder()
72+
.update(PlaylistResource)
73+
.set({ item_id: duplicateItemToKeepId })
74+
.where("item_id = :itemToRemoveId", { itemToRemoveId })
75+
.execute();
76+
}
77+
78+
async getChannelItems(channel_id: number): Promise<Item[]> {
79+
const items = await this.readWriteEntityManager
80+
.getRepository(Item)
81+
.find({ where: { channel: { id: channel_id } } });
82+
return items;
83+
}
84+
85+
async getItemsForBothChannels(id_to_remove: number, duplicate_id_to_keep: number): Promise<{ itemsToRemove: Item[], duplicateItemsToKeep: Item[] }> {
86+
const itemsToRemove = await this.getChannelItems(id_to_remove);
87+
const duplicateItemsToKeep = await this.getChannelItems(duplicate_id_to_keep);
88+
return { itemsToRemove, duplicateItemsToKeep };
89+
}
90+
}

0 commit comments

Comments
 (0)