mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
chore: back to electron + nx
This commit is contained in:
1 parent
f5b3a15b83
commit
99ed5df404
477 files changed
+19213
-17370
No files matched your search
@@ -28,7 +28,8 @@ If applicable, add screenshots to help explain your problem.
|
||||
- PWA or Electron/Tauri application
|
||||
- OS: [e.g. Linux]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
- Version [e.g. 0.16.0]
|
||||
- Feature: [e.g. M3U playlist, Xtream Code, Stalker Portal]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -1,69 +0,0 @@
|
||||
name: 'Tauri Release'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
publish-tauri:
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: 'macos-latest' # for Arm based macs (M1 and above).
|
||||
args: '--target aarch64-apple-darwin'
|
||||
- platform: 'macos-latest' # for Intel based macs.
|
||||
args: '--target x86_64-apple-darwin'
|
||||
- platform: 'ubuntu-22.04' # for Tauri v1 you could replace this with ubuntu-20.04.
|
||||
args: ''
|
||||
- platform: 'windows-latest'
|
||||
args: ''
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: install dependencies (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-22.04' # This must match the platform value defined above.
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
# webkitgtk 4.0 is for Tauri v1 - webkitgtk 4.1 is for Tauri v2.
|
||||
# You can remove the one that doesn't apply to your app to speed up the workflow a bit.
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'npm' # Set this to npm, yarn or pnpm.
|
||||
|
||||
- name: install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
# Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds.
|
||||
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
||||
|
||||
- name: Rust cache
|
||||
uses: swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './src-tauri -> target'
|
||||
|
||||
- name: install frontend dependencies
|
||||
# If you don't have `beforeBuildCommand` configured you may want to build your frontend here too.
|
||||
run: npm install # change this to npm or pnpm depending on which one you use.
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
with:
|
||||
tagName: app-v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version.
|
||||
releaseName: 'App v__VERSION__'
|
||||
releaseBody: 'See the assets to download this version and install.'
|
||||
releaseDraft: true
|
||||
prerelease: false
|
||||
args: ${{ matrix.args }}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"branches": [
|
||||
"master"
|
||||
],
|
||||
"plugins": [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
["@semantic-release/changelog", {
|
||||
"changelogFile": "CHANGELOG.md"
|
||||
}],
|
||||
"@semantic-release/npm", {
|
||||
"npmPublish": false,
|
||||
},
|
||||
["@semantic-release/git", {
|
||||
"assets": ["CHANGELOG.md", "package.json"],
|
||||
"message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
|
||||
}],
|
||||
]
|
||||
}
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"cli": {
|
||||
"schematicCollections": ["@angular-eslint/schematics"],
|
||||
"analytics": false
|
||||
},
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"iptvnator": {
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:application": {
|
||||
"strict": true,
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular/build:application",
|
||||
"options": {
|
||||
"outputPath": {
|
||||
"base": "dist"
|
||||
},
|
||||
"index": "src/index.html",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"polyfills": ["zone.js"],
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"src/m3-theme.scss",
|
||||
"./node_modules/video.js/dist/video-js.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"serviceWorker": "ngsw-config.json",
|
||||
"webWorkerTsConfig": "tsconfig.worker.json",
|
||||
"browser": "src/main.ts"
|
||||
},
|
||||
"configurations": {
|
||||
"dev": {
|
||||
"optimization": false,
|
||||
"outputHashing": "none",
|
||||
"sourceMap": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.dev.ts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"web": {
|
||||
"optimization": true,
|
||||
"outputHashing": "none",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.web.ts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"production": {
|
||||
"optimization": false,
|
||||
"outputHashing": "none",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular/build:dev-server",
|
||||
"options": {
|
||||
"buildTarget": "iptvnator:build"
|
||||
},
|
||||
"configurations": {
|
||||
"dev": {
|
||||
"buildTarget": "iptvnator:build:dev"
|
||||
},
|
||||
"web": {
|
||||
"buildTarget": "iptvnator:build:web"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "iptvnator:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular/build:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "iptvnator:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-builders/jest:run",
|
||||
"options": {}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"iptvnator-e2e": {
|
||||
"root": "e2e",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": ["e2e/**/*.ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"prefix": "app",
|
||||
"style": "scss",
|
||||
"type": "component"
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"prefix": "app",
|
||||
"type": "directive"
|
||||
},
|
||||
"@schematics/angular:service": {
|
||||
"type": "service"
|
||||
},
|
||||
"@schematics/angular:guard": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:interceptor": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:module": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:pipe": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:resolver": {
|
||||
"typeSeparator": "."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import baseConfig from '../../eslint.config.mjs';
|
||||
|
||||
export default [...baseConfig];
|
||||
@@ -0,0 +1,13 @@
|
||||
export default {
|
||||
displayName: 'electron-backend',
|
||||
preset: '../../jest.preset.js',
|
||||
testEnvironment: 'node',
|
||||
transform: {
|
||||
'^.+\\.[tj]s$': [
|
||||
'ts-jest',
|
||||
{ tsconfig: '<rootDir>/tsconfig.spec.json' },
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../../coverage/apps/electron-backend',
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "electron-backend",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "apps/electron-backend/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
{
|
||||
"projects": ["web"],
|
||||
"target": "build"
|
||||
}
|
||||
],
|
||||
"executor": "nx-electron:build",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/apps/electron-backend",
|
||||
"main": "apps/electron-backend/src/main.ts",
|
||||
"tsConfig": "apps/electron-backend/tsconfig.app.json",
|
||||
"assets": ["apps/electron-backend/src/assets"]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"optimization": true,
|
||||
"extractLicenses": true,
|
||||
"inspect": false,
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "apps/electron-backend/src/environments/environment.ts",
|
||||
"with": "apps/electron-backend/src/environments/environment.prod.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"nx run web:serve",
|
||||
"nx run electron-backend:serve-electron"
|
||||
]
|
||||
}
|
||||
},
|
||||
"serve-electron": {
|
||||
"executor": "nx-electron:execute",
|
||||
"options": {
|
||||
"buildTarget": "electron-backend:build"
|
||||
}
|
||||
},
|
||||
"package": {
|
||||
"dependsOn": ["electron-backend:build"],
|
||||
"executor": "nx-electron:package",
|
||||
"options": {
|
||||
"name": "electron-backend",
|
||||
"frontendProject": "web",
|
||||
"sourcePath": "dist/apps",
|
||||
"outputPath": "dist/packages",
|
||||
"prepackageOnly": true
|
||||
}
|
||||
},
|
||||
"make": {
|
||||
"dependsOn": ["electron-backend:build"],
|
||||
"executor": "nx-electron:make",
|
||||
"options": {
|
||||
"name": "electron-backend",
|
||||
"frontendProject": "web",
|
||||
"sourcePath": "dist/apps",
|
||||
"outputPath": "dist/executables"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"command": "eslint apps/electron-backend/**/*.ts"
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "apps/electron-backend/jest.config.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
contextBridge.exposeInMainWorld('electron', {
|
||||
getAppVersion: () => ipcRenderer.invoke('get-app-version'),
|
||||
platform: process.platform,
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
import { BrowserWindow, shell, screen } from 'electron';
|
||||
import { rendererAppName, rendererAppPort } from './constants';
|
||||
import { environment } from '../environments/environment';
|
||||
import { join } from 'path';
|
||||
import { format } from 'url';
|
||||
|
||||
export default class App {
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
static mainWindow: Electron.BrowserWindow;
|
||||
static application: Electron.App;
|
||||
static BrowserWindow;
|
||||
|
||||
public static isDevelopmentMode() {
|
||||
const isEnvironmentSet: boolean = 'ELECTRON_IS_DEV' in process.env;
|
||||
const getFromEnvironment: boolean =
|
||||
parseInt(process.env.ELECTRON_IS_DEV, 10) === 1;
|
||||
|
||||
return isEnvironmentSet ? getFromEnvironment : !environment.production;
|
||||
}
|
||||
|
||||
private static onWindowAllClosed() {
|
||||
if (process.platform !== 'darwin') {
|
||||
App.application.quit();
|
||||
}
|
||||
}
|
||||
|
||||
private static onClose() {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
App.mainWindow = null;
|
||||
}
|
||||
|
||||
private static onRedirect(event: any, url: string) {
|
||||
if (url !== App.mainWindow.webContents.getURL()) {
|
||||
// this is a normal external redirect, open it in a new browser window
|
||||
event.preventDefault();
|
||||
shell.openExternal(url);
|
||||
}
|
||||
}
|
||||
|
||||
private static onReady() {
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
if (rendererAppName) {
|
||||
App.initMainWindow();
|
||||
App.loadMainWindow();
|
||||
}
|
||||
}
|
||||
|
||||
private static onActivate() {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (App.mainWindow === null) {
|
||||
App.onReady();
|
||||
}
|
||||
}
|
||||
|
||||
private static initMainWindow() {
|
||||
const workAreaSize = screen.getPrimaryDisplay().workAreaSize;
|
||||
const width = Math.min(1280, workAreaSize.width || 1280);
|
||||
const height = Math.min(720, workAreaSize.height || 720);
|
||||
|
||||
// Create the browser window.
|
||||
App.mainWindow = new BrowserWindow({
|
||||
width: width,
|
||||
height: height,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
contextIsolation: true,
|
||||
backgroundThrottling: false,
|
||||
preload: join(__dirname, 'main.preload.js'),
|
||||
},
|
||||
});
|
||||
App.mainWindow.setMenu(null);
|
||||
App.mainWindow.center();
|
||||
|
||||
// if main window is ready to show, close the splash window and show the main window
|
||||
App.mainWindow.once('ready-to-show', () => {
|
||||
App.mainWindow.show();
|
||||
});
|
||||
|
||||
// handle all external redirects in a new browser window
|
||||
// App.mainWindow.webContents.on('will-navigate', App.onRedirect);
|
||||
// App.mainWindow.webContents.on('new-window', (event, url, frameName, disposition, options) => {
|
||||
// App.onRedirect(event, url);
|
||||
// });
|
||||
|
||||
// Emitted when the window is closed.
|
||||
App.mainWindow.on('closed', () => {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
App.mainWindow = null;
|
||||
});
|
||||
}
|
||||
|
||||
private static loadMainWindow() {
|
||||
// load the index.html of the app.
|
||||
if (!App.application.isPackaged) {
|
||||
App.mainWindow.loadURL(`http://localhost:${rendererAppPort}`);
|
||||
App.mainWindow.webContents.openDevTools();
|
||||
} else {
|
||||
App.mainWindow.loadURL(
|
||||
format({
|
||||
pathname: join(
|
||||
__dirname,
|
||||
'..',
|
||||
rendererAppName,
|
||||
'index.html'
|
||||
),
|
||||
protocol: 'file:',
|
||||
slashes: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static main(app: Electron.App, browserWindow: typeof BrowserWindow) {
|
||||
// we pass the Electron.App object and the
|
||||
// Electron.BrowserWindow into this function
|
||||
// so this class has no dependencies. This
|
||||
// makes the code easier to write tests for
|
||||
|
||||
App.BrowserWindow = browserWindow;
|
||||
App.application = app;
|
||||
|
||||
App.application.on('window-all-closed', App.onWindowAllClosed); // Quit when all windows are closed.
|
||||
App.application.on('ready', App.onReady); // App is ready to load data
|
||||
App.application.on('activate', App.onActivate); // App is activated
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export const rendererAppPort = 4200;
|
||||
export const rendererAppName = 'web'; // options.name.split('-')[0] + '-web'
|
||||
export const electronAppName = 'iptvnator';
|
||||
export const updateServerUrl = 'https://deployment-server-url.com'; // TODO: insert your update server url here
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* This module is responsible on handling all the inter process communications
|
||||
* between the frontend to the electron backend.
|
||||
*/
|
||||
|
||||
import { app, ipcMain } from 'electron';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
export default class ElectronEvents {
|
||||
static bootstrapElectronEvents(): Electron.IpcMain {
|
||||
return ipcMain;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve app version
|
||||
ipcMain.handle('get-app-version', (event) => {
|
||||
console.log(`Fetching application version... [v${environment.version}]`);
|
||||
|
||||
return environment.version;
|
||||
});
|
||||
|
||||
// Handle App termination
|
||||
ipcMain.on('quit', (event, code) => {
|
||||
app.exit(code);
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* This module is responsible on handling all the setup events that is submitted by squirrel.
|
||||
*/
|
||||
|
||||
import { app } from 'electron';
|
||||
import { spawn } from 'child_process';
|
||||
import { resolve, join, basename } from 'path';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
export default class SquirrelEvents {
|
||||
private static isAppFirstRun = false;
|
||||
|
||||
// app paths
|
||||
private static appFolder = resolve(process.execPath, '..');
|
||||
private static appRootFolder = resolve(SquirrelEvents.appFolder, '..');
|
||||
private static updateExe = resolve(
|
||||
join(SquirrelEvents.appRootFolder, 'Update.exe')
|
||||
);
|
||||
private static exeName = resolve(
|
||||
join(
|
||||
SquirrelEvents.appRootFolder,
|
||||
'app-' + environment.version,
|
||||
basename(process.execPath)
|
||||
)
|
||||
);
|
||||
|
||||
static handleEvents(): boolean {
|
||||
if (process.argv.length === 1 || process.platform !== 'win32') {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (process.argv[1]) {
|
||||
case '--squirrel-install':
|
||||
case '--squirrel-updated':
|
||||
// Install desktop and start menu shortcuts
|
||||
SquirrelEvents.update(['--createShortcut', SquirrelEvents.exeName]);
|
||||
|
||||
return true;
|
||||
|
||||
case '--squirrel-uninstall':
|
||||
// Remove desktop and start menu shortcuts
|
||||
SquirrelEvents.update(['--removeShortcut', SquirrelEvents.exeName]);
|
||||
|
||||
return true;
|
||||
|
||||
case '--squirrel-obsolete':
|
||||
app.quit();
|
||||
return true;
|
||||
|
||||
case '--squirrel-firstrun':
|
||||
// Check if it the first run of the software
|
||||
SquirrelEvents.isAppFirstRun = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static isFirstRun(): boolean {
|
||||
return SquirrelEvents.isAppFirstRun;
|
||||
}
|
||||
|
||||
private static update(args: Array<string>) {
|
||||
try {
|
||||
spawn(SquirrelEvents.updateExe, args, { detached: true }).on(
|
||||
'close',
|
||||
() => setTimeout(app.quit, 1000)
|
||||
);
|
||||
} catch (error) {
|
||||
setTimeout(app.quit, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { app, autoUpdater, dialog, MessageBoxOptions } from 'electron';
|
||||
import { platform, arch } from 'os';
|
||||
import { updateServerUrl } from '../constants';
|
||||
import App from '../app';
|
||||
|
||||
export default class UpdateEvents {
|
||||
// initialize auto update service - most be invoked only in production
|
||||
static initAutoUpdateService() {
|
||||
const platform_arch =
|
||||
platform() === 'win32' ? platform() : platform() + '_' + arch();
|
||||
const version = app.getVersion();
|
||||
const feed: Electron.FeedURLOptions = {
|
||||
url: `${updateServerUrl}/update/${platform_arch}/${version}`,
|
||||
};
|
||||
|
||||
if (!App.isDevelopmentMode()) {
|
||||
console.log('Initializing auto update service...\n');
|
||||
|
||||
autoUpdater.setFeedURL(feed);
|
||||
UpdateEvents.checkForUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
// check for updates - most be invoked after initAutoUpdateService() and only in production
|
||||
static checkForUpdates() {
|
||||
if (!App.isDevelopmentMode() && autoUpdater.getFeedURL() !== '') {
|
||||
autoUpdater.checkForUpdates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
autoUpdater.on(
|
||||
'update-downloaded',
|
||||
(event, releaseNotes, releaseName, releaseDate) => {
|
||||
const dialogOpts: MessageBoxOptions = {
|
||||
type: 'info' as const,
|
||||
buttons: ['Restart', 'Later'],
|
||||
title: 'Application Update',
|
||||
message: process.platform === 'win32' ? releaseNotes : releaseName,
|
||||
detail:
|
||||
'A new version has been downloaded. Restart the application to apply the updates.',
|
||||
};
|
||||
|
||||
dialog.showMessageBox(dialogOpts).then((returnValue) => {
|
||||
if (returnValue.response === 0) autoUpdater.quitAndInstall();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
console.log('Checking for updates...\n');
|
||||
});
|
||||
|
||||
autoUpdater.on('update-available', () => {
|
||||
console.log('New update available!\n');
|
||||
});
|
||||
|
||||
autoUpdater.on('update-not-available', () => {
|
||||
console.log('Up to date!\n');
|
||||
});
|
||||
|
||||
autoUpdater.on('before-quit-for-update', () => {
|
||||
console.log('Application update is about to begin...\n');
|
||||
});
|
||||
|
||||
autoUpdater.on('error', (message) => {
|
||||
console.error('There was a problem updating the application');
|
||||
console.error(message, '\n');
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"$schema": "../../../../../node_modules/nx-electron/src/validation/maker.schema.json"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Assets Folder
|
||||
@@ -0,0 +1,6 @@
|
||||
declare const __BUILD_VERSION__: string;
|
||||
|
||||
export const environment = {
|
||||
production: true,
|
||||
version: __BUILD_VERSION__,
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
declare const __BUILD_VERSION__: string;
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
version: __BUILD_VERSION__,
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import SquirrelEvents from './app/events/squirrel.events';
|
||||
import ElectronEvents from './app/events/electron.events';
|
||||
import UpdateEvents from './app/events/update.events';
|
||||
import { app, BrowserWindow } from 'electron';
|
||||
import App from './app/app';
|
||||
|
||||
export default class Main {
|
||||
static initialize() {
|
||||
if (SquirrelEvents.handleEvents()) {
|
||||
// squirrel event handled (except first run event) and app will exit in 1000ms, so don't do anything else
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
|
||||
static bootstrapApp() {
|
||||
App.main(app, BrowserWindow);
|
||||
}
|
||||
|
||||
static bootstrapAppEvents() {
|
||||
ElectronEvents.bootstrapElectronEvents();
|
||||
|
||||
// initialize auto updater service
|
||||
if (!App.isDevelopmentMode()) {
|
||||
// UpdateEvents.initAutoUpdateService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle setup events as quickly as possible
|
||||
Main.initialize();
|
||||
|
||||
// bootstrap app
|
||||
Main.bootstrapApp();
|
||||
Main.bootstrapAppEvents();
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"jest.config.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts"
|
||||
],
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import playwright from 'eslint-plugin-playwright';
|
||||
import baseConfig from '../../eslint.config.mjs';
|
||||
|
||||
export default [
|
||||
playwright.configs['flat/recommended'],
|
||||
...baseConfig,
|
||||
{
|
||||
files: ['**/*.ts', '**/*.js'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,68 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
import { nxE2EPreset } from '@nx/playwright/preset';
|
||||
import { workspaceRoot } from '@nx/devkit';
|
||||
|
||||
// For CI, you may want to set BASE_URL to the deployed application.
|
||||
const baseURL = process.env['BASE_URL'] || 'http://localhost:4200';
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
...nxE2EPreset(__filename, { testDir: './src' }),
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
baseURL,
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: {
|
||||
command: 'npx nx run web:serve',
|
||||
url: 'http://localhost:4200',
|
||||
reuseExistingServer: true,
|
||||
cwd: workspaceRoot,
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
|
||||
{
|
||||
name: 'firefox',
|
||||
use: { ...devices['Desktop Firefox'] },
|
||||
},
|
||||
|
||||
{
|
||||
name: 'webkit',
|
||||
use: { ...devices['Desktop Safari'] },
|
||||
},
|
||||
|
||||
// Uncomment for mobile browsers support
|
||||
/* {
|
||||
name: 'Mobile Chrome',
|
||||
use: { ...devices['Pixel 5'] },
|
||||
},
|
||||
{
|
||||
name: 'Mobile Safari',
|
||||
use: { ...devices['iPhone 12'] },
|
||||
}, */
|
||||
|
||||
// Uncomment for branded browsers
|
||||
/* {
|
||||
name: 'Microsoft Edge',
|
||||
use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
},
|
||||
{
|
||||
name: 'Google Chrome',
|
||||
use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||
} */
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "web-e2e",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"sourceRoot": "apps/web-e2e/src",
|
||||
"implicitDependencies": ["web"],
|
||||
"// targets": "to see all targets run: nx show project web-e2e --web",
|
||||
"targets": {}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"sourceMap": false,
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
"playwright.config.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import nx from '@nx/eslint-plugin';
|
||||
import baseConfig from '../../eslint.config.mjs';
|
||||
|
||||
export default [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'app',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'app',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
displayName: 'web',
|
||||
preset: '../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../coverage/apps/web',
|
||||
transform: {
|
||||
'^.+\\.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
},
|
||||
],
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"name": "web",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"sourceRoot": "apps/web/src",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@angular/build:application",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": {
|
||||
"base": "dist/apps/web",
|
||||
"browser": ""
|
||||
},
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "apps/web/tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"apps/web/src/favicon.ico",
|
||||
"apps/web/src/assets",
|
||||
"apps/web/src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"apps/web/src/styles.scss",
|
||||
"apps/web/src/m3-theme.scss",
|
||||
"./node_modules/video.js/dist/video-js.css"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "3mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "4kb",
|
||||
"maximumError": "8kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"continuous": true,
|
||||
"executor": "@angular/build:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "web:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "web:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"proxyConfig": "apps/web/proxy.conf.json"
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"executor": "@angular/build:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "web:build"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "apps/web/jest.config.ts",
|
||||
"tsConfig": "apps/web/tsconfig.spec.json"
|
||||
}
|
||||
},
|
||||
"serve-static": {
|
||||
"continuous": true,
|
||||
"executor": "@nx/web:file-server",
|
||||
"options": {
|
||||
"buildTarget": "web:build",
|
||||
"port": 4200,
|
||||
"staticFilePath": "dist/apps/web/browser",
|
||||
"spa": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:3000",
|
||||
"secure": false
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,28 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Router } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { isTauri } from '@tauri-apps/api/core';
|
||||
import { ask } from '@tauri-apps/plugin-dialog';
|
||||
import { relaunch } from '@tauri-apps/plugin-process';
|
||||
import { check } from '@tauri-apps/plugin-updater';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import * as semver from 'semver';
|
||||
import { IpcCommand } from '../../shared/ipc-command.class';
|
||||
import { IpcCommand } from '../shared/ipc-command.class';
|
||||
import {
|
||||
AUTO_UPDATE_PLAYLISTS,
|
||||
ERROR,
|
||||
OPEN_FILE,
|
||||
SETTINGS_UPDATE,
|
||||
SHOW_WHATS_NEW,
|
||||
VIEW_ADD_PLAYLIST,
|
||||
VIEW_SETTINGS,
|
||||
} from '../../shared/ipc-commands';
|
||||
} from '../shared/ipc-commands';
|
||||
import { DataService } from './services/data.service';
|
||||
import { EpgService } from './services/epg.service';
|
||||
import { PlaylistsService } from './services/playlists.service';
|
||||
import { SettingsService } from './services/settings.service';
|
||||
import { WhatsNewService } from './services/whats-new.service';
|
||||
import { Language } from './settings/language.enum';
|
||||
@@ -38,18 +31,28 @@ import { SearchResultsComponent } from './xtream-tauri/search-results/search-res
|
||||
templateUrl: './app.component.html',
|
||||
standalone: false,
|
||||
})
|
||||
export class AppComponent {
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
private dataService = inject(DataService);
|
||||
private dialog = inject(MatDialog);
|
||||
private epgService = inject(EpgService);
|
||||
private router = inject(Router);
|
||||
private store = inject(Store);
|
||||
private snackBar = inject(MatSnackBar);
|
||||
private translate = inject(TranslateService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private whatsNewService = inject(WhatsNewService);
|
||||
|
||||
/** Visibility flag of the "what is new" modal dialog */
|
||||
isDialogVisible$ = this.whatsNewService.dialogState$;
|
||||
readonly isDialogVisible$ = this.whatsNewService.dialogState$;
|
||||
|
||||
/** Dialog options */
|
||||
options = this.whatsNewService.options;
|
||||
readonly options = this.whatsNewService.options;
|
||||
|
||||
/** Modals to show for the updated version of the application */
|
||||
modals: any[] = [];
|
||||
|
||||
/** List of ipc commands with function mapping */
|
||||
commandsList = [
|
||||
private readonly commandsList = [
|
||||
new IpcCommand(VIEW_ADD_PLAYLIST, () => this.navigateToRoute('/')),
|
||||
new IpcCommand(VIEW_SETTINGS, () => this.navigateToRoute('/settings')),
|
||||
new IpcCommand(SHOW_WHATS_NEW, () => this.showWhatsNewDialog()),
|
||||
@@ -59,22 +62,11 @@ export class AppComponent {
|
||||
];
|
||||
|
||||
/** Default language as fallback */
|
||||
DEFAULT_LANG = Language.ENGLISH;
|
||||
private readonly DEFAULT_LANG = Language.ENGLISH;
|
||||
|
||||
listeners = [];
|
||||
private listeners = [];
|
||||
|
||||
constructor(
|
||||
private dataService: DataService,
|
||||
private dialog: MatDialog,
|
||||
private epgService: EpgService,
|
||||
private playlistService: PlaylistsService,
|
||||
private router: Router,
|
||||
private store: Store,
|
||||
private snackBar: MatSnackBar,
|
||||
private translate: TranslateService,
|
||||
private settingsService: SettingsService,
|
||||
private whatsNewService: WhatsNewService
|
||||
) {
|
||||
constructor() {
|
||||
if (
|
||||
((this.dataService.isElectron &&
|
||||
this.dataService?.remote?.process.platform === 'linux') ||
|
||||
@@ -94,7 +86,7 @@ export class AppComponent {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isTauri()) {
|
||||
/* if (isTauri()) {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (event.key === 'f') {
|
||||
@@ -106,7 +98,7 @@ export class AppComponent {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -116,64 +108,6 @@ export class AppComponent {
|
||||
this.setRendererListeners();
|
||||
this.initSettings();
|
||||
this.handleWhatsNewDialog();
|
||||
|
||||
this.triggerAutoUpdateMechanism();
|
||||
this.checkForUpdates();
|
||||
}
|
||||
|
||||
async checkForUpdates() {
|
||||
if (isTauri()) {
|
||||
const update = await check();
|
||||
if (update?.available) {
|
||||
console.log(
|
||||
`found update ${update.version} from ${update.date} with notes ${update.body}`
|
||||
);
|
||||
let downloaded = 0;
|
||||
let contentLength = 0;
|
||||
|
||||
const wantsUpdate = await ask(
|
||||
`New version ${update.version} is available. Do you want to update now?`
|
||||
);
|
||||
|
||||
if (wantsUpdate) {
|
||||
await update.downloadAndInstall((event) => {
|
||||
switch (event.event) {
|
||||
case 'Started':
|
||||
contentLength = event.data.contentLength;
|
||||
console.log(
|
||||
`started downloading ${event.data.contentLength} bytes`
|
||||
);
|
||||
break;
|
||||
case 'Progress':
|
||||
downloaded += event.data.chunkLength;
|
||||
console.log(
|
||||
`downloaded ${downloaded} from ${contentLength}`
|
||||
);
|
||||
break;
|
||||
case 'Finished':
|
||||
console.log('download finished');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('update installed');
|
||||
await relaunch();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async triggerAutoUpdateMechanism() {
|
||||
if (isTauri()) {
|
||||
const playlistForAutoUpdate = await firstValueFrom(
|
||||
this.playlistService.getPlaylistsForAutoUpdate()
|
||||
);
|
||||
if (playlistForAutoUpdate && playlistForAutoUpdate.length > 0)
|
||||
this.dataService.sendIpcEvent(
|
||||
AUTO_UPDATE_PLAYLISTS,
|
||||
playlistForAutoUpdate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,13 +136,13 @@ export class AppComponent {
|
||||
if (settings && Object.keys(settings).length > 0) {
|
||||
this.dataService.sendIpcEvent(SETTINGS_UPDATE, settings);
|
||||
this.translate.use(settings.language ?? this.DEFAULT_LANG);
|
||||
if (
|
||||
/* if (
|
||||
settings.epgUrl?.length > 0 &&
|
||||
settings.epgUrl?.some((u) => u !== '') &&
|
||||
isTauri()
|
||||
) {
|
||||
this.epgService.fetchEpg(settings.epgUrl);
|
||||
}
|
||||
} */
|
||||
|
||||
if (settings.theme) {
|
||||
this.settingsService.changeTheme(settings.theme);
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -4,8 +4,8 @@ import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { ERROR, PLAYLIST_PARSE_RESPONSE } from '../../../shared/ipc-commands';
|
||||
import { Playlist } from '../../../shared/playlist.interface';
|
||||
import { ERROR, PLAYLIST_PARSE_RESPONSE } from '../../shared/ipc-commands';
|
||||
import { Playlist } from '../../shared/playlist.interface';
|
||||
import { DataService } from '../services/data.service';
|
||||
import { HeaderComponent } from '../shared/components/header/header.component';
|
||||
import { addPlaylist } from '../state/actions';
|
||||
File renamed without changes.
+1
-1
@@ -20,7 +20,7 @@ import { isTauri } from '@tauri-apps/api/core';
|
||||
import { save } from '@tauri-apps/plugin-dialog';
|
||||
import { writeTextFile } from '@tauri-apps/plugin-fs';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { Playlist } from '../../../../../shared/playlist.interface';
|
||||
import { Playlist } from '../../../../shared/playlist.interface';
|
||||
import { DatabaseService } from '../../../services/database.service';
|
||||
import { PlaylistsService } from '../../../services/playlists.service';
|
||||
import { PlaylistMeta } from '../../../shared/playlist-meta.type';
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -13,7 +13,7 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { MockComponent, MockModule, MockProvider } from 'ng-mocks';
|
||||
import { NgxSkeletonLoaderComponent } from 'ngx-skeleton-loader';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PLAYLIST_UPDATE } from '../../../../shared/ipc-commands';
|
||||
import { PLAYLIST_UPDATE } from '../../../shared/ipc-commands';
|
||||
import { DataService } from '../../services/data.service';
|
||||
import { DialogService } from '../../services/dialog.service';
|
||||
import { ElectronServiceStub } from '../../services/electron.service.stub';
|
||||
+7
-6
@@ -7,6 +7,7 @@ import {
|
||||
HostListener,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
@@ -22,8 +23,8 @@ import { Store } from '@ngrx/store';
|
||||
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { NgxSkeletonLoaderComponent } from 'ngx-skeleton-loader';
|
||||
import { BehaviorSubject, combineLatest, map } from 'rxjs';
|
||||
import { GLOBAL_FAVORITES_PLAYLIST_ID } from '../../../../shared/constants';
|
||||
import { Playlist } from '../../../../shared/playlist.interface';
|
||||
import { GLOBAL_FAVORITES_PLAYLIST_ID } from '../../../shared/constants';
|
||||
import { Playlist } from '../../../shared/playlist.interface';
|
||||
import { DataService } from '../../services/data.service';
|
||||
import { DatabaseService } from '../../services/database.service';
|
||||
import { SortService } from '../../services/sort.service';
|
||||
@@ -39,7 +40,7 @@ import {
|
||||
MIGRATE_PLAYLISTS,
|
||||
PLAYLIST_UPDATE,
|
||||
PLAYLIST_UPDATE_RESPONSE,
|
||||
} from './../../../../shared/ipc-commands';
|
||||
} from './../../../shared/ipc-commands';
|
||||
import { DialogService } from './../../services/dialog.service';
|
||||
import { PlaylistMeta } from './../../shared/playlist-meta.type';
|
||||
import { PlaylistInfoComponent } from './playlist-info/playlist-info.component';
|
||||
@@ -61,7 +62,7 @@ import { PlaylistItemComponent } from './playlist-item/playlist-item.component';
|
||||
TranslatePipe,
|
||||
],
|
||||
})
|
||||
export class RecentPlaylistsComponent implements OnDestroy {
|
||||
export class RecentPlaylistsComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('searchQuery') searchQueryInput!: ElementRef<HTMLInputElement>;
|
||||
|
||||
readonly searchQuery = new BehaviorSubject('');
|
||||
@@ -289,8 +290,8 @@ export class RecentPlaylistsComponent implements OnDestroy {
|
||||
this.searchQuery.next(searchQuery);
|
||||
}
|
||||
|
||||
@HostListener('window:keydown.control.f', ['$event'])
|
||||
@HostListener('window:keydown.meta.f', ['$event'])
|
||||
/* @HostListener('window:keydown.control.f', ['$event'])
|
||||
@HostListener('window:keydown.meta.f', ['$event']) */
|
||||
onSearchHotkey(event: KeyboardEvent) {
|
||||
// Prevent default browser search behavior
|
||||
event.preventDefault();
|
||||
File renamed without changes.
+1
-1
@@ -12,7 +12,7 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { Playlist } from '../../../../shared/playlist.interface';
|
||||
import { Playlist } from '../../../shared/playlist.interface';
|
||||
import { DataService } from '../../services/data.service';
|
||||
import { addPlaylist } from '../../state/actions';
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -13,7 +13,7 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { Playlist } from '../../../../shared/playlist.interface';
|
||||
import { Playlist } from '../../../shared/playlist.interface';
|
||||
import {
|
||||
PortalStatus,
|
||||
PortalStatusService,
|
||||
File renamed without changes.
+1
-1
@@ -9,7 +9,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import Artplayer from 'artplayer';
|
||||
import Hls from 'hls.js';
|
||||
import { Channel } from '../../../../../shared/channel.interface';
|
||||
import { Channel } from '../../../../shared/channel.interface';
|
||||
|
||||
Artplayer.AUTO_PLAYBACK_TIMEOUT = 10000;
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -25,7 +25,7 @@ import { Store } from '@ngrx/store';
|
||||
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import * as _ from 'lodash';
|
||||
import { map, skipWhile } from 'rxjs';
|
||||
import { Channel } from '../../../../../shared/channel.interface';
|
||||
import { Channel } from '../../../../shared/channel.interface';
|
||||
import { EpgService } from '../../../services/epg.service';
|
||||
import { FilterPipe } from '../../../shared/pipes/filter.pipe';
|
||||
import * as PlaylistActions from '../../../state/actions';
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -11,7 +11,7 @@ import { TranslatePipe } from '@ngx-translate/core';
|
||||
import moment from 'moment';
|
||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { EPG_GET_PROGRAM_DONE } from '../../../../../shared/ipc-commands';
|
||||
import { EPG_GET_PROGRAM_DONE } from '../../../../shared/ipc-commands';
|
||||
import { DataService } from '../../../services/data.service';
|
||||
import { EpgService } from '../../../services/epg.service';
|
||||
import { MomentDatePipe } from '../../../shared/pipes/moment-date.pipe';
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+3
-3
@@ -9,9 +9,9 @@ import {
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import Hls from 'hls.js';
|
||||
import { Channel } from '../../../../../shared/channel.interface';
|
||||
import { CHANNEL_SET_USER_AGENT } from '../../../../../shared/ipc-commands';
|
||||
import { getExtensionFromUrl } from '../../../../../shared/playlist.utils';
|
||||
import { Channel } from '../../../../shared/channel.interface';
|
||||
import { CHANNEL_SET_USER_AGENT } from '../../../../shared/ipc-commands';
|
||||
import { getExtensionFromUrl } from '../../../../shared/playlist.utils';
|
||||
import { DataService } from '../../../services/data.service';
|
||||
|
||||
/**
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { NgStyle } from '@angular/common';
|
||||
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import moment from 'moment';
|
||||
import { Channel } from '../../../../../shared/channel.interface';
|
||||
import { Channel } from '../../../../shared/channel.interface';
|
||||
import { MomentDatePipe } from '../../../shared/pipes/moment-date.pipe';
|
||||
import { EpgProgram } from '../../models/epg-program.model';
|
||||
|
||||
+1
-1
@@ -117,7 +117,7 @@
|
||||
<rect
|
||||
[attr.width]="program.width"
|
||||
[attr.height]="barHeight"
|
||||
[matTooltip]="program.title"
|
||||
[matTooltip]="program.title[0].value"
|
||||
></rect>
|
||||
<foreignObject
|
||||
[attr.width]="program.width"
|
||||
File renamed without changes.
+1
-1
@@ -20,7 +20,7 @@ import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { addDays, differenceInMinutes, format, parse, subDays } from 'date-fns';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { Channel } from '../../../../../shared/channel.interface';
|
||||
import { Channel } from '../../../../shared/channel.interface';
|
||||
import { MomentDatePipe } from '../../../shared/pipes/moment-date.pipe';
|
||||
import { EpgChannel } from '../../models/epg-channel.model';
|
||||
import { EpgProgram } from '../../models/epg-program.model';
|
||||
File renamed without changes.
Loaded 100 of 477 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user