import { Injectable } from '@nestjs/common' import { plainToClass } from 'class-transformer' import { isEmpty } from 'lodash' import { CountryCode, CountryHelper } from '../../../utils/countries' import { Firebender } from '../../../services/firebender' import { Video, VideoPlaylist } from '../../../models' import { VideoPlaylistRepository, VideoRepository } from '../../../repositories' import { Youtube, YoutubePlaylist, YoutubeVideo } from '../../../services/google' type Playlist = { title: string carousel_direction: string playlist_id: string featured_videos: string[] order: number } @Injectable() export class VideoLibrary { private _remoteConfig: any constructor( private readonly videoPlaylistRepository: VideoPlaylistRepository, private readonly videoRepository: VideoRepository, private readonly youtube: Youtube, private readonly firebender: Firebender, ) {} async sync(): Promise { await this.cleanAll() await this.syncFromYoutubeAndRemoteConfig() } private async syncFromYoutubeAndRemoteConfig(): Promise { const countriesConfig = await this.getRemoteConfig() for (const language of Object.keys(countriesConfig)) { try { const config = countriesConfig[language] if (isEmpty(config) || isEmpty(config.playlists) || !Array.isArray(config.playlists)) continue await this.processLanguage(config, language) } catch (error) { console.error(`Error when updating playlists of ${language}`, error) } } } private async processLanguage(config: any, language: string): Promise { for (const playlist of config.playlists) { await this.processPlaylists(playlist, config, CountryHelper.parseCode(language)) } } private async cleanAll(): Promise { await this.videoPlaylistRepository.deleteAll() } private async processPlaylists(playlist: Playlist, config: any, countryCode: CountryCode): Promise { try { const youtubePlaylist = await this.youtube.getPlaylistById(playlist.playlist_id) const videos = await this.youtube.findVideosByPlaylistId(playlist.playlist_id) const videoPlaylist = await this.savePlaylist(youtubePlaylist, countryCode, playlist) for (const youtubeVideo of videos) { const featuredMain = config.featured?.video_ids?.some(videoId => videoId === youtubeVideo.id) || false const featured = playlist.featured_videos?.some(videoId => videoId === youtubeVideo.id) || false await this.saveVideo(youtubeVideo, videoPlaylist, featuredMain, featured) } } catch (error) { console.error(`Error when updating playlist "${playlist.playlist_id}":"${playlist.title}"/${countryCode}`, error) } } private async savePlaylist( youtubePlaylist: YoutubePlaylist, countryCode: CountryCode, playlist: Playlist, ): Promise { const videoPlaylist = plainToClass(VideoPlaylist, { external_id: youtubePlaylist.id, title: playlist.title || youtubePlaylist.snippet.title, carousel_direction: playlist.carousel_direction, order: playlist.order, country_code: countryCode, }) videoPlaylist.id = await this.videoPlaylistRepository.create(videoPlaylist) return videoPlaylist } private async saveVideo( youtubeVideo: YoutubeVideo, videoPlaylist: VideoPlaylist, featuredMain: boolean, featured: boolean, ): Promise { const video = plainToClass(Video, { external_id: youtubeVideo.id, title: youtubeVideo.snippet.title, thumb_url: youtubeVideo.snippet.thumbnails.standard?.url || youtubeVideo.snippet.thumbnails.high?.url || '', description: youtubeVideo.snippet.description, country_code: videoPlaylist.country_code, playlist_id: videoPlaylist.id, uploaded_at: youtubeVideo.snippet.publishedAt.toISOString(), featured_main: featuredMain, featured, }) await this.videoRepository.create(video) } private async getRemoteConfig(): Promise { return ( this._remoteConfig || (this._remoteConfig = JSON.parse( (await this.firebender.firebaseAdmin.remoteConfig().getTemplate()).parameters.videoteca_settings.defaultValue[ 'value' ], )) ) } }