diff options
Diffstat (limited to 'internal/reader/processor/processor.go')
-rw-r--r-- | internal/reader/processor/processor.go | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/internal/reader/processor/processor.go b/internal/reader/processor/processor.go index fa3e53be..c5d368e4 100644 --- a/internal/reader/processor/processor.go +++ b/internal/reader/processor/processor.go @@ -29,6 +29,7 @@ import ( var ( youtubeRegex = regexp.MustCompile(`youtube\.com/watch\?v=(.*)$`) + nebulaRegex = regexp.MustCompile(`^https://nebula\.tv`) odyseeRegex = regexp.MustCompile(`^https://odysee\.com`) iso8601Regex = regexp.MustCompile(`^P((?P<year>\d+)Y)?((?P<month>\d+)M)?((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$`) customReplaceRuleRegex = regexp.MustCompile(`rewrite\("(.*)"\|"(.*)"\)`) @@ -277,6 +278,25 @@ func updateEntryReadingTime(store *storage.Storage, feed *model.Feed, entry *mod } } + if shouldFetchNebulaWatchTime(entry) { + if entryIsNew { + watchTime, err := fetchNebulaWatchTime(entry.URL) + if err != nil { + slog.Warn("Unable to fetch Nebula watch time", + slog.Int64("user_id", user.ID), + slog.Int64("entry_id", entry.ID), + slog.String("entry_url", entry.URL), + slog.Int64("feed_id", feed.ID), + slog.String("feed_url", feed.FeedURL), + slog.Any("error", err), + ) + } + entry.ReadingTime = watchTime + } else { + entry.ReadingTime = store.GetReadTime(feed.ID, entry.Hash) + } + } + if shouldFetchOdyseeWatchTime(entry) { if entryIsNew { watchTime, err := fetchOdyseeWatchTime(entry.URL) @@ -311,6 +331,14 @@ func shouldFetchYouTubeWatchTime(entry *model.Entry) bool { return urlMatchesYouTubePattern } +func shouldFetchNebulaWatchTime(entry *model.Entry) bool { + if !config.Opts.FetchNebulaWatchTime() { + return false + } + matches := nebulaRegex.FindStringSubmatch(entry.URL) + return matches != nil +} + func shouldFetchOdyseeWatchTime(entry *model.Entry) bool { if !config.Opts.FetchOdyseeWatchTime() { return false @@ -350,6 +378,38 @@ func fetchYouTubeWatchTime(websiteURL string) (int, error) { return int(dur.Minutes()), nil } +func fetchNebulaWatchTime(websiteURL string) (int, error) { + requestBuilder := fetcher.NewRequestBuilder() + requestBuilder.WithTimeout(config.Opts.HTTPClientTimeout()) + requestBuilder.WithProxy(config.Opts.HTTPClientProxy()) + + responseHandler := fetcher.NewResponseHandler(requestBuilder.ExecuteRequest(websiteURL)) + defer responseHandler.Close() + + if localizedError := responseHandler.LocalizedError(); localizedError != nil { + slog.Warn("Unable to fetch Nebula watch time", slog.String("website_url", websiteURL), slog.Any("error", localizedError.Error())) + return 0, localizedError.Error() + } + + doc, docErr := goquery.NewDocumentFromReader(responseHandler.Body(config.Opts.HTTPClientMaxBodySize())) + if docErr != nil { + return 0, docErr + } + + durs, exists := doc.Find(`meta[property="video:duration"]`).First().Attr("content") + // durs contains video watch time in seconds + if !exists { + return 0, errors.New("duration has not found") + } + + dur, err := strconv.ParseInt(durs, 10, 64) + if err != nil { + return 0, fmt.Errorf("unable to parse duration %s: %v", durs, err) + } + + return int(dur / 60), nil +} + func fetchOdyseeWatchTime(websiteURL string) (int, error) { requestBuilder := fetcher.NewRequestBuilder() requestBuilder.WithTimeout(config.Opts.HTTPClientTimeout()) |