Skip to content

Commit

Permalink
Merge branch 'development' into feat/add-list-display-settings
Browse files Browse the repository at this point in the history
  • Loading branch information
kommunarr authored Oct 27, 2023
2 parents dc28f86 + 6d6fc73 commit 3d14d28
Show file tree
Hide file tree
Showing 12 changed files with 1,535 additions and 458 deletions.
110 changes: 72 additions & 38 deletions _scripts/ProcessLocalesPlugin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { existsSync, readFileSync } = require('fs')
const { existsSync, readFileSync, statSync } = require('fs')
const { brotliCompress, constants } = require('zlib')
const { promisify } = require('util')
const { load: loadYaml } = require('js-yaml')
Expand All @@ -8,6 +8,7 @@ const brotliCompressAsync = promisify(brotliCompress)
class ProcessLocalesPlugin {
constructor(options = {}) {
this.compress = !!options.compress
this.isIncrementalBuild = false

if (typeof options.inputDir !== 'string') {
throw new Error('ProcessLocalesPlugin: no input directory `inputDir` specified.')
Expand All @@ -21,10 +22,11 @@ class ProcessLocalesPlugin {
}
this.outputDir = options.outputDir

this.locales = []
this.locales = {}
this.localeNames = []
this.activeLocales = []

this.cache = []
this.cache = {}

this.loadLocales()
}
Expand All @@ -37,66 +39,98 @@ class ProcessLocalesPlugin {

compilation.hooks.additionalAssets.tapPromise('process-locales-plugin', async (_assets) => {

// While running in the webpack dev server, this hook gets called for every incrememental build.
// While running in the webpack dev server, this hook gets called for every incremental build.
// For incremental builds we can return the already processed versions, which saves time
// and makes webpack treat them as cached
if (IS_DEV_SERVER && this.cache.length > 0) {
for (const { filename, source } of this.cache) {
compilation.emitAsset(filename, source, { minimized: true })
}
const promises = []
// Prevents `loadLocales` called twice on first time (e.g. release build)
if (this.isIncrementalBuild) {
this.loadLocales(true)
} else {
const promises = []
this.isIncrementalBuild = true
}

for (const { locale, data } of this.locales) {
promises.push(new Promise(async (resolve) => {
if (Object.prototype.hasOwnProperty.call(data, 'Locale Name')) {
delete data['Locale Name']
}
Object.values(this.locales).forEach((localeEntry) => {
const { locale, data, mtimeMs } = localeEntry

this.removeEmptyValues(data)
promises.push(new Promise(async (resolve) => {
if (IS_DEV_SERVER) {
const cacheEntry = this.cache[locale]

let filename = `${this.outputDir}/${locale}.json`
let output = JSON.stringify(data)
if (cacheEntry != null) {
const { filename, source, mtimeMs: cachedMtimeMs } = cacheEntry

if (this.compress) {
filename += '.br'
output = await this.compressLocale(output)
if (cachedMtimeMs === mtimeMs) {
compilation.emitAsset(filename, source, { minimized: true })
resolve()
return
}
}
}

let source = new RawSource(output)
if (Object.prototype.hasOwnProperty.call(data, 'Locale Name')) {
delete data['Locale Name']
}

if (IS_DEV_SERVER) {
source = new CachedSource(source)
this.cache.push({ filename, source })
}
this.removeEmptyValues(data)

compilation.emitAsset(filename, source, { minimized: true })
let filename = `${this.outputDir}/${locale}.json`
let output = JSON.stringify(data)

resolve()
}))
}
if (this.compress) {
filename += '.br'
output = await this.compressLocale(output)
}

let source = new RawSource(output)

if (IS_DEV_SERVER) {
source = new CachedSource(source)
this.cache[locale] = { filename, source, mtimeMs }
}

await Promise.all(promises)
compilation.emitAsset(filename, source, { minimized: true })

resolve()
}))

if (IS_DEV_SERVER) {
// we don't need the unmodified sources anymore, as we use the cache `this.cache`
// so we can clear this to free some memory
delete this.locales
delete localeEntry.data
}
}
})

await Promise.all(promises)
})
})
}

loadLocales() {
const activeLocales = JSON.parse(readFileSync(`${this.inputDir}/activeLocales.json`))
loadLocales(loadModifiedFilesOnly = false) {
if (this.activeLocales.length === 0) {
this.activeLocales = JSON.parse(readFileSync(`${this.inputDir}/activeLocales.json`))
}

for (const locale of activeLocales) {
const contents = readFileSync(`${this.inputDir}/${locale}.yaml`, 'utf-8')
for (const locale of this.activeLocales) {
const filePath = `${this.inputDir}/${locale}.yaml`
// Cannot use `mtime` since values never equal
const mtimeMsFromStats = statSync(filePath).mtimeMs
if (loadModifiedFilesOnly) {
// Skip reading files where mtime (modified time) same as last read
// (stored in mtime)
const existingMtime = this.locales[locale]?.mtimeMs
if (existingMtime != null && existingMtime === mtimeMsFromStats) {
continue
}
}
const contents = readFileSync(filePath, 'utf-8')
const data = loadYaml(contents)
this.locales[locale] = { locale, data, mtimeMs: mtimeMsFromStats }

this.localeNames.push(data['Locale Name'] ?? locale)
this.locales.push({ locale, data })
const localeName = data['Locale Name'] ?? locale
if (!loadModifiedFilesOnly) {
this.localeNames.push(localeName)
}
}
}

Expand Down
16 changes: 15 additions & 1 deletion _scripts/webpack.renderer.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const path = require('path')
const { readFileSync } = require('fs')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const ProcessLocalesPlugin = require('./ProcessLocalesPlugin')
const WatchExternalFilesPlugin = require('webpack-watch-external-files-plugin')

const isDevMode = process.env.NODE_ENV === 'development'

Expand Down Expand Up @@ -128,7 +130,7 @@ const config = {
new MiniCssExtractPlugin({
filename: isDevMode ? '[name].css' : '[name].[contenthash].css',
chunkFilename: isDevMode ? '[id].css' : '[id].[contenthash].css',
})
}),
],
resolve: {
alias: {
Expand All @@ -146,4 +148,16 @@ const config = {
target: 'electron-renderer',
}

if (isDevMode) {
const activeLocales = JSON.parse(readFileSync(path.join(__dirname, '../static/locales/activeLocales.json')))

config.plugins.push(
new WatchExternalFilesPlugin({
files: [
`./static/locales/{${activeLocales.join(',')}}.yaml`,
],
}),
)
}

module.exports = config
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"videojs-mobile-ui": "^0.8.0",
"videojs-overlay": "^3.1.0",
"videojs-vtt-thumbnails-freetube": "0.0.15",
"vue": "^2.7.14",
"vue": "^2.7.15",
"vue-i18n": "^8.28.2",
"vue-observe-visibility": "^1.0.0",
"vue-router": "^3.6.5",
Expand All @@ -90,12 +90,12 @@
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"electron": "^27.0.0",
"electron": "^27.0.2",
"electron-builder": "^24.6.4",
"eslint": "^8.51.0",
"eslint": "^8.52.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsonc": "^2.10.0",
"eslint-plugin-n": "^16.2.0",
"eslint-plugin-prettier": "^5.0.1",
Expand All @@ -114,9 +114,9 @@
"postcss-scss": "^4.0.9",
"prettier": "^2.8.8",
"rimraf": "^5.0.5",
"sass": "^1.69.3",
"sass": "^1.69.4",
"sass-loader": "^13.3.2",
"stylelint": "^15.10.3",
"stylelint": "^15.11.0",
"stylelint-config-sass-guidelines": "^10.0.0",
"stylelint-config-standard": "^34.0.0",
"stylelint-high-performance-animation": "^1.9.0",
Expand All @@ -127,6 +127,7 @@
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-watch-external-files-plugin": "^2.0.0",
"yaml-eslint-parser": "^1.2.2"
}
}
10 changes: 10 additions & 0 deletions src/renderer/components/top-nav/top-nav.scss
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@
padding-block: 0;
padding-inline: 10px 25px;

&:hover {
background-color: var(--side-nav-hover-color);
color: var(--side-nav-hover-text-color);
transition: background 0.2s ease-in;

@include top-nav-is-colored {
background-color: var(--primary-color-hover);
}
}

&:active {
background-color: var(--tertiary-text-color);
transition: background 0.2s ease-in;
Expand Down
32 changes: 32 additions & 0 deletions src/renderer/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -687,3 +687,35 @@ export function sortListUsingMethod(list, property, method) {
break
}
}

/**
* Check if the `name` of the error is `TimeoutError` to know if the error was caused by a timeout or something else.
* @param {number} timeoutMs
* @param {RequestInfo|URL} input
* @param {RequestInit=} init
*/
export async function fetchWithTimeout(timeoutMs, input, init) {
const timeoutSignal = AbortSignal.timeout(timeoutMs)

if (typeof init !== 'undefined') {
init.signal = timeoutSignal
} else {
init = {
signal: timeoutSignal
}
}

try {
return await fetch(input, init)
} catch (err) {
if (err.name === 'AbortError' && timeoutSignal.aborted) {
// According to the spec, fetch should use the original abort reason.
// Unfortunately chromium browsers always throw an AbortError, even when it was caused by a TimeoutError,
// so we need manually throw the original abort reason
// https://bugs.chromium.org/p/chromium/issues/detail?id=1431720
throw timeoutSignal.reason
} else {
throw err
}
}
}
10 changes: 7 additions & 3 deletions src/renderer/store/modules/invidious.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'fs/promises'
import { pathExists } from '../../helpers/filesystem'
import { createWebURL } from '../../helpers/utils'
import { createWebURL, fetchWithTimeout } from '../../helpers/utils'

const state = {
currentInvidiousInstance: '',
Expand All @@ -23,7 +23,7 @@ const actions = {

let instances = []
try {
const response = await fetch(requestUrl)
const response = await fetchWithTimeout(15_000, requestUrl)
const json = await response.json()
instances = json.filter((instance) => {
return !(instance[0].includes('.onion') ||
Expand All @@ -34,7 +34,11 @@ const actions = {
return instance[1].uri.replace(/\/$/, '')
})
} catch (err) {
console.error(err)
if (err.name === 'TimeoutError') {
console.error('Fetching the Invidious instance list timed out after 15 seconds. Falling back to local copy.')
} else {
console.error(err)
}
}
// If the invidious instance fetch isn't returning anything interpretable
if (instances.length === 0) {
Expand Down
Loading

0 comments on commit 3d14d28

Please sign in to comment.