Skip to content
This repository has been archived by the owner on Dec 4, 2022. It is now read-only.

Commit

Permalink
Refactorización de como se escoge si un stream cumple y ahora en Twit…
Browse files Browse the repository at this point in the history
…ch se mira las etiquetas como alternativa al título
  • Loading branch information
luskaner committed Oct 30, 2022
1 parent d4dca43 commit 4f3c2e5
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 53 deletions.
3 changes: 2 additions & 1 deletion assets/data/env.template.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"twitchClientId": "SOME-ID"
"twitchClientId": "SOME-ID",
"twitchGraphQlClientId": "SOME-ID2"
}
7 changes: 1 addition & 6 deletions src/ts/background.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Data from '/assets/data/data.json'

import { ApiStream, EnrichedStream, IslandType, PartialApiStream } from './types/stream'
import { ApiStream, EnrichedStream, IslandType } from './types/stream'
import { DataType } from './types/data'
import { ConnectDisconnectResponse, ConnectMessage, DisconnectMessage, Message, Response, StatusResponse } from './types/messaging'
import PlatformList from './platform/background/BackgroundPlatformListCompile'
Expand Down Expand Up @@ -37,10 +37,6 @@ async function validate(): Promise<void> {
}
}

function acceptStream( stream: PartialApiStream ) {
return stream.subtitle.toLocaleLowerCase().includes( 'tortill' )
}

async function getAllStreams(): Promise<EnrichedStream[]> {
const channelIds: Record<string, Set<string>> = {}
const islands: Record<string, IslandType> = {}
Expand Down Expand Up @@ -71,7 +67,6 @@ async function getAllStreams(): Promise<EnrichedStream[]> {
const apiStreams = await platform.getStreamAdapter().getStreams(
client,
channelIds[identifier],
acceptStream
) as Record<string, ApiStream>
for ( const [channel, apiStream] of Object.entries( apiStreams ) ) {
streams.push( {
Expand Down
10 changes: 5 additions & 5 deletions src/ts/platform/background/streamAdapter/streamAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import OauthClient from "../../../oauthClient/oauthClient";
import { PartialApiStream, ApiStream } from "../../../types/stream";
import { ApiStream } from "../../../types/stream";

export type CachedStream = {
time: number,
data: Record<string, ApiStream>
}
export default abstract class StreamAdapter<Client extends OauthClient> {

protected abstract doGetStreams( client: Client, channelIds: Set<string>, acceptStream?: ( baseStream: PartialApiStream ) => boolean ): Promise<Record<string, ApiStream>>
protected abstract doGetStreams( client: Client, channelIds: Set<string> ): Promise<Record<string, ApiStream>>

async getStreams( client: Client, channelIds: Set<string>, acceptStream?: ( baseStream: PartialApiStream ) => boolean ): Promise<Record<string, ApiStream>> {
async getStreams( client: Client, channelIds: Set<string> ): Promise<Record<string, ApiStream>> {
const cacheMinutes = this.cacheMinutes()
if ( cacheMinutes > 0 ) {
const data = await client.getDataCache()
const currentDate = Date.now()
if ( 'streams' in data && currentDate - (data.streams as CachedStream).time < cacheMinutes * 60 * 1000 ) {
const apiStreams = await this.doGetStreams( client, channelIds, acceptStream )
const apiStreams = await this.doGetStreams( client, channelIds )
const data = await client.getDataCache()
data.streams = {
time: currentDate,
Expand All @@ -25,7 +25,7 @@ export default abstract class StreamAdapter<Client extends OauthClient> {
return apiStreams
}
}
return this.doGetStreams( client, channelIds, acceptStream )
return this.doGetStreams( client, channelIds )
}

protected cacheMinutes(): number {
Expand Down
79 changes: 63 additions & 16 deletions src/ts/platform/background/streamAdapter/twitchStreamAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
import Env from '/assets/data/env.json'
import { EnvType } from "../../../types/env";
import OauthTwitchAPIClient from "../../../oauthClient/twitch/main";
import { Scope } from "../../../oauthClient/twitch/scope";
import { User } from "../../../oauthClient/twitch/types";
import { ApiStream, PartialApiStream } from "../../../types/stream";
import { ApiStream } from "../../../types/stream";
import StreamAdapter from "./streamAdapter";

const env = Env as EnvType

export default class TwitchStreamAdapter extends StreamAdapter<OauthTwitchAPIClient> {
private static broadcasterPriority = {
partner: 2,
affiliate: 1,
'': 0
}

protected async doGetStreams( client: OauthTwitchAPIClient, channelIds: Set<string>, acceptStream?: ( baseStream: PartialApiStream ) => boolean ): Promise<Record<string, ApiStream>> {
protected async doGetStreams( client: OauthTwitchAPIClient, channelIds: Set<string> ): Promise<Record<string, ApiStream>> {
const partialStreams: Record<string, { title: string, subtitle: string, viewers: number, url: string }> = {}
const apiStreams: Record<string, ApiStream> = {}
const streams = await client.getStreams( channelIds )
if ( streams ) {
for ( const s of streams ) {
const baseStream: PartialApiStream = {
subtitle: s.title
let acceptStream = s.title.toLocaleLowerCase().includes( 'tortill' )
if ( !acceptStream ) {
const tags = await this.getTags( s.user_login )
acceptStream = [...tags].some( t => t.toLocaleLowerCase().includes( 'tortill' ) )
}
if ( !acceptStream || acceptStream( baseStream ) ) {
const apiStream = baseStream as ApiStream
apiStream.title = s.user_name
apiStream.viewers = s.viewer_count
apiStream.url = `https://twitch.tv/${ s.user_login }`
apiStreams[s.user_login] = apiStream
if ( acceptStream ) {
partialStreams[s.user_login] = {
title: s.user_name,
subtitle: s.title,
viewers: s.viewer_count,
url: `https://twitch.tv/${ s.user_login }`
}
}
}
if ( Object.keys( apiStreams ).length > 0 ) {
if ( Object.keys( partialStreams ).length > 0 ) {
const followedLogins: Set<string> = new Set()
const scope = client.scopes as Set<Scope> | undefined
if ( scope?.has( Scope.USER_READ_FOLLOWS ) ) {
Expand All @@ -37,15 +45,54 @@ export default class TwitchStreamAdapter extends StreamAdapter<OauthTwitchAPICli
}
}
const userLoginsRecord: Record<string, User> = {};
( ( await client.getUsers( new Set( Object.keys( apiStreams ) ) ) ) as User[] ).forEach( u => { userLoginsRecord[u.login] = u } )
for ( const [login, apiStream] of Object.entries( apiStreams ) ) {
( ( await client.getUsers( new Set( Object.keys( partialStreams ) ) ) ) as User[] ).forEach( u => { userLoginsRecord[u.login] = u } )
for ( const [login, partialStream] of Object.entries( partialStreams ) ) {
const u = userLoginsRecord[login]
apiStream.imageUrl = u.profile_image_url
apiStream.favourite = followedLogins.has( u.login )
apiStream.priority = TwitchStreamAdapter.broadcasterPriority[u.broadcaster_type as keyof typeof TwitchStreamAdapter.broadcasterPriority]
apiStreams[login] = {
...partialStream,
imageUrl: u.profile_image_url,
favourite: followedLogins.has( login ),
priority: TwitchStreamAdapter.broadcasterPriority[u.broadcaster_type]
}
}
}
}
return apiStreams
}

private async getTags( channelId: string ): Promise<Set<string>> {
let tags: string[] = []
try {
const response = await fetch(
'https://gql.twitch.tv/gql',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Client-Id': env.twitchGraphQlClientId
},
body: JSON.stringify(
[{
operationName: 'StreamTagsTrackingChannel',
variables: {
channel: channelId
},
extensions: {
persistedQuery: {
version: 1,
sha256Hash: '6aa3851aaaf88c320d514eb173563d430b28ed70fdaaf7eeef6ed4b812f48608'
}
}
}] )
}
)
if ( response.ok ) {
const json = await response.json()
tags = ( json[0]?.data?.user?.stream?.freeformTags ?? [] ).map( ( t: { name: string } ) => t.name )
}

// eslint-disable-next-line no-empty
} catch { }
return new Set( tags )
}
}
37 changes: 19 additions & 18 deletions src/ts/platform/background/streamAdapter/youtubeStreamAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import OauthYoutubeAPIClient from "../../../oauthClient/youtube/main";
import { ApiStream, PartialApiStream } from "../../../types/stream";
import { ApiStream } from "../../../types/stream";
import StreamAdapter from "./streamAdapter";

export default class YoutubeStreamAdapter extends StreamAdapter<OauthYoutubeAPIClient> {
protected async doGetStreams( client: OauthYoutubeAPIClient, channelIds: Set<string>, acceptStream?: ( baseStream: PartialApiStream ) => boolean ) {
protected async doGetStreams( client: OauthYoutubeAPIClient, channelIds: Set<string> ) {
const partialStreams: Record<string, { subtitle: string, viewers: number, url: string }> = {}
const apiStreams: Record<string, ApiStream> = {}
const streams = await client.getStreams( channelIds )
if ( Object.keys( streams ).length > 0 ) {
const videos = await client.getVideos( new Set( Object.values( streams ) ) )
Object.keys( streams ).forEach( channelId => {
const videoId = streams[channelId]
const video = videos[videoId]
const baseStream: PartialApiStream = {
subtitle: video.title
}
if ( !acceptStream || acceptStream( baseStream ) ) {
const apiStream = baseStream as ApiStream
apiStream.url = `https://www.youtube.com/watch?v=${ videoId }`
apiStream.viewers = video.viewers
apiStreams[channelId] = apiStream
if ( video.title.toLocaleLowerCase().includes( 'tortill' ) ) {
partialStreams[channelId] = {
subtitle: video.title,
url: `https://www.youtube.com/watch?v=${ videoId }`,
viewers: video.viewers,
}
}
} )

if ( Object.keys( apiStreams ).length > 0 ) {
const users = await client.getUsers( new Set(Object.keys( apiStreams )) )
if ( Object.keys( partialStreams ).length > 0 ) {
const users = await client.getUsers( new Set( Object.keys( partialStreams ) ) )
const followedChannels = await client.getSubscribedChannels()
Object.entries( apiStreams ).forEach( apiStreamEntry => {
const [channelId, apiStream] = apiStreamEntry
for ( const [channelId, partialStream] of Object.entries( partialStreams ) ) {
const user = users[channelId]
apiStream.favourite = followedChannels.has( channelId )
apiStream.imageUrl = user.profileImage.toString()
apiStream.title = user.name
} )
apiStreams[channelId] = {
...partialStream,
favourite: followedChannels.has( channelId ),
imageUrl: user.profileImage.toString(),
title: user.name
}
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/ts/types/env.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export type EnvType = {
twitchClientId: string
twitchGraphQlClientId: string
}
10 changes: 3 additions & 7 deletions src/ts/types/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@ type ExtraStream = {
};


type RemainingApiStream = {
export type ApiStream = {
title: string;
subtitle: string;
url: string;
imageUrl: string;
viewers: number;
favourite: boolean;
priority?: number;
}

export type PartialApiStream = {
subtitle: string;
}

export type EnrichedStream = ApiStream & ExtraStream
export type ApiStream = RemainingApiStream & PartialApiStream
export type EnrichedStream = ApiStream & ExtraStream

0 comments on commit 4f3c2e5

Please sign in to comment.