relatica/lib/utils/opengraph_preview_grabber.dart

73 wiersze
2.2 KiB
Dart

import 'dart:convert';
import 'package:html/parser.dart';
import 'package:http/http.dart' as http;
import 'package:result_monad/result_monad.dart';
import '../models/exec_error.dart';
import '../models/link_preview_data.dart';
const ogTitleKey = 'og:title';
const ogDescriptionKey = 'og:description';
const ogSiteNameKey = 'og:site_name';
const ogImageKey = 'og:image';
FutureResult<LinkPreviewData, ExecError> getLinkPreview(String url) async {
final result = await _getOpenGraphData(url);
return result.andThenSuccess((ogData) {
final title = ogData.getValue(ogTitleKey);
final description = ogData.getValue(ogDescriptionKey);
final siteName = ogData.getValue(ogSiteNameKey);
final availableImageUrls = ogData.getValues(ogImageKey);
final selectedImageUrl =
availableImageUrls.isEmpty ? '' : availableImageUrls.first;
return LinkPreviewData(
link: url,
title: title,
description: description,
siteName: siteName,
availableImageUrls: availableImageUrls,
selectedImageUrl: selectedImageUrl,
);
}).execErrorCast();
}
FutureResult<List<MapEntry<String, String>>, dynamic> _getOpenGraphData(
String url) async {
return runCatchingAsync<List<MapEntry<String, String>>>(() async {
final response = await http.get(Uri.parse(url));
if (response.statusCode != 200) {
return buildErrorResult(
type: ErrorType.serverError,
message: 'Error getting link preview: ${response.statusCode}',
);
}
final rawHtml = utf8.decode(response.bodyBytes);
final htmlDoc = parse(rawHtml);
final openGraphTags = htmlDoc.head
?.querySelectorAll("[property*='og:']")
.map((p) {
final key = p.attributes['property'] ?? '';
final value = p.attributes['content'] ?? '';
return MapEntry(key, value);
})
.where((e) => e.key.isNotEmpty)
.toList();
return Result.ok(openGraphTags ?? []);
});
}
extension OpenGraphFinders on List<MapEntry<String, String>> {
String getValue(String key) {
return firstWhere(
(e) => e.key == key,
orElse: () => const MapEntry('', ''),
).value;
}
List<String> getValues(String key) {
return where((e) => e.key == key).map((e) => e.value).toList();
}
}