kopia lustrzana https://gitlab.com/mysocialportal/relatica
83 wiersze
2.7 KiB
Dart
83 wiersze
2.7 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:html/parser.dart';
|
|
import 'package:result_monad/result_monad.dart';
|
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
|
|
import '../../models/exec_error.dart';
|
|
import '../../models/link_preview_data.dart';
|
|
import '../rp_provider_extension.dart';
|
|
import 'network_services.dart';
|
|
|
|
part 'link_preview_services.g.dart';
|
|
|
|
const ogTitleKey = 'og:title';
|
|
const ogDescriptionKey = 'og:description';
|
|
const ogSiteNameKey = 'og:site_name';
|
|
const ogImageKey = 'og:image';
|
|
|
|
@riverpod
|
|
Future<Result<LinkPreviewData, ExecError>> linkPreview(
|
|
Ref ref, String url) async {
|
|
return await ref.read(_openGraphDataProvider(url).future).transform((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,
|
|
);
|
|
}).execErrorCastAsync();
|
|
}
|
|
|
|
@riverpod
|
|
Future<Result<List<MapEntry<String, String>>, ExecError>> _openGraphData(
|
|
Ref ref, String url) async {
|
|
ref.cacheFor(const Duration(minutes: 5));
|
|
final headers = ref.watch(userAgentHeaderProvider);
|
|
final request = NetworkRequest(Uri.parse(url), headers: headers);
|
|
return await ref
|
|
.read(httpGetRawBytesProvider(request).future)
|
|
.transform((bodyBytes) {
|
|
final rawHtml = utf8.decode(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 openGraphTags ?? [];
|
|
}).mapError((error) {
|
|
final type = error is ExecError ? error.type : ErrorType.serverError;
|
|
final message = error is ExecError
|
|
? 'Error getting link preview: ${error.message}'
|
|
: 'Error getting link preview';
|
|
return ExecError(type: type, message: message);
|
|
});
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|