background fetch running

This commit is contained in:
Fabian Freund
2025-03-05 14:27:13 +01:00
parent 20bdc4df3e
commit 70d1c1b50e
62 changed files with 898 additions and 173 deletions
@@ -1,5 +1,4 @@
import 'package:collection/collection.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/web_feed/data/database/database.dart';
import 'package:lensai/features/web_feed/data/models/feed_article.dart';
@@ -79,28 +78,11 @@ class FeedParser {
}
}
Future<List<FeedArticle>> readArticles() async {
List<FeedArticle> readArticles() {
final fetchDate = DateTime.now();
switch (_feed) {
case final Rss1Feed feed:
final processedContents =
await GeckoBrowserExtensionService.turndownHtml(
feed.items.map((item) => item.content?.value ?? '').toList(),
);
final processedSummaries =
await GeckoBrowserExtensionService.turndownHtml(
feed.items
.map(
(item) =>
item.description.whenNotEmpty ??
item.dc?.description ??
'',
)
.toList(),
);
return feed.items.mapIndexed((i, item) {
final title = item.title.whenNotEmpty ?? item.dc?.title.whenNotEmpty;
@@ -119,32 +101,19 @@ class FeedParser {
authors: item.dc?.creator.whenNotEmpty.mapNotNull(
(creator) => [FeedAuthor(name: creator)],
),
summaryPlain: processedSummaries[i].plain.whenNotEmpty,
summaryMarkdown: processedSummaries[i].markdown.whenNotEmpty,
links: link.mapNotNull((link) => [FeedLink(uri: link)]),
summaryHtml:
item.description.whenNotEmpty ??
item.dc?.description.whenNotEmpty,
links: link.mapNotNull(
(link) => [
FeedLink(uri: link, relation: FeedLinkRelation.alternate),
],
),
tags: item.dc?.toFeedCategories(),
contentPlain: processedContents[i].plain.trim().whenNotEmpty,
contentMarkdown: processedContents[i].markdown?.trim().whenNotEmpty,
contentHtml: item.content?.value.whenNotEmpty,
);
}).toList();
case final RssFeed feed:
final processedContents =
await GeckoBrowserExtensionService.turndownHtml(
feed.items.map((item) => item.content?.value ?? '').toList(),
);
final processedSummaries =
await GeckoBrowserExtensionService.turndownHtml(
feed.items
.map(
(item) =>
item.description.whenNotEmpty ??
item.dc?.description ??
'',
)
.toList(),
);
return feed.items.mapIndexed((i, item) {
final title = item.title.whenNotEmpty ?? item.dc?.title.whenNotEmpty;
@@ -173,28 +142,22 @@ class FeedParser {
authors: author.mapNotNull(
(creator) => [FeedAuthor(name: creator)],
),
summaryPlain: processedSummaries[i].plain.whenNotEmpty,
summaryMarkdown: processedSummaries[i].markdown.whenNotEmpty,
links: link.mapNotNull((link) => [FeedLink(uri: link)]),
summaryHtml:
item.description.whenNotEmpty ??
item.dc?.description.whenNotEmpty,
links: link.mapNotNull(
(link) => [
FeedLink(uri: link, relation: FeedLinkRelation.alternate),
],
),
tags:
categories.isNotEmpty
? categories
: item.dc?.toFeedCategories(),
contentPlain: processedContents[i].plain.trim().whenNotEmpty,
contentMarkdown: processedContents[i].markdown?.trim().whenNotEmpty,
contentHtml: item.content?.value.whenNotEmpty,
);
}).toList();
case final AtomFeed feed:
final processedContents =
await GeckoBrowserExtensionService.turndownHtml(
feed.items.map((item) => item.content ?? '').toList(),
);
final processedSummaries =
await GeckoBrowserExtensionService.turndownHtml(
feed.items.map((item) => item.summary ?? '').toList(),
);
final feedLink = feed.links.toFeedLinks().getRelation(
FeedLinkRelation.self,
);
@@ -218,16 +181,14 @@ class FeedParser {
id: uniqueId,
feedId: url,
title: item.title.whenNotEmpty,
summaryPlain: processedSummaries[i].plain.whenNotEmpty,
summaryMarkdown: processedSummaries[i].markdown.whenNotEmpty,
summaryHtml: item.summary.whenNotEmpty,
links: itemLinks,
authors: authors.isNotEmpty ? authors : null,
tags: tags,
fetched: fetchDate,
created: published,
updated: updated,
contentPlain: processedContents[i].plain.trim().whenNotEmpty,
contentMarkdown: processedContents[i].markdown?.trim().whenNotEmpty,
contentHtml: item.content.whenNotEmpty,
);
}).toList();
default:
@@ -0,0 +1,35 @@
import 'package:background_fetch/background_fetch.dart';
import 'package:lensai/core/error_observer.dart';
import 'package:lensai/core/logger.dart';
import 'package:lensai/features/web_feed/presentation/controllers/fetch_articles.dart';
import 'package:riverpod/riverpod.dart';
@pragma('vm:entry-point')
Future<void> backgroundFetch(HeadlessTask task) async {
final taskId = task.taskId;
final isTimeout = task.timeout;
if (isTimeout) {
// This task has exceeded its allowed running-time.
// You must stop what you're doing and immediately .finish(taskId)
logger.e("[BackgroundFetch] Headless task timed-out: $taskId");
await BackgroundFetch.finish(taskId);
return;
}
final ref = ProviderContainer(observers: const [ErrorObserver()]);
try {
await ref.read(fetchArticlesControllerProvider.notifier).fetchAllArticles();
logger.i('Fetched articles in background');
} catch (e, s) {
logger.e('Failed fetching articles', error: e, stackTrace: s);
} finally {
ref.dispose();
// Give everything a bit of time to properly dispose
await Future.delayed(const Duration(seconds: 1));
await BackgroundFetch.finish(taskId);
}
}