caps_lock_to_hyper.json
488 B
1) Caps Lock = Hyper
Caps Lock забран под Hyper-модификатор (cmd+ctrl+option+shift).
Это самое популярное решение и его умеют делать многие утилиты, но мне важно, чтобы все, что относится к вводу, жило в одном месте. Я хочу, чтобы именно Karabiner отвечал за “железо клавиатуры”. Поэтому Hyper я тоже задаю на этом уровне.
Задача Karabiner тут ровно одна: заменить Caps Lock на комбинацию, которая почти нигде не занята. Это превращает Caps в удобную “суперклавишу” под мизинцем и даёт большой чистый слой хоткеев.
Прикладываю JSON-файл с модификацией
Дальше покажу, как Space-hold превращается в слой команд (хоткеев).
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
Caps Lock забран под Hyper-модификатор (cmd+ctrl+option+shift).
Это самое популярное решение и его умеют делать многие утилиты, но мне важно, чтобы все, что относится к вводу, жило в одном месте. Я хочу, чтобы именно Karabiner отвечал за “железо клавиатуры”. Поэтому Hyper я тоже задаю на этом уровне.
Задача Karabiner тут ровно одна: заменить Caps Lock на комбинацию, которая почти нигде не занята. Это превращает Caps в удобную “суперклавишу” под мизинцем и даёт большой чистый слой хоткеев.
Прикладываю JSON-файл с модификацией
Дальше покажу, как Space-hold превращается в слой команд (хоткеев).
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
✍4❤3❤🔥1
space_hold_f18.json
402 B
2) Слой на Space: tap = пробел, hold = F18
Это мой базовый слой и один из самых удачных с точки зрения эргономики.
Логика простая: пробел остаётся пробелом, а удержание пробела превращается в “служебную клавишу режима” (в моём случае это виртуальный F18). Дальше Hammerspoon слушает F18 и включает слой команд.
Почему именно Space: он находится в центре клавиатуры и всегда под большим пальцем. Это максимально естественная “кнопка режима”, потому что большой палец почти всегда свободен и его удобно держать в удержании, не мешая печати.
Ключевое преимущество такого подхода в том, что он не ломает текстовый ввод. Я печатаю как обычно: быстрый тап по пробелу всегда даёт пробел. А когда нужно управление, я просто зажимаю Space и получаю временный слой команд, который живёт ровно пока удержание активно.
Самый важный нюанс здесь это тайминг удержания. Я несколько раз перенастраивал этот слой и переписывал JSON, пока не нашёл комфортное значение to_if_held_down_threshold_milliseconds: чтобы режим не включался случайно при печати, но включался мгновенно, когда нужно.
JSON с этой модификацией прикрепляю.
Дальше разберу, как я строю иерархию команд через F17.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
Это мой базовый слой и один из самых удачных с точки зрения эргономики.
Логика простая: пробел остаётся пробелом, а удержание пробела превращается в “служебную клавишу режима” (в моём случае это виртуальный F18). Дальше Hammerspoon слушает F18 и включает слой команд.
Почему именно Space: он находится в центре клавиатуры и всегда под большим пальцем. Это максимально естественная “кнопка режима”, потому что большой палец почти всегда свободен и его удобно держать в удержании, не мешая печати.
Ключевое преимущество такого подхода в том, что он не ломает текстовый ввод. Я печатаю как обычно: быстрый тап по пробелу всегда даёт пробел. А когда нужно управление, я просто зажимаю Space и получаю временный слой команд, который живёт ровно пока удержание активно.
Самый важный нюанс здесь это тайминг удержания. Я несколько раз перенастраивал этот слой и переписывал JSON, пока не нашёл комфортное значение to_if_held_down_threshold_milliseconds: чтобы режим не включался случайно при печати, но включался мгновенно, когда нужно.
JSON с этой модификацией прикрепляю.
Дальше разберу, как я строю иерархию команд через F17.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
✍3⚡3❤2
grave_hold_f17.json
485 B
3) backtick hold = F17
По той же логике у меня есть второй слой на backtick: тап печатает символ, а удержание отдаёт виртуальный F17. Этот слой я использую как “вход” в дополнительную иерархию команд через Leader Key.
Схема такая: удержание backtick включает слой, а дальше я могу проваливаться в подслои по смыслу.
Например, backtick (hold) + F у меня означает Finder. После этого ближайшие клавиши на клавиатуре становятся быстрыми шорткатами для открытия нужных мне папок в Finder. Это превращает навигацию по файловой структуре в пару нажатий, без поиска по боковой панели и без ручного кликанья.
Точно так же у меня сделан подслой для ChatGPT: backtick (hold) + G. В этом режиме ближайшие клавиши открывают нужный проект в ChatGPT. По сути, это “меню проектов под пальцами”, которое вызывается моментально и работает предсказуемо.
Такой второй слой удобен тем, что он не смешивает всё в одну плоскость: один слой у меня про повседневную навигацию, а backtick-слой становится входом в более глубокие, тематические подслои (папки, проекты, инструменты).
В следующем посте покажу, как я точечно правлю RU-пунктуацию в Karabiner, не ломая раскладку.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
По той же логике у меня есть второй слой на backtick: тап печатает символ, а удержание отдаёт виртуальный F17. Этот слой я использую как “вход” в дополнительную иерархию команд через Leader Key.
Схема такая: удержание backtick включает слой, а дальше я могу проваливаться в подслои по смыслу.
Например, backtick (hold) + F у меня означает Finder. После этого ближайшие клавиши на клавиатуре становятся быстрыми шорткатами для открытия нужных мне папок в Finder. Это превращает навигацию по файловой структуре в пару нажатий, без поиска по боковой панели и без ручного кликанья.
Точно так же у меня сделан подслой для ChatGPT: backtick (hold) + G. В этом режиме ближайшие клавиши открывают нужный проект в ChatGPT. По сути, это “меню проектов под пальцами”, которое вызывается моментально и работает предсказуемо.
Такой второй слой удобен тем, что он не смешивает всё в одну плоскость: один слой у меня про повседневную навигацию, а backtick-слой становится входом в более глубокие, тематические подслои (папки, проекты, инструменты).
В следующем посте покажу, как я точечно правлю RU-пунктуацию в Karabiner, не ломая раскладку.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
✍2❤1🤯1👌1
ru_option_punct.json
1003 B
4) RU-раскладка: точечные правила только для русского набора
У русскоязычных пользователей macOS есть типовая проблема: в стандартной RU-раскладке запятая и точка находятся на Shift+6 и Shift+7. При активном письме это неудобно и замедляет набор.
Самый распространённый обходной путь, который я видел, это перейти в системных настройках на раскладку Russian PC. Там пунктуация размещена практичнее. Но цена такого решения для меня слишком высокая: меняется поведение части символов, и в том числе ломается привычная предсказуемость со знаком вопроса между EN и RU.
Поэтому я оставил стандартную RU-раскладку и сделал точечное исправление через Karabiner. Правило включается только в русском input source и не трогает английскую раскладку:
- Option + запятая печатает ,
- Option + точка печатает .
В результате , и . в RU оказываются в тех же позициях, что и в EN, но с удержанием Option. Для моего набора это удобнее, чем переход на Russian PC: пунктуация становится доступной, а остальная логика русской раскладки остаётся неизменной.
Здесь я описываю только текущую версию моего подхода к настройкам Karabiner. Вариантов реализации и комбинаций правил можно придумать множество, и со временем мой сетап, скорее всего, будет расширяться и эволюционировать. В этом посте я фиксирую именно те решения, которые на данный момент лучше всего работают в моём ежедневном workflow.
В следующем посте расскажу, как эти сигналы из Karabiner связаны с Hammerspoon и как я превращаю их в конкретные действия.
В следующем посте покажу мой базовый каркас конфига Hammerspoon.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
У русскоязычных пользователей macOS есть типовая проблема: в стандартной RU-раскладке запятая и точка находятся на Shift+6 и Shift+7. При активном письме это неудобно и замедляет набор.
Самый распространённый обходной путь, который я видел, это перейти в системных настройках на раскладку Russian PC. Там пунктуация размещена практичнее. Но цена такого решения для меня слишком высокая: меняется поведение части символов, и в том числе ломается привычная предсказуемость со знаком вопроса между EN и RU.
Поэтому я оставил стандартную RU-раскладку и сделал точечное исправление через Karabiner. Правило включается только в русском input source и не трогает английскую раскладку:
- Option + запятая печатает ,
- Option + точка печатает .
В результате , и . в RU оказываются в тех же позициях, что и в EN, но с удержанием Option. Для моего набора это удобнее, чем переход на Russian PC: пунктуация становится доступной, а остальная логика русской раскладки остаётся неизменной.
Здесь я описываю только текущую версию моего подхода к настройкам Karabiner. Вариантов реализации и комбинаций правил можно придумать множество, и со временем мой сетап, скорее всего, будет расширяться и эволюционировать. В этом посте я фиксирую именно те решения, которые на данный момент лучше всего работают в моём ежедневном workflow.
В следующем посте расскажу, как эти сигналы из Karabiner связаны с Hammerspoon и как я превращаю их в конкретные действия.
В следующем посте покажу мой базовый каркас конфига Hammerspoon.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝3👍1🤔1
Hammerspoon это инструмент, который превращает macOS в настраиваемую среду под конкретный workflow. По сути, это лёгкая надстройка, которая слушает события клавиатуры и системы и выполняет действия, описанные кодом на Lua. Без лишнего UI и “мастеров настроек”: поведение задаётся напрямую, в виде правил и сценариев.
Главная ценность Hammerspoon в том, что он работает не как “ещё один ремаппер”, а как движок логики:
Запуск/фокус приложений, окна (позиции/раскладки/мониторы), навигация по окнам, медиа-контроль, системные действия, логика (проверки/задержки/фолбэки).
В моей практике это выглядит так:
- Caps Lock (мой “суперклавишный” слой): Caps Lock + любая цифра/клавиша у меня привязаны к запуску или фокусу конкретных приложений. Это основной способ перемещаться по системе без Dock и поиска.
- Space-layer: удержание Space включает отдельный режим, где собраны быстрые действия, в том числе медиа-контроль и запуск дополнительных приложений.
- BackTick-layer: удержание backtick завязано на Leader Key: это вход в подслои, где, например, отдельные клавиши открывают нужные папки в Finder или конкретные проекты в ChatGPT. Я описывал об этом ранее тут.
Технически всё это описывается в конфигурации init.lua. Стандартный путь:
~/.hammerspoon/init.lua
Конфиг удобно хранить как код: версионировать, переносить на другие устройства, развивать поэтапно. В моём сетапе конфигурация синхронизируется через iCloud (через симлинк): система видит стандартный путь ~/.hammerspoon/, но фактически конфиг лежит в папке iCloud Drive, поэтому одинаковое поведение доступно на всех устройствах.
Базовый пример того, как выглядит действие в Hammerspoon: создаётся хоткей и привязывается к функции, которая делает нужное действие, например открывает приложение. В реальном конфиге это дополняется слоями, модальными режимами и сценариями, которые делают управление системой быстрым и предсказуемым.
Как выглядит запуск приложения в Hammerspoon
Базовая конструкция в конфиге выглядит так:
-
-
- "
-
-
Альтернативный вариант для многих задач:
В следующем посте расскажу про Window Manager в Hammerspoon.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
Главная ценность Hammerspoon в том, что он работает не как “ещё один ремаппер”, а как движок логики:
Запуск/фокус приложений, окна (позиции/раскладки/мониторы), навигация по окнам, медиа-контроль, системные действия, логика (проверки/задержки/фолбэки).
В моей практике это выглядит так:
- Caps Lock (мой “суперклавишный” слой): Caps Lock + любая цифра/клавиша у меня привязаны к запуску или фокусу конкретных приложений. Это основной способ перемещаться по системе без Dock и поиска.
- Space-layer: удержание Space включает отдельный режим, где собраны быстрые действия, в том числе медиа-контроль и запуск дополнительных приложений.
- BackTick-layer: удержание backtick завязано на Leader Key: это вход в подслои, где, например, отдельные клавиши открывают нужные папки в Finder или конкретные проекты в ChatGPT. Я описывал об этом ранее тут.
Технически всё это описывается в конфигурации init.lua. Стандартный путь:
~/.hammerspoon/init.lua
Конфиг удобно хранить как код: версионировать, переносить на другие устройства, развивать поэтапно. В моём сетапе конфигурация синхронизируется через iCloud (через симлинк): система видит стандартный путь ~/.hammerspoon/, но фактически конфиг лежит в папке iCloud Drive, поэтому одинаковое поведение доступно на всех устройствах.
Базовый пример того, как выглядит действие в Hammerspoon: создаётся хоткей и привязывается к функции, которая делает нужное действие, например открывает приложение. В реальном конфиге это дополняется слоями, модальными режимами и сценариями, которые делают управление системой быстрым и предсказуемым.
Как выглядит запуск приложения в Hammerspoon
Базовая конструкция в конфиге выглядит так:
hs.hotkey.bind(hyper, "i", function()
hs.application.open("путь к приложению")
end)
-
hs.hotkey.bind(...) регистрирует сочетание клавиш-
hyper это набор модификаторов (Hyper-слой)- "
i" это клавиша-триггер внутри слоя-
function() ... end это действие, которое выполняется при нажатии-
hs.application.open(...) открывает приложение по точному путиАльтернативный вариант для многих задач:
hs.application.launchOrFocus("AppName").В следующем посте расскажу про Window Manager в Hammerspoon.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2❤🔥1❤1
Window Manager в Hammerspoon
Оконный менеджмент в macOS часто сбоит на уровне деталей: фокус, анимации и внутренние состояния приложений. В моем сетапе с Hammerspoon окна управляются напрямую через API Hammerspoon (работа с объектом окна и его геометрией, а не через UI-состояния), поэтому результат получается более стабильным и предсказуемым.
Базовый интерфейс управления окнами у меня построен на слое Ctrl + Option + клавиша. Это быстрые, мышечно-запоминаемые команды:
- Ctrl+Option+A прижимает окно к левому краю (левая половина)
- Ctrl+Option+D прижимает к правому краю (правая половина)
- Ctrl+Option+S ставит окно в центр (и т.д.)
Таких сочетаний много: под каждую “логичную” кнопку заведено своё действие для перемещения или изменения размеров окна. Для скорости работы на Mac это критично: окно перестаёт быть объектом, который “нужно руками расставлять”, и становится состоянием, которое вызывается одной командой.
1) Нулевая анимация
hs.window.animationDuration = 0
2) Два способа позиционирования
2.1) moveToUnit(...)
Надёжный тайлинг в долях экрана (0..1): левый/правый полукран, четверти и т.п. Устойчив к разным экранам и Dock/Menu Bar.
2.2) setFrame(...)
Когда нужна конкретная зона: колонка 25%, нижняя четверть, центрирование и т.д. Пример “правая колонка 25%” из конфига:
Просто: берётся рабочая область экрана и окну задаётся прямоугольник, где ширина 25%, а x начинается с 75% (то есть край справа).
3) Toggle “разложить / вернуть”
Перед тайлингом сохраняется исходный frame в windowStates, затем можно одним действием вернуть окно “как было”. Это делает раскладки обратимыми.
4) Сетка окон приложения + возврат
Сценарий собирает окна текущего приложения, раскладывает сеткой по экрану, повторным запуском восстанавливает исходные позиции. Быстро наводит порядок и так же быстро возвращает обратно.
5) Перенос между мониторами
Перемещение на соседний экран по топологии: west/east/north/south. Это удобнее, чем “монитор №2”, потому что работает по реальному расположению экранов.
6) Микро-подстройка
Шаговые сдвиги (в конфиге шаг 100px) закрывают кейс “почти идеально, но нужно чуть подвинуть”.
Почему это надёжнее системных хоткеев и части готовых решений
Системные/некоторые сторонние команды часто завязаны на UI-состояние (фокус, анимации, “успел ли апп”). Здесь всё детерминированно: Hammerspoon получает окно, вычисляет целевую геометрию и применяет её напрямую (moveToUnit / setFrame / moveToScreen), при необходимости сохраняя состояние (windowStates). Плюс Hammerspoon в том, что я могу собирать собственные команды под себя и задавать в них любые параметры: точные размеры и координаты, привязку к экрану, шаги смещения, условия, сохранение/восстановление состояния и любые дополнительные характеристики, которые нужны именно моему workflow.
В следующем посте: мой подход к управлению медиа через Hammerspoon с задержками и fallback.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
Оконный менеджмент в macOS часто сбоит на уровне деталей: фокус, анимации и внутренние состояния приложений. В моем сетапе с Hammerspoon окна управляются напрямую через API Hammerspoon (работа с объектом окна и его геометрией, а не через UI-состояния), поэтому результат получается более стабильным и предсказуемым.
Базовый интерфейс управления окнами у меня построен на слое Ctrl + Option + клавиша. Это быстрые, мышечно-запоминаемые команды:
- Ctrl+Option+A прижимает окно к левому краю (левая половина)
- Ctrl+Option+D прижимает к правому краю (правая половина)
- Ctrl+Option+S ставит окно в центр (и т.д.)
Таких сочетаний много: под каждую “логичную” кнопку заведено своё действие для перемещения или изменения размеров окна. Для скорости работы на Mac это критично: окно перестаёт быть объектом, который “нужно руками расставлять”, и становится состоянием, которое вызывается одной командой.
1) Нулевая анимация
hs.window.animationDuration = 0
2) Два способа позиционирования
2.1) moveToUnit(...)
Надёжный тайлинг в долях экрана (0..1): левый/правый полукран, четверти и т.п. Устойчив к разным экранам и Dock/Menu Bar.
2.2) setFrame(...)
Когда нужна конкретная зона: колонка 25%, нижняя четверть, центрирование и т.д. Пример “правая колонка 25%” из конфига:
local win = hs.window.focusedWindow()
local screenFrame = win:screen():frame()
win:setFrame({
x = screenFrame.x + screenFrame.w * 0.75,
y = screenFrame.y,
w = screenFrame.w * 0.25,
h = screenFrame.h
})
Просто: берётся рабочая область экрана и окну задаётся прямоугольник, где ширина 25%, а x начинается с 75% (то есть край справа).
3) Toggle “разложить / вернуть”
Перед тайлингом сохраняется исходный frame в windowStates, затем можно одним действием вернуть окно “как было”. Это делает раскладки обратимыми.
4) Сетка окон приложения + возврат
Сценарий собирает окна текущего приложения, раскладывает сеткой по экрану, повторным запуском восстанавливает исходные позиции. Быстро наводит порядок и так же быстро возвращает обратно.
5) Перенос между мониторами
Перемещение на соседний экран по топологии: west/east/north/south. Это удобнее, чем “монитор №2”, потому что работает по реальному расположению экранов.
6) Микро-подстройка
Шаговые сдвиги (в конфиге шаг 100px) закрывают кейс “почти идеально, но нужно чуть подвинуть”.
Почему это надёжнее системных хоткеев и части готовых решений
Системные/некоторые сторонние команды часто завязаны на UI-состояние (фокус, анимации, “успел ли апп”). Здесь всё детерминированно: Hammerspoon получает окно, вычисляет целевую геометрию и применяет её напрямую (moveToUnit / setFrame / moveToScreen), при необходимости сохраняя состояние (windowStates). Плюс Hammerspoon в том, что я могу собирать собственные команды под себя и задавать в них любые параметры: точные размеры и координаты, привязку к экрану, шаги смещения, условия, сохранение/восстановление состояния и любые дополнительные характеристики, которые нужны именно моему workflow.
В следующем посте: мой подход к управлению медиа через Hammerspoon с задержками и fallback.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Медиа-контроль как сценарии в Hammerspoon
Хочется иметь “внутренний цифровой микшер”, привязанный к одному устройству управления: чтобы быстро контролировать медиа и звук во всех открытых приложениях. Полного решения под это я пока не нашел: то, что пробовал, даёт задержку и иногда ведёт себя нестабильно.
Через Hammerspoon получилось приблизиться к нужному ощущению хотя бы на уровне практичного минимума: быстро и предсказуемо управлять воспроизведением в конкретных приложениях. Это не микшер в полном смысле, но уже радикально ускоряет работу с медиа.
Принцип
Не “универсальная кнопка паузы”, а сценарии под конкретные приложения:
активировать нужное приложение → дать фокусу “успокоиться” → отправить правильную команду → при необходимости иметь запасной путь.
1) Spotify: play/pause напрямую
Для Spotify используется AppleScript-команда playpause.
Плюс такого подхода: это не эмуляция клавиш, команда уходит прямо в Spotify и не зависит от того, какое окно активно.
2) YouTube / Twitch и похожие плееры: фокус → k
Для YouTube и Twitch сделан одинаковый паттерн:
- активируется приложение
- выход из Space-layer перед отправкой k, чтобы слой не перехватил клавишу
- короткая задержка
- отправляется k (стандартный play/pause в плеерах)
Важно: выход из слоя здесь не “косметика”, а инженерная деталь. Если оставить слой активным, k может интерпретироваться как команда слоя, а не как команда плеера.
Пример:
Разбор:
- spaceLayer:bind({}, "t", ...) — внутри Space-layer клавиша T запускает сценарий.
- hs.application.find("Twitch") — найти запущенный Twitch (иначе показать alert).
- app:activate() — сфокусировать Twitch.
- spaceLayer:exit() — выйти из слоя, чтобы k не перехватился хоткеями слоя.
- hs.timer.doAfter(0.05, ...) — небольшая задержка для переключения фокуса.
- hs.eventtap.keyStroke({}, "k") — отправить k (play/pause).
3) Safari: тот же принцип “поймать нужный плеер”
Отдельно заведены сценарии под Safari, с тем же смыслом:
- сфокусировать браузер
- небольшая пауза
- отправить k в активную вкладку/плеер
Это удобно, когда медиа играет в разных местах: команда сначала приводит контекст в порядок, а потом уже нажимает “пауза”.
4) Apple Music и Podcasts: фокус → space + задержка
Для Music и Podcasts используется другой “язык управления”:
- активируется приложение
- небольшая задержка (чтобы фокус точно перешёл)
- отправляется space как play/pause
Задержка тут важна: без неё часть команд уходит “в никуда” или в неправильный элемент интерфейса.
5) Apple Books: UI scripting + fallback (самый надёжный вариант)
Books сделан максимально устойчиво, потому что интерфейс и состояния там бывают капризные:
- приложение активируется
- через System Events пробуется нажатие Play/Pause в меню (учтены разные названия меню и RU/EN)
- если меню не сработало, fallback: отправка space в приложение
Это по сути “двухконтурная система”: сначала самый надёжный путь (меню), затем резервный (клавиша).
В следующем посте покажу базовый код HTTP-сервера в Hammerspoon и структуру эндпоинтов.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
Хочется иметь “внутренний цифровой микшер”, привязанный к одному устройству управления: чтобы быстро контролировать медиа и звук во всех открытых приложениях. Полного решения под это я пока не нашел: то, что пробовал, даёт задержку и иногда ведёт себя нестабильно.
Через Hammerspoon получилось приблизиться к нужному ощущению хотя бы на уровне практичного минимума: быстро и предсказуемо управлять воспроизведением в конкретных приложениях. Это не микшер в полном смысле, но уже радикально ускоряет работу с медиа.
Принцип
Не “универсальная кнопка паузы”, а сценарии под конкретные приложения:
активировать нужное приложение → дать фокусу “успокоиться” → отправить правильную команду → при необходимости иметь запасной путь.
1) Spotify: play/pause напрямую
Для Spotify используется AppleScript-команда playpause.
Плюс такого подхода: это не эмуляция клавиш, команда уходит прямо в Spotify и не зависит от того, какое окно активно.
2) YouTube / Twitch и похожие плееры: фокус → k
Для YouTube и Twitch сделан одинаковый паттерн:
- активируется приложение
- выход из Space-layer перед отправкой k, чтобы слой не перехватил клавишу
- короткая задержка
- отправляется k (стандартный play/pause в плеерах)
Важно: выход из слоя здесь не “косметика”, а инженерная деталь. Если оставить слой активным, k может интерпретироваться как команда слоя, а не как команда плеера.
Пример:
spaceLayer:bind({}, "t", function()
local app = hs.application.find("Twitch")
if not app then return hs.alert.show("Twitch не запущен") end
app:activate()
spaceLayer:exit()
hs.timer.doAfter(0.05, function() hs.eventtap.keyStroke({}, "k") end)
end)
Разбор:
- spaceLayer:bind({}, "t", ...) — внутри Space-layer клавиша T запускает сценарий.
- hs.application.find("Twitch") — найти запущенный Twitch (иначе показать alert).
- app:activate() — сфокусировать Twitch.
- spaceLayer:exit() — выйти из слоя, чтобы k не перехватился хоткеями слоя.
- hs.timer.doAfter(0.05, ...) — небольшая задержка для переключения фокуса.
- hs.eventtap.keyStroke({}, "k") — отправить k (play/pause).
3) Safari: тот же принцип “поймать нужный плеер”
Отдельно заведены сценарии под Safari, с тем же смыслом:
- сфокусировать браузер
- небольшая пауза
- отправить k в активную вкладку/плеер
Это удобно, когда медиа играет в разных местах: команда сначала приводит контекст в порядок, а потом уже нажимает “пауза”.
4) Apple Music и Podcasts: фокус → space + задержка
Для Music и Podcasts используется другой “язык управления”:
- активируется приложение
- небольшая задержка (чтобы фокус точно перешёл)
- отправляется space как play/pause
Задержка тут важна: без неё часть команд уходит “в никуда” или в неправильный элемент интерфейса.
5) Apple Books: UI scripting + fallback (самый надёжный вариант)
Books сделан максимально устойчиво, потому что интерфейс и состояния там бывают капризные:
- приложение активируется
- через System Events пробуется нажатие Play/Pause в меню (учтены разные названия меню и RU/EN)
- если меню не сработало, fallback: отправка space в приложение
Это по сути “двухконтурная система”: сначала самый надёжный путь (меню), затем резервный (клавиша).
В следующем посте покажу базовый код HTTP-сервера в Hammerspoon и структуру эндпоинтов.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
5
Hammerspoon как локальный сервер и “пульт” управления
Одна из самых практичных вещей в моём сетапе Hammerspoon: он работает не только как хоткеи, но и как локальный HTTP-сервер. Это превращает Mac в “хаб”, который можно дергать командами с других устройств.
Как это устроено
- Поднимается HTTP server на фиксированном порту (в моём случае 9876).
- Авторизация сделана через header token (X-Auth-Token) вместо Basic Auth, потому что так удобнее и стабильнее интегрироваться с Shortcuts.
- Есть простой rate limit (минимальный интервал между вызовами), чтобы не спамить одинаковыми командами и не ловить гонки.
- Есть health-check / watchdog: сервер регулярно проверяет /ping и, если что-то пошло не так, перезапускается. То есть он не “умирает тихо”.
Что даёт на практике
Получается очень прямой “пульт управления”, где каждый endpoint это действие:
- медиа-команды по конкретным приложениям (например, Twitch/YouTube/Spotify/Music/Podcasts/Books)
- системные команды, включая pmset displaysleepnow (быстро погасить дисплеи)
Ключевой момент: это не “универсальная кнопка”, а точечные сценарии, завязанные на конкретные приложения и нужную последовательность шагов (активация, задержка, команда и т.д.).
Архитектура
- Mac Studio выступает как хаб: он выполняет действия локально и предсказуемо.
- Shortcuts на iPhone или другом Mac выступают как клиенты: нажал кнопку → ушёл HTTP-запрос → на Mac выполнилась команда.
Это даёт ощущение “домашнего пульта”: управление медиа и системными действиями не привязано к тому, где сейчас руки, какое окно активно и на каком устройстве ты находишься.
Дальше разберу связку Hammerspoon + displayplacer: авто-раскладка экранов без ручной правки.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
Одна из самых практичных вещей в моём сетапе Hammerspoon: он работает не только как хоткеи, но и как локальный HTTP-сервер. Это превращает Mac в “хаб”, который можно дергать командами с других устройств.
Как это устроено
- Поднимается HTTP server на фиксированном порту (в моём случае 9876).
- Авторизация сделана через header token (X-Auth-Token) вместо Basic Auth, потому что так удобнее и стабильнее интегрироваться с Shortcuts.
- Есть простой rate limit (минимальный интервал между вызовами), чтобы не спамить одинаковыми командами и не ловить гонки.
- Есть health-check / watchdog: сервер регулярно проверяет /ping и, если что-то пошло не так, перезапускается. То есть он не “умирает тихо”.
Что даёт на практике
Получается очень прямой “пульт управления”, где каждый endpoint это действие:
- медиа-команды по конкретным приложениям (например, Twitch/YouTube/Spotify/Music/Podcasts/Books)
- системные команды, включая pmset displaysleepnow (быстро погасить дисплеи)
Ключевой момент: это не “универсальная кнопка”, а точечные сценарии, завязанные на конкретные приложения и нужную последовательность шагов (активация, задержка, команда и т.д.).
Архитектура
- Mac Studio выступает как хаб: он выполняет действия локально и предсказуемо.
- Shortcuts на iPhone или другом Mac выступают как клиенты: нажал кнопку → ушёл HTTP-запрос → на Mac выполнилась команда.
Это даёт ощущение “домашнего пульта”: управление медиа и системными действиями не привязано к тому, где сейчас руки, какое окно активно и на каком устройстве ты находишься.
Дальше разберу связку Hammerspoon + displayplacer: авто-раскладка экранов без ручной правки.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
Мониторы и стабильность рабочего места (Hammerspoon)
Многомониторный сетап в macOS часто ведёт себя хаотично: после переподключения дисплеев окна “переезжают”, Dock может оказаться не на том экране, а раскладка мониторов меняется непредсказуемо. Чтобы это не ломало рабочее место, часть логики вынесена в Hammerspoon.
1) displayplacer + отслеживание подключений
Раскладка экранов применяется не вручную, а через displayplacer: Hammerspoon проверяет наличие конкретного дисплея по persistent ID и понимает, какая конфигурация сейчас активна (например, подключён ли iPad/Sidecar).
2) Авто-применение лэйаутов через скрипты
Под разные сценарии подготовлены скрипты в ~/.layouts/... (условно “с iPad” и “без iPad”). При изменении набора экранов Hammerspoon ждёт короткую паузу, даёт macOS “успокоиться”, и затем применяет нужный лэйаут автоматически.
3) Антихаос после отключения Sidecar/iPad
Одна из самых неприятных ситуаций: отключаешь iPad, и часть окон улетает на “крайний” экран. В этой конфигурации Hammerspoon отслеживает момент отключения и аккуратно возвращает такие окна на primary display, чтобы не искать их по всей комнате.
В следующем посте покажу, как в Hammerspoon я поднимаю WebSocket-связь с OBS.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
Многомониторный сетап в macOS часто ведёт себя хаотично: после переподключения дисплеев окна “переезжают”, Dock может оказаться не на том экране, а раскладка мониторов меняется непредсказуемо. Чтобы это не ломало рабочее место, часть логики вынесена в Hammerspoon.
1) displayplacer + отслеживание подключений
Раскладка экранов применяется не вручную, а через displayplacer: Hammerspoon проверяет наличие конкретного дисплея по persistent ID и понимает, какая конфигурация сейчас активна (например, подключён ли iPad/Sidecar).
2) Авто-применение лэйаутов через скрипты
Под разные сценарии подготовлены скрипты в ~/.layouts/... (условно “с iPad” и “без iPad”). При изменении набора экранов Hammerspoon ждёт короткую паузу, даёт macOS “успокоиться”, и затем применяет нужный лэйаут автоматически.
3) Антихаос после отключения Sidecar/iPad
Одна из самых неприятных ситуаций: отключаешь iPad, и часть окон улетает на “крайний” экран. В этой конфигурации Hammerspoon отслеживает момент отключения и аккуратно возвращает такие окна на primary display, чтобы не искать их по всей комнате.
В следующем посте покажу, как в Hammerspoon я поднимаю WebSocket-связь с OBS.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥1❤1
MIDI-клавиатура и OBS как аппаратный контроллер (Hammerspoon)
У меня несколько MIDI-клавиатур, в следствии появилась простая идея: использовать одну из них, как физическую панель управления, где клавиши запускают команды. Hammerspoon здесь работает как “прошивка”: принимает MIDI-события, переводит их в действия и управляет OBS по WebSocket.
Пример:
В конфиге есть карта действий smkActions[...], и для ноты 48 назначено:
Что это значит:
- smkActions[48] — слот для конкретной MIDI-ноты (48).
- .label — человекочитаемое имя (для логов/понимания).
- .fn = function() ... end — функция, которая выполняется при нажатии этой MIDI-клавиши.
- obsSetSceneByIndex(1) — переключить OBS на сцену с индексом 1 (индекс берётся из списка сцен OBS).
Дальше в обработчике MIDI-событий есть логика: выполнять действия только в command mode (который включается sustain-педалью/CC64).
Ключевой фрагмент (суть):
-
-
-
-
-
-
-
Дальше
1. Получает список сцен OBS (через кеш с маленьким TTL, чтобы не дергать OBS на каждый клик).
2. По индексу 1 берёт имя сцены.
3. Отправляет в OBS команду SetCurrentProgramScene через WebSocket.
Итоговая логика одной клавиши
MIDI note 48 → (command mode ON) → находим smkActions[48] → вызываем obsSetSceneByIndex(1) → OBS переключает сцену на #1.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
У меня несколько MIDI-клавиатур, в следствии появилась простая идея: использовать одну из них, как физическую панель управления, где клавиши запускают команды. Hammerspoon здесь работает как “прошивка”: принимает MIDI-события, переводит их в действия и управляет OBS по WebSocket.
Пример:
В конфиге есть карта действий smkActions[...], и для ноты 48 назначено:
smkActions[48].label = "OBS: Scene #01"
smkActions[48].fn = function() obsSetSceneByIndex(1) end
Что это значит:
- smkActions[48] — слот для конкретной MIDI-ноты (48).
- .label — человекочитаемое имя (для логов/понимания).
- .fn = function() ... end — функция, которая выполняется при нажатии этой MIDI-клавиши.
- obsSetSceneByIndex(1) — переключить OBS на сцену с индексом 1 (индекс берётся из списка сцен OBS).
Дальше в обработчике MIDI-событий есть логика: выполнять действия только в command mode (который включается sustain-педалью/CC64).
Ключевой фрагмент (суть):
if smkCommandMode and cmdType == "noteOn" then
local note = mdTable.note
local vel = mdTable.velocity or 0
if not note or vel <= 0 then return end
if note < SMK_NOTE_MIN or note > SMK_NOTE_MAX then return end
local act, layer = smkResolveAction(note)
if not act then return end
if not act.hotkey and not act.fn then return end
-- дальше debounce и выполнение act.fn / act.hotkey
end
-
smkCommandMode — булевый флаг “режим команд включён”.-
cmdType == "noteOn" — реагируем на нажатие клавиши (Note On).-
mdTable.note — номер ноты (например, 48).-
mdTable.velocity — сила нажатия; vel <= 0 отсекается (чтобы не триггерить “пустые” события).-
SMK_NOTE_MIN/MAX — ограничение диапазона, чтобы обрабатывать только нужные 25 клавиш.-
smkResolveAction(note) — достаёт действие для конкретной ноты (с учётом возможных слоёв/оверрайдов).-
act.fn — если у ноты есть функция, она будет вызвана.Дальше
obsSetSceneByIndex(1) делает примерно такую цепочку:1. Получает список сцен OBS (через кеш с маленьким TTL, чтобы не дергать OBS на каждый клик).
2. По индексу 1 берёт имя сцены.
3. Отправляет в OBS команду SetCurrentProgramScene через WebSocket.
Итоговая логика одной клавиши
MIDI note 48 → (command mode ON) → находим smkActions[48] → вызываем obsSetSceneByIndex(1) → OBS переключает сцену на #1.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
4❤1👀1
Тема клавиатурных слоев на этом этапе закрыта.
Важно понимать: здесь описана лишь часть того, что можно построить на связке Karabiner + Hammerspoon. Я показал решения, которые придумал под себя и которые реально использую каждый день: слои, сигналы, логика и сценарии.
При этом потенциал намного шире: можно собирать любые комбинации, добавлять новые режимы, правила и контекст, и постепенно доводить управление системой до состояния, которое идеально подходит именно вам.
Я буду регулярно возвращаться к этой теме и постепенно расширять и улучшать свой сетап, так что история со слоями точно не заканчивается, она будет только развиваться
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#macOSAutomation@annsFlow
💻 annFlow
Важно понимать: здесь описана лишь часть того, что можно построить на связке Karabiner + Hammerspoon. Я показал решения, которые придумал под себя и которые реально использую каждый день: слои, сигналы, логика и сценарии.
При этом потенциал намного шире: можно собирать любые комбинации, добавлять новые режимы, правила и контекст, и постепенно доводить управление системой до состояния, которое идеально подходит именно вам.
Я буду регулярно возвращаться к этой теме и постепенно расширять и улучшать свой сетап, так что история со слоями точно не заканчивается, она будет только развиваться
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#macOSAutomation@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
От “железа” к логике: как я строю клавиатурные слои, чтобы ускорить работу на Mac.
Как вам тема?💻
Как вам тема?
Anonymous Poll
14%
Высокая ценность, внедряю
29%
Полезно, но точечно
29%
Интересно, но пока не нужно
29%
Мимо
Without Streaming: Telegram-канал как радиостанция
● Радио из Telegram в один клик
● Две ручки настроения: стиль и энергия
● Как это работает внутри: индексатор → плейлист → стрим
● Инженерная реальность: плеер, фон и PWA
● Without Streaming перестал быть плеером и стал радиостанцией
#WithoutStreaming@annsFlow
Навигация по проектам канала📍
💻 annFlow
● Радио из Telegram в один клик
● Две ручки настроения: стиль и энергия
● Как это работает внутри: индексатор → плейлист → стрим
● Инженерная реальность: плеер, фон и PWA
● Without Streaming перестал быть плеером и стал радиостанцией
#WithoutStreaming@annsFlow
Навигация по проектам канала
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Please open Telegram to view this post
VIEW IN TELEGRAM
Радио из Telegram в один клик
У меня есть Telegram-канал Without Streaming, куда я фиксирую свою личную музыкальную медиатеку, а так же короткие видео-вырезки: из своих проектов @arsenynn и workflow@arsenynn. Для меня это не просто подборка, а слепок вкуса и времени.
Изначально все началось прагматично: прийти в компанию, на тусовку или сесть в машину и не подключаться к колонке или проигрывателю, а просто открыть свой канал на телефоне того, кто уже подключён, и включить музыку в один шаг.
Музыка и GIF здесь работают как компактная запись состояния: вместе они сохраняют настроение момента быстрее и точнее, чем любые пояснения.
Telegram как CMS. Новый пост в канале автоматически становится частью эфира, без отдельной админки и медиатеки.
Визуалы как «телеканал». Один визуал повторяется три раза, затем переключается на следующий; стартовый набор каждый раз перемешан.
Аудио стримится «на лету» из Telegram без сохранения на диск. Это осознанный выбор: минимум инфраструктуры и минимум «следов».
Техническая проекция простая: FastAPI + Telethon → JSON-плейлист → chunked-стрим
В следующем посте расскажу, как две ручки (Jazz ⇄ Hip-hop и Drifting ⇄ Driving) управляют настроением эфира через простые теги в Telegram.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#WithoutStreaming@annsFlow
💻 annFlow
У меня есть Telegram-канал Without Streaming, куда я фиксирую свою личную музыкальную медиатеку, а так же короткие видео-вырезки: из своих проектов @arsenynn и workflow@arsenynn. Для меня это не просто подборка, а слепок вкуса и времени.
Изначально все началось прагматично: прийти в компанию, на тусовку или сесть в машину и не подключаться к колонке или проигрывателю, а просто открыть свой канал на телефоне того, кто уже подключён, и включить музыку в один шаг.
Музыка и GIF здесь работают как компактная запись состояния: вместе они сохраняют настроение момента быстрее и точнее, чем любые пояснения.
Telegram как CMS. Новый пост в канале автоматически становится частью эфира, без отдельной админки и медиатеки.
Визуалы как «телеканал». Один визуал повторяется три раза, затем переключается на следующий; стартовый набор каждый раз перемешан.
Аудио стримится «на лету» из Telegram без сохранения на диск. Это осознанный выбор: минимум инфраструктуры и минимум «следов».
Техническая проекция простая: FastAPI + Telethon → JSON-плейлист → chunked-стрим
В следующем посте расскажу, как две ручки (Jazz ⇄ Hip-hop и Drifting ⇄ Driving) управляют настроением эфира через простые теги в Telegram.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#WithoutStreaming@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
Without Streaming
Music with GIFs @arsenynn
❤1🔥1
Две ручки настроения: стиль и энергия
В Without Streaming я сделал управление как на пульте:
одна ручка смещает эфир Jazz ⇄ Hip-hop, вторая меняет энергию Drifting ⇄ Driving.
Основа намеренно простая и прозрачная: я помечаю треки тегами в Telegram (#J / #H / #JxH и #drift / #drive / #dd). Индексатор собирает это в playlist.json, а сервер по запросу /playlist?mix=&energy= пересобирает порядок треков. #JxH и #dd это треки, которые я сам считаю смешанными по стилю (Jazz×Hip-hop) и треки со смешанной энергией (между drift и drive).
Дальше код делает простую вещь: превращает положение ползунков в коэффициенты “сколько внимания дать каждой стороне”, и считает итоговую оценку трека score. Чем выше score, тем выше трек поднимается в выдаче /playlist.
• mix и energy превращаются в коэффициенты *_bias: это “сколько влияния” дать каждой стороне шкалы.
• w_jazz / w_hiphop / w_drift / w_drive это веса трека, которые получаются из тегов при индексации.
• score это итоговая оценка: чем она выше, тем раньше трек окажется в эфире при текущих настройках.
Дальше включается важная инженерная часть: чтобы эфир не зацикливался на узкой категории, сервер сначала отдаёт “профильные” треки (и мосты), а затем подмешивает остальные.
Почему пока не идеально: теги субъективны, две оси создают много комбинаций, и “музыкально правильно” не всегда совпадает с “самой высокой оценкой”.
Авто-режимы (Auto Flow / Auto Energy) двигают ручки плавно и не ломают текущий трек, а эффект проявляется на следующем.
Кстати, авто-режим можно включить прямо с ползунка: нажмите на “точку” (ползунок) и он начнёт двигаться автоматически.
В следующем посте разберу внутренний конвейер Without Streaming: как индексатор собирает playlist.json, как /playlist формирует эфир под ручки и как /audio стримит медиа из Telegram chunked без сохранения на диск.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#WithoutStreaming@annsFlow
💻 annFlow
В Without Streaming я сделал управление как на пульте:
одна ручка смещает эфир Jazz ⇄ Hip-hop, вторая меняет энергию Drifting ⇄ Driving.
Основа намеренно простая и прозрачная: я помечаю треки тегами в Telegram (#J / #H / #JxH и #drift / #drive / #dd). Индексатор собирает это в playlist.json, а сервер по запросу /playlist?mix=&energy= пересобирает порядок треков. #JxH и #dd это треки, которые я сам считаю смешанными по стилю (Jazz×Hip-hop) и треки со смешанной энергией (между drift и drive).
Дальше код делает простую вещь: превращает положение ползунков в коэффициенты “сколько внимания дать каждой стороне”, и считает итоговую оценку трека score. Чем выше score, тем выше трек поднимается в выдаче /playlist.
jazz_bias = 1.0 - hiphop_bias
drive_bias = energy / 100.0
drift_bias = 1.0 - drive_bias
score = jazz_bias * w_jazz + hiphop_bias * w_hiphop \
+ drift_bias * w_drift + drive_bias * w_drive
• mix и energy превращаются в коэффициенты *_bias: это “сколько влияния” дать каждой стороне шкалы.
• w_jazz / w_hiphop / w_drift / w_drive это веса трека, которые получаются из тегов при индексации.
• score это итоговая оценка: чем она выше, тем раньше трек окажется в эфире при текущих настройках.
Дальше включается важная инженерная часть: чтобы эфир не зацикливался на узкой категории, сервер сначала отдаёт “профильные” треки (и мосты), а затем подмешивает остальные.
Почему пока не идеально: теги субъективны, две оси создают много комбинаций, и “музыкально правильно” не всегда совпадает с “самой высокой оценкой”.
Авто-режимы (Auto Flow / Auto Energy) двигают ручки плавно и не ломают текущий трек, а эффект проявляется на следующем.
Кстати, авто-режим можно включить прямо с ползунка: нажмите на “точку” (ползунок) и он начнёт двигаться автоматически.
В следующем посте разберу внутренний конвейер Without Streaming: как индексатор собирает playlist.json, как /playlist формирует эфир под ручки и как /audio стримит медиа из Telegram chunked без сохранения на диск.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#WithoutStreaming@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
Without Streaming
Music with GIFs @arsenynn
Как это работает внутри: индексатор → плейлист → стрим
Проект устроен как конвейер: Telegram-канал → каталог → эфир.
1) Индексатор пробегает канал, находит аудио и GIF/MP4, читает теги и формирует playlist.json.
2) /playlist?mix=&energy= отдаёт JSON с треками и визуалами уже в нужном порядке под текущие ручки.
3) /audio/{id} и /gif/{id} запрашивают медиа в Telegram и отдают его в браузер.
Ключевой принцип: аудио не сохраняется на диск. Сервер отдаёт данные потоково (chunked) по мере получения.
Визуалы кэшируются на диск, потому что они короткие и повторяются, а кэш заметно ускоряет переключения.
Самая важная часть (то, что превращает канал в эфир):
— @app.get("/audio/{message_id}")
Маршрут: GET /audio/12345 попадает сюда, {message_id} это ID сообщения с треком в Telegram.
— async def audio(message_id: int):
Асинхронный обработчик, чтобы сервер не блокировался во время загрузки/отдачи. int фиксирует тип параметра.
— async def gen(): ... yield chunk
Асинхронный генератор: читает трек из Telegram порциями (async for) и сразу отдаёт каждый кусок (yield).
— StreamingResponse(gen(), ...)
Оборачивает поток в корректный HTTP-стрим: воспроизведение может начаться до полной загрузки файла.
— media_type="audio/mpeg"
Явно задаёт Content-Type, чтобы браузер правильно воспроизводил аудио.
— headers
no-store — не кэшировать аудио. Accept-Ranges: none — без Range-запросов (перемотка/дозагрузка “с середины” не предусмотрены).
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#WithoutStreaming@annsFlow
💻 annFlow
Проект устроен как конвейер: Telegram-канал → каталог → эфир.
1) Индексатор пробегает канал, находит аудио и GIF/MP4, читает теги и формирует playlist.json.
2) /playlist?mix=&energy= отдаёт JSON с треками и визуалами уже в нужном порядке под текущие ручки.
3) /audio/{id} и /gif/{id} запрашивают медиа в Telegram и отдают его в браузер.
Ключевой принцип: аудио не сохраняется на диск. Сервер отдаёт данные потоково (chunked) по мере получения.
Визуалы кэшируются на диск, потому что они короткие и повторяются, а кэш заметно ускоряет переключения.
Самая важная часть (то, что превращает канал в эфир):
@app.get("/audio/{message_id}")
async def audio(message_id: int):
async def gen():
async for chunk in telethon_stream_bytes(message_id):
yield chunk
return StreamingResponse(
gen(),
media_type="audio/mpeg",
headers={"Cache-Control": "no-store", "Accept-Ranges": "none"},
)
— @app.get("/audio/{message_id}")
Маршрут: GET /audio/12345 попадает сюда, {message_id} это ID сообщения с треком в Telegram.
— async def audio(message_id: int):
Асинхронный обработчик, чтобы сервер не блокировался во время загрузки/отдачи. int фиксирует тип параметра.
— async def gen(): ... yield chunk
Асинхронный генератор: читает трек из Telegram порциями (async for) и сразу отдаёт каждый кусок (yield).
— StreamingResponse(gen(), ...)
Оборачивает поток в корректный HTTP-стрим: воспроизведение может начаться до полной загрузки файла.
— media_type="audio/mpeg"
Явно задаёт Content-Type, чтобы браузер правильно воспроизводил аудио.
— headers
no-store — не кэшировать аудио. Accept-Ranges: none — без Range-запросов (перемотка/дозагрузка “с середины” не предусмотрены).
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#WithoutStreaming@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Please open Telegram to view this post
VIEW IN TELEGRAM
🍾3
Инженерная реальность: плеер, фон и PWA
Сделать “плейлист в браузере” легко. Сделать радио-ощущение сложнее: нужно бесшовное звучание, устойчивый UI и поведение, которое не ломается при смене условий (сеть, устройство, фон).
1) Плеер как движок
Эффект “радио” держится на двух вещах: кроссфейд и состояния. Плеер работает как state machine (idle → buffering → playing), чтобы не было “двойного звука” и гонок при переключениях. Параллельно идёт префетч следующего трека, чтобы реакции на Next/Prev ощущались быстрыми.
2) Зачем нужна кнопка «Слушать в фоне»
На iOS/WebView “богатый” режим (WebAudio, видео-лупы, таймеры, префетч) часто агрессивно режется при сворачивании. Поэтому кнопка «Слушать в фоне» переводит радио в облегчённый профиль: отключает тяжёлые части, оставляет стабильное воспроизведение через простой <audio> и даёт быстрый возврат в основной режим без потери контекста.
3) PWA
PWA здесь не про “нативность”. Оно даёт более предсказуемый запуск интерфейса и повышает шанс, что сессия проживёт дольше. Но я не обещаю невозможного: фон в вебе нельзя гарантировать, поэтому и нужен отдельный “фон-режим”.
4) Gesture gate: Telegram Mini App / Safari WebView
Есть ещё один “взрослый” слой надёжности: когда платформа запрещает autoplay (особенно в WebView), проект не выглядит сломанным. Визуалы переводятся в режим ожидания до первого пользовательского жеста, а после тапа аккуратно стартуют заново. Это редкая, но важная деталь: большинство веб-плееров в такой ситуации просто молча “не играет” и выглядит как баг, а здесь это осмысленная деградация под реальные ограничения.
Telegram-контент превращается в медиа-объект, а не в ленту ссылок, и при этом система честно учитывает ограничения платформ.
Оглавление темы 📌
Навигация по проектам канала📍
Все проекты🎶
#WithoutStreaming@annsFlow
💻 annFlow
Сделать “плейлист в браузере” легко. Сделать радио-ощущение сложнее: нужно бесшовное звучание, устойчивый UI и поведение, которое не ломается при смене условий (сеть, устройство, фон).
1) Плеер как движок
Эффект “радио” держится на двух вещах: кроссфейд и состояния. Плеер работает как state machine (idle → buffering → playing), чтобы не было “двойного звука” и гонок при переключениях. Параллельно идёт префетч следующего трека, чтобы реакции на Next/Prev ощущались быстрыми.
2) Зачем нужна кнопка «Слушать в фоне»
На iOS/WebView “богатый” режим (WebAudio, видео-лупы, таймеры, префетч) часто агрессивно режется при сворачивании. Поэтому кнопка «Слушать в фоне» переводит радио в облегчённый профиль: отключает тяжёлые части, оставляет стабильное воспроизведение через простой <audio> и даёт быстрый возврат в основной режим без потери контекста.
3) PWA
PWA здесь не про “нативность”. Оно даёт более предсказуемый запуск интерфейса и повышает шанс, что сессия проживёт дольше. Но я не обещаю невозможного: фон в вебе нельзя гарантировать, поэтому и нужен отдельный “фон-режим”.
4) Gesture gate: Telegram Mini App / Safari WebView
Есть ещё один “взрослый” слой надёжности: когда платформа запрещает autoplay (особенно в WebView), проект не выглядит сломанным. Визуалы переводятся в режим ожидания до первого пользовательского жеста, а после тапа аккуратно стартуют заново. Это редкая, но важная деталь: большинство веб-плееров в такой ситуации просто молча “не играет” и выглядит как баг, а здесь это осмысленная деградация под реальные ограничения.
Telegram-контент превращается в медиа-объект, а не в ленту ссылок, и при этом система честно учитывает ограничения платформ.
Оглавление темы 📌
Навигация по проектам канала
Все проекты
#WithoutStreaming@annsFlow
Please open Telegram to view this post
VIEW IN TELEGRAM