From 57c5d1f4a9f818c7d5cfb06b007fdbab758a4eec Mon Sep 17 00:00:00 2001 From: tommyxchow Date: Fri, 26 Nov 2021 01:14:47 -0500 Subject: [PATCH] Twitch API tweaks & additions (#41) - Added request for streams under a desired category - Tweaked models --- lib/api/twitch_api.dart | 49 +++++++++++++++---- lib/models/category.dart | 7 ++- lib/models/category.g.dart | 8 +++ lib/models/stream.dart | 13 +++++ lib/models/stream.g.dart | 8 +++ lib/screens/search/search.dart | 2 - .../followed_streams_store.dart | 6 +-- .../streams_top/top_streams_store.dart | 6 +-- 8 files changed, 80 insertions(+), 19 deletions(-) diff --git a/lib/api/twitch_api.dart b/lib/api/twitch_api.dart index 2a3c6ded..f50550fb 100644 --- a/lib/api/twitch_api.dart +++ b/lib/api/twitch_api.dart @@ -97,6 +97,7 @@ class Twitch { /// Returns the user's info given their token (headers) static Future getUserInfo({required Map? headers}) async { final response = await http.get(Uri.parse('https://api.twitch.tv/helix/users'), headers: headers); + if (response.statusCode == 200) { final userData = jsonDecode(response.body)['data'] as List; @@ -133,7 +134,11 @@ class Twitch { /// Returns the validity of the given token static Future validateToken({required String token}) async { debugPrint('Validating token...'); - final response = await http.get(Uri.parse('https://id.twitch.tv/oauth2/validate'), headers: {'Authorization': 'Bearer $token'}); + final response = await http.get( + Uri.parse('https://id.twitch.tv/oauth2/validate'), + headers: {'Authorization': 'Bearer $token'}, + ); + if (response.statusCode == 200) { debugPrint('Token validated!'); return true; @@ -144,7 +149,7 @@ class Twitch { } /// Returns a map containing top 20 streams and a cursor for further requests. - static Future?> getTopStreams({required Map? headers, required String? cursor}) async { + static Future getTopStreams({required Map? headers, required String? cursor}) async { final Uri uri; if (cursor == null) { @@ -157,16 +162,19 @@ class Twitch { if (response.statusCode == 200) { final decoded = jsonDecode(response.body); - final data = decoded['data'] as List; - return {'streams': data.map((stream) => StreamTwitch.fromJson(stream)).toList(), 'cursor': decoded['pagination']['cursor']}; + return StreamsTwitch.fromJson(decoded); } else { debugPrint('Failed to update top streams'); } } /// Returns a map with the given user's top 20 followed streams and a cursor for further requests. - static Future?> getFollowedStreams({required String id, required Map? headers, required String? cursor}) async { + static Future getFollowedStreams({ + required String id, + required Map? headers, + required String? cursor, + }) async { final Uri uri; if (cursor == null) { @@ -179,14 +187,38 @@ class Twitch { if (response.statusCode == 200) { final decoded = jsonDecode(response.body); - final data = decoded['data'] as List; - return {'streams': data.map((stream) => StreamTwitch.fromJson(stream)).toList(), 'cursor': decoded['pagination']['cursor']}; + return StreamsTwitch.fromJson(decoded); } else { debugPrint('Failed to update followed streams'); } } + /// Returns the list of streams under the given game/category ID. + static Future getStreamsUnderGame({ + required String gameId, + required Map? headers, + required String? cursor, + }) async { + final Uri uri; + + if (cursor == null) { + uri = Uri.parse('https://api.twitch.tv/helix/streams?game_id=$gameId'); + } else { + uri = Uri.parse('https://api.twitch.tv/helix/streams?game_id=$gameId&after=$cursor'); + } + + final response = await http.get(uri, headers: headers); + + if (response.statusCode == 200) { + final decoded = jsonDecode(response.body); + + return StreamsTwitch.fromJson(decoded); + } else { + debugPrint('Failed to update game streams'); + } + } + /// Returns the stream info given the user login. static Future getStream({required String userLogin, required Map? headers}) async { final uri = Uri.parse('https://api.twitch.tv/helix/streams?user_login=$userLogin'); @@ -263,9 +295,8 @@ class Twitch { if (response.statusCode == 200) { final decoded = jsonDecode(response.body); - final data = decoded['data'] as List; - return CategoriesTwitch(data.map((category) => CategoryTwitch.fromJson(category)).toList(), decoded['pagination']['cursor']); + return CategoriesTwitch.fromJson(decoded); } else { debugPrint('Failed to update top games'); } diff --git a/lib/models/category.dart b/lib/models/category.dart index a6dc1c41..fdaca1e2 100644 --- a/lib/models/category.dart +++ b/lib/models/category.dart @@ -17,12 +17,15 @@ class CategoryTwitch { factory CategoryTwitch.fromJson(Map json) => _$CategoryTwitchFromJson(json); } +@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake) class CategoriesTwitch { final List data; - final String cursor; + final Map pagination; const CategoriesTwitch( this.data, - this.cursor, + this.pagination, ); + + factory CategoriesTwitch.fromJson(Map json) => _$CategoriesTwitchFromJson(json); } diff --git a/lib/models/category.g.dart b/lib/models/category.g.dart index 607abdc4..13ba544b 100644 --- a/lib/models/category.g.dart +++ b/lib/models/category.g.dart @@ -12,3 +12,11 @@ CategoryTwitch _$CategoryTwitchFromJson(Map json) => json['id'] as String, json['name'] as String, ); + +CategoriesTwitch _$CategoriesTwitchFromJson(Map json) => + CategoriesTwitch( + (json['data'] as List) + .map((e) => CategoryTwitch.fromJson(e as Map)) + .toList(), + Map.from(json['pagination'] as Map), + ); diff --git a/lib/models/stream.dart b/lib/models/stream.dart index 57cfe144..b1b9cba1 100644 --- a/lib/models/stream.dart +++ b/lib/models/stream.dart @@ -39,3 +39,16 @@ class StreamTwitch { factory StreamTwitch.fromJson(Map json) => _$StreamTwitchFromJson(json); } + +@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake) +class StreamsTwitch { + final List data; + final Map pagination; + + const StreamsTwitch( + this.data, + this.pagination, + ); + + factory StreamsTwitch.fromJson(Map json) => _$StreamsTwitchFromJson(json); +} diff --git a/lib/models/stream.g.dart b/lib/models/stream.g.dart index 96a87042..3db2848b 100644 --- a/lib/models/stream.g.dart +++ b/lib/models/stream.g.dart @@ -22,3 +22,11 @@ StreamTwitch _$StreamTwitchFromJson(Map json) => StreamTwitch( (json['tag_ids'] as List?)?.map((e) => e as String).toList(), json['is_mature'] as bool, ); + +StreamsTwitch _$StreamsTwitchFromJson(Map json) => + StreamsTwitch( + (json['data'] as List) + .map((e) => StreamTwitch.fromJson(e as Map)) + .toList(), + Map.from(json['pagination'] as Map), + ); diff --git a/lib/screens/search/search.dart b/lib/screens/search/search.dart index 1aac4992..19ed99e4 100644 --- a/lib/screens/search/search.dart +++ b/lib/screens/search/search.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:frosty/models/channel.dart'; import 'package:frosty/screens/channel/video_chat.dart'; import 'package:frosty/screens/search/search_store.dart'; -import 'package:mobx/mobx.dart'; class Search extends StatefulWidget { final SearchStore searchStore; diff --git a/lib/screens/stream_list/streams_followed/followed_streams_store.dart b/lib/screens/stream_list/streams_followed/followed_streams_store.dart index 8d6fea62..74fffaf1 100644 --- a/lib/screens/stream_list/streams_followed/followed_streams_store.dart +++ b/lib/screens/stream_list/streams_followed/followed_streams_store.dart @@ -38,11 +38,11 @@ abstract class _FollowedStreamsStoreBase with Store { if (newFollowedStreams != null) { if (_followedStreamsCurrentCursor == null) { - _followedStreams = ObservableList.of(newFollowedStreams['streams']); + _followedStreams = ObservableList.of(newFollowedStreams.data); } else { - _followedStreams.addAll(newFollowedStreams['streams']); + _followedStreams.addAll(newFollowedStreams.data); } - _followedStreamsCurrentCursor = newFollowedStreams['cursor']; + _followedStreamsCurrentCursor = newFollowedStreams.pagination['cursor']; } _isLoading = false; diff --git a/lib/screens/stream_list/streams_top/top_streams_store.dart b/lib/screens/stream_list/streams_top/top_streams_store.dart index c4a95494..64c4b7a3 100644 --- a/lib/screens/stream_list/streams_top/top_streams_store.dart +++ b/lib/screens/stream_list/streams_top/top_streams_store.dart @@ -38,11 +38,11 @@ abstract class _TopStreamsStoreBase with Store { if (newTopStreams != null) { if (_topStreamsCurrentCursor == null) { - _topStreams = ObservableList.of(newTopStreams['streams']); + _topStreams = ObservableList.of(newTopStreams.data); } else { - _topStreams.addAll(newTopStreams['streams']); + _topStreams.addAll(newTopStreams.data); } - _topStreamsCurrentCursor = newTopStreams['cursor']; + _topStreamsCurrentCursor = newTopStreams.pagination['cursor']; } _isLoading = false;