VideoWeb/app/VideoWeb/Invidious.class.st

186 lines
4.5 KiB
Smalltalk

"
I model the API for the [Invidious](https://invidious.io/) open source front-end for YouTube.
"
Class {
#name : #Invidious,
#superclass : #Object,
#instVars : [
'uri',
'region',
'api',
'monitor'
],
#classInstVars : [
'default'
],
#category : #VideoWeb
}
{ #category : #accessing }
Invidious class >> configFile [
| configFolder |
configFolder := (FileLocator userData / 'Mutabit' / 'VideoWeb') ensureCreateDirectory.
^ configFolder / 'config.ston'
]
{ #category : #accessing }
Invidious class >> configFolder [
]
{ #category : #accessing }
Invidious class >> default: anObject [
default := anObject
]
{ #category : #accessing }
Invidious class >> defaultInstance [
^ default ifNil: [ default := self responseTimes first key ]
]
{ #category : #accessing }
Invidious class >> instances [
^ self rawInstances collect: [ :instanceData |
| instanceDict |
instanceDict := instanceData second.
Invidious new fromDictionary: instanceDict ]
]
{ #category : #accessing }
Invidious class >> instancesWithAPIEnabled [
^ self instances select: [ :instance | instance api = true ]
]
{ #category : #accessing }
Invidious class >> loadConfiguration [
^ self default: (STON fromString: self configFile contents)
]
{ #category : #accessing }
Invidious class >> rawInstances [
^ STON
fromString: 'https://api.invidious.io/instances.json?sort_by=type,users' asUrl
retrieveContents
]
{ #category : #accessing }
Invidious class >> responseTimes [
| responses |
responses := Dictionary new.
self instancesWithAPIEnabled do: [ :instance |
responses at: instance put: instance responseTime.
].
^ (responses associations asSortedCollection: [ :x :y| x value < y value ])
collect: [:assoc | assoc ]
]
{ #category : #accessing }
Invidious class >> storeConfiguration [
| configFolder |
configFolder := (FileLocator userData / 'Mutabit' / 'VideoWeb') ensureCreateDirectory.
MarkupFile exportAsFileOn: configFolder / 'config.ston' containing: ( STON toStringPretty: self defaultInstance ).
^ configFolder / 'config.ston'
]
{ #category : #accessing }
Invidious >> api [
^ api
]
{ #category : #accessing }
Invidious >> api: booleanValue [
api := booleanValue.
]
{ #category : #accessing }
Invidious >> authorIdForVideo: aString [
]
{ #category : #accessing }
Invidious >> channel: authorId [
| queryURL dataDict |
queryURL := self uri asString, 'api/v1/channels/', authorId .
dataDict := STONJSON fromString: queryURL asUrl retrieveContents.
^ VideoChannel new fromDictionary: dataDict
]
{ #category : #accessing }
Invidious >> channelContainingVideo: videoID [
^ self channel: (self video: videoID) authorId
]
{ #category : #accessing }
Invidious >> fromDictionary: aDictionary [
| rawMonitor|
self
uri: (aDictionary at: 'uri');
region: (aDictionary at: 'region');
api: (aDictionary at: 'api').
rawMonitor := aDictionary at: 'monitor'.
rawMonitor ifNil: [ ^ self ].
self monitor
at: '90DaysRatio' put: ((aDictionary at: 'monitor' at: '90dRatio') at: 'ratio');
at: '30DaysRatio' put: ((aDictionary at: 'monitor' at: '30dRatio') at: 'ratio');
yourself.
]
{ #category : #accessing }
Invidious >> monitor [
^ monitor ifNil: [ monitor := Dictionary new]
]
{ #category : #accessing }
Invidious >> monitor: aDictionary [
monitor := aDictionary
]
{ #category : #accessing }
Invidious >> printOn: aStream [
| ratio |
ratio := self monitor at: '90DaysRatio' ifAbsent: [ '--' ].
super printOn: aStream.
^ aStream nextPutAll: ' ( ', self uri asString, ' | ', self region, ' | ', ratio, '% ) '
]
{ #category : #accessing }
Invidious >> region [
^ region
]
{ #category : #accessing }
Invidious >> region: countryISOCode [
region := countryISOCode
]
{ #category : #accessing }
Invidious >> responseTime [
| responseEvaluation |
responseEvaluation := [ ZnClient new
beOneShot;
url: self uri;
get;
response ].
[ ^ responseEvaluation timeToRun ] onErrorDo: [ ^ 1 class maxVal asDuration "Internal convention for max duration when an error raises." ].
]
{ #category : #accessing }
Invidious >> uri [
^ uri
]
{ #category : #accessing }
Invidious >> uri: urlString [
uri := urlString asUrl
]
{ #category : #accessing }
Invidious >> video: videoId [
| dataUrl queryFields dataDict |
queryFields := 'videoId,title,description,author,authorId,authorUrl,likeCount,lengthSeconds,videoThumbnails'.
dataUrl := (self uri asString, 'api/v1/videos/', videoId, '?fields=', queryFields, '&pretty=1') asUrl.
dataDict := STONJSON fromString: dataUrl retrieveContents.
^ WebVideo new fromDictionary: dataDict
]