Migrate 7TV emotes to V3 API (#287)
* Migrate 7TV global emotes to V3 API * Migrate 7TV channel emotes to V3 API
This commit is contained in:
parent
26c5bb2437
commit
a981cec250
5 changed files with 77 additions and 178 deletions
|
|
@ -12,12 +12,12 @@ class SevenTVApi {
|
|||
|
||||
/// Returns a map of global 7TV emotes to their URL.
|
||||
Future<List<Emote>> getEmotesGlobal() async {
|
||||
final url = Uri.parse('https://api.7tv.app/v2/emotes/global');
|
||||
final url = Uri.parse('https://7tv.io/v3/emote-sets/global');
|
||||
|
||||
final response = await _client.get(url);
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
final emotes = decoded.map((emote) => Emote7TV.fromJson(emote));
|
||||
final decoded = jsonDecode(response.body)['emotes'] as List;
|
||||
final emotes = decoded.map((emote) => Emote7TV.fromJson(emote['data']));
|
||||
|
||||
return emotes
|
||||
.map((emote) => Emote.from7TV(emote, EmoteType.sevenTVGlobal))
|
||||
|
|
@ -29,12 +29,12 @@ class SevenTVApi {
|
|||
|
||||
/// Returns a map of a channel's 7TV emotes to their URL.
|
||||
Future<List<Emote>> getEmotesChannel({required String id}) async {
|
||||
final url = Uri.parse('https://api.7tv.app/v2/users/$id/emotes');
|
||||
final url = Uri.parse('https://7tv.io/v3/users/twitch/$id');
|
||||
|
||||
final response = await _client.get(url);
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = jsonDecode(response.body) as List;
|
||||
final emotes = decoded.map((emote) => Emote7TV.fromJson(emote));
|
||||
final decoded = jsonDecode(response.body)['emote_set']['emotes'] as List;
|
||||
final emotes = decoded.map((emote) => Emote7TV.fromJson(emote['data']));
|
||||
|
||||
return emotes
|
||||
.map((emote) => Emote.from7TV(emote, EmoteType.sevenTVChannel))
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ class _MyAppState extends State<MyApp> {
|
|||
final themes = FrostyThemes();
|
||||
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Frosty',
|
||||
theme: themes.light,
|
||||
darkTheme: settingsStore.themeType == ThemeType.black
|
||||
|
|
|
|||
|
|
@ -107,24 +107,54 @@ class EmoteFFZ {
|
|||
|
||||
@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake)
|
||||
class Emote7TV {
|
||||
final String id;
|
||||
final String name;
|
||||
final List<String> visibilitySimple;
|
||||
final List<int> width;
|
||||
final List<int> height;
|
||||
final List<List<String>> urls;
|
||||
final List<String>? tags;
|
||||
final Emote7TVHost host;
|
||||
|
||||
const Emote7TV(
|
||||
this.id,
|
||||
this.name,
|
||||
this.visibilitySimple,
|
||||
this.width,
|
||||
this.height,
|
||||
this.urls,
|
||||
this.tags,
|
||||
this.host,
|
||||
);
|
||||
|
||||
factory Emote7TV.fromJson(Map<String, dynamic> json) =>
|
||||
_$Emote7TVFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake)
|
||||
class Emote7TVHost {
|
||||
final String url;
|
||||
final List<Emote7TVFile> files;
|
||||
|
||||
Emote7TVHost(
|
||||
this.url,
|
||||
this.files,
|
||||
);
|
||||
|
||||
factory Emote7TVHost.fromJson(Map<String, dynamic> json) =>
|
||||
_$Emote7TVHostFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false, fieldRename: FieldRename.snake)
|
||||
class Emote7TVFile {
|
||||
final String name;
|
||||
final int width;
|
||||
final int height;
|
||||
final String format;
|
||||
|
||||
Emote7TVFile(
|
||||
this.name,
|
||||
this.width,
|
||||
this.height,
|
||||
this.format,
|
||||
);
|
||||
|
||||
factory Emote7TVFile.fromJson(Map<String, dynamic> json) =>
|
||||
_$Emote7TVFileFromJson(json);
|
||||
}
|
||||
|
||||
/// The common emote class.
|
||||
@JsonSerializable()
|
||||
class Emote {
|
||||
|
|
@ -171,14 +201,22 @@ class Emote {
|
|||
type: type,
|
||||
);
|
||||
|
||||
factory Emote.from7TV(Emote7TV emote, EmoteType type) => Emote(
|
||||
name: emote.name,
|
||||
width: emote.width.first,
|
||||
height: emote.height.first,
|
||||
zeroWidth: emote.visibilitySimple.contains('ZERO_WIDTH'),
|
||||
url: emote.urls[3][1],
|
||||
type: type,
|
||||
);
|
||||
factory Emote.from7TV(Emote7TV emote, EmoteType type) {
|
||||
final url = emote.host.url;
|
||||
// Flutter doesn't support AVIF yet.
|
||||
final file = emote.host.files.reversed.firstWhere(
|
||||
(file) => file.format != 'AVIF' && file.name.contains('4x'),
|
||||
);
|
||||
|
||||
return Emote(
|
||||
name: emote.name,
|
||||
width: emote.host.files.first.width,
|
||||
height: emote.host.files.first.height,
|
||||
zeroWidth: emote.tags?.contains('zerowidth') ?? false,
|
||||
url: 'https:$url/${file.name}',
|
||||
type: type,
|
||||
);
|
||||
}
|
||||
|
||||
factory Emote.fromJson(Map<String, dynamic> json) => _$EmoteFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$EmoteToJson(this);
|
||||
|
|
|
|||
|
|
@ -52,17 +52,26 @@ EmoteFFZ _$EmoteFFZFromJson(Map<String, dynamic> json) => EmoteFFZ(
|
|||
);
|
||||
|
||||
Emote7TV _$Emote7TVFromJson(Map<String, dynamic> json) => Emote7TV(
|
||||
json['id'] as String,
|
||||
json['name'] as String,
|
||||
(json['visibility_simple'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
(json['width'] as List<dynamic>).map((e) => e as int).toList(),
|
||||
(json['height'] as List<dynamic>).map((e) => e as int).toList(),
|
||||
(json['urls'] as List<dynamic>)
|
||||
.map((e) => (e as List<dynamic>).map((e) => e as String).toList())
|
||||
(json['tags'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||
Emote7TVHost.fromJson(json['host'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Emote7TVHost _$Emote7TVHostFromJson(Map<String, dynamic> json) => Emote7TVHost(
|
||||
json['url'] as String,
|
||||
(json['files'] as List<dynamic>)
|
||||
.map((e) => Emote7TVFile.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Emote7TVFile _$Emote7TVFileFromJson(Map<String, dynamic> json) => Emote7TVFile(
|
||||
json['name'] as String,
|
||||
json['width'] as int,
|
||||
json['height'] as int,
|
||||
json['format'] as String,
|
||||
);
|
||||
|
||||
Emote _$EmoteFromJson(Map<String, dynamic> json) => Emote(
|
||||
name: json['name'] as String,
|
||||
width: json['width'] as int?,
|
||||
|
|
|
|||
|
|
@ -198,153 +198,4 @@ void main() {
|
|||
expect(emote.urls.url4x, '//cdn.frankerfacez.com/emote/27081/4');
|
||||
});
|
||||
});
|
||||
|
||||
group('7TV', () {
|
||||
test('global emote should parse correctly', () {
|
||||
const sampleGlobal7TVEmote = '''
|
||||
{
|
||||
"id": "603ca884faf3a00014dff0ab",
|
||||
"name": "gachiBASS",
|
||||
"owner": null,
|
||||
"visibility": 2,
|
||||
"visibility_simple": [
|
||||
"GLOBAL"
|
||||
],
|
||||
"mime": "image/gif",
|
||||
"status": 3,
|
||||
"tags": [
|
||||
|
||||
],
|
||||
"width": [
|
||||
32,
|
||||
48,
|
||||
76,
|
||||
128
|
||||
],
|
||||
"height": [
|
||||
32,
|
||||
48,
|
||||
76,
|
||||
128
|
||||
],
|
||||
"urls": [
|
||||
[
|
||||
"1",
|
||||
"https://cdn.7tv.app/emote/603ca884faf3a00014dff0ab/1x"
|
||||
],
|
||||
[
|
||||
"2",
|
||||
"https://cdn.7tv.app/emote/603ca884faf3a00014dff0ab/2x"
|
||||
],
|
||||
[
|
||||
"3",
|
||||
"https://cdn.7tv.app/emote/603ca884faf3a00014dff0ab/3x"
|
||||
],
|
||||
[
|
||||
"4",
|
||||
"https://cdn.7tv.app/emote/603ca884faf3a00014dff0ab/4x"
|
||||
]
|
||||
]
|
||||
}
|
||||
''';
|
||||
|
||||
final json = jsonDecode(sampleGlobal7TVEmote);
|
||||
final emote = Emote7TV.fromJson(json);
|
||||
|
||||
expect(emote.name, 'gachiBASS');
|
||||
expect(emote.visibilitySimple, ['GLOBAL']);
|
||||
expect(emote.width, [32, 48, 76, 128]);
|
||||
expect(emote.height, [32, 48, 76, 128]);
|
||||
|
||||
final urls = [
|
||||
['1', 'https://cdn.7tv.app/emote/603ca884faf3a00014dff0ab/1x'],
|
||||
['2', 'https://cdn.7tv.app/emote/603ca884faf3a00014dff0ab/2x'],
|
||||
['3', 'https://cdn.7tv.app/emote/603ca884faf3a00014dff0ab/3x'],
|
||||
['4', 'https://cdn.7tv.app/emote/603ca884faf3a00014dff0ab/4x'],
|
||||
];
|
||||
|
||||
expect(emote.urls, urls);
|
||||
});
|
||||
|
||||
test('channel emote should parse correctly', () {
|
||||
const sampleEmote = '''
|
||||
{
|
||||
"id": "603caea243b9e100141caf4f",
|
||||
"name": "TrollDespair",
|
||||
"owner": {
|
||||
"id": "603cae2496832ffa78c758cd",
|
||||
"twitch_id": "",
|
||||
"login": "swyfty_",
|
||||
"display_name": "swyfty_",
|
||||
"role": {
|
||||
"id": "000000000000000000000000",
|
||||
"name": "",
|
||||
"position": 0,
|
||||
"color": 0,
|
||||
"allowed": 523,
|
||||
"denied": 0,
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"visibility": 0,
|
||||
"visibility_simple": [
|
||||
|
||||
],
|
||||
"mime": "image/png",
|
||||
"status": 3,
|
||||
"tags": [
|
||||
|
||||
],
|
||||
"width": [
|
||||
32,
|
||||
48,
|
||||
76,
|
||||
128
|
||||
],
|
||||
"height": [
|
||||
32,
|
||||
48,
|
||||
76,
|
||||
128
|
||||
],
|
||||
"urls": [
|
||||
[
|
||||
"1",
|
||||
"https://cdn.7tv.app/emote/603caea243b9e100141caf4f/1x"
|
||||
],
|
||||
[
|
||||
"2",
|
||||
"https://cdn.7tv.app/emote/603caea243b9e100141caf4f/2x"
|
||||
],
|
||||
[
|
||||
"3",
|
||||
"https://cdn.7tv.app/emote/603caea243b9e100141caf4f/3x"
|
||||
],
|
||||
[
|
||||
"4",
|
||||
"https://cdn.7tv.app/emote/603caea243b9e100141caf4f/4x"
|
||||
]
|
||||
]
|
||||
}
|
||||
''';
|
||||
|
||||
final json = jsonDecode(sampleEmote);
|
||||
final emote = Emote7TV.fromJson(json);
|
||||
|
||||
expect(emote.name, 'TrollDespair');
|
||||
|
||||
expect(emote.visibilitySimple, []);
|
||||
expect(emote.width, [32, 48, 76, 128]);
|
||||
expect(emote.height, [32, 48, 76, 128]);
|
||||
|
||||
final urls = [
|
||||
['1', 'https://cdn.7tv.app/emote/603caea243b9e100141caf4f/1x'],
|
||||
['2', 'https://cdn.7tv.app/emote/603caea243b9e100141caf4f/2x'],
|
||||
['3', 'https://cdn.7tv.app/emote/603caea243b9e100141caf4f/3x'],
|
||||
['4', 'https://cdn.7tv.app/emote/603caea243b9e100141caf4f/4x'],
|
||||
];
|
||||
|
||||
expect(emote.urls, urls);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue