<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>КББ</title><generator>teletype.in</generator><description><![CDATA[Ёмко об IT, фото, фильмах, книгах, музыке, играх и гик-культуре]]></description><image><url>https://img4.teletype.in/files/72/71/727171d3-ff31-4618-862a-e82870ead774.png</url><title>КББ</title><link>https://teletype.in/@beaverclan</link></image><link>https://teletype.in/@beaverclan?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/beaverclan?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/beaverclan?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Mon, 03 Aug 2026 22:58:57 GMT</pubDate><lastBuildDate>Mon, 03 Aug 2026 22:58:57 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@beaverclan/youtubemcp</guid><link>https://teletype.in/@beaverclan/youtubemcp?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/youtubemcp?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>YouTube MCP: скачиваем видео и вшиваем субтитры</title><pubDate>Thu, 02 Jul 2026 19:15:00 GMT</pubDate><description><![CDATA[<img src="https://blog.tatarinovms.space/images/posts/youtubemcp/logo.webp"></img>Я часто скачиваю видео с ютуба, чтобы перекинуть в телеграм канал. В телеграме видео можно глянуть и без звука, да и принято щас так, как в рилсах кароче. Да и ролики бывают не на славянских языках, вшивать их каждый раз руками это эт лень.]]></description><content:encoded><![CDATA[
  <figure id="hPPt" class="m_original">
    <img src="https://blog.tatarinovms.space/images/posts/youtubemcp/logo.webp" width="800" />
  </figure>
  <h2 id="зачем-это-нужно">Зачем это нужно</h2>
  <p id="Mevx">Я часто скачиваю видео с ютуба, чтобы перекинуть в телеграм канал. В телеграме видео можно глянуть и без звука, да и принято щас так, как в рилсах кароче. Да и ролики бывают не на славянских языках, вшивать их каждый раз руками это эт лень.</p>
  <p id="YBy2">Я запилил эмсипи-сервер для оупенкода, который делает всё одной командой: скачивает видео, достаёт субтитры (русские или английские), переводит их на русский прямо в чате и вшивает в видео. Никаких апи-ключей, никаких внешних сервисов — только yt-dlp, ffmpeg и сам оупенкод.</p>
  <h2 id="как-это-работает">Как это работает</h2>
  <p id="bhRJ">Процесс выглядит так:</p>
  <ol id="YYZn">
    <li id="5OTk">Спрашиваю у оупенкода «скачай видос с ютуба, вшей субтитры»</li>
    <li id="e9lP">Оупенкод спрашивает у меня ссылку и куда сохранить</li>
    <li id="7fY1">Эмсипи сервер скачивает видео в 480p (для телеграма самое то)</li>
    <li id="2kFi">Сервер достаёт субтитры — сперва русские если есть, если нет — английские</li>
    <li id="V0zR">Если субтитры на английском, оупенкод переводит их на русский прямо в диалоге</li>
    <li id="3v7G">Ффмпег вшивает субтитры в видео</li>
    <li id="DF33">Готовый файл лежит в ~/Downloads</li>
  </ol>
  <p id="1rLE">Всё это занимает пару минут и не требует ни одной команды в терминале.</p>
  <h2 id="архитектура">Архитектура</h2>
  <p id="D4Cf">Система состоит из трёх частей:</p>
  <h3 id="1-эмсипи-сервер">1. Эмсипи-сервер</h3>
  <p id="vPPl">Пайтон-скрипт на FastMCP с четырьмя инструментами:</p>
  <ul id="Fa4E">
    <li id="keIO"><code>download_video</code> — скачивает 480p через yt-dlp</li>
    <li id="DWpC"><code>extract_subs</code> — достаёт субтитры (с авто-субтитрами как запасной вариант)</li>
    <li id="UUmo"><code>save_subs</code> — сохраняет переведённые субтитры</li>
    <li id="heAf"><code>burn_subs</code> — вшивает субтитры через ffmpeg</li>
  </ul>
  <p id="7Nn1">Сервер лежит в <code>~/.config/opencode/mcp/youtube_mcp_server.py</code>:</p>
  <p id="ZUaA"><a href="https://blog.tatarinovms.space/static/scripts/youtubemcp/youtube_mcp_server.py" target="_blank">Скачать готовый файл</a></p>
  <h3 id="2-регистрация-в-оупенкод">2. Регистрация в оупенкод</h3>
  <p id="P2sD">Добавляем в <code>~/.config/opencode/opencode.jsonc</code>:</p>
  <pre id="DkZt">&quot;youtube&quot;: {
  &quot;type&quot;: &quot;local&quot;,
  &quot;command&quot;: [
    &quot;/Users/ваш_юзер/.config/opencode/mcp/venv/bin/python3&quot;,
    &quot;/Users/ваш_юзер/.config/opencode/mcp/youtube_mcp_server.py&quot;
  ]
}
</pre>
  <h3 id="3-скилл">3. Скилл</h3>
  <p id="n2As">Чтобы оупенкод сам понимал когда вызывать инструменты, создаём скилл в <code>~/.opencode/skills/youtube-processor/SKILL.md</code>:</p>
  <p id="QfCc"><a href="https://blog.tatarinovms.space/static/scripts/youtubemcp/SKILL.md" target="_blank">Скачать готовый файл</a></p>
  <p id="CiID">В скилле описана последовательность: спросить ссылку и путь → скачать → достать субтитры → если английские — перевести → вшить → отдать результат.</p>
  <h2 id="как-я-перевожу-субтитры-без-апи-ключей">Как я перевожу субтитры без апи-ключей</h2>
  <p id="WwCE">Самый ленивый момент — перевод. Вместо того чтобы дёргать гугл-транслейт или оплачивать апи, я просто отдаю текст субтитров самому оупенкоду. Он переводит всё в диалоге, сохраняя таймкоды и структуру SRT, сохраняет обратно и передаёт на вшивание. Можно локально конечно, но не у всех есть. Да и в целом зачем лишний шаг.</p>
  <h2 id="что-получилось-в-итоге">Что получилось в итоге</h2>
  <p id="V6fM">Протестировал на видео со <a href="https://youtu.be/CwVkYFsX5Fw?si=mMc9M0RSAbgWUKtR" target="_blank">новостями</a> на стопгейм, ютуб выдаёт автоматические русские субтитры, так что перевод не понадобился. Видео 8 минут скачалось за 10 секунд, субтитры за 3 секунды, вшивание заняло пару минут. На выходе — mp4 со встроенными субтитрами, готовый для телеграма.</p>
  <p id="6HkV">Если у видео нет русских субтитров, оупенкод скачает английские и переведёт их на русский прямо в диалоге — никаких лишних телодвижений.</p>
  <h2 id="особенности">Особенности</h2>
  <ul id="Qb95">
    <li id="Z6d5">Для работы нужен FFMpeg с libass. Если <code>subtitles</code> фильтр не найден — <code>brew install libass &amp;&amp; brew install homebrew-ffmpeg/ffmpeg/ffmpeg</code></li>
    <li id="WFwu">Если субтитров нет вообще — оупенкод спросит, сохранить видео без них или отменить</li>
    <li id="ARsU">Для “возрастных видео” может понадобиться куки браузера</li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@beaverclan/markitdownmcp</guid><link>https://teletype.in/@beaverclan/markitdownmcp?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/markitdownmcp?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>MarkItDown: конвертируем в макрдаун из оупенкод</title><pubDate>Sun, 28 Jun 2026 07:12:17 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/ba/4e/ba4e4551-614b-4657-9af2-4078cac5b400.png"></media:content><description><![CDATA[<img src="https://blog.tatarinovms.space/images/posts/markitdownmcp/logo.webp"></img>В работе с оупенкодом да и с агентами в целом, постоянно нужно читать документы, пдф файлы, эксель таблицы, презентации и прочие, не очень текстовые форматы. ЭЛЛ’мки хорошо понимают макрдаун, поэтому задача конвертации в маркдаун возникает постоянно. Также таким образом я — набиваю свое хранилище в обсидиан всякими гайдами.]]></description><content:encoded><![CDATA[
  <figure id="IH0j" class="m_original">
    <img src="https://blog.tatarinovms.space/images/posts/markitdownmcp/logo.webp" width="800" />
  </figure>
  <h2 id="зачем-это-нужно">Зачем это нужно</h2>
  <p id="gIXp">В работе с оупенкодом да и с агентами в целом, постоянно нужно читать документы, пдф файлы, эксель таблицы, презентации и прочие, не очень текстовые форматы. ЭЛЛ’мки хорошо понимают макрдаун, поэтому задача конвертации в маркдаун возникает постоянно. Также таким образом я — набиваю свое хранилище в обсидиан всякими гайдами.</p>
  <p id="PRgd">Майкрософт выпустила утилиту <a href="https://github.com/microsoft/markitdown" target="_blank">MarkItDown</a> пайтон библиотеку, которая умеет конвертировать, практически, всё во что угодно в маркдаун:</p>
  <ul id="SZhs">
    <li id="uOOH">*.PDF</li>
    <li id="1ED0">*.DOCX</li>
    <li id="AS6T">*.XLSX</li>
    <li id="fP1A">*.PPTX</li>
    <li id="fuoC">HTML</li>
    <li id="QWIo">*.EPUB</li>
    <li id="3jRb">*.XML</li>
    <li id="jLHT">ну и так далее</li>
  </ul>
  <h2 id="что-такое-mcp-и-как-это-работает">Что такое MCP и как это работает</h2>
  <p id="52le">MCP (Model Context Protocol) — это стандарт, который позволяет подключать внешние инструменты к эай-ассистентам (в нашем случае — к оупенкод). Через эмсипи мы даём оупенкод возможность выполнять произвольные действия: читать файлы, запускать команды, ходить в эпиай.</p>
  <p id="2gMV">Мы создадим эмсипи сервер, который оборачивает MarkItDown в один простой инструмент <code>convert_to_markdown</code>. После этого оупенкод сможет сам сконвертировать файл в маркдаун, когда это понадобится.</p>
  <h2 id="установка">Установка</h2>
  <h3 id="1-виртуальное-окружение-и-зависимости">1. Виртуальное окружение и зависимости</h3>
  <p id="hnMd">Макось больше не разрешает ставить пакеты через <code>pip</code> глобально, поэтому создадим <code>venv</code> и установим всё туда:</p>
  <pre id="tej6">python3 -m venv ~/.config/opencode/mcp/venv
~/.config/opencode/mcp/venv/bin/pip3 install &#x27;markitdown[all]&#x27; &#x27;mcp[cli]&#x27;
</pre>
  <p id="Uz7w">Разбиваем на составляющие: <code>markitdown[all]</code> сама библиотека конвертации со всеми опциональными зависимостями, <code>mcp[cli]</code> Python SDK для создания эмсипи-серверов.</p>
  <h3 id="2-скрипт-эмсипи-сервера">2. Скрипт эмсипи-сервера</h3>
  <p id="QufM">Создаём файл <code>~/.config/opencode/mcp/markitdown_server.py</code>:</p>
  <p id="mSOE"><a href="https://blog.tatarinovms.space/static/scripts/markitdownmcp/markitdown_server.py" target="_blank">Скачать готовый файл</a></p>
  <pre id="0BOX">from mcp.server.fastmcp import FastMCP
from markitdown import MarkItDown
import os

mcp = FastMCP(&quot;MarkItDown&quot;)

@mcp.tool()
def convert_to_markdown(file_path: str) -&gt; str:
    &quot;&quot;&quot;Convert any supported file (PDF, DOCX, PPTX, XLSX, HTML, EPUB, XML) to Markdown.&quot;&quot;&quot;
    file_path = os.path.expanduser(os.path.abspath(file_path))
    if not os.path.exists(file_path):
        return f&quot;Error: File not found: {file_path}&quot;
    md = MarkItDown()
    result = md.convert(file_path)
    return result.text_content

if __name__ == &quot;__main__&quot;:
    mcp.run(transport=&quot;stdio&quot;)
</pre>
  <p id="Of16">Здесь мы используем <a href="https://gofastmcp.com/getting-started/welcome" target="_blank">FastMCP</a> - фреймворк, который берёт на себя всю работу: парсинг JSON-RPC, логирование и т.д. Кароче способ для ленивых.</p>
  <h3 id="3-регистрация-в-оупенкод">3. Регистрация в оупенкод</h3>
  <p id="Cn1J">Добавляем сервер в <code>~/.config/opencode/оупенкод.jsonc</code>:</p>
  <pre id="79tz">{
  &quot;$schema&quot;: &quot;https://opencode.ai/config.json&quot;,
  &quot;mcp&quot;: {
    &quot;bb-browser&quot;: { ... },
    &quot;markitdown&quot;: {
      &quot;type&quot;: &quot;local&quot;,
      &quot;command&quot;: [
        &quot;/Users/beaverbloger/.config/opencode/mcp/venv/bin/python3&quot;,
        &quot;/Users/beaverbloger/.config/opencode/mcp/markitdown_server.py&quot;
      ]
    }
  }
}
</pre>
  <p id="WeVe">Готово! оупенкод теперь знает про сервер и будет вызывать его автоматически.</p>
  <h3 id="4-скилл-для-оупенкод">4. Скилл для оупенкод</h3>
  <p id="Q8RJ">Чтобы агент сам знал когда вызывать <code>convert_to_markdown</code>, создаём скилл. Он подскажет агенту какие форматы поддерживаются и как сохранять результат.</p>
  <p id="fBRZ">Создайте файл <code>~/.opencode/skills/markitdown-converter/SKILL.md</code>:</p>
  <p id="O8Ce"><a href="https://blog.tatarinovms.space/static/scripts/markitdownmcp/SKILL.md" target="_blank">Скачать готовый файл</a></p>
  <pre id="0SpM">---
name: markitdown-converter
description: Convert files to Markdown using MCP tool
---

When the user provides a file in a non-text format that needs to be read or converted to Markdown:

1. **Supported formats:** &#x60;.pdf&#x60;, &#x60;.docx&#x60;, &#x60;.xlsx&#x60;, &#x60;.pptx&#x60;, &#x60;.html&#x60;, &#x60;.epub&#x60;, &#x60;.xml&#x60;

2. **Use the &#x60;convert_to_markdown&#x60; MCP tool** with the file path. The path can be:
   - Absolute: &#x60;/Users/name/file.pdf&#x60;
   - With &#x60;~&#x60;: &#x60;~/Downloads/file.docx&#x60;
   - Relative: &#x60;./file.xlsx&#x60;

3. **Save the result** as &#x60;.md&#x60; file next to the original:
   - &#x60;~/Documents/doc.pdf&#x60; → &#x60;~/Documents/doc.md&#x60;
   - &#x60;~/Downloads/report.xlsx&#x60; → &#x60;~/Downloads/report.md&#x60;

4. **Return the path** to the saved &#x60;.md&#x60; file to the user.
</pre>
  <p id="gFCf">Теперь оупенкод сам понимает — файл не текстовый, надо конвертнуть.</p>
  <h2 id="как-это-работает">Как это работает</h2>
  <p id="aYDQ">Когда оупенкоду нужно обработать документ, он:</p>
  <ol id="3mNY">
    <li id="TNhg">Видит, что есть инструмент <code>convert_to_markdown</code></li>
    <li id="H4eY">Отправляет к эмсипи-запрос с путём к файлу</li>
    <li id="M7Bb">Наш сервер запускает MarkItDown, конвертирует файл</li>
    <li id="PrBn">Возвращает готовый маркдаун</li>
  </ol>
  <p id="2p6Q">Всё это происходит прозрачно. Оупенкод может вызвать этот инструмент самостоятельно, когда понимает, что файл нужно прочитать.</p>
  <h2 id="проверка">Проверка</h2>
  <p id="Yan6">Перезапустим оупенкод чтобы эмсипи у нас подхватился и просим конвертировать файл пдф в маркдаун. Или просто скажи «прочитай этот файл» — скилл подскажет агенту что делать и тот сам вызовет <code>convert_to_markdown</code>:</p>
  <figure id="DqhG" class="m_original">
    <img src="https://blog.tatarinovms.space/images/posts/markitdownmcp/1.webp" width="800" />
  </figure>
  <ul id="srdc">
    <li id="CbzM">Результат</li>
  </ul>
  <figure id="P8A8" class="m_original">
    <img src="https://blog.tatarinovms.space/images/posts/markitdownmcp/2.webp" width="800" />
  </figure>
  <h2 id="особенности">Особенности</h2>
  <p id="RroL">Из коробки библиотека использует встроенные инструменты: pdfplumber для пдф, mammoth для DOCX, openpyxl для XLSX, python-pptx для PPTX, beautifulsoup4 для HTML и т.д.</p>
  <h2 id="итого">Итого</h2>
  <p id="sPHJ">MarkItDown как эмсипи-сервер для оупенкод для меня это полезная комбинация. Юзайте для себя и вашего агента.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@beaverclan/duckduckgosearchmcp</guid><link>https://teletype.in/@beaverclan/duckduckgosearchmcp?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/duckduckgosearchmcp?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>DuckDuckGo Search MCP: веб-поиск для оупенкод</title><pubDate>Sun, 28 Jun 2026 07:11:17 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/06/a6/06a66b02-fdf2-416b-ac0e-a28d85af986d.png"></media:content><description><![CDATA[<img src="https://blog.tatarinovms.ru/images/posts/duckduckgosearchmcp/logo.webp"></img>Оупенкод хорош, но у него есть ограничение — он ничего не знает о мире за пределами того, что заложено в моделях которые ты юзаешь в агенте.]]></description><content:encoded><![CDATA[
  <figure id="qoTI" class="m_original">
    <img src="https://blog.tatarinovms.ru/images/posts/duckduckgosearchmcp/logo.webp" width="800" />
  </figure>
  <h2 id="зачем-это-нужно">Зачем это нужно</h2>
  <p id="vwRL">Оупенкод хорош, но у него есть ограничение — он ничего не знает о мире за пределами того, что заложено в моделях которые ты юзаешь в агенте.</p>
  <p id="hsLf">DuckDuckGo Search MCP сервер решает эту проблему: он даёт оупенкод два инструмента — поиск в вебе и загрузку контента со страниц. Теперь агент может сам сходить в поисковик за актуальной информацией.</p>
  <p id="bZVZ">Чем это отличается от встроенного <code>WebFetch</code>? WebFetch умеет только загружать контент по конкретному URL. Если агент не знает адрес — он бесполезен. А DuckDuckGo Search MCP добавляет <strong>поиск</strong>: агент сам находит нужные страницы по запросу, а потом может прочитать их через <code>fetch_content</code>. То есть это полноценный веб-сёрфинг, а не просто загрузка по известной ссылке.</p>
  <h2 id="что-такое-mcp-и-как-это-работает">Что такое MCP и как это работает</h2>
  <p id="fosG">MCP (Model Context Protocol) — стандарт для подключения внешних инструментов к ИИ-ассистентам. Через эмсипи мы даём оупенкод доступ к произвольным действиям: поиск в интернете, чтение файлов, вызов API.</p>
  <p id="qUar">Мы устанавливаем DuckDuckGo эмсипи сервер, регистрируем его в конфиге оупенкод, и агент получает инструменты <code>search</code> и <code>fetch_content</code>. Дальше он сам решает, когда ими воспользоваться.</p>
  <h2 id="установка">Установка</h2>
  <h3 id="1-устанавливаем-uv">1. Устанавливаем uv</h3>
  <p id="6RHn"><code>uv</code> — менеджер пакетов для Python, через него запускается сервер:</p>
  <pre id="MVlv">curl -LsSf https://astral.sh/uv/install.sh | sh
</pre>
  <p id="cY5e">После установки перезапустите терминал или добавьте <code>~/.local/bin</code> в PATH.</p>
  <h3 id="2-регистрация-в-оупенкод">2. Регистрация в оупенкод</h3>
  <p id="N38i">Добавляем сервер в <code>~/.config/opencode/opencode.jsonc</code>:</p>
  <pre id="z8Pw">{
  &quot;$schema&quot;: &quot;https://opencode.ai/config.json&quot;,
  &quot;mcp&quot;: {
    &quot;bb-browser&quot;: { ... },
    &quot;markitdown&quot;: { ... },
    &quot;ddg-search&quot;: {
      &quot;type&quot;: &quot;local&quot;,
      &quot;command&quot;: [&quot;/Users/beaverbloger/.local/bin/uvx&quot;, &quot;duckduckgo-mcp-server&quot;],
      &quot;env&quot;: {
        &quot;DDG_SAFE_SEARCH&quot;: &quot;MODERATE&quot;,
        &quot;DDG_REGION&quot;: &quot;wt-wt&quot;
      }
    }
  }
}
</pre>
  <p id="t878">Параметры окружения:</p>
  <ul id="Xjvr">
    <li id="mhF7"><code>DDG_SAFE_SEARCH</code> — уровень фильтрации контента: <code>STRICT</code>, <code>MODERATE</code> (по умолчанию), <code>OFF</code></li>
    <li id="u5z8"><code>DDG_REGION</code> — регион по умолчанию: <code>us-en</code>, <code>ru-ru</code>, <code>wt-wt</code> (без региона) и т.д.</li>
  </ul>
  <p id="pHbC">Готово! После перезапуска оупенкод подхватит сервер.</p>
  <h2 id="инструменты">Инструменты</h2>
  <p id="fNFa">Сервер предоставляет два инструмента:</p>
  <h3 id="searchquery-max_results10-region">search(query, max_results=10, region=&quot;&quot;)</h3>
  <p id="wBcD">Поиск в DuckDuckGo. Возвращает ссылки с заголовками и сниппетами. Можно указать регион для локализации результатов.</p>
  <h3 id="fetch_contenturl-start_index0-max_length8000">fetch_content(url, start_index=0, max_length=8000)</h3>
  <p id="xUoA">Загрузка и очистка контента со страницы. Убирает навигацию, шапки, подвалы, скрипты — оставляет чистый текст. Поддерживает пагинацию для длинных страниц.</p>
  <h2 id="проверка">Проверка</h2>
  <p id="IRoj">Проверим, как это работает. Прошу оупенкод найти рецепт курута:</p>
  <figure id="4eo9" class="m_original">
    <img src="https://blog.tatarinovms.ru/images/posts/duckduckgosearchmcp/1.webp" width="800" />
  </figure>
  <p id="Fhu8">Агент вызывает <code>search(&quot;как готовить курут рецепт&quot;)</code>, находит ссылки и краткие описания.</p>
  <p id="BEm5">А теперь попросим узнать погоду в Абзаково:</p>
  <figure id="jO75" class="m_original">
    <img src="https://blog.tatarinovms.ru/images/posts/duckduckgosearchmcp/2.webp" width="800" />
  </figure>
  <p id="1xLX">Работает! Агент сам решил, что ему не хватает данных, сходил в интернет и вернул актуальный ответ.</p>
  <h2 id="как-это-работает">Как это работает</h2>
  <p id="xCiT">Когда оупенкоду нужно найти что-то в интернете, он:</p>
  <ol id="8lh6">
    <li id="cefK">Видит, что доступен инструмент <code>search</code></li>
    <li id="d34u">Формирует поисковый запрос</li>
    <li id="isyV">Получает результаты</li>
    <li id="elcN">Если нужно прочитать конкретную страницу — вызывает <code>fetch_content</code></li>
    <li id="eed8">Анализирует полученную информацию</li>
  </ol>
  <p id="YbET">Всё прозрачно: агенту не нужно объяснять, как искать — он делает это сам, когда понимает, что ему не хватает знаний.</p>
  <h2 id="особенности">Особенности</h2>
  <ul id="gkDT">
    <li id="1uG7"><strong>Без API-ключей</strong> — DuckDuckGo не требует регистрации, всё бесплатно</li>
    <li id="lob3"><strong>Rate limiting</strong> — 30 запросов в минуту для поиска, 20 для загрузки контента</li>
    <li id="5maL"><strong>SafeSearch</strong> — настраиваемая фильтрация контента</li>
    <li id="RSGn"><strong>Регионы</strong> — можно локализовать поиск под нужную страну</li>
  </ul>
  <h2 id="итого">Итого</h2>
  <p id="WDJ9">DuckDuckGo Search эмсипи — простой способ дать оупенкод доступ к веб-поиску. Без регистраций, без API-ключей, без смс. Поставил за минуту — и агент сам ходит в интернет за актуальной информацией.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@beaverclan/deepwikimcp</guid><link>https://teletype.in/@beaverclan/deepwikimcp?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/deepwikimcp?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>DeepWiki MCP: AI-документация для любого репозитория</title><pubDate>Sun, 28 Jun 2026 07:10:27 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/fd/20/fd205c21-adac-4e53-839e-33f5bd4df623.png"></media:content><description><![CDATA[<img src="https://blog.tatarinovms.ru/images/posts/deepwikimcp/logo.webp"></img>В работе с опенсорсом постоянно возникает ситуация: нашёл интересный репозиторий, а документация — это просто README. Хочется быстро разобраться, как всё устроено, но читать исходники в лоб — долго и лениво.]]></description><content:encoded><![CDATA[
  <figure id="PR8p" class="m_original">
    <img src="https://blog.tatarinovms.ru/images/posts/deepwikimcp/logo.webp" width="800" />
  </figure>
  <h2 id="зачем-это-нужно">Зачем это нужно</h2>
  <p id="AKrG">В работе с опенсорсом постоянно возникает ситуация: нашёл интересный репозиторий, а документация — это просто README. Хочется быстро разобраться, как всё устроено, но читать исходники в лоб — долго и лениво.</p>
  <p id="qS8S">DeepWiki решает эту проблему: он анализирует код и README репозитория, и генерирует структурированную документацию с разделами, ссылками на исходники и с возможностью задавать вопросы.</p>
  <p id="sSVt">А через MCP (Model Context Protocol) это становится доступно прямо из оупенкод — агент может сам сходить в диипвики за документацией по любому репозиторию.</p>
  <h2 id="что-такое-диипвики">Что такое диипвики</h2>
  <p id="Cc7z"><a href="https://deepwiki.com/" target="_blank">DeepWiki</a> — это AI-движок документации от Cognition Labs (те же ребята, что сделали Devin). Он берёт любой публичный GitHub-репозиторий, анализирует его код, README и файлы, и превращает всё это в удобную структурированную документацию.</p>
  <p id="V4yV">Каждая страница документации содержит:</p>
  <ul id="JhYn">
    <li id="ABQV">пояснения как работает тот или иной компонент</li>
    <li id="35sW">ссылки на конкретные строки исходного кода (Sources)</li>
    <li id="uW6B">навигацию по разделам</li>
  </ul>
  <h2 id="диипвики-в-браузере">Диипвики в браузере</h2>
  <p id="STPg">диипвики работает прямо в браузере. Просто замени <code>github.com</code> на <code>deepwiki.com</code> в URL любого публичного репозитория.</p>
  <p id="EN3H">Вот так выглядит страница репозитория <a href="https://deepwiki.com/LOWERTOP/Shadowrocket" target="_blank">LOWERTOP/Shadowrocket</a> на диипвики:</p>
  <figure id="ZJ6K" class="m_original">
    <img src="https://blog.tatarinovms.ru/images/posts/deepwikimcp/2.webp" width="800" />
  </figure>
  <p id="G89L">Слева — навигация по разделам: Proxy Management, Traffic Routing, Rules System, Configuration Files и т.д. Справа — контент с пояснениями и ссылками на исходники. Гораздо удобнее, чем читать README в GitHub.</p>
  <p id="xBJU">Работает почти для любого публичного репозитория: <code>https://deepwiki.com/username/reponame</code>.</p>
  <h2 id="диипвики-эмсипи-для-оупенкод">Диипвики эмсипи для оупенкод</h2>
  <p id="GwZf">Cognition Labs предоставляют официальный эмсипи-сервер DeepWiki. Это удалённый сервер, поэтому не нужно ничего устанавливать локально — никаких Python, venv, uvx. Просто URL в конфиге.</p>
  <p id="rDts">Добавляем в <code>~/.config/opencode/opencode.jsonc</code>:</p>
  <pre id="ytSR">{
  &quot;$schema&quot;: &quot;https://opencode.ai/config.json&quot;,
  &quot;mcp&quot;: {
    &quot;deepwiki&quot;: {
      &quot;type&quot;: &quot;remote&quot;,
      &quot;url&quot;: &quot;https://mcp.deepwiki.com/mcp&quot;
    }
  }
}
</pre>
  <p id="E90b">Готово. Никаких зависимостей, никаких API-ключей — сервер бесплатный и не требует аутентификации.</p>
  <h2 id="инструменты">Инструменты</h2>
  <p id="up03">Сервер предоставляет три инструмента:</p>
  <h3 id="read_wiki_structurereponame">read_wiki_structure(repoName)</h3>
  <p id="B9xo">Возвращает структуру документации репозитория — список разделов и подразделов.</p>
  <h3 id="read_wiki_contentsreponame">read_wiki_contents(repoName)</h3>
  <p id="UQ3Y">Возвращает полное содержимое документации репозитория.</p>
  <h3 id="ask_questionreponame-question">ask_question(repoName, question)</h3>
  <p id="kpb1">Задать вопрос по коду репозитория. DeepWiki отвечает с привязкой к конкретным строкам исходников.</p>
  <h2 id="проверка">Проверка</h2>
  <p id="vRO8">Спрашиваю у оупенкод: “как устроена маршрутизация в ShadowRocket?”:</p>
  <figure id="jraj" class="m_original">
    <img src="https://blog.tatarinovms.ru/images/posts/deepwikimcp/1.webp" width="800" />
  </figure>
  <p id="6e9I">Оупенкод вызывает <code>ask_question(&quot;LOWERTOP/Shadowrocket&quot;, &quot;как устроена маршрутизация&quot;)</code>, диипвики анализирует код и возвращает структурированный ответ: глобальные режимы (Configuration/Proxy/Direct/Scene), система правил, типы правил. Каждый пункт привязан к исходному коду.</p>
  <h2 id="итого">Итого</h2>
  <p id="7aEf">DeepWiki эмсипи — это документация для почти любого репозитория прямо из оупенкод.</p>
  <p id="JEPE">Вместе с <a href="https://blog.tatarinovms.ru/posts/duckduckgosearchmcp/" target="_blank">DuckDuckGo Search эмсипи</a> получается идеальная пара: DuckDuckGo ищет в интернете, DeepWiki объясняет код репозиториев. Оупенкод наконец-то видит и интернет, и документацию.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@beaverclan/blogontimewebs3</guid><link>https://teletype.in/@beaverclan/blogontimewebs3?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/blogontimewebs3?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>Хостинг Hugo-блога на S3 Timeweb</title><pubDate>Sat, 13 Jun 2026 00:15:01 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/de/76/de76d744-9a6b-4441-8ca2-03b88d336bc6.png"></media:content><category>Blog</category><description><![CDATA[<img src="https://img4.teletype.in/files/f0/c6/f0c67423-5f38-43cf-bcf2-2c5a7e93c49d.png"></img>Долгое время мой блог жил на VDS — nginx, certbot, systemd-сервисы, бекапы, короче полноценный сервер. В какой-то момент стало лень следить за ещё одной машиной и особенно меня подтолкнули проблемы с разными хостингами и я решил переехать на статический S3-хостинг.]]></description><content:encoded><![CDATA[
  <figure id="foTe" class="m_original">
    <img src="https://img4.teletype.in/files/f0/c6/f0c67423-5f38-43cf-bcf2-2c5a7e93c49d.png" />
  </figure>
  <p id="kp5T">Долгое время мой блог жил на VDS — nginx, certbot, systemd-сервисы, бекапы, короче полноценный сервер. В какой-то момент стало лень следить за ещё одной машиной и особенно меня подтолкнули проблемы с разными хостингами и я решил переехать на статический S3-хостинг.</p>
  <p id="f9Kc">Пост про то — как я переехал, с какими граблями столкнулся и что в итоге получилось.</p>
  <h2 id="почему-timeweb-s3">Почему Timeweb S3</h2>
  <p id="iZHc">Почему именно Timeweb, а не Яндекс Облако или Selectel? Тут всё просто: у меня уже были там аккаунты, а S3 у них стоит копейки. Для статики — идеально.</p>
  <p id="fKiz">Из коробки Timeweb S3 умеет раздавать статический сайт, выдаёт URL вида <code>*.website.twcstorage.ru</code>, поддерживает кастомные заголовки и политики.</p>
  <h2 id="что-нужно">Что нужно</h2>
  <ul id="qJS4">
    <li id="7FQr">Аккаунт в Timeweb (уже был)</li>
    <li id="MEAR">Hugo</li>
    <li id="fRuO">Репозиторий блога на GitHub (уже был)</li>
    <li id="WjTf">Домен (уже был)</li>
  </ul>
  <h2 id="шаг-1-создание-бакета">Шаг 1: Создание бакета</h2>
  <ol id="4pHe">
    <li id="MN1U">Заходим в панель Timeweb → S3 → Создать бакет</li>
    <li id="LL1P">Выбираем регион, имя бакета</li>
    <li id="Mqk0">После создания открываем бакет → Настройки → Хостинг статического сайта → Включаем</li>
    <li id="3ZPg">Получаем URL: <code>https://&lt;id&gt;.website.twcstorage.ru</code></li>
  </ol>
  <h2 id="шаг-2-s3-ключи">Шаг 2: S3-ключи</h2>
  <p id="ImNG">В панели Timeweb идём в сервисные аккаунты → создаём ключ с доступом к бакету (на запись и чтение). Нам понадобятся:</p>
  <ul id="zcdA">
    <li id="VP5n"><code>AWS_ACCESS_KEY_ID</code></li>
    <li id="MK2D"><code>AWS_SECRET_ACCESS_KEY</code></li>
    <li id="snl7"><code>AWS_ENDPOINT_URL</code> — <code>https://s3.twcstorage.ru</code></li>
    <li id="PKhx">И имя бакета</li>
  </ul>
  <h2 id="шаг-3-github-actions">Шаг 3: GitHub Actions</h2>
  <p id="lfMF">Деплой настроил через GitHub Actions. Workflow выглядит так:</p>
  <pre id="9uie">name: Deploy blog to S3

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: peaceiris/actions-hugo@v3
        with:
          extended: true

      - run: hugo --gc

      - uses: jakejarvis/s3-sync-action@master
        with:
          args: --delete
        env:
          AWS_S3_BUCKET: ${{ secrets.AWS_BUCKET_NAME }}
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_ENDPOINT_URL: ${{ secrets.AWS_ENDPOINT_URL }}
</pre>
  <p id="HyxA">Секреты (<code>AWS_*</code>) храню в настройках репозитория GitHub — Settings → Secrets and variables → Actions.</p>
  <h2 id="шаг-4-деплой">Шаг 4: Деплой</h2>
  <p id="fh1C">Пушим в main:</p>
  <pre id="1vJW">git push origin main
</pre>
  <p id="aGH9">GitHub Actions собирает сайт и синхронизирует с бакетом. Всё, сайт на S3, никаких ssh и ручных заливок.</p>
  <h2 id="шаг-5-домены-не-так-всё-просто">Шаг 5: Домены (не так всё просто)</h2>
  <p id="g7dg">Настраиваем CNAME в DNS:</p>
  <pre id="AAlh">blog.tatarinovms.ru → s3.twcstorage.ru
blog.tatarinovms.space → s3.twcstorage.ru
</pre>
  <p id="N0ZI">Ждём, пока DNS обновится, открываем браузер… и получаем <code>AccessDenied</code>.</p>
  <h3 id="подводный-камень-кастомные-домены-не-работают">Подводный камень: кастомные домены не работают</h3>
  <p id="B8Uu">Как оказалось, Timeweb S3 <a href="https://timeweb.cloud/docs/s3-storage/supported-features/static-websites#ogranicheniya" target="_blank">не поддерживает кастомные домены</a>. Вообще. Только их <code>*.website.twcstorage.ru</code>. На момент написания поста ответ поддержки это подтвердил.</p>
  <p id="YhcL">Есть два пути:</p>
  <p id="1Bix"><strong>Вариант 1: CloudFlare</strong> Меняем NS на CloudFlare, он проксирует запросы на S3-эндпоинт и выдаёт свой SSL. VDS не нужен. Бесплатно. Но мы знаем как в некоторых регионах работает CloudFlare, скажем мягко - с перебоями.</p>
  <p id="FMwK"><strong>Вариант 2: nginx reverse proxy</strong> Пришла простая идея, так как есть в загашнике VDS, то мы ставим nginx на VDS, он проксирует кастомные домены на S3 URL. SSL через certbot. VDS всё равно есть под другие задачи. Конфиг nginx:</p>
  <pre id="WmqL">server {
    listen 80 default_server;
    server_name blog.tatarinovms.ru blog.tatarinovms.space;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name blog.tatarinovms.ru blog.tatarinovms.space;

    ssl_certificate /etc/letsencrypt/live/blog.tatarinovms.space/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blog.tatarinovms.space/privkey.pem;

    location / {
        proxy_pass https://id.website.twcstorage.ru;
        proxy_ssl_verify off;
        proxy_ssl_server_name on;
        proxy_set_header Host id.website.twcstorage.ru;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
</pre>
  <p id="FouX">SSL получил через certbot:</p>
  <pre id="6kJP">certbot --nginx -d blog.tatarinovms.ru -d blog.tatarinovms.space
</pre>
  <h2 id="итоговая-схема">Итоговая схема</h2>
  <pre id="NmyK">Пуш в main → GitHub Actions → Hugo build → aws s3 sync → S3 бакет → DNS: blog.tatarinovms.ru / blog.tatarinovms.space → A → VDS → nginx reverse proxy → S3 (*.website.twcstorage.ru)
</pre>
  <p id="8g5B">При пуше в main GitHub Actions сам собирает Hugo и синхронизирует с S3 — nginx просто проксирует то, что уже лежит в бакете. Редеплой занимает меньше минуты.</p>
  <p id="jUw3">Timeweb S3 как хостинг статики — отличное решение. Но с кастомными доменами придётся извернуться. Надеюсь, когда-нибудь они добавят эту возможность, но пока — nginx.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@beaverclan/finetune</guid><link>https://teletype.in/@beaverclan/finetune?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/finetune?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>FineTune — бесплатный аудио-микшер для macOS</title><pubDate>Thu, 28 May 2026 16:03:17 GMT</pubDate><description><![CDATA[<img src="https://blog.tatarinovms.space/images/posts/finetune/logo.webp"></img>FineTune — это бесплатная open-source утилита для macOS, которая живёт в меню-баре и даёт полный контроль над аудио на системном уровне. Регулировка громкости каждого приложения независимо, маршрутизация звука на разные устройства, 10-полосный эквалайзер и коррекция АЧХ для наушников — всё в одном окне.]]></description><content:encoded><![CDATA[
  <figure id="x4gH" class="m_original">
    <img src="https://blog.tatarinovms.space/images/posts/finetune/logo.webp" width="800" />
  </figure>
  <h2 id="что-такое-finetune">Что такое FineTune?</h2>
  <p id="HB1K">FineTune — это бесплатная open-source утилита для macOS, которая живёт в меню-баре и даёт полный контроль над аудио на системном уровне. Регулировка громкости каждого приложения независимо, маршрутизация звука на разные устройства, 10-полосный эквалайзер и коррекция АЧХ для наушников — всё в одном окне.</p>
  <p id="9piW">Для перехвата аудиопотоков FineTune создаёт виртуальное аудиоустройство. Оно появляется в системных настройках звука и работает как прослойка между приложениями и вашими колонками/наушниками.</p>
  <p id="c6RU">Написан на SwiftUI, требует macOS 15.0+.</p>
  <h2 id="основные-возможности">Основные возможности</h2>
  <h3 id="управление-громкостью">Управление громкостью</h3>
  <ul id="XUet">
    <li id="mqq7"><strong>Per-app громкость</strong> — отдельный слайдер и mute для каждого приложения</li>
    <li id="f3op"><strong>Pinned apps</strong> — закрепить приложение в меню-баре, даже когда оно не воспроизводит звук (удобно настроить EQ и маршрутизацию заранее)</li>
    <li id="Ubss"><strong>Ignore apps</strong> — полностью исключить приложение из управления FineTune</li>
    <li id="VzXt"><strong>Scroll-wheel</strong> — наведите курсор на любой слайдер и крутите колёсико</li>
  </ul>
  <h3 id="горячие-клавиши">Горячие клавиши</h3>
  <ul id="UFfO">
    <li id="LXwg"><strong>Глобальные хоткеи</strong> — назначьте свои комбинации на App Volume Up/Down и Mute. Работают даже когда приложение свёрнуто</li>
    <li id="3DdG"><strong>Toggle Popup</strong> — повесьте хоткей на открытие/закрытие окна FineTune, работает из полноэкранных приложений</li>
    <li id="G9CD"><strong>Навигация внутри попапа</strong> — стрелки <code>↑↓</code> между строками, <code>←→</code> регулировка громкости, <code>M</code>mute, <code>Return/Space</code> активация, <code>Tab</code> переключение вкладок устройств</li>
  </ul>
  <h3 id="маршрутизация">Маршрутизация</h3>
  <ul id="ulU3">
    <li id="4T2Q"><strong>Multi-device output</strong> — вывод звука на несколько устройств одновременно</li>
    <li id="MqmG"><strong>Audio routing</strong> — отправлять разные приложения на разные устройства</li>
    <li id="5tvL"><strong>Device priority</strong> — автопереключение при подключении нового устройства</li>
    <li id="hetq"><strong>Auto-restore</strong> — при переподключении устройства настройки (громкость, маршрут, EQ) восстанавливаются автоматически</li>
  </ul>
  <h3 id="эквалайзер">Эквалайзер</h3>
  <ul id="XRNY">
    <li id="8x5C"><strong>10 полос</strong>, 20 готовых пресетов</li>
    <li id="ppow"><strong>Пользовательские пресеты</strong> — сохраняйте свои настройки и применяйте к разным приложениям</li>
    <li id="Cwzm"><strong>AutoEQ headphone correction</strong> — коррекция АЧХ под конкретную модель наушников из базы AutoEQ (тысячи профилей). Можно импортировать свои ParametricEQ.txt</li>
    <li id="b1f4"><strong>Загрузка пресета</strong> — выбрав пресет для своих наушников, FineTune запоминает его и применяет автоматически при каждом подключении этой модели</li>
    <li id="eutN"><strong>Loudness compensation</strong> — автоматическая коррекция низких и высоких частот на малой громкости по стандарту ISO 226:2023</li>
  </ul>
  <h3 id="системное">Системное</h3>
  <ul id="YOCS">
    <li id="qoSj"><strong>Input device control</strong> — уровень микрофона</li>
    <li id="HUp6"><strong>Alert volume</strong> — громкость системных уведомлений</li>
    <li id="Aiuu"><strong>DDC</strong> — регулировка громкости внешних мониторов</li>
    <li id="yjVx"><strong>Volume HUD</strong> — встроенный индикатор громкости (Tahoe или Classic стиль)</li>
    <li id="Qj8J"><strong>Медиа-клавиши F10-F12</strong> — работают даже на USB DAC и HDMI, где штатные клавиши macOS бесполезны</li>
    <li id="mly5"><strong>Bluetooth</strong> — подключение устройств прямо из меню-бара</li>
    <li id="mUyX"><strong>URL schemes</strong> — автоматизация через shell, Shortcuts или Raycast’о подобное</li>
  </ul>
  <h2 id="установка">Установка</h2>
  <pre id="kHTU">brew install --cask finetune
</pre>
  <p id="9llU">Или скачайте DMG с <a href="https://github.com/ronitsingh10/FineTune/releases/latest" target="_blank">GitHub Releases</a>.</p>
  <h2 id="быстрый-старт">Быстрый старт</h2>
  <ol id="CW8s">
    <li id="5mkA">Запустите FineTune из папки Applications</li>
    <li id="82RN">Разрешите <strong>Screen &amp; System Audio Recording</strong> — это необходимо для перехвата аудиопотоков</li>
    <li id="PTmo">Нажмите на иконку FineTune в меню-баре — приложения, воспроизводящие звук, появятся автоматически</li>
  </ol>
  <p id="BoLb">Готово. Регулируйте громкость, маршрутизируйте звук, настраивайте EQ.</p>
  <h2 id="полезные-фышки">Полезные фЫшки</h2>
  <ul id="BauV">
    <li id="swJr"><strong>Автопереключение на Bluetooth-гарнитуру</strong>: откройте edit mode (карандаш) и перетащите устройство выше встроенных динамиков — FineTune будет переключаться на него при подключении</li>
    <li id="8F7i"><strong>Управление с клавиатуры</strong>: откройте попап и пользуйтесь стрелками.</li>
    <li id="SHMR"><strong>URL schemes</strong> — automate volume, mute, device routing, and more from scriptsFineTune понимает URL-команды формата <code>finetune://</code>.<code>finetune://set-volumes?app=com.spotify.client&amp;volume=50 finetune://set-mute?app=com.spotify.client&amp;muted=true finetune://toggle-mute?app=com.hnc.Discord finetune://set-device?app=com.apple.Music&amp;device=DEVICE_UID finetune://reset </code></li>
  </ul>
  <figure id="x9TA" class="m_original">
    <img src="https://blog.tatarinovms.space/images/posts/finetune/1.webp" width="500" />
  </figure>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@beaverclan/qwen3asronmacos</guid><link>https://teletype.in/@beaverclan/qwen3asronmacos?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/qwen3asronmacos?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>Qwen3 ASR на macOS</title><pubDate>Sun, 03 May 2026 11:51:17 GMT</pubDate><description><![CDATA[<img src="https://blog.tatarinovms.space/images/posts/qwen3asronmacos/logo.webp"></img>В этом гайде я покажу, как развернуть и запустить модель распознавания речи Qwen3‑ASR через пакет mlx-qwen3-asr на macOS с M-чипом. Решение позволяет транскрибировать аудио из видеофайлов с поддержкой русского языка и сохранять результат в разных форматах: SRT, VTT, TXT, JSON.]]></description><content:encoded><![CDATA[
  <figure id="qDLy" class="m_original">
    <img src="https://blog.tatarinovms.space/images/posts/qwen3asronmacos/logo.webp" width="800" />
  </figure>
  <p id="H5S1">В этом гайде я покажу, как развернуть и запустить модель распознавания речи Qwen3‑ASR через пакет mlx-qwen3-asr на macOS с M-чипом. Решение позволяет транскрибировать аудио из видеофайлов с поддержкой русского языка и сохранять результат в разных форматах: SRT, VTT, TXT, JSON.</p>
  <h1 id="что-такое-mlx-qwen3-asr">Что такое mlx-qwen3-asr?</h1>
  <p id="DpqW"><a href="https://github.com/moona3k/mlx-qwen3-asr" target="_blank">mlx-qwen3-asr</a> - это Python‑пакет, предоставляющий удобный интерфейс к модели Qwen3‑ASR‑1.7B от Alibaba Cloud, оптимизированной для работы на чипах Apple Silicon через фреймворк MLX. Модель поддерживает несколько языков, включая наш родной русский, и позволяет получать транскрипцию с тайм‑кодами.</p>
  <h2 id="требования">Требования</h2>
  <ul id="brvV">
    <li id="Bjzn">macOS</li>
    <li id="aSjr">Установленный Python 3.11</li>
    <li id="aImQ">Руки</li>
    <li id="syvz">Свободное место на диске (модель ~3 ГБ)</li>
  </ul>
  <h1 id="установка-и-настройка">Установка и настройка</h1>
  <h2 id="подготовка-директории-проекта">Подготовка директории проекта</h2>
  <p id="P4NZ">Создайте отдельную папку для проекта и перейдите в неё, у меня будет так:</p>
  <pre id="PQaw">mkdir -p ~/Documents/ai/asr
cd ~/Documents/ai/asr
</pre>
  <h2 id="создание-виртуального-окружения">Создание виртуального окружения</h2>
  <pre id="QSXM">python3.11 -m venv venv
</pre>
  <h2 id="активация-окружения">Активация окружения</h2>
  <p id="wtYF">Активируйте виртуальное окружение:</p>
  <pre id="Yv0b">source venv/bin/activate
</pre>
  <h2 id="обновление-pip-и-установка-пакета">Обновление pip и установка пакета</h2>
  <p id="jKp5">Обновите менеджер пакетов и установите mlx-qwen3-asr:</p>
  <pre id="ex17">pip install --upgrade pip
pip install mlx-qwen3-asr
</pre>
  <p id="Qrot">Установка может занять несколько минут — загружаются зависимости и модель.</p>
  <h2 id="создание-скрипта-автоматизации">Создание скрипта автоматизации</h2>
  <p id="QI99">Чтобы не вводить команды вручную каждый раз, создадим shell‑скрипт с небольшим интерактивным интерфейсом.</p>
  <h3 id="создайте-файл-скрипта">Создайте файл скрипта</h3>
  <ul id="m9UL">
    <li id="KfxS">Создадим скрипт в любимом текстовом редакторе:</li>
  </ul>
  <pre id="76SI">vim asr_process.sh
</pre>
  <ul id="uQps">
    <li id="RADG">Вставьте этот <a href="https://blog.tatarinovms.space/static/scripts/qwen3asronmacos/asr_process.sh" target="_blank">код</a></li>
    <li id="FFX8">Укажите настройку <code>VENV_BIN=</code>, где будет путь к созданной вами папке.</li>
    <li id="9JJi">Сохраняем файл</li>
    <li id="hzFO">Делаем скрипт исполняемым</li>
  </ul>
  <pre id="dNfd">chmod +x asr_process.sh
</pre>
  <h2 id="отправляем-файл-на-распознавание">Отправляем файл на распознавание</h2>
  <ul id="zhFw">
    <li id="hDj1">Запускаем скрипт</li>
  </ul>
  <pre id="Pxyg">./asr_process.sh
</pre>
  <ul id="SogN">
    <li id="UtVV">откроет диалоговое окно для выбора файла</li>
    <li id="DBQx">потом скрипт предложит выбрать формат вывода</li>
    <li id="dS5f">позволит указать папку для сохранения</li>
    <li id="J5mK">запустится распознавание и покажет прогресс (при первом запуске модель скачается)</li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@beaverclan/voxcpm</guid><link>https://teletype.in/@beaverclan/voxcpm?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/voxcpm?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>VoxCPM - на ноуте. Клонируем свой голос</title><pubDate>Sun, 26 Apr 2026 17:26:16 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/35/72/35721e82-9c8b-4e50-926b-70083be2ca65.png"></media:content><category>Blog</category><description><![CDATA[<img src="https://blog.tatarinovms.space/images/posts/voxcpm/logo.webp"></img>Данное руководство содержит пошаговые инструкции по установке и запуску VoxCPM на macOS с процессорами Apple Silicon. Игрался на своем ноуте.]]></description><content:encoded><![CDATA[
  <figure id="Id5N" class="m_original">
    <img src="https://blog.tatarinovms.space/images/posts/voxcpm/logo.webp" width="800" />
  </figure>
  <h1 id="развёртывание-voxcpm-на-macbook-с-чипом-apple-silicon">Развёртывание VoxCPM на MacBook с чипом Apple Silicon</h1>
  <p id="Gh74">Данное руководство содержит пошаговые инструкции по установке и запуску <a href="https://github.com/OpenBMB/VoxCPM" target="_blank">VoxCPM</a> на macOS с процессорами Apple Silicon. Игрался на своем ноуте.</p>
  <h2 id="скачиваем-репозиторий-проекта">Скачиваем репозиторий проекта</h2>
  <p id="snAR">Переходим в папку, где у вас всё будет аккуратно лежать и скачиваем репу:</p>
  <pre id="6Xat">git clone https://github.com/OpenBMB/VoxCPM.git
cd VoxCPM
</pre>
  <h2 id="настройка-доступа-к-hugging-face">Настройка доступа к Hugging Face</h2>
  <p id="C49j">Для ускорения загрузки моделей и повышения лимитов запросов рекомендуется настроить токен доступа на Hugging Face</p>
  <h3 id="получение-токена">Получение токена</h3>
  <ol id="TSEf">
    <li id="Mod9">Перейдите на <a href="https://huggingface.co/settings/tokens" target="_blank">https://huggingface.co/settings/tokens</a></li>
    <li id="WUoP">Создайте новый токен с правами <strong>Read</strong></li>
    <li id="LtUN">Скопируйте токен (формат: <code>hf_...</code>)</li>
  </ol>
  <h3 id="настройка-токена">Настройка токена</h3>
  <p id="ZO7z"><strong>Способ 1: Через huggingface-cli</strong></p>
  <pre id="mmYn">pip install -U huggingface_hub
hf auth login
# Вставьте токен при запросе
</pre>
  <p id="3ooi"><strong>Способ 2: Через переменную окружения</strong></p>
  <pre id="tu4Z"># Временно для сессии
export HF_TOKEN=&quot;hf_ваш_токен_здесь&quot;

# Или навсегда (добавить в ~/.zshrc)
echo &#x27;export HF_TOKEN=&quot;hf_ваш_токен_здесь&quot;&#x27; &gt;&gt; ~/.zshrc
source ~/.zshrc
</pre>
  <h2 id="установка-python-310-через-pyenv">Установка Python 3.10+ через pyenv</h2>
  <h3 id="установка-homebrew-если-не-установлен-но-навряд-ли-конечно">Установка Homebrew (если не установлен, но навряд ли, конечно)</h3>
  <pre id="2Xt7">/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;
</pre>
  <h3 id="установка-pyenv-и-зависимостей">Установка pyenv и зависимостей</h3>
  <pre id="9gRa">brew install pyenv openssl readline sqlite3 xz zlib pkgconf autoconf
</pre>
  <h3 id="настройка-окружения-оболочки">Настройка окружения оболочки</h3>
  <p id="8IBO">Добавьте следующие строки в <code>~/.zshrc</code>:</p>
  <pre id="nQdC">export PYENV_ROOT=&quot;$HOME/.pyenv&quot;
export PATH=&quot;$PYENV_ROOT/bin:$PATH&quot;
eval &quot;$(pyenv init --path)&quot;
eval &quot;$(pyenv init -)&quot;
</pre>
  <p id="KWgk">Примените изменения:</p>
  <pre id="Kr3r">source ~/.zshrc
</pre>
  <h3 id="установка-python-31014">Установка Python 3.10.14</h3>
  <pre id="eZcN">pyenv install 3.10.14
</pre>
  <h3 id="настройка-версии-для-проекта">Настройка версии для проекта</h3>
  <p id="U8EM">Перейдите в папку проекта, которую мы скачали ранее и указываем локальную версию Python:</p>
  <pre id="T3Rc">cd ~/Documents/ai/VoxCPM 
pyenv local 3.10.14
python --version  # Должно вывести: Python 3.10.14
</pre>
  <h2 id="настройка-проекта-и-установка-зависимостей">Настройка проекта и установка зависимостей</h2>
  <h3 id="создание-виртуального-окружения">Создание виртуального окружения</h3>
  <pre id="fHZ0"># В папке проекта
python -m venv venv
source venv/bin/activate
pip install --upgrade pip
</pre>
  <h3 id="установка-проекта-в-режиме-разработки">Установка проекта в режиме разработки</h3>
  <pre id="6BbR">pip install -e .
</pre>
  <h3 id="проверка-установки">Проверка установки</h3>
  <pre id="KE1d">which voxcpm
# Должно указывать на путь внутри venv

voxcpm --help
# Должно отобразить справку по командам и доступным флагам
</pre>
  <h3 id="скачиваем-модель-в-папку">Скачиваем модель в папку</h3>
  <p id="V0TC">Делаем папку для модели и качаем модель:</p>
  <pre id="Ptl3">mkdir -p models/VoxCPM-2B
hf download openbmb/VoxCPM2 --local-dir ./models/VoxCPM2
</pre>
  <h2 id="использование-через-cli">Использование через CLI</h2>
  <h3 id="базовый-синтаксис">Базовый синтаксис</h3>
  <p id="jQ7M">В без все просто:</p>
  <pre id="CvTx">voxcpm &lt;режим&gt; [параметры]
</pre>
  <p id="TQmi">Но мы пойдём путём клонирования своего голоса для озвучки текста.</p>
  <h3 id="готовим-файл-для-клонирования">Готовим файл для клонирования</h3>
  <p id="DrjQ">Я просто записал аудиосообщение в одном голубом мессенджере, сохранил его и перекодировал через ffmpeg в wav. Текст сообщения был такой: Это образец моего голоса. Я говорю здесь целых пять секунд.</p>
  <p id="HGR6">Файл с текстом и аудио сохранил в папке ./ref. Т. е. ref.txt и ref.wav</p>
  <h3 id="готовим-текст-для-озвучки">Готовим текст для озвучки</h3>
  <p id="byzC">Создам папку output и inpit чтобы папку не замусоривать. Создаю текст для озвучки:</p>
  <pre id="vV2y">vim ./inpit/anime.txt
</pre>
  <p id="bXiU">Накидал туда описание аниме - <a href="https://hd.kinopoisk.ru/film/b04631f828704a91b2c4730cf1c32699" target="_blank">Ателье колдовских колпаков</a></p>
  <h3 id="запускаем-озвучку-с-клонированием-нашего-голоса">Запускаем озвучку с клонированием нашего голоса</h3>
  <p id="k30l">Запускаем озвучку нашего текста с клонированием голоса в папку output:</p>
  <pre id="IcdW">voxcpm clone --text &quot;$(cat ./inpit/anime.txt)&quot; --prompt-audio ./ref/ref.wav --prompt-text &quot;$(cat ./ref/ref.txt)&quot; --reference-audio ./ref/ref.wav --output ./output/anime_18.wav --model-path ./models/VoxCPM2 --device mps --no-optimize --cfg-value 1.8 --inference-timesteps 20 --normalize --no-denoiser
</pre>
  <h3 id="что-у-нас-по-параметрам">Что у нас по параметрам?</h3>
  <p id="Pkth">Параметр</p>
  <p id="lKG2">Значение в команде</p>
  <p id="2fJZ">Назначение</p>
  <p id="cZQq"><code>voxcpm clone</code></p>
  <p id="fXwd"><code>clone</code></p>
  <p id="i9Fc">Активация режима клонирования голоса</p>
  <p id="o0gO"><code>--text</code></p>
  <p id="yx27"><code>&quot;$(cat ./inpit/anime.txt)&quot;</code></p>
  <p id="3vRL">Целевой текст для генерации</p>
  <p id="MVHt"><code>--prompt-audio</code></p>
  <p id="cF0I"><code>./ref/ref.wav</code></p>
  <p id="t0Z6">Аудио-референс</p>
  <p id="hCLk"><code>--prompt-text</code></p>
  <p id="uHDf"><code>&quot;$(cat ./ref/ref.txt)&quot;</code></p>
  <p id="8qsw">Точная транскрипция <code>prompt-audio</code></p>
  <p id="WvYm"><code>--reference-audio</code></p>
  <p id="viCK"><code>./ref/ref.wav</code></p>
  <p id="AeX3">Аудио-референс для тембральной идентичности</p>
  <p id="aQ72"><code>--output</code></p>
  <p id="eCSx"><code>./output/anime_18.wav</code></p>
  <p id="Vqfu">Путь к выходному файлу</p>
  <p id="WIPA"><code>--model-path</code></p>
  <p id="Gjg0"><code>./models/VoxCPM2</code></p>
  <p id="YCvs">Локальный каталог с весами модели</p>
  <p id="6f01"><code>--device</code></p>
  <p id="93G5"><code>mps</code></p>
  <p id="kQWd">Платформа на которой мы играемся</p>
  <p id="KT3S"><code>--no-optimize</code></p>
  <p id="Y1j8"><em>без значения</em></p>
  <p id="sXTW">Отключение <code>torch.compile</code></p>
  <p id="B0ER"><code>--cfg-value</code></p>
  <p id="VLUk"><code>1.8</code></p>
  <p id="NOvi">Значение Classifier-Free Guidance</p>
  <p id="wbf1"><code>--inference-timesteps</code></p>
  <p id="YXSN"><code>20</code></p>
  <p id="FXOK">Количество шагов диффузионного декодера</p>
  <p id="830G"><code>--normalize</code></p>
  <p id="BdLc"><em>без значения</em></p>
  <p id="dlwR">Нормализация громкости выхода</p>
  <p id="AvqR"><code>--no-denoiser</code></p>
  <p id="THKW"><em>без значения</em></p>
  <p id="envz">Отключение модуля ZipEnhancer</p>
  <h3 id="результат">Результат</h3>
  <p id="V13l">На выходе получился файл 35.02. Послушать его можно ниже:</p>
  <p id="1m1X"><a href="https://blog.tatarinovms.space/audio/voxcpm/output.mp3" target="_blank">Скачать запись</a></p>
  <figure id="AdV3" class="m_column">
    <iframe src="https://www.youtube.com/embed/TS3jRcnweWY?autoplay=0&loop=0&mute=0"></iframe>
    <figcaption>ютуб</figcaption>
  </figure>
  <figure id="p4F9" class="m_column">
    <iframe src="https://rutube.ru/play/embed/e9b02da8de79e9494f3959cfece86918/"></iframe>
    <figcaption>рутуб</figcaption>
  </figure>
  <figure id="9sta" class="m_column">
    <iframe src="https://vk.com/video_ext.php?oid=-230469137&id=456239119&autoplay=0"></iframe>
    <figcaption>вк видео</figcaption>
  </figure>
  <p id="FYKn"></p>
  <h2 id="литература-для-изучения">Литература для изучения</h2>
  <ul id="bt54">
    <li id="Baz8">GitHub репозиторий: <a href="https://github.com/OpenBMB/VoxCPM" target="_blank">https://github.com/OpenBMB/VoxCPM</a></li>
    <li id="KkuA">Страница модели на Hugging Face: <a href="https://huggingface.co/openbmb/VoxCPM2" target="_blank">https://huggingface.co/openbmb/VoxCPM2</a></li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@beaverclan/synctogit</guid><link>https://teletype.in/@beaverclan/synctogit?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/synctogit?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>Синхронизация кода в два репозитория одновременно</title><pubDate>Tue, 21 Apr 2026 19:28:52 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/6c/4f/6c4f1c6d-5012-4a37-8ea0-d35943788975.png"></media:content><category>Blog</category><description><![CDATA[<img src="https://img4.teletype.in/files/f4/7d/f47d57b0-4b14-4c17-b2c1-f538fb36e8ad.png"></img>В условиях турбулентной работы облачных сервисов (снова так скажем) вопрос избыточности хранения кода становится актуальным. Держать проект только на GitHub — это риск. Решением может стать зеркалирование кода в другой сервис, например, на GitVerse.]]></description><content:encoded><![CDATA[
  <figure id="pUWv" class="m_column">
    <img src="https://img4.teletype.in/files/f4/7d/f47d57b0-4b14-4c17-b2c1-f538fb36e8ad.png" width="1424" />
  </figure>
  <p id="r3zy">В условиях турбулентной работы облачных сервисов (снова так скажем) вопрос избыточности хранения кода становится актуальным. Держать проект только на GitHub — это риск. Решением может стать зеркалирование кода в другой сервис, например, на GitVerse.</p>
  <p id="l7KB">Обычно мы привыкли, что один <code>remote</code> (например, <code>origin</code>) привязан к одному URL. Но Git позволяет добавить несколько URL для push-операций в один remote.</p>
  <h3 id="PajP">Как это настроить</h3>
  <p id="1jAd">Если у вас уже есть основной репозиторий на GitHub, достаточно добавить второй URL для отправки данных:</p>
  <pre id="QRad">git remote set-url --add --push origin git@gitverse.ru:tatarinovms/ShadowRocketSimpleConfig.git
</pre>
  <p id="SQC6">Теперь при выполнении команды <code>git push origin</code> ваши изменения полетят сразу в оба репозитория.</p>
  <h3 id="hnjv">Нюансы с конфликтами</h3>
  <p id="oYBm">Главная проблема возникает, когда в одном из зеркал есть изменения, которых нет в другом (например, вы отредактировали README прямо в веб-интерфейсе GitVerse). В этом случае обычный push будет отклонен.</p>
  <p id="v8pg">Решить это можно через создание временного remote для «проблемного» зеркала и слияние веток:</p>
  <ol id="fet7">
    <li id="lb7T">Добавляем зеркало как отдельный remote:<code>git remote add gitverse git@gitverse.ru:tatarinovms/ShadowRocketSimpleConfig.git </code></li>
    <li id="YDZ7">Подтягиваем изменения, разрешая несвязанные истории:<code>git pull gitverse main --no-rebase --allow-unrelated-histories </code></li>
    <li id="FNnm">После разрешения конфликтов снова пушим всё в <code>origin</code>.</li>
  </ol>
  <p id="jKqx">Теперь код синхронизирован, а вы спите спокойно, зная, что копия вашего проекта лежит на разных площадках.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@beaverclan/podsync</guid><link>https://teletype.in/@beaverclan/podsync?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan</link><comments>https://teletype.in/@beaverclan/podsync?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=beaverclan#comments</comments><dc:creator>beaverclan</dc:creator><title>Podsync - YouTube канал в подкаст-приемник</title><pubDate>Sun, 12 Apr 2026 14:01:27 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/07/93/0793e1f1-591a-48a0-bd29-95d73c38e184.png"></media:content><category>Blog</category><description><![CDATA[<img src="https://blog.tatarinovms.space/images/posts/podsync/logo.webp"></img>Podsync — маленький проектик на go, который преобразует каналы с YouTube или Vimeo в обычные подкаст-фиды (RSS). Идея проста: у меня есть каналы, которые я больше слушаю, чем смотрю, и мне хотелось бы делать это в моём подкаст-приёмнике. Гайд будет на Debian-based системы.]]></description><content:encoded><![CDATA[
  <figure id="j1gZ" class="m_retina">
    <img src="https://blog.tatarinovms.space/images/posts/podsync/logo.webp" width="1344" />
  </figure>
  <p id="JCSQ">Podsync — маленький проектик на go, который преобразует каналы с YouTube или Vimeo в обычные подкаст-фиды (RSS). Идея проста: у меня есть каналы, которые я больше слушаю, чем смотрю, и мне хотелось бы делать это в моём подкаст-приёмнике. Гайд будет на Debian-based системы.</p>
  <h2 id="как-это-работает">Как это работает</h2>
  <p id="lkiv">Принцип работы Podsync довольно простой:</p>
  <ol id="LBrG">
    <li id="H2nY">В конфиге прописываем каналы</li>
    <li id="Hd2o">Podsync по расписанию проверяет обновления</li>
    <li id="ySm7">Скачивает новые видео через yt-dlp и конвертирует их в аудио в MP3 через ffmpeg или оставляет видео. Тут уж как хотите.</li>
    <li id="0D3o">Генерирует RSS-фид, доступный по HTTP/HTTPS</li>
  </ol>
  <p id="UXGH">Вы просто добавляете ссылку вида <code>https://your-domain.com/feed_id.xml</code> в подкаст-клиент — и всё работает.</p>
  <h2 id="установка-зависимостей">Установка зависимостей</h2>
  <p id="tOdq">Для работы Podsync нужны <code>yt-dlp</code>, <code>ffmpeg</code> и <code>deno</code> (как JS runtime для yt-dlp на всякий случай):</p>
  <pre id="Mbyk">apt update &amp;&amp; apt install -y yt-dlp ffmpeg curl
</pre>
  <p id="XTC9">Deno устанавливается через официальный скрипт:</p>
  <pre id="Y1O8">curl -fsSL https://deno.land/install.sh | sh
</pre>
  <p id="7MxG">Также понадобится Go, если планируете собирать из исходников:</p>
  <pre id="Zeyp">apt install -y golang
</pre>
  <h2 id="установка-podsync">Установка Podsync</h2>
  <h3 id="вариант-a-сборка-из-исходников">Вариант A: Сборка из исходников</h3>
  <pre id="retv">git clone https://github.com/mxpv/podsync /mnt/dietpi_userdata/podsync
cd /mnt/dietpi_userdata/podsync
make
</pre>
  <h3 id="вариант-бэ-готовый-бинарь">Вариант Бэ: Готовый бинарь</h3>
  <p id="U8Wn">Скачиваем с <a href="https://github.com/mxpv/podsync/releases" target="_blank">GitHub Releases</a>:</p>
  <pre id="SeE6">cd /mnt/dietpi_userdata/podsync
curl -L -o podsync &quot;https://github.com/mxpv/podsync/releases/download/v2.8.0/podsync-v2.8.0-linux-amd64&quot;
chmod +x podsync
</pre>
  <h3 id="вариант-цэ-docker">Вариант Цэ: Docker</h3>
  <p id="jZPr">Если предпочитаете контейнеры:</p>
  <pre id="7wvj">services:
  podsync:
    image: ghcr.io/mxpv/podsync
    container_name: podsync
    volumes:
      - ./data:/app/data/
      - ./db:/app/db/
      - ./config.toml:/app/config.toml
    ports:
      - 8181:8080
</pre>
  <p id="SFhS">Запуск:</p>
  <pre id="1PpR">docker compose up -d
</pre>
  <h2 id="youtube-data-api-v3-ключ">YouTube Data API v3 ключ</h2>
  <p id="YnNI">Ключевой момент — для работы Podsync нужен <strong>YouTube Data API v3 ключ</strong>. Без него парсинг каналов не работает.</p>
  <p id="D3X8">Получить ключ можно здесь: <a href="https://developers.google.com/youtube/registering_an_application" target="_blank">https://developers.google.com/youtube/registering_an_application</a></p>
  <p id="if7x">В конфиге нужно указать ключ в секции:</p>
  <pre id="nujC">[tokens]
youtube = &quot;YOUR_YOUTUBE_API_KEY&quot;
</pre>
  <p id="y8yT">Или даже несколько для ротации:</p>
  <pre id="2DlI">youtube = [&quot;KEY1&quot;, &quot;KEY2&quot;, &quot;KEY3&quot;]
</pre>
  <h2 id="настройка-конфигурации">Настройка конфигурации</h2>
  <p id="rgyM">Собственно и сам конфиг. Создаём файл <code>config.toml</code> на основе примера:</p>
  <pre id="Jzne">cp /mnt/dietpi_userdata/podsync/config.toml.example /mnt/dietpi_userdata/podsync/config.toml
nano /mnt/dietpi_userdata/podsync/config.toml
</pre>
  <p id="cJUH">Для примера:</p>
  <pre id="n90n">[server]
port = 8181
hostname = &quot;https://poddown.tatarinovlovesbeaver.su&quot;

[cleanup]
keep_last = 2

[storage]
  [storage.local]
  data_dir = &quot;/mnt/dietpi_userdata/podsync/data&quot;

[tokens]
youtube = &quot;тут ваш ключ из пункта выше&quot;

[feeds]
    [feeds.MaximTatarinov]
    url = &quot;https://www.youtube.com/@MaximTatarinov&quot;
    format = &quot;video&quot;
    update_period = &quot;8h&quot;
    page_size = 5
    youtube_dl_args = [&quot;--js-runtimes&quot;, &quot;deno:/root/.deno/bin/deno&quot;]

[log]
filename = &quot;podsync.log&quot;
max_size = 50
max_age = 30
max_backups = 1
compress = true
debug = false
</pre>
  <p id="MW7H">Конфиг готов.</p>
  <h2 id="запуск-как-systemd-сервис">Запуск как systemd-сервис</h2>
  <p id="b0c1">Чтобы Podsync работал в фоне и запускался автоматически, создаём сервис:</p>
  <pre id="FSD8">nano /etc/systemd/system/podsync.service
</pre>
  <pre id="WNoQ">[Unit]
Description=Podsync
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/mnt/dietpi_userdata/podsync/bin/podsync
WorkingDirectory=/mnt/dietpi_userdata/podsync
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
Environment=&quot;PATH=/root/.deno/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&quot;

[Install]
WantedBy=multi-user.target
</pre>
  <p id="ajdG">Активируем и запускаем:</p>
  <pre id="8h33">systemctl daemon-reload
systemctl enable podsync
systemctl start podsync
</pre>
  <p id="JPEE">Проверить статус:</p>
  <pre id="HBrk">systemctl status podsync
journalctl -u podsync -f
</pre>
  <h2 id="reverse-proxy-через-nginx">Reverse proxy через nginx</h2>
  <p id="EKGa">Podsync у меня работает без TLS, поэтому HTTPS у нас будет обрабатывать nginx. Конфиг для сайта:</p>
  <pre id="YBhW">server {
    listen 80;
    listen [::]:80;
    server_name poddown.tatarinovlovesbeaver.su;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    listen 443 quic;
    listen [::]:443 quic;
    quic_retry on;
    quic_gso on;
    add_header Alt-Svc &#x27;h3=&quot;:443&quot;; ma=86400&#x27; always;

    server_name poddown.tatarinovlovesbeaver.su;

    ssl_certificate /etc/letsencrypt/live/poddown.tatarinovlovesbeaver.su/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/poddown.tatarinovlovesbeaver.su/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://127.0.0.1:8181;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
</pre>
  <p id="sBDX">Применить:</p>
  <pre id="kWA0">nginx -t &amp;&amp; systemctl reload nginx
</pre>
  <h2 id="фильтры-и-тонкая-настройка">Фильтры и тонкая настройка</h2>
  <p id="shWg">Podsync позволяет не просто скачивать всё подряд, а выбирать контент по правилам. Например, скачивать только эпизоды, содержащие «обзор» и длительностью до 2 часов:</p>
  <pre id="ANlH">[feeds]
    [feeds.ID1]
    url = &quot;https://www.youtube.com/@channel&quot;
    format = &quot;audio&quot;
    filters = { title = &quot;обзор&quot;, max_duration = 7200, max_age = 30 }
</pre>
  <p id="cae8">Доступные фильтры:</p>
  <p id="kQBP">Параметр</p>
  <p id="yhPa">Описание</p>
  <p id="Qm25"><code>title</code></p>
  <p id="qdcf">Regex для совпадения в заголовке</p>
  <p id="kOdT"><code>not_title</code></p>
  <p id="636V">Regex для исключения по заголовку</p>
  <p id="wD2V"><code>description</code></p>
  <p id="tgd7">Regex для совпадения в описании</p>
  <p id="ab6m"><code>not_description</code></p>
  <p id="a0Aa">Regex для исключения по описанию</p>
  <p id="IZ99"><code>min_duration</code></p>
  <p id="5KCC">Минимальная длительность (секунды)</p>
  <p id="ORoW"><code>max_duration</code></p>
  <p id="EvTJ">Максимальная длительность (секунды)</p>
  <p id="Ecnz"><code>min_age</code></p>
  <p id="Ei9M">Минимальный возраст (дни)</p>
  <p id="bFNo"><code>max_age</code></p>
  <p id="2Iyo">Максимальный возраст (дни)</p>
  <p id="TOTF">Также можно задать кастомный формат видео:</p>
  <pre id="9DgJ">[feeds]
    [feeds.MaximTatarinov]
    format = &quot;custom&quot;
    custom_format = { youtube_dl_format = &quot;bestaudio[ext=m4a]&quot;, extension = &quot;m4a&quot; }
</pre>
  <h2 id="итог">Итог</h2>
  <p id="G2uO">RSS-лента доступна по адресу:</p>
  <pre id="TewD">https://poddown.tatarinovlovesbeaver.su/MaximTatarinov.xml
</pre>
  <p id="aoWA">Подставляем её в ваш подкаст-клиент и вуаля!</p>

]]></content:encoded></item></channel></rss>