Skip to content

Instantly share code, notes, and snippets.

View diousk's full-sized avatar

David diousk

  • Taipei
View GitHub Profile
@diousk
diousk / offset.md
Created December 12, 2024 16:08 — forked from hexcowboy/offset.md
Twitch Helix API - Get Video/VOD Offset and Comments from Clip ID

Since Twitch decomissioned API V5, there's no correct way to get the VOD offset for a clip. So I just inspected what the Twitch web app was doing and came up with a super cool hack.

I know this looks really complicated but really it just does two things. First it gets the clip information from the official API, then it uses Twitch GraphQL API to get the offset.

kimne78kx3ncx6brgo4mv6wki5h1ko is the Client ID that the Twitch web app uses. It can be used to access Twitch's GraphQL API (undocumented & not for public use). The postData variable contains a request and a sha256Hash. The sha256Hash is basically a job ID and may change in the future, but I added the current hash which should work for all clips as of posting this.

const clientId = "" // Put your Twitch Client ID here
const clientSecret = "" // Put your Twitch Client Secret here
@Singleton
class SessionManager @Inject constructor(
private val sessionComponentFactory: SessionComponent.Factory
) {
var sessionComponent: SessionComponent? = null
private set
fun isUserLoggedIn() = sessionComponent != null
fun logout() {
class SettingActivity : DaggerSessionActivity() {
@Inject
lateinit var sessionManager: SessionManager
@Inject
lateinit var sessionPrefs: SessionPrefs
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_setting)
class MainActivity : DaggerSessionActivity() {
@Inject
lateinit var sessionPrefs: SessionPrefs
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Timber.d("main sessionPrefs = $sessionPrefs")
class LoginActivity : DaggerAppCompatActivity() {
@Inject lateinit var sessionManager: SessionManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
Timber.d("sessionManager = $sessionManager")
goMain.setOnClickListener {
abstract class DaggerSessionActivity : AppCompatActivity(), HasAndroidInjector {
@Inject
lateinit var androidInjector: DispatchingAndroidInjector<Any>
override fun onCreate(savedInstanceState: Bundle?) {
val sessionManager = (application as App).appComponent.sessionManager()
sessionManager.sessionComponent?.injector()?.inject(this)
super.onCreate(savedInstanceState)
}
@SessionScope
@Subcomponent(modules = [SessionActivityBuilder::class, SessionModule::class])
interface SessionComponent {
@Subcomponent.Factory
interface Factory {
fun create(): SessionComponent
}
fun injector(): DispatchingAndroidInjector<Any>
}
@Module
abstract class ActivityBuilder {
 @ContributesAndroidInjector
 abstract fun bindLoginActivity(): LoginActivity
}
@Module(subcomponents = [SessionComponent::class])
abstract class AppSubcomponents
@Singleton
@Component(
 modules = [
 AndroidSupportInjectionModule::class,
 ActivityBuilder::class,
 AppSubcomponents::class,
 AppModule::class
 ]
)
interface AppComponent : AndroidInjector<App> {
@Module
abstract class ActivityBuilder {
 @PerActivity
 @ContributesAndroidInjector(modules = [SomeModule::class])
 abstract fun bindSomeActivity(): SomeActivity
}
@Module
abstract class SomeModule {
 @Provides