intermediate

This commit is contained in:
Fabian Freund
2025-01-07 00:06:31 +01:00
parent ea0880b97a
commit 4a2191b01d
84 changed files with 2655 additions and 1383 deletions
+94 -46
View File
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:ui';
import 'package:exceptions/exceptions.dart';
@@ -10,6 +11,7 @@ import 'package:http/http.dart' as http;
import 'package:lensai/core/http_error_handler.dart';
import 'package:lensai/data/models/web_page_info.dart';
import 'package:lensai/features/geckoview/domain/entities/browser_icon.dart';
import 'package:lensai/features/user/domain/repositories/cache.dart';
import 'package:lensai/utils/lru_cache.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -56,15 +58,19 @@ class GenericWebsiteService extends _$GenericWebsiteService {
late http.Client _client;
final LRUCache<String, BrowserIcon> _iconCache;
//Global icon cache
late CacheRepository _cacheRepository;
//Local decoded icon cache
final LRUCache<String, BrowserIcon> _browserIconCache;
GenericWebsiteService()
: _iconsService = GeckoIconService(),
_iconCache = LRUCache(50);
_browserIconCache = LRUCache(50);
@override
void build() {
_client = http.Client();
_cacheRepository = ref.watch(cacheRepositoryProvider.notifier);
}
static Map<String, dynamic> _serializeResource(Resource resource) {
@@ -89,7 +95,14 @@ class GenericWebsiteService extends _$GenericWebsiteService {
);
}
static List<Resource> _extractIcons(Document document) {
static Uri _resolveRelativeUri(Uri baseUri, Uri uri) {
if (!uri.isAbsolute) {
return baseUri.resolveUri(uri);
}
return uri;
}
static List<Resource> _extractIcons(Uri baseUrl, Document document) {
final List<Resource> icons = [];
void collectLinkIcons(String rel) {
@@ -99,15 +112,17 @@ class GenericWebsiteService extends _$GenericWebsiteService {
final type = _typeMap[rel];
final mimeType = link.attributes['type'];
if (href != null && type != null) {
icons.add(
Resource(
url: href,
type: type,
sizes: sizesToList(link.attributes['sizes']).toList(),
mimeType: (mimeType?.isNotEmpty ?? true) ? null : mimeType,
maskable: false,
),
);
if (Uri.tryParse(href) case final Uri url) {
icons.add(
Resource(
url: _resolveRelativeUri(baseUrl, url).toString(),
type: type,
sizes: sizesToList(link.attributes['sizes']).toList(),
mimeType: (mimeType?.isNotEmpty ?? true) ? null : mimeType,
maskable: false,
),
);
}
}
}
}
@@ -118,14 +133,16 @@ class GenericWebsiteService extends _$GenericWebsiteService {
final content = meta.attributes['content'];
final type = _typeMap[property];
if (content != null && type != null) {
icons.add(
Resource(
type: type,
url: content,
sizes: [],
maskable: false,
),
);
if (Uri.tryParse(content) case final Uri url) {
icons.add(
Resource(
type: type,
url: _resolveRelativeUri(baseUrl, url).toString(),
sizes: [],
maskable: false,
),
);
}
}
}
}
@@ -136,14 +153,16 @@ class GenericWebsiteService extends _$GenericWebsiteService {
final content = meta.attributes['content'];
final type = _typeMap[name];
if (content != null && type != null) {
icons.add(
Resource(
type: type,
url: content,
sizes: [],
maskable: false,
),
);
if (Uri.tryParse(content) case final Uri url) {
icons.add(
Resource(
type: type,
url: _resolveRelativeUri(baseUrl, url).toString(),
sizes: [],
maskable: false,
),
);
}
}
}
}
@@ -166,7 +185,7 @@ class GenericWebsiteService extends _$GenericWebsiteService {
return icons;
}
Future<Result<WebPageInfo>> getInfo(Uri url) async {
Future<Result<WebPageInfo>> fetchPageInfo(Uri url) async {
return Result.fromAsync(
() async {
final response =
@@ -175,23 +194,28 @@ class GenericWebsiteService extends _$GenericWebsiteService {
final result = await compute(
(args) async {
final document = html_parser.parse(args[0]);
final baseUri = Uri.parse(args[1]);
final title = document.querySelector('title')?.text;
final resources = _extractIcons(document);
final resources = _extractIcons(baseUri, document);
return {
'title': title,
'resources': resources.map(_serializeResource).toList(),
};
},
[response.body],
[response.body, url.toString()],
);
final resources = (result['resources']! as List<Map<String, dynamic>>)
.map(_deserializeResource)
.toList();
final favicon = await getUrlFavicon(url: url, resources: resources);
final favicon = await getCachedIcon(url) ??
await loadIcon(
url: url,
resources: resources,
);
return WebPageInfo(
url: url,
@@ -203,26 +227,50 @@ class GenericWebsiteService extends _$GenericWebsiteService {
);
}
Future<BrowserIcon> getUrlFavicon({
required Uri url,
List<Resource> resources = const [],
}) async {
final cached = _iconCache.get(url.host);
if (cached == null) {
final result =
await _iconsService.loadIcon(url: url, resources: resources);
Future<BrowserIcon?> getCachedIcon(Uri url) async {
final cachedBrowserIcon = _browserIconCache.get(url.origin);
if (cachedBrowserIcon != null) {
return cachedBrowserIcon;
}
return _iconCache.set(
url.host,
final cachedIcon = await _cacheRepository.getCachedIcon(url.origin);
if (cachedIcon != null) {
return _browserIconCache.set(
url.origin,
await BrowserIcon.fromBytes(
result.image,
dominantColor: (result.color != null) ? Color(result.color!) : null,
source: result.source,
cachedIcon,
dominantColor: null,
source: IconSource.disk,
),
);
}
return cached;
return null;
}
Future<BrowserIcon> loadIcon({
required Uri url,
required List<Resource> resources,
bool isPrivate = false,
bool waitOnNetworkLoad = true,
}) async {
final result = await _iconsService.loadIcon(
url: url,
resources: resources,
isPrivate: isPrivate,
waitOnNetworkLoad: waitOnNetworkLoad,
);
// unawaited(_cacheRepository.cacheIcon(url, result.image));
return _browserIconCache.set(
url.origin,
await BrowserIcon.fromBytes(
result.image,
dominantColor: (result.color != null) ? Color(result.color!) : null,
source: result.source,
),
);
}
// Future<Uri?> tryUpgradeToHttps(Uri httpUri) async {
@@ -7,7 +7,7 @@ part of 'generic_website.dart';
// **************************************************************************
String _$genericWebsiteServiceHash() =>
r'7dea42d6914b0f06250a96bb425ba10a331ef0d3';
r'8eab8ab1448d4826dc0cda0ff6b47143735ba176';
/// See also [GenericWebsiteService].
@ProviderFor(GenericWebsiteService)