<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bruce</title>
    <description>The latest articles on DEV Community by Bruce (@the_bvl).</description>
    <link>https://dev.to/the_bvl</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3961526%2F2347575a-f584-4567-8157-a462d198bc09.jpg</url>
      <title>DEV Community: Bruce</title>
      <link>https://dev.to/the_bvl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/the_bvl"/>
    <language>en</language>
    <item>
      <title>How I built a local-first content processor with Python + Ollama</title>
      <dc:creator>Bruce</dc:creator>
      <pubDate>Wed, 03 Jun 2026 18:40:00 +0000</pubDate>
      <link>https://dev.to/the_bvl/how-i-built-a-local-first-content-processor-with-python-ollama-hp5</link>
      <guid>https://dev.to/the_bvl/how-i-built-a-local-first-content-processor-with-python-ollama-hp5</guid>
      <description>&lt;p&gt;I have 847 saved videos, articles, and podcasts I will never watch or read.&lt;/p&gt;

&lt;p&gt;For two years I told myself I'd get to them. I didn't. The pile just grew.&lt;/p&gt;

&lt;p&gt;The problem isn't discipline. It's that "save for later" is a one-way door — things go in but never come out as anything useful.&lt;/p&gt;

&lt;p&gt;So I built something to fix that. It's called DRIP.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;DRIP reads your saved content — YouTube videos, articles, podcasts — and converts each one into a structured Markdown note that drops directly into your Obsidian vault (or any folder you point it at).&lt;/p&gt;

&lt;p&gt;A saved recipe becomes a note with an ingredient table and steps. A podcast becomes guest, topics, and key takeaways. A long article becomes a summary with the core arguments pulled out.&lt;/p&gt;

&lt;p&gt;Everything runs locally on your machine. Nothing is uploaded anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; for orchestration and file handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ollama&lt;/strong&gt; for local LLM inference (llama3, but model-agnostic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;yt-dlp&lt;/strong&gt; for pulling transcripts from YouTube&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readability&lt;/strong&gt; for extracting clean article text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whisper&lt;/strong&gt; (optional) for podcasts without transcripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core loop is simple: fetch content → extract clean text → send to Ollama with a structured prompt → write Markdown to the output directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why local-first
&lt;/h2&gt;

&lt;p&gt;Two reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy.&lt;/strong&gt; My saved content includes private reading habits, half-formed research, and things I save "just in case." I didn't want to pipe all of that through a third-party API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost.&lt;/strong&gt; Running a cloud LLM on 847 items would get expensive fast. Running Ollama locally is free after setup.&lt;/p&gt;

&lt;p&gt;The tradeoff is speed — local inference is slower than API calls. But this is a background process; I kick it off and come back later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard parts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Transcript quality varies a lot.&lt;/strong&gt; YouTube auto-captions are often garbled, especially for technical talks. I added a cleaning pass before the LLM step to strip filler words and fix common OCR-style errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting reliable structured output from Ollama.&lt;/strong&gt; I needed clean Markdown with consistent heading levels — not JSON, not prose. This took more prompt iteration than expected. The fix was being extremely explicit in the system prompt with a concrete example of the exact output format.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrency limits.&lt;/strong&gt; Even locally, hammering Ollama with many concurrent requests degrades output quality. I settled on a small queue with a configurable concurrency limit (default: 3).&lt;/p&gt;

&lt;h2&gt;
  
  
  Current state
&lt;/h2&gt;

&lt;p&gt;It's working and I use it daily. I've processed about 600 items so far — the notes are genuinely useful and I've actually started surfacing things I saved years ago.&lt;/p&gt;

&lt;p&gt;I packaged it as a one-time purchase tool at &lt;a href="https://thebvl.com" rel="noopener noreferrer"&gt;thebvl.com&lt;/a&gt; — $39, runs on your machine, no subscription, you own it.&lt;/p&gt;

&lt;p&gt;Happy to answer questions about the implementation, particularly the Ollama prompt structure or the transcript pipeline. Both took longer to get right than I expected.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ollama</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>What do you do after you saved doomscrolling video's</title>
      <dc:creator>Bruce</dc:creator>
      <pubDate>Mon, 01 Jun 2026 06:13:53 +0000</pubDate>
      <link>https://dev.to/the_bvl/processing-your-saved-videos-without-keeping-a-single-one-21h6</link>
      <guid>https://dev.to/the_bvl/processing-your-saved-videos-without-keeping-a-single-one-21h6</guid>
      <description>&lt;p&gt;&lt;strong&gt;How I built DRIP&lt;/strong&gt; — a local-first tool that turns years of saved bookmarks, videos, and posts into usable documents, without leaving a media library behind.&lt;br&gt;
I had a problem most people have and never name: I am a compulsive saver. YouTube "Watch Later" hundreds deep. Browser bookmarks going back years. Saved posts across half a dozen platforms. All of it filed away with the quiet promise that I'd come back to it.&lt;/p&gt;

&lt;p&gt;I never came back to it. Almost nobody does. Saving had become a substitute for reading, not a precursor to it.&lt;br&gt;
So I built a tool to actually do something with all of it. This is a write-up of the decisions that turned out to matter more than I expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first decision:&lt;/strong&gt; don't keep the video&lt;br&gt;
The obvious way to process a saved video is to download it and run something over it. That's how most tools in this space work, and it has two costs people underestimate: storage (downloading hundreds of saved videos is gigabytes you'll never watch) and terms-of-service exposure (pulling full video files is against most platforms' rules).&lt;/p&gt;

&lt;p&gt;I didn't want either. And I realised I didn't need the video at all — for almost everything you save, the value is in the words, not the pixels. A tutorial, a recipe, a research talk: what you actually want is the transcript and the structure.&lt;/p&gt;

&lt;p&gt;So the rule became: no media library, ever.&lt;br&gt;
In practice that's two paths. For the majority of saved videos, there's already a caption or subtitle track — DRIP reads that directly, and no media touches your drive at all. For the minority with no captions, it downloads audio only to a temporary file, transcribes it locally, and deletes the file the moment transcription finishes. No video is ever stored, and nothing is left behind.&lt;/p&gt;

&lt;p&gt;This is not a compromise. It's the right architecture — it keeps the common case completely clean and handles the edge case honestly instead of pretending it doesn't exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The second decision:&lt;/strong&gt; keep it local, but give people the choice&lt;br&gt;
Once it's a text problem, you decide where the processing happens. The thing being processed is a deeply personal map of someone's interests over years. That's not data I want to hold, and it's not data most people want to upload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So DRIP is local-first:&lt;/strong&gt; it runs against a local model through Ollama or LM Studio by default, and your content never leaves the machine. But I didn't want to be dogmatic — if you'd rather use a hosted model, you plug in your own Claude, GPT, or Grok key and pay per run. There's also an "auto" mode that tries local first and falls back to cloud only if local fails.&lt;br&gt;
The principle: your AI, your cost, your call. No subscription, no key required to start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The third decision:&lt;/strong&gt; one document per item, routed by type&lt;br&gt;
A generic "summarise this" output is almost useless, because saved content isn't homogeneous. A recipe and a podcast want completely different things from a processor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So DRIP classifies each saved item, then generates one focused document in the format that content actually calls for:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A recipe becomes an ingredient table (imperial + metric) with a numbered method.&lt;br&gt;
A workout becomes an exercise table with sets, reps, and rest.&lt;br&gt;
A podcast becomes guest, host, key topics, quotes, and takeaways.&lt;br&gt;
A business/strategy piece becomes sections with action items and source attribution.&lt;br&gt;
A tech/learning item becomes a step-by-step guide with references.&lt;/p&gt;

&lt;p&gt;One PDF per item, sorted into a folder for its topic — so a Workouts file is about one workout, not five unrelated things jammed together. Each PDF comes with a matching Markdown file, which drops straight into Obsidian and similar tools with zero friction.&lt;br&gt;
The part I didn't expect to build: a learning loop&lt;br&gt;
The classifier was good, but generic — it didn't know my patterns. So I added a feedback loop, loosely inspired by Karpathy's idea of generating, testing, and keeping what works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's deliberately low-effort:&lt;/strong&gt; after a run, you go through the day's PDFs and mark each keep or trash — about 30 seconds. Every trashed item becomes a signal: the system derives a one-line rule from the mistake and stores it locally. Future runs inject those learned rules into the classifier as examples. There's also an experiment mode that generates new rules, tests them against examples you've already verified, and commits only the winners.&lt;/p&gt;

&lt;p&gt;After a couple of weeks of light feedback, it classifies my content noticeably better than the generic prompt did — because it has actually seen how I sort things. All of that learning lives in a local folder. None of it is uploaded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it is now:&lt;/strong&gt;&lt;br&gt;
Built in Python. Runs on macOS, Windows, and Linux. Reads from YouTube, X, Instagram, Facebook, TikTok, LinkedIn, and browser bookmarks. Processing is local by default, cloud if you choose. A background scheduler runs it each morning so the documents are just there when you wake up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I packaged it as DRIP if you want to try it:&lt;/strong&gt; thebvl.gumroad.com/l/rvwbcv — one-time, no subscription.&lt;br&gt;
But mostly I wanted to write up the captions-first / throwaway-audio approach, because it's the decision I'm happiest with and the one I haven't seen others take. &lt;/p&gt;

&lt;p&gt;Happy to answer anything.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>privacy</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
