@@ -26,6 +26,7 @@ import {
2626 getInvidiousChannelPlaylists ,
2727 getInvidiousChannelPodcasts ,
2828 getInvidiousChannelReleases ,
29+ getInvidiousChannelCourses ,
2930 getInvidiousChannelShorts ,
3031 getInvidiousChannelVideos ,
3132 invidiousGetChannelId ,
@@ -87,6 +88,7 @@ export default defineComponent({
8788 liveContinuationData : null ,
8889 releaseContinuationData : null ,
8990 podcastContinuationData : null ,
91+ coursesContinuationData : null ,
9092 playlistContinuationData : null ,
9193 searchContinuationData : null ,
9294 communityContinuationData : null ,
@@ -112,6 +114,7 @@ export default defineComponent({
112114 latestLive : [ ] ,
113115 latestReleases : [ ] ,
114116 latestPodcasts : [ ] ,
117+ latestCourses : [ ] ,
115118 latestPlaylists : [ ] ,
116119 latestCommunityPosts : [ ] ,
117120 searchResults : [ ] ,
@@ -134,6 +137,7 @@ export default defineComponent({
134137 'live' ,
135138 'releases' ,
136139 'podcasts' ,
140+ 'courses' ,
137141 'playlists' ,
138142 'community' ,
139143 'about'
@@ -144,6 +148,7 @@ export default defineComponent({
144148 'live' ,
145149 'releases' ,
146150 'podcasts' ,
151+ 'courses' ,
147152 'playlists' ,
148153 'community' ,
149154 'about'
@@ -234,6 +239,8 @@ export default defineComponent({
234239 return ! isNullOrEmpty ( this . releaseContinuationData )
235240 case 'podcasts' :
236241 return ! isNullOrEmpty ( this . podcastContinuationData )
242+ case 'courses' :
243+ return ! isNullOrEmpty ( this . coursesContinuationData )
237244 case 'playlists' :
238245 return ! isNullOrEmpty ( this . playlistContinuationData )
239246 case 'community' :
@@ -261,6 +268,10 @@ export default defineComponent({
261268 return this . $store . getters . getHideChannelReleases
262269 } ,
263270
271+ hideChannelCourses : function ( ) {
272+ return this . $store . getters . getHideChannelCourses
273+ } ,
274+
264275 hideChannelPlaylists : function ( ) {
265276 return this . $store . getters . getHideChannelPlaylists
266277 } ,
@@ -302,6 +313,10 @@ export default defineComponent({
302313 indexToRemove . push ( values . indexOf ( 'releases' ) )
303314 }
304315
316+ if ( this . hideChannelCourses ) {
317+ indexToRemove . push ( values . indexOf ( 'courses' ) )
318+ }
319+
305320 if ( this . hideChannelHome ) {
306321 indexToRemove . push ( values . indexOf ( 'home' ) )
307322 }
@@ -668,6 +683,11 @@ export default defineComponent({
668683 this . getChannelReleasesLocal ( )
669684 }
670685
686+ if ( ! this . hideChannelCourses && channel . has_courses ) {
687+ tabs . push ( 'courses' )
688+ this . getChannelCoursesLocal ( )
689+ }
690+
671691 if ( ! this . hideChannelPlaylists ) {
672692 if ( channel . has_playlists ) {
673693 tabs . push ( 'playlists' )
@@ -1126,6 +1146,10 @@ export default defineComponent({
11261146 this . channelInvidiousReleases ( )
11271147 }
11281148
1149+ if ( ! this . hideChannelCourses && response . tabs . includes ( 'courses' ) ) {
1150+ this . channelInvidiousCourses ( )
1151+ }
1152+
11291153 if ( ! this . hideChannelPlaylists && response . tabs . includes ( 'playlists' ) ) {
11301154 this . getPlaylistsInvidious ( )
11311155 }
@@ -1560,7 +1584,7 @@ export default defineComponent({
15601584
15611585 const parsedPodcasts = continuation . playlists . map ( playlist => parseLocalListPlaylist ( playlist , this . id , this . channelName ) )
15621586 this . latestPodcasts = this . latestPodcasts . concat ( parsedPodcasts )
1563- this . releaseContinuationData = continuation . has_continuation ? continuation : null
1587+ this . podcastContinuationData = continuation . has_continuation ? continuation : null
15641588 } catch ( err ) {
15651589 console . error ( err )
15661590 const errorMessage = this . $t ( 'Local API Error (Click to copy)' )
@@ -1620,6 +1644,108 @@ export default defineComponent({
16201644 } )
16211645 } ,
16221646
1647+ getChannelCoursesLocal : async function ( ) {
1648+ this . isElementListLoading = true
1649+ const expectedId = this . id
1650+
1651+ try {
1652+ /**
1653+ * @type {import('youtubei.js').YT.Channel }
1654+ */
1655+ const channel = this . channelInstance
1656+ const coursesTab = await channel . getCourses ( )
1657+
1658+ if ( expectedId !== this . id ) {
1659+ return
1660+ }
1661+
1662+ this . latestCourses = coursesTab . playlists . map ( playlist => parseLocalListPlaylist ( playlist , this . id , this . channelName ) )
1663+ this . coursesContinuationData = coursesTab . has_continuation ? coursesTab : null
1664+ this . isElementListLoading = false
1665+ } catch ( err ) {
1666+ console . error ( err )
1667+ const errorMessage = this . $t ( 'Local API Error (Click to copy)' )
1668+ showToast ( `${ errorMessage } : ${ err } ` , 10000 , ( ) => {
1669+ copyToClipboard ( err )
1670+ } )
1671+ if ( this . backendPreference === 'local' && this . backendFallback ) {
1672+ showToast ( this . $t ( 'Falling back to Invidious API' ) )
1673+ this . channelInvidiousCourses ( )
1674+ } else {
1675+ this . isLoading = false
1676+ }
1677+ }
1678+ } ,
1679+
1680+ getChannelCoursesLocalMore : async function ( ) {
1681+ try {
1682+ /**
1683+ * @type {import('youtubei.js').YT.ChannelListContinuation }
1684+ */
1685+ const continuation = await this . coursesContinuationData . getContinuation ( )
1686+
1687+ const parsedCourses = continuation . playlists . map ( playlist => parseLocalListPlaylist ( playlist , this . id , this . channelName ) )
1688+ this . latestCourses = this . latestCourses . concat ( parsedCourses )
1689+ this . coursesContinuationData = continuation . has_continuation ? continuation : null
1690+ } catch ( err ) {
1691+ console . error ( err )
1692+ const errorMessage = this . $t ( 'Local API Error (Click to copy)' )
1693+ showToast ( `${ errorMessage } : ${ err } ` , 10000 , ( ) => {
1694+ copyToClipboard ( err )
1695+ } )
1696+ }
1697+ } ,
1698+
1699+ channelInvidiousCourses : function ( ) {
1700+ this . isElementListLoading = true
1701+
1702+ getInvidiousChannelCourses ( this . id ) . then ( ( response ) => {
1703+ this . coursesContinuationData = response . continuation || null
1704+ this . latestCourses = response . playlists
1705+ this . isElementListLoading = false
1706+ } ) . catch ( async ( err ) => {
1707+ console . error ( err )
1708+ const errorMessage = this . $t ( 'Invidious API Error (Click to copy)' )
1709+ showToast ( `${ errorMessage } : ${ err } ` , 10000 , ( ) => {
1710+ copyToClipboard ( err )
1711+ } )
1712+ if ( process . env . SUPPORTS_LOCAL_API && this . backendPreference === 'invidious' && this . backendFallback ) {
1713+ showToast ( this . $t ( 'Falling back to Local API' ) )
1714+ if ( ! this . channelInstance ) {
1715+ this . channelInstance = await getLocalChannel ( this . id )
1716+ }
1717+ this . getChannelCoursesLocal ( )
1718+ } else {
1719+ this . isLoading = false
1720+ }
1721+ } )
1722+ } ,
1723+
1724+ channelInvidiousCoursesMore : function ( ) {
1725+ if ( this . coursesContinuationData === null ) {
1726+ console . warn ( 'There are no more courses available for this channel' )
1727+ return
1728+ }
1729+
1730+ getInvidiousChannelCourses ( this . id , this . coursesContinuationData ) . then ( ( response ) => {
1731+ this . coursesContinuationData = response . continuation || null
1732+ this . latestCourses = this . latestCourses . concat ( response . playlists )
1733+ this . isElementListLoading = false
1734+ } ) . catch ( ( err ) => {
1735+ console . error ( err )
1736+ const errorMessage = this . $t ( 'Invidious API Error (Click to copy)' )
1737+ showToast ( `${ errorMessage } : ${ err } ` , 10000 , ( ) => {
1738+ copyToClipboard ( err )
1739+ } )
1740+ if ( process . env . SUPPORTS_LOCAL_API && this . backendPreference === 'invidious' && this . backendFallback ) {
1741+ showToast ( this . $t ( 'Falling back to Local API' ) )
1742+ this . getChannelLocal ( )
1743+ } else {
1744+ this . isLoading = false
1745+ }
1746+ } )
1747+ } ,
1748+
16231749 getCommunityPostsLocal : async function ( ) {
16241750 const expectedId = this . id
16251751
@@ -1780,10 +1906,25 @@ export default defineComponent({
17801906 }
17811907 break
17821908 case 'releases' :
1783- this . getChannelReleasesLocalMore ( )
1909+ if ( this . apiUsed === 'local' ) {
1910+ this . getChannelReleasesLocalMore ( )
1911+ } else {
1912+ this . channelInvidiousReleasesMore ( )
1913+ }
17841914 break
17851915 case 'podcasts' :
1786- this . getChannelPodcastsLocalMore ( )
1916+ if ( this . apiUsed === 'local' ) {
1917+ this . getChannelPodcastsLocalMore ( )
1918+ } else {
1919+ this . channelInvidiousPodcastsMore ( )
1920+ }
1921+ break
1922+ case 'courses' :
1923+ if ( this . apiUsed === 'local' ) {
1924+ this . getChannelCoursesLocalMore ( )
1925+ } else {
1926+ this . channelInvidiousCoursesMore ( )
1927+ }
17871928 break
17881929 case 'playlists' :
17891930 switch ( this . apiUsed ) {
0 commit comments