Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/improve-responsiveness-when-chan…
Browse files Browse the repository at this point in the history
…nel-names-are-long' into development

# Conflicts:
#	package.json
#	src/renderer/components/player-settings/player-settings.js
#	src/renderer/helpers/utils.js
#	src/renderer/store/modules/settings.js
#	src/renderer/views/Channel/Channel.js
#	static/locales/en-US.yaml
#	yarn.lock
  • Loading branch information
MarmadileManteater committed Jul 13, 2023
2 parents 4e47bbb + fbeb81a commit 096b7aa
Show file tree
Hide file tree
Showing 170 changed files with 1,877 additions and 8,222 deletions.
148 changes: 148 additions & 0 deletions _scripts/getRegions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/**
* This script updates the files in static/geolocations with the available locations on YouTube.
*
* It tries to map every active FreeTube language (static/locales/activelocales.json)
* to it's equivalent on YouTube.
*
* It then uses those language mappings,
* to scrape the location selection menu on the YouTube website, in every mapped language.
*
* All languages it couldn't find on YouTube, that don't have manually added mapping,
* get logged to the console, as well as all unmapped YouTube languages.
*/

import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs'
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { Innertube, Misc } from 'youtubei.js'

const STATIC_DIRECTORY = `${dirname(fileURLToPath(import.meta.url))}/../static`

const activeLanguagesPath = `${STATIC_DIRECTORY}/locales/activeLocales.json`
/** @type {string[]} */
const activeLanguages = JSON.parse(readFileSync(activeLanguagesPath, { encoding: 'utf8' }))

// en-US is en on YouTube
const initialResponse = await scrapeLanguage('en')

// Scrape language menu in en-US

/** @type {string[]} */
const youTubeLanguages = initialResponse.data.actions[0].openPopupAction.popup.multiPageMenuRenderer.sections[1].multiPageMenuSectionRenderer.items[1].compactLinkRenderer.serviceEndpoint.signalServiceEndpoint.actions[0].getMultiPageMenuAction.menu.multiPageMenuRenderer.sections[0].multiPageMenuSectionRenderer.items
.map(({ compactLinkRenderer }) => {
return compactLinkRenderer.serviceEndpoint.signalServiceEndpoint.actions[0].selectLanguageCommand.hl
})

// map FreeTube languages to their YouTube equivalents

const foundLanguageNames = ['en-US']
const unusedYouTubeLanguageNames = []
const languagesToScrape = []

for (const language of youTubeLanguages) {
if (activeLanguages.includes(language)) {
foundLanguageNames.push(language)
languagesToScrape.push({
youTube: language,
freeTube: language
})
} else if (activeLanguages.includes(language.replace('-', '_'))) {
const withUnderScore = language.replace('-', '_')
foundLanguageNames.push(withUnderScore)
languagesToScrape.push({
youTube: language,
freeTube: withUnderScore
})
}
// special cases
else if (language === 'de') {
foundLanguageNames.push('de-DE')
languagesToScrape.push({
youTube: 'de',
freeTube: 'de-DE'
})
} else if (language === 'fr') {
foundLanguageNames.push('fr-FR')
languagesToScrape.push({
youTube: 'fr',
freeTube: 'fr-FR'
})
} else if (language === 'no') {
// according to https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
// "no" is the macro language for "nb" and "nn"
foundLanguageNames.push('nb_NO', 'nn')
languagesToScrape.push({
youTube: 'no',
freeTube: 'nb_NO'
})
languagesToScrape.push({
youTube: 'no',
freeTube: 'nn'
})
} else if (language !== 'en') {
unusedYouTubeLanguageNames.push(language)
}
}

console.log("Active FreeTube languages that aren't available on YouTube:")
console.log(activeLanguages.filter(lang => !foundLanguageNames.includes(lang)).sort())

console.log("YouTube languages that don't have an equivalent active FreeTube language:")
console.log(unusedYouTubeLanguageNames.sort())

// Scrape the location menu in various languages and write files to the file system

rmSync(`${STATIC_DIRECTORY}/geolocations`, { recursive: true })
mkdirSync(`${STATIC_DIRECTORY}/geolocations`)

processGeolocations('en-US', 'en', initialResponse)

for (const { youTube, freeTube } of languagesToScrape) {
const response = await scrapeLanguage(youTube)

processGeolocations(freeTube, youTube, response)
}



async function scrapeLanguage(youTubeLanguageCode) {
const session = await Innertube.create({
retrieve_player: false,
generate_session_locally: true,
lang: youTubeLanguageCode
})

return await session.actions.execute('/account/account_menu')
}

function processGeolocations(freeTubeLanguage, youTubeLanguage, response) {
const geolocations = response.data.actions[0].openPopupAction.popup.multiPageMenuRenderer.sections[1].multiPageMenuSectionRenderer.items[3].compactLinkRenderer.serviceEndpoint.signalServiceEndpoint.actions[0].getMultiPageMenuAction.menu.multiPageMenuRenderer.sections[0].multiPageMenuSectionRenderer.items
.map(({ compactLinkRenderer }) => {
return {
name: new Misc.Text(compactLinkRenderer.title).toString().trim(),
code: compactLinkRenderer.serviceEndpoint.signalServiceEndpoint.actions[0].selectCountryCommand.gl
}
})

const normalisedFreeTubeLanguage = freeTubeLanguage.replace('_', '-')

// give Intl.Collator 4 locales, in the hopes that it supports one of them
// deduplicate the list so it doesn't have to do duplicate work
const localeSet = new Set()
localeSet.add(normalisedFreeTubeLanguage)
localeSet.add(youTubeLanguage)
localeSet.add(normalisedFreeTubeLanguage.split('-')[0])
localeSet.add(youTubeLanguage.split('-')[0])

const locales = Array.from(localeSet)

// only sort if node supports sorting the language, otherwise hope that YouTube's sorting was correct
// node 20.3.1 doesn't support sorting `eu`
if (Intl.Collator.supportedLocalesOf(locales).length > 0) {
const collator = new Intl.Collator(locales)

geolocations.sort((a, b) => collator.compare(a.name, b.name))
}

writeFileSync(`${STATIC_DIRECTORY}/geolocations/${freeTubeLanguage}.json`, JSON.stringify(geolocations))
}
2 changes: 1 addition & 1 deletion _scripts/webpack.web.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ config.plugins.push(
processLocalesPlugin,
new webpack.DefinePlugin({
'process.env.LOCALE_NAMES': JSON.stringify(processLocalesPlugin.localeNames),
'process.env.GEOLOCATION_NAMES': JSON.stringify(fs.readdirSync(path.join(__dirname, '..', 'static', 'geolocations')))
'process.env.GEOLOCATION_NAMES': JSON.stringify(fs.readdirSync(path.join(__dirname, '..', 'static', 'geolocations')).map(filename => filename.replace('.json', '')))
}),
new CopyWebpackPlugin({
patterns: [
Expand Down
33 changes: 17 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"dev:web": "node _scripts/dev-runner.js --web",
"dev-runner": "node _scripts/dev-runner.js",
"get-instances": "node _scripts/getInstances.js",
"get-regions": "node _scripts/getRegions.mjs",
"lint-all": "run-p lint lint-json lint-style",
"lint-fix": "eslint --fix --ext .js,.vue ./",
"lint": "eslint --ext .js,.vue ./",
Expand Down Expand Up @@ -82,56 +83,56 @@
"vue-router": "^3.6.5",
"vue-tiny-slider": "^0.1.39",
"vuex": "^3.6.2",
"youtubei.js": "^5.1.0",
"youtubei.js": "^5.2.1",
"jintr-patch": "https://github.com/LuanRT/Jinter.git"
},
"devDependencies": {
"@babel/core": "^7.22.5",
"@babel/eslint-parser": "^7.22.5",
"@babel/core": "^7.22.8",
"@babel/eslint-parser": "^7.22.7",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/preset-env": "^7.22.5",
"@babel/preset-env": "^7.22.7",
"@double-great/stylelint-a11y": "^2.0.2",
"babel-loader": "^9.1.2",
"babel-loader": "^9.1.3",
"copy-webpack-plugin": "^11.0.0",
"cordova": "^11.0.0",
"core-js": "^3.27.2",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"electron": "^22.3.13",
"electron": "^22.3.16",
"electron-builder": "^23.6.0",
"eslint": "^8.43.0",
"eslint": "^8.44.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsonc": "^2.9.0",
"eslint-plugin-n": "^16.0.0",
"eslint-plugin-n": "^16.0.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-unicorn": "^47.0.0",
"eslint-plugin-vue": "^9.15.0",
"eslint-plugin-vue": "^9.15.1",
"eslint-plugin-vuejs-accessibility": "^2.1.0",
"eslint-plugin-yml": "^1.8.0",
"html-webpack-plugin": "^5.5.3",
"js-yaml": "^4.1.0",
"json-minimizer-webpack-plugin": "^4.0.0",
"lefthook": "^1.4.2",
"lefthook": "^1.4.3",
"mini-css-extract-plugin": "^2.7.6",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.24",
"postcss": "^8.4.25",
"postcss-scss": "^4.0.6",
"prettier": "^2.8.8",
"rimraf": "^5.0.1",
"sass": "^1.63.4",
"sass": "^1.63.6",
"sass-loader": "^13.3.2",
"stylelint": "^14.16.1",
"stylelint-config-sass-guidelines": "^9.0.1",
"stylelint-config-standard": "^29.0.0",
"stylelint": "^15.10.1",
"stylelint-config-sass-guidelines": "^10.0.0",
"stylelint-config-standard": "^34.0.0",
"stylelint-high-performance-animation": "^1.8.0",
"tree-kill": "1.2.2",
"vue-devtools": "^5.1.4",
"vue-eslint-parser": "^9.3.1",
"vue-loader": "^15.10.0",
"webpack": "^5.87.0",
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"xml2js": "^0.4.23",
Expand Down
4 changes: 4 additions & 0 deletions src/cordova/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
<preference name="AndroidWindowSplashScreenBackground" value="#212121" />
<preference name="AndroidWindowSplashScreenAnimatedIcon" value="res/icon/android/iconColor_256_padded.png" />
<preference name="AutoHideSplashScreen" value="false" />
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
<application android:usesCleartextTraffic="true" />
</edit-config>
</platform>
<preference name="AndroidPersistentFileLocation" value="Compatibility"/>
<preference name="AllowInlineMediaPlayback" value="true"/>
<preference name="scheme" value="http" />
<universal-links>
<host name="youtu.be" scheme="https" event="youtube_shortended" />
<host name="youtube.com" scheme="https" event="youtube" />
Expand Down
2 changes: 1 addition & 1 deletion src/cordova/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = {
restore: 'npx cordova platform add android'
},
devDependencies: {
'cordova-android': '^11.0.0',
'cordova-android': '^12.0.0',
'cordova-clipboard': '^1.3.0',
'cordova-plugin-background-mode': 'git+https://bitbucket.org/TheBosZ/cordova-plugin-run-in-background.git',
'cordova-plugin-android-permissions': '^1.1.4',
Expand Down
23 changes: 20 additions & 3 deletions src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import baseHandlers from '../datastores/handlers/base'
import { extractExpiryTimestamp, ImageCache } from './ImageCache'
import { existsSync } from 'fs'

import packageDetails from '../../package.json'

if (process.argv.includes('--version')) {
app.exit()
} else {
Expand Down Expand Up @@ -263,6 +265,12 @@ function runApp() {
})
}

const fixedUserAgent = session.defaultSession.getUserAgent()
.split(' ')
.filter(part => !part.includes('Electron') && !part.includes(packageDetails.productName))
.join(' ')
session.defaultSession.setUserAgent(fixedUserAgent)

// Set CONSENT cookie on reasonable domains
const consentCookieDomains = [
'https://www.youtube.com',
Expand All @@ -279,10 +287,19 @@ function runApp() {

// make InnerTube requests work with the fetch function
// InnerTube rejects requests if the referer isn't YouTube or empty
const innertubeRequestFilter = { urls: ['https://www.youtube.com/youtubei/*'] }
const innertubeAndMediaRequestFilter = { urls: ['https://www.youtube.com/youtubei/*', 'https://*.googlevideo.com/videoplayback?*'] }

session.defaultSession.webRequest.onBeforeSendHeaders(innertubeAndMediaRequestFilter, ({ requestHeaders, url }, callback) => {
requestHeaders.Referer = 'https://www.youtube.com/'
requestHeaders.Origin = 'https://www.youtube.com'

if (url.startsWith('https://www.youtube.com/youtubei/')) {
requestHeaders['Sec-Fetch-Site'] = 'same-origin'
} else {
// YouTube doesn't send the Content-Type header for the media requests, so we shouldn't either
delete requestHeaders['Content-Type']
}

session.defaultSession.webRequest.onBeforeSendHeaders(innertubeRequestFilter, ({ requestHeaders }, callback) => {
requestHeaders.referer = 'https://www.youtube.com'
// eslint-disable-next-line n/no-callback-literal
callback({ requestHeaders })
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ export default defineComponent({
return Number.parseInt(b.width) - Number.parseInt(a.width)
})

return imageArrayCopy.at(0)?.url ?? ''
// Remove cropping directives when applicable
return imageArrayCopy.at(0)?.url?.replace(/-c-fcrop64=.*/i, '') ?? ''
}
}
})
6 changes: 5 additions & 1 deletion src/renderer/components/ft-element-list/ft-element-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ export default defineComponent({
showVideoWithLastViewedPlaylist: {
type: Boolean,
default: false
}
},
useChannelsHiddenPreference: {
type: Boolean,
default: true,
},
},
computed: {
listType: function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
:first-screen="index < 16"
:layout="displayValue"
:show-video-with-last-viewed-playlist="showVideoWithLastViewedPlaylist"
:use-channels-hidden-preference="useChannelsHiddenPreference"
/>
</ft-auto-grid>
</template>
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/components/ft-icon-button/ft-icon-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ export default defineComponent({
// wait until the dropdown is visible
// then focus it so we can hide it automatically when it loses focus
setTimeout(() => {
this.$refs.dropdown.focus()
}, 0)
this.$refs.dropdown?.focus()
})
}
} else {
this.$emit('click')
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/components/ft-input/ft-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ export default defineComponent({
this.$refs.input.focus()
},

blur() {
this.$refs.input.blur()
},

...mapActions([
'getYoutubeUrlInfo'
])
Expand Down
13 changes: 3 additions & 10 deletions src/renderer/components/ft-list-channel/ft-list-channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,8 @@ export default defineComponent({

this.channelName = this.data.name
this.id = this.data.id
if (this.hideChannelSubscriptions || this.data.subscribers == null) {
this.subscriberCount = null
} else {
this.subscriberCount = this.data.subscribers.replace(/ subscriber(s)?/, '')
}
this.subscriberCount = this.data.subscribers != null ? this.data.subscribers.replace(/ subscriber(s)?/, '') : null

if (this.data.videos === null) {
this.videoCount = 0
} else {
Expand All @@ -79,11 +76,7 @@ export default defineComponent({

this.channelName = this.data.author
this.id = this.data.authorId
if (this.hideChannelSubscriptions) {
this.subscriberCount = null
} else {
this.subscriberCount = formatNumber(this.data.subCount)
}
this.subscriberCount = formatNumber(this.data.subCount)
this.videoCount = formatNumber(this.data.videoCount)
this.description = this.data.description
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
</router-link>
<div class="infoLine">
<span
v-if="subscriberCount !== null"
v-if="subscriberCount !== null && !hideChannelSubscriptions"
class="subscriberCount"
>
{{ subscriberCount }} subscribers -
Expand Down
Loading

0 comments on commit 096b7aa

Please sign in to comment.