media

package
v0.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 10, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Overview

Package media provides media transcript retrieval via yt-dlp. It wraps yt-dlp for subtitle download, cleans VTT caption files to remove bloat from auto-generated subtitles, and stores transcripts durably as markdown files with YAML frontmatter.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CleanVTT

func CleanVTT(raw string) string

CleanVTT takes raw VTT subtitle content and produces clean, readable plain text. The pipeline strips headers, timing lines, HTML tags, and deduplicates rolling caption lines that auto-generated subtitles repeat across overlapping segments. The result is typically 25-30% the size of the raw VTT input.

func CleanVTTWithParagraphs

func CleanVTTWithParagraphs(raw string) string

CleanVTTWithParagraphs is like CleanVTT but inserts paragraph breaks (double newlines) when the timing gap between consecutive cues exceeds 2 seconds. This produces more readable output for long transcripts where topic shifts align with speaker pauses.

func FeedsDefinition added in v0.8.0

func FeedsDefinition() map[string]any

FeedsDefinition returns the JSON Schema for the media_feeds tool.

func FollowDefinition added in v0.8.0

func FollowDefinition() map[string]any

FollowDefinition returns the JSON Schema for the media_follow tool.

func SaveDefinition added in v0.8.0

func SaveDefinition() map[string]any

SaveDefinition returns the JSON Schema for the media_save_analysis tool.

func ToolDefinition

func ToolDefinition() map[string]any

ToolDefinition returns the JSON Schema parameters for the media_transcript tool.

func ToolHandler

func ToolHandler(c *Client) func(ctx context.Context, args map[string]any) (string, error)

ToolHandler returns a function compatible with the tools.Tool Handler signature. It wraps the Client for use as an agent tool.

func UnfollowDefinition added in v0.8.0

func UnfollowDefinition() map[string]any

UnfollowDefinition returns the JSON Schema for the media_unfollow tool.

Types

type AnalysisPage added in v0.8.0

type AnalysisPage struct {
	Title        string
	Channel      string
	URL          string
	Published    string // YYYY-MM-DD or empty (falls back to today)
	Topics       []string
	TrustZone    string
	QualityScore float64
	AnalyzedAt   time.Time
	Content      string // Markdown body written by the agent
}

AnalysisPage holds the data for a single media analysis markdown file. The Content field is the agent-generated analysis body; everything else becomes YAML frontmatter.

type AnalysisTools added in v0.8.0

type AnalysisTools struct {
	// contains filtered or unexported fields
}

AnalysisTools provides tool handlers for persisting media analysis to an Obsidian-compatible vault and tracking engagement.

func NewAnalysisTools added in v0.8.0

func NewAnalysisTools(
	state *opstate.Store,
	store *MediaStore,
	writer *VaultWriter,
	defaultOutputPath string,
	logger *slog.Logger,
) *AnalysisTools

NewAnalysisTools creates analysis tool handlers. The defaultOutputPath is used when a feed has no per-feed output_path configured.

func (*AnalysisTools) SaveHandler added in v0.8.0

func (at *AnalysisTools) SaveHandler() func(ctx context.Context, args map[string]any) (string, error)

SaveHandler returns the tool handler for media_save_analysis.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client retrieves and cleans media transcripts.

func New

func New(cfg Config, logger *slog.Logger) *Client

New creates a media transcript client. The yt-dlp binary path is resolved via Config.YtDlpPath or exec.LookPath.

func (*Client) GetTranscript

func (c *Client) GetTranscript(ctx context.Context, rawURL, language, focus string, detail DetailLevel) (*Result, error)

GetTranscript fetches the transcript for the given media URL. It prefers manual subtitles over auto-generated, and falls back to Whisper transcription via Ollama when no subtitles are available.

The focus parameter, when non-empty, guides summarization to emphasize content related to the topic. The detail parameter controls processing: DetailFull returns the raw transcript, DetailSummary produces a map-reduce summary, and DetailBrief produces an aggressive ~500-char summary.

func (*Client) SetSummarizer

func (c *Client) SetSummarizer(fn SummarizeFunc)

SetSummarizer configures the LLM summarization function used for map-reduce transcript processing. When nil, the client returns raw transcripts regardless of the requested detail level.

type Config

type Config struct {
	// YtDlpPath is the path to the yt-dlp binary. If empty, the binary
	// is located via exec.LookPath.
	YtDlpPath string

	// CookiesFile is an optional path to a Netscape-format cookie file
	// for accessing auth-required content.
	CookiesFile string

	// SubtitleLanguage is the preferred subtitle language code (default "en").
	SubtitleLanguage string

	// MaxTranscriptChars limits the transcript text returned in-context.
	// Longer transcripts are truncated. Default: 50000.
	MaxTranscriptChars int

	// WhisperModel is the Ollama model name for audio transcription
	// fallback when no subtitles are available (default "large-v3").
	WhisperModel string

	// TranscriptDir is the directory for durable transcript storage.
	// Each transcript is saved as a markdown file with YAML frontmatter.
	// If empty, transcripts are returned in-context only.
	TranscriptDir string

	// OllamaURL is the base URL for Ollama API calls (Whisper fallback).
	OllamaURL string
}

Config holds settings for the media transcript client.

type DetailLevel

type DetailLevel string

DetailLevel controls how much processing is applied to a transcript before it is returned to the caller.

const (
	// DetailFull returns the raw cleaned transcript without summarization.
	DetailFull DetailLevel = "full"

	// DetailSummary produces a map-reduce summary of ~2000-3000 characters.
	DetailSummary DetailLevel = "summary"

	// DetailBrief produces an aggressive summary of ~500 characters.
	DetailBrief DetailLevel = "brief"
)

type Engagement added in v0.8.0

type Engagement struct {
	ID            string
	EntryURL      string
	FeedID        string
	AnalysisPath  string
	AnalysisDepth string
	Topics        []string
	TrustZone     string
	QualityScore  float64
	Engaged       bool
	AnalyzedAt    time.Time
	SessionID     string
}

Engagement records a media analysis event — what was analyzed, where the output was saved, and metadata for future interest prediction.

type Feed added in v0.8.0

type Feed struct {
	Title   string
	Entries []FeedEntry
}

Feed represents a parsed RSS or Atom feed with its entries normalized into a common structure.

type FeedEntry added in v0.8.0

type FeedEntry struct {
	ID        string // <guid> (RSS) or <id> (Atom)
	Title     string
	Link      string
	Published time.Time
}

FeedEntry represents a single item in a feed.

type FeedPoller added in v0.8.0

type FeedPoller struct {
	// contains filtered or unexported fields
}

FeedPoller checks followed RSS/Atom feeds for new entries by comparing entry IDs against a persisted high-water mark. It follows the same pattern as email.Poller — infrastructure code called by the scheduler task executor.

func NewFeedPoller added in v0.8.0

func NewFeedPoller(state *opstate.Store, logger *slog.Logger) *FeedPoller

NewFeedPoller creates a feed poller that checks all followed feeds and tracks state in the provided opstate store.

func (*FeedPoller) CheckFeeds added in v0.8.0

func (p *FeedPoller) CheckFeeds(ctx context.Context) (string, error)

CheckFeeds checks all followed feeds for new entries. Returns a formatted wake message describing new content, or empty string if nothing new was found. Network errors are logged and skipped per-feed; a failure on one feed does not prevent checking others.

type FeedTools added in v0.8.0

type FeedTools struct {
	// contains filtered or unexported fields
}

FeedTools provides tool handlers and definitions for feed management.

func NewFeedTools added in v0.8.0

func NewFeedTools(state *opstate.Store, logger *slog.Logger, maxFeeds int) *FeedTools

NewFeedTools creates a FeedTools instance. The HTTP client is created internally via httpkit.

func (*FeedTools) FeedsHandler added in v0.8.0

func (ft *FeedTools) FeedsHandler() func(ctx context.Context, args map[string]any) (string, error)

FeedsHandler returns the tool handler for media_feeds.

func (*FeedTools) FollowHandler added in v0.8.0

func (ft *FeedTools) FollowHandler() func(ctx context.Context, args map[string]any) (string, error)

FollowHandler returns the tool handler for media_follow.

func (*FeedTools) UnfollowHandler added in v0.8.0

func (ft *FeedTools) UnfollowHandler() func(ctx context.Context, args map[string]any) (string, error)

UnfollowHandler returns the tool handler for media_unfollow.

type MediaStore added in v0.8.0

type MediaStore struct {
	// contains filtered or unexported fields
}

MediaStore persists media engagement records in SQLite. It tracks which content has been analyzed, enabling deduplication and (in future) interest prediction from engagement history.

func NewMediaStore added in v0.8.0

func NewMediaStore(dbPath string, logger *slog.Logger) (*MediaStore, error)

NewMediaStore opens (or creates) the engagement database at dbPath and runs migrations.

func (*MediaStore) Close added in v0.8.0

func (s *MediaStore) Close() error

Close closes the underlying database connection.

func (*MediaStore) HasBeenAnalyzed added in v0.8.0

func (s *MediaStore) HasBeenAnalyzed(ctx context.Context, entryURL string) (bool, error)

HasBeenAnalyzed reports whether the given entry URL has already been analyzed. Used to avoid duplicate analysis on re-polls.

func (*MediaStore) RecordAnalysis added in v0.8.0

func (s *MediaStore) RecordAnalysis(ctx context.Context, e *Engagement) error

RecordAnalysis inserts an engagement record. If the Engagement has no ID, a new UUIDv7 is generated. If AnalyzedAt is zero, the current time is used.

type Result

type Result struct {
	Title            string `json:"title"`
	Channel          string `json:"channel,omitempty"`
	Duration         string `json:"duration,omitempty"`
	UploadDate       string `json:"upload_date,omitempty"`
	Description      string `json:"description,omitempty"`
	Transcript       string `json:"transcript"`
	Source           string `json:"source"`
	ID               string `json:"id"`
	TranscriptPath   string `json:"transcript_path,omitempty"`
	Truncated        bool   `json:"truncated,omitempty"`
	Summarized       bool   `json:"summarized,omitempty"`
	DetailLevel      string `json:"detail_level,omitempty"`
	Focus            string `json:"focus,omitempty"`
	AnalysisGuidance string `json:"analysis_guidance,omitempty"`
}

Result holds the fetched transcript and associated metadata.

type SummarizeFunc

type SummarizeFunc func(ctx context.Context, prompt string) (string, error)

SummarizeFunc is a function that sends a prompt to an LLM and returns the response text. It decouples the media package from specific LLM and router implementations. The caller (typically cmd/thane/main.go) builds a closure that handles model selection and provider routing.

type VaultWriter added in v0.8.0

type VaultWriter struct {
	// contains filtered or unexported fields
}

VaultWriter writes structured analysis markdown files to an Obsidian-compatible vault directory. Each channel gets a subdirectory under Channels/ with an auto-maintained _channel.md index file.

func NewVaultWriter added in v0.8.0

func NewVaultWriter(logger *slog.Logger) *VaultWriter

NewVaultWriter creates a vault writer.

func (*VaultWriter) WriteAnalysis added in v0.8.0

func (w *VaultWriter) WriteAnalysis(outputPath string, page *AnalysisPage) (string, error)

WriteAnalysis writes an analysis page to the vault and updates the channel index. Returns the path of the written file (absolute if outputPath is absolute).

Directory structure:

{outputPath}/Channels/{channel-slug}/{date}-{title-slug}-{hash}.md
{outputPath}/Channels/{channel-slug}/_channel.md

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL