Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import FtLoader from '../ft-loader/ft-loader.vue'
import FtCard from '../ft-card/ft-card.vue'
import FtListVideoLazy from '../ft-list-video-lazy/ft-list-video-lazy.vue'
import { copyToClipboard, showToast } from '../../helpers/utils'
import { getLocalPlaylist, parseLocalPlaylistVideo } from '../../helpers/api/local'
import {
getLocalPlaylist,
parseLocalPlaylistVideo,
untilEndOfLocalPlayList,
} from '../../helpers/api/local'
import { invidiousGetPlaylistInfo } from '../../helpers/api/invidious'

export default defineComponent({
Expand Down Expand Up @@ -327,21 +331,12 @@ export default defineComponent({
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious' || cachedPlaylist.continuationData === null) {
this.playlistItems = cachedPlaylist.items
} else {
const items = cachedPlaylist.items
let playlist = cachedPlaylist.continuationData
const videos = cachedPlaylist.items
await untilEndOfLocalPlayList(cachedPlaylist.continuationData, (p) => {
videos.push(...p.items.map(parseLocalPlaylistVideo))
}, { runCallbackOnceFirst: false })

do {
playlist = await playlist.getContinuation()

const parsedVideos = playlist.items.map(parseLocalPlaylistVideo)
items.push(...parsedVideos)

if (!playlist.has_continuation) {
playlist = null
}
} while (playlist !== null)

this.playlistItems = items
this.playlistItems = videos
}

this.isLoading = false
Expand All @@ -351,7 +346,7 @@ export default defineComponent({
this.isLoading = true

try {
let playlist = await getLocalPlaylist(this.playlistId)
const playlist = await getLocalPlaylist(this.playlistId)

let channelName

Expand All @@ -368,14 +363,10 @@ export default defineComponent({
this.channelName = channelName
this.channelId = playlist.info.author?.id

const videos = playlist.items.map(parseLocalPlaylistVideo)

while (playlist.has_continuation) {
playlist = await playlist.getContinuation()

const parsedVideos = playlist.items.map(parseLocalPlaylistVideo)
videos.push(...parsedVideos)
}
const videos = []
await untilEndOfLocalPlayList(playlist, (p) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using promises and callbacks together is a weird code pattern, which I'm not a fan of (the whole point of async/await in javascript was to get away from callbacks), but as we are in a rush and the code works, i'll accept it this time.

videos.push(...p.items.map(parseLocalPlaylistVideo))
})

this.playlistItems = videos

Expand Down
41 changes: 41 additions & 0 deletions src/renderer/helpers/api/local.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,47 @@ export async function getLocalPlaylist(id) {
return await innertube.getPlaylist(id)
}

/**
* @param {Playlist} playlist
* @returns {Playlist|null} null when no valid playlist can be found (e.g. `empty continuation response`)
*/
export async function getLocalPlaylistContinuation(playlist) {
try {
return await playlist.getContinuation()
} catch (error) {
// Youtube can provide useless continuation data
if (!error.message.includes('Got empty continuation response.')) {
// Re-throw unhandled error
throw error
}

return null
}
}

/**
* Callback for adding two numbers.
*
* @callback untilEndOfLocalPlayListCallback
* @param {Playlist} playlist
*/

/**
* @param {Playlist} playlist
* @param {untilEndOfLocalPlayListCallback} callback
* @param {object} options
* @param {boolean} options.runCallbackOnceFirst
*/
export async function untilEndOfLocalPlayList(playlist, callback, options = { runCallbackOnceFirst: true }) {
if (options.runCallbackOnceFirst) { callback(playlist) }

while (playlist != null && playlist.has_continuation) {
playlist = await getLocalPlaylistContinuation(playlist)

if (playlist != null) { callback(playlist) }
}
}

/**
* @param {string} location
* @param {'default'|'music'|'gaming'|'movies'} tab
Expand Down
20 changes: 14 additions & 6 deletions src/renderer/views/Playlist/Playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import PlaylistInfo from '../../components/playlist-info/playlist-info.vue'
import FtListVideoLazy from '../../components/ft-list-video-lazy/ft-list-video-lazy.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtButton from '../../components/ft-button/ft-button.vue'
import { getLocalPlaylist, parseLocalPlaylistVideo } from '../../helpers/api/local'
import {
getLocalPlaylist,
getLocalPlaylistContinuation,
parseLocalPlaylistVideo,
} from '../../helpers/api/local'
import { extractNumberFromString } from '../../helpers/utils'
import { invidiousGetPlaylistInfo, youtubeImageUrlToInvidious } from '../../helpers/api/invidious'

Expand Down Expand Up @@ -188,12 +192,16 @@ export default defineComponent({
getNextPageLocal: function () {
this.isLoadingMore = true

this.continuationData.getContinuation().then((result) => {
const parsedVideos = result.items.map(parseLocalPlaylistVideo)
this.playlistItems = this.playlistItems.concat(parsedVideos)
getLocalPlaylistContinuation(this.continuationData).then((result) => {
if (result) {
const parsedVideos = result.items.map(parseLocalPlaylistVideo)
this.playlistItems = this.playlistItems.concat(parsedVideos)

if (result.has_continuation) {
this.continuationData = result
if (result.has_continuation) {
this.continuationData = result
} else {
this.continuationData = null
}
} else {
this.continuationData = null
}
Expand Down