Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate the trending page to YouTube.js #3005

Merged
merged 3 commits into from
Dec 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"@fortawesome/vue-fontawesome": "^2.0.9",
"@freetube/youtube-chat": "^1.1.2",
"@freetube/yt-comment-scraper": "^6.2.0",
"@freetube/yt-trending-scraper": "^3.1.1",
"@silvermine/videojs-quality-selector": "^1.2.5",
"autolinker": "^4.0.0",
"browserify": "^17.0.0",
Expand Down
64 changes: 59 additions & 5 deletions src/renderer/helpers/api/local.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Innertube, Session } from 'youtubei.js'
import { join } from 'path'

import { PlayerCache } from './PlayerCache'
import { getUserDataPath } from '../utils'
import { extractNumberFromString, getUserDataPath } from '../utils'

/**
* Creates a lightweight Innertube instance, which is faster to create or
Expand All @@ -12,21 +12,24 @@ import { getUserDataPath } from '../utils'
* 1. the request for the session
* 2. fetch a page that contains a link to the player
* 3. if the player isn't cached, it is downloaded and transformed
* @param {boolean} withPlayer set to true to get an Innertube instance that can decode the streaming URLs
* @param {object} options
* @param {boolean} options.withPlayer set to true to get an Innertube instance that can decode the streaming URLs
* @param {string|undefined} options.location the geolocation to pass to YouTube get different content
* @returns the Innertube instance
*/
async function createInnertube(withPlayer = false) {
if (withPlayer) {
async function createInnertube(options = { withPlayer: false, location: undefined }) {
if (options.withPlayer) {
const userData = await getUserDataPath()

return await Innertube.create({
// use browser fetch
location: options.location,
fetch: (input, init) => fetch(input, init),
cache: new PlayerCache(join(userData, 'player_cache'))
})
} else {
// from https://github.com/LuanRT/YouTube.js/pull/240
const sessionData = await Session.getSessionData()
const sessionData = await Session.getSessionData(undefined, options.location)

const session = new Session(
sessionData.context,
Expand Down Expand Up @@ -62,6 +65,35 @@ export async function getLocalPlaylist(id) {
return await innertube.getPlaylist(id)
}

/**
* @typedef {import('youtubei.js/dist/src/core/TabbedFeed').default} TabbedFeed
*/

/**
* @param {string} location
* @param {string} tab
* @param {TabbedFeed|null} instance
*/
export async function getLocalTrending(location, tab, instance) {
if (instance === null) {
const innertube = await createInnertube({ location })
instance = await innertube.getTrending()
}

// youtubei.js's tab names are localised, so we need to use the index to get tab name that youtubei.js expects
const tabIndex = ['default', 'music', 'gaming', 'movies'].indexOf(tab)
const resultsInstance = await instance.getTabByName(instance.tabs[tabIndex])

const results = resultsInstance.videos
.filter((video) => video.type === 'Video')
.map(parseLocalListVideo)

return {
results,
instance: resultsInstance
}
}

/**
* @typedef {import('youtubei.js/dist/src/parser/classes/PlaylistVideo').default} PlaylistVideo
*/
Expand All @@ -78,3 +110,25 @@ export function parseLocalPlaylistVideo(video) {
lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds
}
}

/**
* @typedef {import('youtubei.js/dist/src/parser/classes/Video').default} Video
*/

/**
* @param {Video} video
*/
function parseLocalListVideo(video) {
return {
type: 'video',
videoId: video.id,
title: video.title.text,
author: video.author.name,
authorId: video.author.id,
description: video.description,
viewCount: extractNumberFromString(video.view_count.text),
publishedText: video.published.text,
lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds,
liveNow: video.is_live
}
}
4 changes: 2 additions & 2 deletions src/renderer/views/Search/Search.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { mapActions } from 'vuex'
import FtLoader from '../../components/ft-loader/ft-loader.vue'
import FtCard from '../../components/ft-card/ft-card.vue'
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
import { calculateLengthInSeconds } from '@freetube/yt-trending-scraper/src/HtmlParser'
import { timeToSeconds } from 'youtubei.js/dist/src/utils/Utils'
import { copyToClipboard, searchFiltersMatch, showToast } from '../../helpers/utils'

export default Vue.extend({
Expand Down Expand Up @@ -155,7 +155,7 @@ export default Vue.extend({
let videoDuration = video.duration
const videoId = video.id
if (videoDuration !== null && videoDuration !== '' && videoDuration !== 'LIVE' && videoDuration !== 'UPCOMING' && videoDuration !== 'PREMIERE') {
videoDuration = calculateLengthInSeconds(video.duration)
videoDuration = timeToSeconds(video.duration)
}
dataToShow.push(
{
Expand Down
29 changes: 12 additions & 17 deletions src/renderer/views/Trending/Trending.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
import FtIconButton from '../../components/ft-icon-button/ft-icon-button.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'

import { scrapeTrendingPage } from '@freetube/yt-trending-scraper'
import { copyToClipboard, showToast } from '../../helpers/utils'
import { getLocalTrending } from '../../helpers/api/local'

export default Vue.extend({
name: 'Trending',
Expand All @@ -22,7 +22,8 @@ export default Vue.extend({
return {
isLoading: false,
shownResults: [],
currentTab: 'default'
currentTab: 'default',
trendingInstance: null
}
},
computed: {
Expand Down Expand Up @@ -77,27 +78,21 @@ export default Vue.extend({
}
},

getTrendingInfoLocal: function () {
getTrendingInfoLocal: async function () {
absidue marked this conversation as resolved.
Show resolved Hide resolved
this.isLoading = true

const param = {
parseCreatorOnRise: false,
page: this.currentTab,
geoLocation: this.region
}

scrapeTrendingPage(param).then((result) => {
const returnData = result.filter((item) => {
return item.type === 'video' || item.type === 'channel' || item.type === 'playlist'
})
try {
const { results, instance } = await getLocalTrending(this.region, this.currentTab, this.trendingInstance)

this.shownResults = returnData
this.shownResults = results
this.isLoading = false
this.$store.commit('setTrendingCache', { value: returnData, page: this.currentTab })
this.trendingInstance = instance

this.$store.commit('setTrendingCache', { value: results, page: this.currentTab })
setTimeout(() => {
this.$refs[this.currentTab].focus()
})
}).catch((err) => {
} catch (err) {
console.error(err)
const errorMessage = this.$t('Local API Error (Click to copy)')
showToast(`${errorMessage}: ${err}`, 10000, () => {
Expand All @@ -109,7 +104,7 @@ export default Vue.extend({
} else {
this.isLoading = false
}
})
}
},

getTrendingInfoCache: function () {
Expand Down
7 changes: 0 additions & 7 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1060,13 +1060,6 @@
dependencies:
axios "^0.27.2"

"@freetube/yt-trending-scraper@^3.1.1":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@freetube/yt-trending-scraper/-/yt-trending-scraper-3.1.1.tgz#82d11ac3dad3ea25bc0f30d68e315364e9977046"
integrity sha512-qgWFLefcKiLfJmETtEeDVuv9MQ50JFwkGlR3NITDG/SzdrhTFBizG5Ggs34sub6UQd6M6Ib8HxI/lgtKYqN7qA==
dependencies:
axios "^0.27.2"

"@humanwhocodes/config-array@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
Expand Down