Skip to content
Snippets Groups Projects
TwitchScript.js 58 KiB
Newer Older
Koen's avatar
Koen committed
    }

    /** @type {import("./types.d.ts").AllSearchResponse} */
    const json = callGQL(gql)

    const sf = json.data.searchFor

    /** @type {PlatformVideo[]} */
    const results = []

    for (const e of sf.channels.edges) {
        if (e.item.stream !== null) {
            results.push(searchTaggedToPlatformVideo(e.item))
        }
    }

    for (const e of sf.channelsWithTag.edges) {
        results.push(searchTaggedToPlatformVideo(e.item))
    }

    // for (const e of sf.channels.edges) {
    //   results.push(searchTaggedToPlatformVideo(e.item))
    // }

    for (const e of sf.relatedLiveChannels.edges) {
        results.push(searchLiveToPlatformVideo(e.item))
    }

    for (const e of sf.videos.edges) {
        results.push(searchVideoToPlatformVideo(e.item))
    }

    return new SearchPagerAll(context, results)
}

/**
 * Gets a search pager for channels
 * @param {import("./types").SearchContext} context the query params
 * @returns {SearchPagerChannels} returns the search pager
 * @throws {ScriptException}
 */
function getSearchPagerChannels(context) {
    const gql = {
        extensions: {
            persistedQuery: {
                sha256Hash: '6ea6e6f66006485e41dbe3ebd69d5674c5b22896ce7b595d7fce6411a3790138',
                version: 1,
            },
        },
        operationName: 'SearchResultsPage_SearchResults',
        variables: {
            options: {
                targets: [
                    {
                        index: 'CHANNEL',
                        limit: context.page_size,
                        cursor: context?.cursor ?? null,
                    },
                ],
            },
            query: context.q,
            requestID: '',
        },
        query: '#import "twilight/features/tags/models/freeform-tag-fragment.gql" #import "twilight/features/tags/models/tag-fragment.gql" query SearchResultsPage_SearchResults( $query: String! $options: SearchForOptions $requestID: ID ) { searchFor( userQuery: $query platform: "web" options: $options requestID: $requestID ) { channels { ...searchForChannelsFragment } channelsWithTag { ...searchForChannelsWithTagFragment } games { ...searchForGamesFragment } videos { ...searchForVideosFragment } relatedLiveChannels { ...relatedLiveChannelsFragment } } } fragment relatedLiveChannelsFragment on SearchForResultRelatedLiveChannels { edges { trackingID item { ...searchRelatedLiveChannelFragment } } score } fragment searchForGamesFragment on SearchForResultGames { cursor edges { trackingID item { ...searchForGameFragment ...searchForVideoFragment ...searchForUserFragment } } score totalMatches } fragment searchForChannelsFragment on SearchForResultUsers { cursor edges { trackingID item { ...searchForUserFragment ...searchForVideoFragment ...searchForGameFragment } } score totalMatches } fragment searchForChannelsWithTagFragment on SearchForResultUsers { cursor edges { trackingID item { ...searchForUserFragment ...searchForVideoFragment ...searchForGameFragment } } score totalMatches } fragment searchForVideosFragment on SearchForResultVideos { cursor edges { trackingID item { ...searchForVideoFragment ...searchForUserFragment ...searchForGameFragment } } score totalMatches } fragment searchRelatedLiveChannelFragment on User { id stream { id viewersCount previewImageURL(height: 112 width: 200) game { name id } broadcaster { id primaryColorHex login displayName broadcastSettings { id title } roles { isPartner } } } watchParty { session { id contentRestriction } } } fragment searchForGameFragment on Game { id name displayName boxArtURL(height: 120 width: 90) tags(tagType: CONTENT) { ...tagFragment } viewersCount } fragment searchForScheduleSegmentFragment on ScheduleSegment { id startAt endAt title hasReminder categories { id name } } fragment searchForUserFragment on User { broadcastSettings { id title } displayName followers { totalCount } id lastBroadcast { id startedAt } login profileImageURL(width: 150) description channel { id schedule { id nextSegment { ...searchForScheduleSegmentFragment } } } self { canFollow follower { disableNotifications } } latestVideo: videos(first: 1 sort: TIME type: ARCHIVE) { edges { node { ...searchForFeaturedVideoFragment } } } topClip: clips(first: 1 criteria: { sort: VIEWS_DESC }) { edges { node { ...searchForFeaturedClipFragment } } } roles { isPartner } stream { game { id name displayName } id previewImageURL(height: 120 width: 214) freeformTags { ...freeformTagFragment } type viewersCount } watchParty { session { id contentRestriction } } } fragment searchForFeaturedVideoFragment on Video { id lengthSeconds title previewThumbnailURL(width: 100 height: 56) } fragment searchForFeaturedClipFragment on Clip { id title durationSeconds thumbnailURL slug } fragment searchForVideoFragment on Video { createdAt owner { id displayName login roles { isPartner } } id game { id name displayName } lengthSeconds previewThumbnailURL(height: 120 width: 214) title viewCount }',
    }

    /** @type {import("./types.d.ts").AllSearchResponse}*/
    const json = callGQL(gql)

    const results = json.data.searchFor.channels.edges.map((edge) => searchChannelToPlatformChannel(edge.item))

    context.cursor = json.data.searchFor.channels.cursor

    context.results_returned = results.length + context.results_returned

    return new SearchPagerChannels(results, json.data.searchFor.channels.totalMatches > context.results_returned, context)
}

//Pagers
class HomePagerPopular extends VideoPager {
    /**
     * @param {PlatformVideo[]} results the initial results
     * @param {boolean} hasNextPage if there is a next page
     * @param {import("./types.d.ts").HomeContext} context the context
     */
    constructor(results, hasNextPage, context) {
        super(results, hasNextPage, context)
    }

    nextPage() {
        return getHomePagerPopular(this.context)
    }
}

class HomePagerPersonalSections extends VideoPager {
    /**
     * @param {import("./types.d.ts").PagerBaseContext} context the context
     * @param {PlatformVideo[]} results the initial results
     */
    constructor(context, results) {
        super(results, results.length >= context.page_size, context)
    }

    nextPage() {
        this.context.page = this.context.page + 1
        return getHomePagerPersonalSections(this.context)
    }
}

class ChannelVideoPager extends VideoPager {
    /**
     * @param {import("./types.d.ts").URLContext} context the context
     * @param {PlatformVideo[]} results the initial results
     * @param {boolean} hasNextPage if there is a next page
     */
    constructor(context, results, hasNextPage) {
        super(results, hasNextPage, context)
    }

    nextPage() {
        return getChannelPager(this.context)
    }
}

class SearchPagerAll extends VideoPager {
    /**
     * @param {import("./types.d.ts").SearchContext} context the query params
     * @param {(PlatformVideo | PlatformChannel)[]} results the initial results
     */
    constructor(context, results) {
        super(results, false, context)
    }

    nextPage() {
        return null
    }
}

class SearchPagerVideos extends VideoPager {
    /**
     * @param {import("./types").SearchContext} context the query params
     * @param {PlatformVideo[]} results the initial results
     */
    constructor(context, results) {
        super(results, results.length >= context.page_size, context)
    }

    nextPage() {
        this.context.page = this.context.page + 1
        return getSearchPagerTracks(this.context)
    }
}

class SearchPagerChannels extends ChannelPager {
    constructor(results, hasNextPage, context) {
        super(results, hasNextPage, context)
    }

    nextPage() {
        return getSearchPagerChannels(this.context)
    }
}

class ExtendableCommentPager extends CommentPager {
    constructor(context, results) {
        super(results, results.length >= context.page_size, context)
    }

    nextPage() {
        this.context.page = this.context.page + 1
        return getCommentPager(this.context)
    }
}

//* Converters

/**
 * Convert a Live Twitch to a PlatformVideo
 * @param { import("./types.d.ts").RelatedLiveSearchResponse } sl
 * @returns { PlatformVideo }
 */
function searchLiveToPlatformVideo(sl) {
    return new PlatformVideo({
        id: new PlatformID(PLATFORM, sl.id, config.id),
        name: sl.stream.broadcaster.broadcastSettings.title,
        thumbnails: new Thumbnails([new Thumbnail(sl.stream.previewImageURL, 0)]),
        author: new PlatformAuthorLink(
            new PlatformID(PLATFORM, sl.stream.broadcaster.id, config.id, PLATFORM_CLAIMTYPE),
Koen's avatar
Koen committed
            sl.stream.broadcaster.displayName,
            BASE_URL + sl.stream.broadcaster.login,
            sl.stream.broadcaster.profileImageURL || ''
        ),
        uploadDate: parseInt(new Date().getTime() / 1000),
        duration: 0,
        viewCount: sl.stream.viewersCount,
        url: BASE_URL + sl.stream.broadcaster.login,
        isLive: true,
    })
}

/**
 * Convert a Video Twitch to a PlatformVideo
 * @param { import("./types.d.ts").VideoSearchResponse } sv
 * @returns { PlatformVideo }
 */
function searchVideoToPlatformVideo(sv) {
    return new PlatformVideo({
        id: new PlatformID(PLATFORM, sv.id, config.id),
        name: sv.title,
        thumbnails: new Thumbnails([new Thumbnail(sv.previewThumbnailURL, 0)]),
        author: new PlatformAuthorLink(new PlatformID(PLATFORM, sv.owner.id, config.id, PLATFORM_CLAIMTYPE), sv.owner.displayName, BASE_URL + sv.owner.login, ''),
Koen's avatar
Koen committed
        uploadDate: parseInt(new Date(sv.createdAt).getTime() / 1000),
        duration: parseInt(sv.lengthSeconds),
        viewCount: sv.viewCount,
        url: BASE_URL + sv.owner.login + '/video/' + sv.id,
        isLive: false,
    })
}

/**
 * Convert a Channel Twitch to a PlatformVideo
 * @param { import("./types.d.ts").ChannelSearchResponse } st
 * @returns { PlatformChannel }
 */
function searchTaggedToPlatformVideo(st) {
    return new PlatformVideo({
        id: new PlatformID(PLATFORM, st.stream.id, config.id),
        name: st.broadcastSettings.title,
        thumbnails: new Thumbnails([new Thumbnail(st.stream.previewImageURL, 0)]),
        author: new PlatformAuthorLink(new PlatformID(PLATFORM, st.id, config.id, PLATFORM_CLAIMTYPE), st.displayName, BASE_URL + st.login, st.profileImageURL || ''),
Koen's avatar
Koen committed
        uploadDate: parseInt(new Date().getTime() / 1000),
        duration: 0,
        viewCount: st.stream.viewersCount,
        url: BASE_URL + st.login,
        isLive: true,
    })
}

/**
 * Convert a Channel Twitch to a PlatformChannel
 * @param { import("./types.d.ts").ChannelSearchResponse } sc
 * @returns { PlatformChannel }
 */
function searchChannelToPlatformChannel(sc) {
    return new PlatformChannel({
        id: new PlatformID(PLATFORM, sc.id, config.id, PLATFORM_CLAIMTYPE),
Koen's avatar
Koen committed
        name: sc.displayName,
        thumbnail: sc.profileImageURL,
        banner: '',
        subscribers: sc.followers.totalCount,
        description: sc.description,
        url: BASE_URL + sc.login,
        links: [],
    })
}

console.log('LOADED')