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