From 046d53fbfdba900ffa140057792b299fecdd8aa6 Mon Sep 17 00:00:00 2001 From: 4gray Date: Mon, 17 May 2021 21:00:20 +0200 Subject: [PATCH 1/7] refactor: add new i18n translations --- src/assets/i18n/de.json | 6 +++++- src/assets/i18n/en.json | 6 +++++- src/assets/i18n/ru.json | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 4f6d55919..b008fc4ff 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -57,7 +57,11 @@ "TITLE": "Titel", "USER_AGENT": "User agent", "SAVE": "Speichern", - "ORIGINAL_FILENAME": "Original-Dateinamen" + "ORIGINAL_FILENAME": "Original-Dateinamen", + "FILE_PATH": "Dateipfad", + "UPDATE_FAILED": "Update fehlgeschlagen", + "AUTO_UPDATE": "Auto-update", + "AUTO_UPDATE_DESCRIPTION": "Wenn die Auto-Refresh-Funktion aktiviert ist, wird die Wiedergabeliste bei jedem Start der Anwendung automatisch aktualisiert." }, "UPDATED": "Aktualisiert am", "REFRESH": "Aktualisieren" diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index b201471fd..79d383489 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -25,7 +25,11 @@ "CLOSE": "Close", "SAVE": "Save", "USER_AGENT": "User agent", - "ORIGINAL_FILENAME": "Original filename" + "ORIGINAL_FILENAME": "Original filename", + "FILE_PATH": "File path", + "UPDATE_FAILED": "Last playlist update failed", + "AUTO_UPDATE": "Auto-update", + "AUTO_UPDATE_DESCRIPTION": "If the auto-refresh function is activated, the playlist will be updated automatically each time the app is started." }, "UPDATED": "Updated", "REFRESH": "Refresh playlist" diff --git a/src/assets/i18n/ru.json b/src/assets/i18n/ru.json index dd041ca98..d434365cf 100644 --- a/src/assets/i18n/ru.json +++ b/src/assets/i18n/ru.json @@ -25,7 +25,11 @@ "CLOSE": "Закрыть", "SAVE": "Сохранить", "USER_AGENT": "User agent", - "ORIGINAL_FILENAME": "Имя файла при загрузке" + "ORIGINAL_FILENAME": "Имя файла при загрузке", + "FILE_PATH": "Путь к файлу", + "UPDATE_FAILED": "Не удалось обновить плейлист", + "AUTO_UPDATE": "Автообновление", + "AUTO_UPDATE_DESCRIPTION": "Если активирована функция автообновления, список воспроизведения будет обновляться автоматически при каждом запуске приложения." }, "UPDATED": "Обновлен", "REFRESH": "Обновить плейлист" From c62cc9c34cdc49981856de795b5d39eab8efb90f Mon Sep 17 00:00:00 2001 From: 4gray Date: Mon, 17 May 2021 21:04:50 +0200 Subject: [PATCH 2/7] refactor: adapt typedef in playlist interface --- src/app/home/playlist.interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/home/playlist.interface.ts b/src/app/home/playlist.interface.ts index 9e4e091e5..98ede0f9d 100644 --- a/src/app/home/playlist.interface.ts +++ b/src/app/home/playlist.interface.ts @@ -26,6 +26,6 @@ export interface Playlist { userAgent?: string; filePath?: string; autoRefresh: boolean; - updateDate?: string; + updateDate?: number; updateState?: PlaylistUpdateState; } From e8ab576d8b797a39eb1206e80f19e04abe88bdb4 Mon Sep 17 00:00:00 2001 From: 4gray Date: Tue, 18 May 2021 13:39:03 +0200 Subject: [PATCH 3/7] feat: auto-refresh playlists on app startup --- api.ts | 141 +++++++++++++----- .../playlist-info.component.html | 21 +++ .../playlist-info.component.spec.ts | 3 +- .../playlist-info/playlist-info.component.ts | 9 +- .../recent-playlists.component.html | 9 ++ 5 files changed, 146 insertions(+), 37 deletions(-) diff --git a/api.ts b/api.ts index 096bc05e3..6c05fa161 100644 --- a/api.ts +++ b/api.ts @@ -2,8 +2,11 @@ import { app, BrowserWindow, ipcMain } from 'electron'; import { parse } from 'iptv-playlist-parser'; import axios from 'axios'; import { guid } from '@datorama/akita'; -import { Playlist } from './src/app/home/playlist.interface'; -import Nedb from 'nedb-promises-ts'; +import { + Playlist, + PlaylistUpdateState, +} from './src/app/home/playlist.interface'; +import Nedb, { Cursor } from 'nedb-promises-ts'; import { EPG_ERROR, EPG_FETCH, @@ -147,16 +150,11 @@ export class Api { ); ipcMain.on(PLAYLIST_SAVE_DETAILS, async (event, args) => { - const updated = await db.update( - { _id: args._id }, - { - $set: { - title: args.title, - userAgent: args.userAgent, - autoRefresh: args.autoRefresh, - }, - } - ); + const updated = await this.updatePlaylistById(args.id, { + title: args.title, + userAgent: args.userAgent, + autoRefresh: args.autoRefresh, + }); if (!updated.numAffected || updated.numAffected === 0) { console.error('Error: Playlist details were not updated'); } @@ -173,6 +171,30 @@ export class Api { } } ); + + this.refreshPlaylists(); + } + + /** + * Starts the update process for all the playlists with the enabled auto-refresh flag + */ + refreshPlaylists() { + this.getAllPlaylistsMeta().then((playlists) => { + playlists.forEach((playlist) => { + if (playlist.autoRefresh && playlist.autoRefresh === true) { + if (playlist.url) { + this.fetchPlaylistByUrl(playlist._id, playlist.url); + } else if (playlist.filePath) { + this.fetchPlaylistByFilePath( + playlist._id, + playlist.filePath + ); + } else { + console.log('skip...'); + } + } + }); + }); } /** @@ -180,9 +202,20 @@ export class Api { * @param event main event */ async sendAllPlaylists(event: Electron.IpcMainEvent): Promise { - const playlists = await db.find( - { type: { $exists: false } }, - { + const playlists = await this.getAllPlaylistsMeta(); + event.sender.send('playlist-all-result', { + payload: playlists, + }); + } + + /** + * Returns all existing playlists with meta information from the database + * @returns + */ + getAllPlaylistsMeta(): Cursor { + return db + .find({ type: { $exists: false } }) + .projection({ count: 1, title: 1, _id: 1, @@ -193,11 +226,9 @@ export class Api { filePath: 1, autoRefresh: 1, updateDate: 1, - } - ); - event.sender.send('playlist-all-result', { - payload: playlists, - }); + updateState: 1, + }) + .sort({ importDate: -1 }); } /** @@ -280,11 +311,33 @@ export class Api { playlist, count: playlist.items.length, updateDate: Date.now(), + updateState: PlaylistUpdateState.UPDATED, }, } ); } + /** + * Updates the provided playlist in the database + * @param id id of the playlist + * @param data playlist data to update + * @returns + */ + updatePlaylistById( + id: string, + data: Partial + ): Promise<{ + numAffected: number; + upsert: boolean; + }> { + return db.update( + { _id: id }, + { + $set: data, + } + ); + } + /** * Converts the fetched playlist string to the playlist object, updates it in the database and sends the updated playlists array back to the renderer * @param id id of the playlist to update @@ -294,20 +347,27 @@ export class Api { async handlePlaylistRefresh( id: string, playlistString: any, - event: Electron.IpcMainEvent + event?: Electron.IpcMainEvent ): Promise { const playlist = this.convertFileStringToPlaylist(playlistString); - const updated = await this.saveUpdatedPlaylist(id, playlist); + const updated = await this.updatePlaylistById(id, { + playlist, + count: playlist.items.length, + updateDate: Date.now(), + updateState: PlaylistUpdateState.UPDATED, + }); if (!updated.numAffected || updated.numAffected === 0) { console.error('Error: Playlist details were not updated'); } - event.sender.send(PLAYLIST_UPDATE_RESPONSE, { - message: `Success! The playlist was successfully updated (${playlist.items.length} channels)`, - }); + if (event) { + event.sender.send(PLAYLIST_UPDATE_RESPONSE, { + message: `Success! The playlist was successfully updated (${playlist.items.length} channels)`, + }); - // send all playlists back to the renderer process - this.sendAllPlaylists(event); + // send all playlists back to the renderer process + this.sendAllPlaylists(event); + } } /** @@ -319,7 +379,7 @@ export class Api { async fetchPlaylistByUrl( id: string, url: string, - event: Electron.IpcMainEvent + event?: Electron.IpcMainEvent ): Promise { try { await axios @@ -328,6 +388,9 @@ export class Api { this.handlePlaylistRefresh(id, result.data, event) ); } catch (err) { + this.updatePlaylistById(id, { + updateState: PlaylistUpdateState.NOT_UPDATED, + }); event.sender.send(ERROR, { message: `${err.response.statusText}. Please check the entered playlist URL again.`, status: err.response.status, @@ -344,19 +407,19 @@ export class Api { fetchPlaylistByFilePath( id: string, path: string, - event: Electron.IpcMainEvent + event?: Electron.IpcMainEvent ): void { try { fs.readFile(path, 'utf-8', async (err, data) => { if (err) { - this.handleFileNotFoundError(err, event); + this.handleFileNotFoundError(err, id, event); return; } this.handlePlaylistRefresh(id, data, event); }); } catch (err) { - this.handleFileNotFoundError(err, event); + this.handleFileNotFoundError(err, id, event); } } @@ -372,13 +435,21 @@ export class Api { syscall: string; path: string; }, - event: Electron.IpcMainEvent + id: string, + event?: Electron.IpcMainEvent ) { console.error(error); - event.sender.send(ERROR, { - message: `Sorry, playlist was not found (${error.path})`, - status: 'ENOENT', + this.updatePlaylistById(id, { + updateState: PlaylistUpdateState.NOT_UPDATED, }); + if (event) { + // send all playlists back to the renderer process + this.sendAllPlaylists(event); + event.sender.send(ERROR, { + message: `Sorry, playlist was not found (${error.path})`, + status: 'ENOENT', + }); + } } convertFileStringToPlaylist(m3uString: string): any { diff --git a/src/app/home/recent-playlists/playlist-info/playlist-info.component.html b/src/app/home/recent-playlists/playlist-info/playlist-info.component.html index d56c52b73..858fd4a0e 100644 --- a/src/app/home/recent-playlists/playlist-info/playlist-info.component.html +++ b/src/app/home/recent-playlists/playlist-info/playlist-info.component.html @@ -42,6 +42,27 @@ }} + + + {{ 'HOME.PLAYLISTS.INFO_DIALOG.FILE_PATH' | translate }} + + + + {{ 'HOME.PLAYLISTS.INFO_DIALOG.UPDATE_FAILED' | translate }} + + + + {{ 'HOME.PLAYLISTS.INFO_DIALOG.AUTO_UPDATE' | translate }} + +

+ {{ + 'HOME.PLAYLISTS.INFO_DIALOG.AUTO_UPDATE_DESCRIPTION' | translate + }} +