<?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>Forem: Giuseppe Carlà</title>
    <description>The latest articles on Forem by Giuseppe Carlà (@scibilo).</description>
    <link>https://forem.com/scibilo</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3856138%2F9ae1dbfb-a981-467c-b4cc-54ab8107e4de.jpg</url>
      <title>Forem: Giuseppe Carlà</title>
      <link>https://forem.com/scibilo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/scibilo"/>
    <language>en</language>
    <item>
      <title>I built a free, local video transcription tool, because I didn't want to pay $10/hour or upload my files to a stranger's server</title>
      <dc:creator>Giuseppe Carlà</dc:creator>
      <pubDate>Sat, 09 May 2026 16:10:14 +0000</pubDate>
      <link>https://forem.com/scibilo/i-built-a-free-local-video-transcription-tool-because-i-didnt-want-to-pay-10hour-or-upload-my-7e9</link>
      <guid>https://forem.com/scibilo/i-built-a-free-local-video-transcription-tool-because-i-didnt-want-to-pay-10hour-or-upload-my-7e9</guid>
      <description>&lt;p&gt;Every time I needed to transcribe a video at work, I hit the same wall: &lt;br&gt;
the good tools cost money per minute, and the free ones upload your files &lt;br&gt;
to a remote server. Neither was acceptable for work content.&lt;/p&gt;

&lt;p&gt;So I built "Pitchfall" - a local transcription tool that runs entirely &lt;br&gt;
on your own machine.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Upload any video or audio file (or paste a YouTube URL), and Pitchfall:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transcribes it locally using &lt;a href="https://github.com/SYSTRAN/faster-whisper" rel="noopener noreferrer"&gt;faster-whisper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Shows a real-time progress bar with the current segment being recognized&lt;/li&gt;
&lt;li&gt;Syncs the transcript to the video — click any line to jump to that moment&lt;/li&gt;
&lt;li&gt;Exports as &lt;code&gt;.txt&lt;/code&gt; or &lt;code&gt;.srt&lt;/code&gt; subtitle file&lt;/li&gt;
&lt;li&gt;Optionally translates into 10 languages via OpenRouter free models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;No API key needed for transcription. No account. No cloud.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpn2h633pichpfpw3j5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpn2h633pichpfpw3j5u.png" alt="Pitchfall result screen — video player synced to transcript segments" width="800" height="703"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;p&gt;faster-whisper (local Whisper model)&lt;br&gt;
│&lt;br&gt;
▼ streaming SSE&lt;br&gt;
FastAPI (Python)&lt;br&gt;
│&lt;br&gt;
▼&lt;br&gt;
Next.js 16 + Tailwind CSS 4&lt;/p&gt;

&lt;p&gt;The backend streams transcription progress via Server-Sent Events — &lt;br&gt;
each segment gets sent to the frontend as it's recognized, so you see &lt;br&gt;
the text appear in real time rather than waiting for the whole file &lt;br&gt;
to finish.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why local matters more than I expected
&lt;/h2&gt;

&lt;p&gt;When I started this I thought "local vs cloud" was mainly a cost issue. &lt;br&gt;
It turned out to be a correctness issue too.&lt;/p&gt;

&lt;p&gt;Faster-whisper on CPU with the &lt;code&gt;small&lt;/code&gt; model is genuinely fast enough &lt;br&gt;
for practical use — a 5-minute video takes about 2-3 minutes on a &lt;br&gt;
mid-range laptop. More importantly, the transcript never touches a &lt;br&gt;
third-party server. For work content, legal recordings, or anything &lt;br&gt;
sensitive, that distinction matters.&lt;/p&gt;
&lt;h2&gt;
  
  
  The part that took longest: memory management
&lt;/h2&gt;

&lt;p&gt;The original version leaked memory on every transcription. The culprit &lt;br&gt;
was &lt;code&gt;URL.createObjectURL()&lt;/code&gt; — a blob URL that keeps the entire video &lt;br&gt;
file in RAM. It was never revoked, so after 3-4 sessions the browser &lt;br&gt;
was holding multiple full videos in memory.&lt;/p&gt;

&lt;p&gt;The fix is a single line, but finding it required profiling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before reset, always revoke the previous blob URL&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isBlobUrl&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;mediaUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mediaUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend had a similar problem: temp files from crashed SSE &lt;br&gt;
connections weren't getting cleaned up. I solved it with a FastAPI &lt;br&gt;
&lt;code&gt;lifespan&lt;/code&gt; context manager that wipes &lt;code&gt;.tmp/&lt;/code&gt; on startup and shutdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm less happy with
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Translation reliability.&lt;/strong&gt; The free OpenRouter models have rate limits &lt;br&gt;
and occasionally go offline. Pitchfall tries 5 models in order with &lt;br&gt;
automatic fallback, but if they're all saturated you get a 503. For &lt;br&gt;
casual use it's fine; for production you'd want a paid model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No GPU support in the Docker image.&lt;/strong&gt; The Dockerfile uses CPU-only &lt;br&gt;
inference. Adding CUDA support means a much heavier image and &lt;br&gt;
nvidia-container-toolkit as a prerequisite — I left it out for now &lt;br&gt;
to keep the setup simple.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YouTube sync limitation.&lt;/strong&gt; For uploaded files, clicking a transcript &lt;br&gt;
segment seeks the video instantly. For YouTube URLs, the video loads &lt;br&gt;
as an iframe embed — the YouTube API doesn't allow external seek &lt;br&gt;
control without a more complex integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/scibilo/pitchfall" rel="noopener noreferrer"&gt;https://github.com/scibilo/pitchfall&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Manual setup takes about 5 minutes if you have Python 3.10+ and Node.js &lt;br&gt;
18+ installed. Docker setup is one command.&lt;/p&gt;

&lt;p&gt;The only hard dependency that people often don't have: &lt;strong&gt;ffmpeg&lt;/strong&gt;. &lt;br&gt;
&lt;code&gt;sudo apt install ffmpeg&lt;/code&gt; on Ubuntu, &lt;code&gt;brew install ffmpeg&lt;/code&gt; on Mac.&lt;/p&gt;




&lt;p&gt;I'm curious: do you handle transcription in any of your projects? &lt;br&gt;
What's your current setup — local model, cloud API, or something else?&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>fastapi</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Telepage – I built a self-hosted PHP app that turns any Telegram channel into a website</title>
      <dc:creator>Giuseppe Carlà</dc:creator>
      <pubDate>Wed, 01 Apr 2026 17:55:48 +0000</pubDate>
      <link>https://forem.com/scibilo/telepage-i-built-a-self-hosted-php-app-that-turns-any-telegram-channel-into-a-website-2i2b</link>
      <guid>https://forem.com/scibilo/telepage-i-built-a-self-hosted-php-app-that-turns-any-telegram-channel-into-a-website-2i2b</guid>
      <description>&lt;p&gt;If you run a Telegram channel, you already know the problem: your content is invisible to Google, there's no search, old posts are buried, and readers need the app just to see your work.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Telepage&lt;/strong&gt; to fix that.&lt;/p&gt;

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

&lt;p&gt;Telepage connects to your Telegram channel via a bot webhook and turns every post into a searchable web card — automatically, in real time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0zcxkaob0c3c5sglju1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0zcxkaob0c3c5sglju1.png" alt="Recipes site with 952 posts, colored tags and AI summaries" width="800" height="1309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every hashtag in your Telegram posts becomes a colored navigation filter. Every link gets its Open Graph metadata scraped. Every post gets an AI-generated summary and tags if you connect a Gemini key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6d7bufxigjavv8og5swp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6d7bufxigjavv8og5swp.png" alt="Science/news channel" width="800" height="1076"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Pure PHP 8.1, SQLite with WAL mode, vanilla JS. No frameworks, no Composer, no build step, no MySQL. It runs on standard shared hosting — I tested it on Aruba (a very restrictive Italian host).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Telegram channel
      │
      ▼ webhook (instant)
PHP 8.1 + SQLite
      │
      ▼
Your website — card grid, search, tag filters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interesting technical decisions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Session isolation per installation&lt;/strong&gt;&lt;br&gt;
Multiple Telepage sites on the same domain (e.g. &lt;code&gt;site.com/news/&lt;/code&gt; and &lt;code&gt;site.com/recipes/&lt;/code&gt;) need completely separate admin sessions. I solved this with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tp_'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TELEPAGE_ROOT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nb"&gt;session_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each installation path produces a unique session name — no shared cookies, no cross-login.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;History Scanner&lt;/strong&gt;&lt;br&gt;
Telegram's Bot API has no "get all past messages" endpoint. To import historical content I use the &lt;code&gt;forwardMessage&lt;/code&gt; trick: forward each message ID from the channel to itself, read the content, then immediately delete the forwarded copy. It scans backwards from the most recent ID, skipping gaps from deleted messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI integration&lt;/strong&gt;&lt;br&gt;
Optional Google Gemini integration auto-tags and summarizes every post. The models available via the free tier change frequently — I built a cascade fallback that tries multiple model names in order and logs exactly which one succeeded.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like in production
&lt;/h2&gt;

&lt;p&gt;I've been running it on two test channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A science/news channel: 23 posts, tagged by topic&lt;/li&gt;
&lt;li&gt;A recipes channel: 952 posts, fully tagged and summarized by AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The recipes site went from zero to 952 searchable, tagged posts in a few hours using the History Scanner.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm less happy with
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI calls are currently synchronous in the admin panel — for large archives you click "Process AI" repeatedly. A proper background queue would be better.&lt;/li&gt;
&lt;li&gt;The History Scanner requires manual ID tuning when posts are missing — not ideal for non-technical users.&lt;/li&gt;
&lt;li&gt;No pagination on the install wizard, though the 5-step flow works fine in practice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/scibilo/telepage" rel="noopener noreferrer"&gt;github.com/scibilo/telepage&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's MIT licensed. Works on any PHP 8.1+ shared hosting with HTTPS. The install wizard takes about 5 minutes.&lt;/p&gt;

&lt;p&gt;Feedback welcome — this is the first public release and I'm actively improving it.&lt;/p&gt;

</description>
      <category>php</category>
      <category>opensource</category>
      <category>telegram</category>
      <category>selfhosted</category>
    </item>
  </channel>
</rss>
