[{"data":1,"prerenderedAt":20948},["ShallowReactive",2],{"navigation_docs":3,"-client-server-architecture-streaming-media-protocols-and-webrtc":5180,"-client-server-architecture-streaming-media-protocols-and-webrtc-surround":20943},[4,1707,2084,2556,2737,2863,3070,3192,3242,3299,3333,3459,3536,3550,3689,3983,4060,4793,4968,5061,5114,5176],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","\u002Fcsharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1364,1654,1703],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","\u002Fcsharp\u002Ffundamentals","01.csharp\u002F01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","\u002Fcsharp\u002Ffundamentals\u002Fintroduction-to-ecosystem","01.csharp\u002F01.fundamentals\u002F01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","\u002Fcsharp\u002Ffundamentals\u002Fprogram-structure","01.csharp\u002F01.fundamentals\u002F02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","\u002Fcsharp\u002Ffundamentals\u002Fvariables-data-types","01.csharp\u002F01.fundamentals\u002F03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","\u002Fcsharp\u002Ffundamentals\u002Farrays","01.csharp\u002F01.fundamentals\u002F04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","\u002Fcsharp\u002Ffundamentals\u002Fstrings-text-handling","01.csharp\u002F01.fundamentals\u002F05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","\u002Fcsharp\u002Ffundamentals\u002Fdates-time-handling","01.csharp\u002F01.fundamentals\u002F06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","\u002Fcsharp\u002Ffundamentals\u002Fcontrol-flow","01.csharp\u002F01.fundamentals\u002F07.control-flow",{"title":48,"path":49,"stem":50},"Методи","\u002Fcsharp\u002Ffundamentals\u002Fmethods","01.csharp\u002F01.fundamentals\u002F08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","\u002Fcsharp\u002Ffundamentals\u002Fdebugging-basics","01.csharp\u002F01.fundamentals\u002F09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","\u002Fcsharp\u002Ffundamentals\u002Finteractive-console","01.csharp\u002F01.fundamentals\u002F10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","\u002Fcsharp\u002Foop","01.csharp\u002F02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","\u002Fcsharp\u002Foop\u002Fpackage-management","01.csharp\u002F02.oop\u002F01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","\u002Fcsharp\u002Foop\u002Fclasses-objects","01.csharp\u002F02.oop\u002F02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","\u002Fcsharp\u002Foop\u002Fproperties-fields","01.csharp\u002F02.oop\u002F03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","\u002Fcsharp\u002Foop\u002Foop-pillars","01.csharp\u002F02.oop\u002F04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","\u002Fcsharp\u002Foop\u002Fadvanced-types","01.csharp\u002F02.oop\u002F05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","\u002Fcsharp\u002Foop\u002Fnamespaces","01.csharp\u002F02.oop\u002F06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","\u002Fcsharp\u002Fadvanced-core","01.csharp\u002F03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","\u002Fcsharp\u002Fadvanced-core\u002Fgenerics","01.csharp\u002F03.advanced-core\u002F01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","\u002Fcsharp\u002Fadvanced-core\u002Fdelegates-events-lambdas","01.csharp\u002F03.advanced-core\u002F02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","\u002Fcsharp\u002Fadvanced-core\u002Finterfaces-deep-dive","01.csharp\u002F03.advanced-core\u002F03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","\u002Fcsharp\u002Fadvanced-core\u002Fexception-handling","01.csharp\u002F03.advanced-core\u002F04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","\u002Fcsharp\u002Fadvanced-core\u002Fpattern-matching","01.csharp\u002F03.advanced-core\u002F05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","\u002Fcsharp\u002Fadvanced-core\u002Fadditional-features","01.csharp\u002F03.advanced-core\u002F06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","\u002Fcsharp\u002Farchitecture-best-practices","01.csharp\u002F04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fsoftware-design-principles","01.csharp\u002F04.architecture-best-practices\u002F01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fsingleton","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fbuilder","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","\u002Fcsharp\u002Farchitecture-best-practices\u002Fbuilding-professional-clis","01.csharp\u002F04.architecture-best-practices\u002F03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","\u002Fcsharp\u002Farchitecture-best-practices\u002Fvalidation-flow-control","01.csharp\u002F04.architecture-best-practices\u002F04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fmodern-dotnet-host","01.csharp\u002F04.architecture-best-practices\u002F05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part1","01.csharp\u002F04.architecture-best-practices\u002F06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part2","01.csharp\u002F04.architecture-best-practices\u002F07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fthe-dependency-problem","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fbuild-your-own-container","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-locator-pattern","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdependency-injection-patterns","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fmicrosoft-di-deep-dive","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-lifetimes-and-scopes","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdi-anti-patterns-and-best-practices","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","\u002Fcsharp\u002Fstandard-library","01.csharp\u002F05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","\u002Fcsharp\u002Fstandard-library\u002Fcollections","01.csharp\u002F05.standard-library\u002F01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","\u002Fcsharp\u002Fstandard-library\u002Fhigh-performance-types","01.csharp\u002F05.standard-library\u002F02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","\u002Fcsharp\u002Fstandard-library\u002Flinq","01.csharp\u002F05.standard-library\u002F03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","\u002Fcsharp\u002Fsystem-internals-concurrency","01.csharp\u002F06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmemory-management","01.csharp\u002F06.system-internals-concurrency\u002F01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Freflection-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fattributes-dynamic","01.csharp\u002F06.system-internals-concurrency\u002F03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fexpression-trees-compiled","01.csharp\u002F06.system-internals-concurrency\u002F04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsource-generators","01.csharp\u002F06.system-internals-concurrency\u002F05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmultithreading-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsynchronization-primitives","01.csharp\u002F06.system-internals-concurrency\u002F07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","\u002Fcsharp\u002Fsystem-programming-windows","01.csharp\u002F07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","\u002Fcsharp\u002Fsystem-programming-windows\u002Fhow-os-works","01.csharp\u002F07.system-programming-windows\u002F01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","\u002Fcsharp\u002Fsystem-programming-windows\u002Fprocesses-in-dotnet","01.csharp\u002F07.system-programming-windows\u002F02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","\u002Fcsharp\u002Fsystem-programming-windows\u002F02a.processes-ipc","01.csharp\u002F07.system-programming-windows\u002F02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","\u002Fcsharp\u002Fsystem-programming-windows\u002Fappdomains-assemblies","01.csharp\u002F07.system-programming-windows\u002F03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","\u002Fcsharp\u002Fsystem-programming-windows\u002F03a.appdomains-plugin-system","01.csharp\u002F07.system-programming-windows\u002F03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-fundamentals","01.csharp\u002F07.system-programming-windows\u002F04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","\u002Fcsharp\u002Fsystem-programming-windows\u002F04a.thread-lifecycle-priorities","01.csharp\u002F07.system-programming-windows\u002F04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","\u002Fcsharp\u002Fsystem-programming-windows\u002Fshared-state-problems","01.csharp\u002F07.system-programming-windows\u002F05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","\u002Fcsharp\u002Fsystem-programming-windows\u002F05a.shared-state-memory-model","01.csharp\u002F07.system-programming-windows\u002F05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-fundamentals","01.csharp\u002F07.system-programming-windows\u002F06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","\u002Fcsharp\u002Fsystem-programming-windows\u002F06a.synchronization-walkthrough","01.csharp\u002F07.system-programming-windows\u002F06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-advanced","01.csharp\u002F07.system-programming-windows\u002F07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002F07a.synchronization-advanced-walkthrough","01.csharp\u002F07.system-programming-windows\u002F07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002Finterlocked-cas-lockfree","01.csharp\u002F07.system-programming-windows\u002F08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","\u002Fcsharp\u002Fsystem-programming-windows\u002F08a.volatile-memory-model","01.csharp\u002F07.system-programming-windows\u002F08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-pool","01.csharp\u002F07.system-programming-windows\u002F09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","\u002Fcsharp\u002Fsystem-programming-windows\u002F09a.thread-pool-advanced","01.csharp\u002F07.system-programming-windows\u002F09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","\u002Fcsharp\u002Fsystem-programming-windows\u002Fconcurrent-collections","01.csharp\u002F07.system-programming-windows\u002F10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","\u002Fcsharp\u002Fsystem-programming-windows\u002Ftpl-parallel-plinq","01.csharp\u002F07.system-programming-windows\u002F11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","\u002Fcsharp\u002Fsystem-programming-windows\u002F11a.tpl-parallel-plinq-advanced","01.csharp\u002F07.system-programming-windows\u002F11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async\u002FAwait — Фундамент Асинхронного Програмування","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-fundamentals","01.csharp\u002F07.system-programming-windows\u002F12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-context-configureawait","01.csharp\u002F07.system-programming-windows\u002F13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-advanced","01.csharp\u002F07.system-programming-windows\u002F14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","\u002Fcsharp\u002Fsystem-programming-windows\u002Fchannels","01.csharp\u002F07.system-programming-windows\u002F15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-synchronization","01.csharp\u002F07.system-programming-windows\u002F16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","\u002Fcsharp\u002Fsystem-programming-windows\u002Funsafe-code","01.csharp\u002F07.system-programming-windows\u002F17.unsafe-code",{"title":364,"path":365,"stem":366},"P\u002FInvoke та Windows API — Міст між .NET та Native Code","\u002Fcsharp\u002Fsystem-programming-windows\u002Fpinvoke-winapi","01.csharp\u002F07.system-programming-windows\u002F18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-registry","01.csharp\u002F07.system-programming-windows\u002F19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-hooks-services","01.csharp\u002F07.system-programming-windows\u002F20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","\u002Fcsharp\u002Fsystem-programming-windows\u002Fimplementation_plan","01.csharp\u002F07.system-programming-windows\u002Fimplementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","\u002Fcsharp\u002Fio","01.csharp\u002F08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","\u002Fcsharp\u002Fio\u002Ffile-system-basics","01.csharp\u002F08.io\u002F01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","\u002Fcsharp\u002Fio\u002Fstreams-serialization","01.csharp\u002F08.io\u002F02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","\u002Fcsharp\u002Fio\u002Fjson-serialization","01.csharp\u002F08.io\u002F03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","\u002Fcsharp\u002Fio\u002Fxml-serialization","01.csharp\u002F08.io\u002F04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","\u002Fcsharp\u002Fio\u002Fbinary-serialization","01.csharp\u002F08.io\u002F05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","\u002Fcsharp\u002Fado-net","01.csharp\u002F09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","\u002Fcsharp\u002Fado-net\u002Fintroduction-to-adonet","01.csharp\u002F09.ado-net\u002F01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","\u002Fcsharp\u002Fado-net\u002Fconnection","01.csharp\u002F09.ado-net\u002F02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","\u002Fcsharp\u002Fado-net\u002Fcommand-and-queries","01.csharp\u002F09.ado-net\u002F03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","\u002Fcsharp\u002Fado-net\u002Fdatareader","01.csharp\u002F09.ado-net\u002F04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","\u002Fcsharp\u002Fado-net\u002Fparameters-and-sql-injection","01.csharp\u002F09.ado-net\u002F05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","\u002Fcsharp\u002Fado-net\u002Ftransactions","01.csharp\u002F09.ado-net\u002F06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","\u002Fcsharp\u002Fado-net\u002Fprovider-factory","01.csharp\u002F09.ado-net\u002F07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","\u002Fcsharp\u002Fado-net\u002Fasync-data-access","01.csharp\u002F09.ado-net\u002F08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","\u002Fcsharp\u002Fado-net\u002Fdisconnected-mode-dataset","01.csharp\u002F09.ado-net\u002F09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","\u002Fcsharp\u002Fado-net\u002Fdata-adapter","01.csharp\u002F09.ado-net\u002F10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","\u002Fcsharp\u002Fado-net\u002Fdata-mapper-repository","01.csharp\u002F09.ado-net\u002F11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","\u002Fcsharp\u002Fado-net\u002Fadvanced-patterns","01.csharp\u002F09.ado-net\u002F12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","\u002Fcsharp\u002Fef-core","01.csharp\u002F10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","\u002Fcsharp\u002Fef-core\u002Fwhat-is-orm","01.csharp\u002F10.ef-core\u002F01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","\u002Fcsharp\u002Fef-core\u002Ffirst-project","01.csharp\u002F10.ef-core\u002F02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","\u002Fcsharp\u002Fef-core\u002Fdbcontext-deep-dive","01.csharp\u002F10.ef-core\u002F03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","\u002Fcsharp\u002Fef-core\u002Fdatabase-providers","01.csharp\u002F10.ef-core\u002F04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","\u002Fcsharp\u002Fef-core\u002Fconventions","01.csharp\u002F10.ef-core\u002F05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","\u002Fcsharp\u002Fef-core\u002Ffluent-api-vs-annotations","01.csharp\u002F10.ef-core\u002F06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","\u002Fcsharp\u002Fef-core\u002Frelationships-basics","01.csharp\u002F10.ef-core\u002F07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","\u002Fcsharp\u002Fef-core\u002Frelationships-advanced","01.csharp\u002F10.ef-core\u002F08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part1","01.csharp\u002F10.ef-core\u002F09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part2","01.csharp\u002F10.ef-core\u002F09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part1","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part2","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part1","01.csharp\u002F10.ef-core\u002F11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part2","01.csharp\u002F10.ef-core\u002F11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finheritance-part1","01.csharp\u002F10.ef-core\u002F12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finheritance-part2","01.csharp\u002F10.ef-core\u002F12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part1","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part2","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fseeding-part1","01.csharp\u002F10.ef-core\u002F14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fseeding-part2","01.csharp\u002F10.ef-core\u002F14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part1","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part2","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part1","01.csharp\u002F10.ef-core\u002F16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part2","01.csharp\u002F10.ef-core\u002F16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part1","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part2","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part1","01.csharp\u002F10.ef-core\u002F18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part2","01.csharp\u002F10.ef-core\u002F18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part1","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part2","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part1","01.csharp\u002F10.ef-core\u002F20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part2","01.csharp\u002F10.ef-core\u002F20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part1","01.csharp\u002F10.ef-core\u002F21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part2","01.csharp\u002F10.ef-core\u002F21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part1","01.csharp\u002F10.ef-core\u002F22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part2","01.csharp\u002F10.ef-core\u002F22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part1","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part2","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part1","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part2","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part1","01.csharp\u002F10.ef-core\u002F25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part2","01.csharp\u002F10.ef-core\u002F25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fperformance-fundamentals-part1","01.csharp\u002F10.ef-core\u002F26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part1","01.csharp\u002F10.ef-core\u002F29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part2","01.csharp\u002F10.ef-core\u002F29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","\u002Fcsharp\u002Fef-core\u002Fimplementation_plan","01.csharp\u002F10.ef-core\u002Fimplementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","\u002Fcsharp\u002Faspnet","01.csharp\u002F11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289,1346],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","\u002Fcsharp\u002Faspnet\u002Fminimal-api","01.csharp\u002F11.aspnet\u002F01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fintroduction","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Ffirst-application","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fwebapplication-builder","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frequest-pipeline-middleware","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-files","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-assets","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-fundamentals","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-options","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstate-management","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fsession-state","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fproject-structure","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fscalar-openapi-fluent","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger \u002F Swashbuckle у Minimal API: окремий класичний шлях","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fswagger-swashbuckle","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","\u002Fcsharp\u002Faspnet\u002Fapi","01.csharp\u002F11.aspnet\u002F02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fwhat-is-api","01.csharp\u002F11.aspnet\u002F02.api\u002F01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fdata-formats","01.csharp\u002F11.aspnet\u002F02.api\u002F02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-paradigms-rest","01.csharp\u002F11.aspnet\u002F02.api\u002F03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fhttp-methods-status-codes","01.csharp\u002F11.aspnet\u002F02.api\u002F04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Frest-organizing","01.csharp\u002F11.aspnet\u002F02.api\u002F05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","\u002Fcsharp\u002Faspnet\u002Fapi\u002Furl-nomenclature-crud","01.csharp\u002F11.aspnet\u002F02.api\u002F06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-naming","01.csharp\u002F11.aspnet\u002F02.api\u002F07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-validation","01.csharp\u002F11.aspnet\u002F02.api\u002F08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Ferror-handling-http","01.csharp\u002F11.aspnet\u002F02.api\u002F09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fidempotency-sync","01.csharp\u002F11.aspnet\u002F02.api\u002F10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fpagination-lists","01.csharp\u002F11.aspnet\u002F02.api\u002F11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fsecurity-auth","01.csharp\u002F11.aspnet\u002F02.api\u002F12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-process","01.csharp\u002F11.aspnet\u002F02.api\u002F13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fopenapi","01.csharp\u002F11.aspnet\u002F02.api\u002F14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","\u002Fcsharp\u002Faspnet\u002Fauth","01.csharp\u002F11.aspnet\u002F03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauth-fundamentals","01.csharp\u002F11.aspnet\u002F03.auth\u002F01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fjwt-authentication","01.csharp\u002F11.aspnet\u002F03.auth\u002F02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauthorization-policies","01.csharp\u002F11.aspnet\u002F03.auth\u002F03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcookie-auth-identity","01.csharp\u002F11.aspnet\u002F03.auth\u002F04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","\u002Fcsharp\u002Faspnet\u002Fauth\u002F04b.identity-auth-jwt","01.csharp\u002F11.aspnet\u002F03.auth\u002F04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-email-confirmation","01.csharp\u002F11.aspnet\u002F03.auth\u002F05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-two-factor","01.csharp\u002F11.aspnet\u002F03.auth\u002F06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-internals","01.csharp\u002F11.aspnet\u002F03.auth\u002F07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-external-providers","01.csharp\u002F11.aspnet\u002F03.auth\u002F08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fsecurity-hardening","01.csharp\u002F11.aspnet\u002F03.auth\u002F09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-theory","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foidc-keycloak","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fapi-keys","01.csharp\u002F11.aspnet\u002F03.auth\u002F11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frate-limiting","01.csharp\u002F11.aspnet\u002F03.auth\u002F12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frefresh-token-rotation","01.csharp\u002F11.aspnet\u002F03.auth\u002F13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcertificate-auth","01.csharp\u002F11.aspnet\u002F03.auth\u002F14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frbac-abac-rebac","01.csharp\u002F11.aspnet\u002F03.auth\u002F15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fmulti-tenancy","01.csharp\u002F11.aspnet\u002F03.auth\u002F16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","\u002Fcsharp\u002Faspnet\u002Fnotifications","01.csharp\u002F11.aspnet\u002F04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fin-app-database-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fpolling","01.csharp\u002F11.aspnet\u002F04.notifications\u002F02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fserver-sent-events","01.csharp\u002F11.aspnet\u002F04.notifications\u002F03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fwebsockets","01.csharp\u002F11.aspnet\u002F04.notifications\u002F04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fsignalr","01.csharp\u002F11.aspnet\u002F04.notifications\u002F05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fbackground-services","01.csharp\u002F11.aspnet\u002F04.notifications\u002F06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fweb-push","01.csharp\u002F11.aspnet\u002F04.notifications\u002F07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Femail-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fchoosing-the-right-approach","01.csharp\u002F11.aspnet\u002F04.notifications\u002F09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire","01.csharp\u002F11.aspnet\u002F04.notifications\u002F10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-image-webp","01.csharp\u002F11.aspnet\u002F04.notifications\u002F11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-video-hls","01.csharp\u002F11.aspnet\u002F04.notifications\u002F12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Ftelegram-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","\u002Fcsharp\u002Faspnet\u002Fi18n","01.csharp\u002F11.aspnet\u002F05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Finternationalization","01.csharp\u002F11.aspnet\u002F05.i18n\u002F01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Fhumanizer","01.csharp\u002F11.aspnet\u002F05.i18n\u002F02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","\u002Fcsharp\u002Faspnet\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching\u002F01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fmemory-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fdistributed-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fresponse-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Foutput-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","\u002Fcsharp\u002Faspnet\u002Ftesting","01.csharp\u002F11.aspnet\u002F07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-is-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-pyramid","01.csharp\u002F11.aspnet\u002F07.testing\u002F02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-schools","01.csharp\u002F11.aspnet\u002F07.testing\u002F03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftdd-and-bdd","01.csharp\u002F11.aspnet\u002F07.testing\u002F04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-to-test","01.csharp\u002F11.aspnet\u002F07.testing\u002F05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftest-frameworks","01.csharp\u002F11.aspnet\u002F07.testing\u002F06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-basics","01.csharp\u002F11.aspnet\u002F07.testing\u002F07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fmocking-with-moq","01.csharp\u002F11.aspnet\u002F07.testing\u002F09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fdatabase-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","\u002Fcsharp\u002Faspnet\u002Ftesting\u002F11a.integration-testing-practice","01.csharp\u002F11.aspnet\u002F07.testing\u002F11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fpostman-professional","01.csharp\u002F11.aspnet\u002F07.testing\u002F13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fhttpclient-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwiremock-net","01.csharp\u002F11.aspnet\u002F07.testing\u002F15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-patterns","01.csharp\u002F11.aspnet\u002F07.testing\u002F16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fadvanced-testing-tools","01.csharp\u002F11.aspnet\u002F07.testing\u002F17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Farchitecture-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fperformance-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fremaining_plan","01.csharp\u002F11.aspnet\u002F07.testing\u002Fremaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","\u002Fcsharp\u002Faspnet\u002Fpayments","01.csharp\u002F11.aspnet\u002F08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-fundamentals","01.csharp\u002F11.aspnet\u002F08.payments\u002F01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-methods-ukraine","01.csharp\u002F11.aspnet\u002F08.payments\u002F02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpci-dss-security","01.csharp\u002F11.aspnet\u002F08.payments\u002F03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-architecture","01.csharp\u002F11.aspnet\u002F08.payments\u002F04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fliqpay-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fmonobank-acquiring","01.csharp\u002F11.aspnet\u002F08.payments\u002F06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fstripe-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fwebhooks-deep-dive","01.csharp\u002F11.aspnet\u002F08.payments\u002F08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fsubscriptions-recurring","01.csharp\u002F11.aspnet\u002F08.payments\u002F09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Frefunds-disputes","01.csharp\u002F11.aspnet\u002F08.payments\u002F10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Ftesting-payments","01.csharp\u002F11.aspnet\u002F08.payments\u002F11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fproduction-checklist","01.csharp\u002F11.aspnet\u002F08.payments\u002F12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","\u002Fcsharp\u002Faspnet\u002Flibraries","01.csharp\u002F11.aspnet\u002F09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-validation","01.csharp\u002F11.aspnet\u002F09.libraries\u002F01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmapster","01.csharp\u002F11.aspnet\u002F09.libraries\u002F02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ferroror-result-pattern","01.csharp\u002F11.aspnet\u002F09.libraries\u002F03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fserilog","01.csharp\u002F11.aspnet\u002F09.libraries\u002F04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmediatr","01.csharp\u002F11.aspnet\u002F09.libraries\u002F05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fpolly","01.csharp\u002F11.aspnet\u002F09.libraries\u002F06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F09.libraries\u002F07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffeature-management","01.csharp\u002F11.aspnet\u002F09.libraries\u002F08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-email","01.csharp\u002F11.aspnet\u002F09.libraries\u002F09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fquest-pdf","01.csharp\u002F11.aspnet\u002F09.libraries\u002F10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fbogus","01.csharp\u002F11.aspnet\u002F09.libraries\u002F11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhumanizer-guard","01.csharp\u002F11.aspnet\u002F09.libraries\u002F12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fplan","01.csharp\u002F11.aspnet\u002F09.libraries\u002Fplan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","\u002Fcsharp\u002Faspnet\u002Frazor-pages","01.csharp\u002F11.aspnet\u002F10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ffrom-minimal-api","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fpage-model","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Frazor-syntax","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ftag-helpers","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fforms-validation","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fproject-task-manager","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc","01.csharp\u002F11.aspnet\u002F11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-pattern","01.csharp\u002F11.aspnet\u002F11.mvc\u002F01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffrom-razor-pages","01.csharp\u002F11.aspnet\u002F11.mvc\u002F02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fcontrollers-actions","01.csharp\u002F11.aspnet\u002F11.mvc\u002F03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Frouting-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmodel-binding","01.csharp\u002F11.aspnet\u002F11.mvc\u002F05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fviews-viewdata-tempdata","01.csharp\u002F11.aspnet\u002F11.mvc\u002F06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffilters","01.csharp\u002F11.aspnet\u002F11.mvc\u002F07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fareas","01.csharp\u002F11.aspnet\u002F11.mvc\u002F08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fview-components","01.csharp\u002F11.aspnet\u002F11.mvc\u002F09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fdisplay-editor-templates","01.csharp\u002F11.aspnet\u002F11.mvc\u002F10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fvalidation-advanced","01.csharp\u002F11.aspnet\u002F11.mvc\u002F11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx","01.csharp\u002F11.aspnet\u002F11.mvc\u002F12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fajax-htmx-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffile-upload","01.csharp\u002F11.aspnet\u002F11.mvc\u002F15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fglobalization-localization","01.csharp\u002F11.aspnet\u002F11.mvc\u002F16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fplan","01.csharp\u002F11.aspnet\u002F11.mvc\u002Fplan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","\u002Fcsharp\u002Faspnet\u002Fweb-api","01.csharp\u002F11.aspnet\u002F12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffrom-minimal-api-to-controllers","01.csharp\u002F11.aspnet\u002F12.web-api\u002F01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontroller-base-actionresult","01.csharp\u002F11.aspnet\u002F12.web-api\u002F02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontent-negotiation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-versioning","01.csharp\u002F11.aspnet\u002F12.web-api\u002F04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fproblemdetails-error-handling","01.csharp\u002F11.aspnet\u002F12.web-api\u002F05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffilters-for-api","01.csharp\u002F11.aspnet\u002F12.web-api\u002F06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fpagination-filtering-sorting","01.csharp\u002F11.aspnet\u002F12.web-api\u002F07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhateoas-resource-expansion","01.csharp\u002F11.aspnet\u002F12.web-api\u002F08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fminimal-api-vs-controllers-hybrid","01.csharp\u002F11.aspnet\u002F12.web-api\u002F09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-documentation-generation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhealth-checks-monitoring","01.csharp\u002F11.aspnet\u002F12.web-api\u002F11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fweb-api-project","01.csharp\u002F11.aspnet\u002F12.web-api\u002F12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fplan","01.csharp\u002F11.aspnet\u002F12.web-api\u002Fplan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Моніторинг","i-lucide-activity","\u002Fcsharp\u002Faspnet\u002Fmonitoring","01.csharp\u002F11.aspnet\u002F13.monitoring",[1352,1356,1360],{"title":1353,"path":1354,"stem":1355},"Спостережуваність: від console.log до production-систем","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fobservability-intro","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F01.observability-intro",{"title":1357,"path":1358,"stem":1359},"Health Checks: перший рівень observability","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F02.health-checks",{"title":1361,"path":1362,"stem":1363},"Вбудовані метрики .NET 10 та System.Diagnostics.Metrics","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fdotnet-metrics","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F03.dotnet-metrics",{"title":1365,"icon":1366,"path":1367,"stem":1368,"children":1369,"page":59},"Desktop UI","i-lucide-app-window","\u002Fcsharp\u002Fdesktop-ui","01.csharp\u002F12.desktop-ui",[1370,1374,1378,1382,1386,1390,1394,1398,1402,1406,1410,1414,1418,1422,1426,1430,1434,1438,1442,1446,1450,1454,1458,1462,1466,1470,1474,1478,1482,1486,1490,1494,1498,1502,1506,1510,1514,1518,1522,1526,1530,1534,1538,1542,1546,1550,1554,1558,1562,1566,1570,1574,1578,1582,1586,1590,1594,1598,1602,1606,1610,1614,1618,1622,1626,1630,1634,1638,1642,1646,1650],{"title":1371,"path":1372,"stem":1373},"Що таке десктопна розробка?","\u002Fcsharp\u002Fdesktop-ui\u002Fwhat-is-desktop-dev","01.csharp\u002F12.desktop-ui\u002F01.what-is-desktop-dev",{"title":1375,"path":1376,"stem":1377},"Архітектура WPF — як влаштований графічний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-architecture","01.csharp\u002F12.desktop-ui\u002F02.wpf-architecture",{"title":1379,"path":1380,"stem":1381},"Перший WPF-проєкт — від нуля до вікна","\u002Fcsharp\u002Fdesktop-ui\u002Ffirst-wpf-app","01.csharp\u002F12.desktop-ui\u002F03.first-wpf-app",{"title":1383,"path":1384,"stem":1385},"Перший Avalonia-проєкт: WPF для всіх платформ","\u002Fcsharp\u002Fdesktop-ui\u002F03a.first-avalonia-app","01.csharp\u002F12.desktop-ui\u002F03a.first-avalonia-app",{"title":1387,"path":1388,"stem":1389},"XAML: декларативний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-basics","01.csharp\u002F12.desktop-ui\u002F04.xaml-basics",{"title":1391,"path":1392,"stem":1393},"Fluent UI у WPF — сучасний дизайн Windows 11","\u002Fcsharp\u002Fdesktop-ui\u002F04a.wpf-fluent-ui","01.csharp\u002F12.desktop-ui\u002F04a.wpf-fluent-ui",{"title":1395,"path":1396,"stem":1397},"WPF UI — сучасна бібліотека Fluent контролів","\u002Fcsharp\u002Fdesktop-ui\u002F04b.wpf-ui-library","01.csharp\u002F12.desktop-ui\u002F04b.wpf-ui-library",{"title":1399,"path":1400,"stem":1401},"HandyControl — велика бібліотека UI контролів для WPF","\u002Fcsharp\u002Fdesktop-ui\u002F04c.handycontrol-library","01.csharp\u002F12.desktop-ui\u002F04c.handycontrol-library",{"title":1403,"path":1404,"stem":1405},"Простори імен та ресурси XAML","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-namespaces-resources","01.csharp\u002F12.desktop-ui\u002F05.xaml-namespaces-resources",{"title":1407,"path":1408,"stem":1409},"XAML в Avalonia: ключові відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F05a.avalonia-xaml-differences","01.csharp\u002F12.desktop-ui\u002F05a.avalonia-xaml-differences",{"title":1411,"path":1412,"stem":1413},"Розширення розмітки XAML (Markup Extensions)","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-markup-extensions","01.csharp\u002F12.desktop-ui\u002F06.xaml-markup-extensions",{"title":1415,"path":1416,"stem":1417},"Панелі Layout: StackPanel, WrapPanel, DockPanel","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part1","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part1",{"title":1419,"path":1420,"stem":1421},"Grid, Canvas, UniformGrid","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part2","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part2",{"title":1423,"path":1424,"stem":1425},"Просунуті техніки Layout","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-advanced","01.csharp\u002F12.desktop-ui\u002F08.layout-advanced",{"title":1427,"path":1428,"stem":1429},"Адаптивний Layout та найкращі практики","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-responsive","01.csharp\u002F12.desktop-ui\u002F09.layout-responsive",{"title":1431,"path":1432,"stem":1433},"Layout в Avalonia: відмінності та нові можливості","\u002Fcsharp\u002Fdesktop-ui\u002F09a.layout-avalonia","01.csharp\u002F12.desktop-ui\u002F09a.layout-avalonia",{"title":1435,"path":1436,"stem":1437},"Button, Image, ProgressBar та інші базові контроли","\u002Fcsharp\u002Fdesktop-ui\u002Fbasic-controls","01.csharp\u002F12.desktop-ui\u002F10.basic-controls",{"title":1439,"path":1440,"stem":1441},"Контроли в Avalonia: відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F10a.controls-avalonia","01.csharp\u002F12.desktop-ui\u002F10a.controls-avalonia",{"title":1443,"path":1444,"stem":1445},"Текстові контроли — TextBlock, TextBox, RichTextBox","\u002Fcsharp\u002Fdesktop-ui\u002Ftext-controls","01.csharp\u002F12.desktop-ui\u002F11.text-controls",{"title":1447,"path":1448,"stem":1449},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","\u002Fcsharp\u002Fdesktop-ui\u002Fselection-controls","01.csharp\u002F12.desktop-ui\u002F12.selection-controls",{"title":1451,"path":1452,"stem":1453},"Content Model — GroupBox, Expander, TabControl, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fcontent-controls","01.csharp\u002F12.desktop-ui\u002F13.content-controls",{"title":1455,"path":1456,"stem":1457},"UI\u002FUX принципи десктопних застосунків","\u002Fcsharp\u002Fdesktop-ui\u002F13a.ui-ux-principles","01.csharp\u002F12.desktop-ui\u002F13a.ui-ux-principles",{"title":1459,"path":1460,"stem":1461},"Dependency Properties — Концепція та Value Resolution","\u002Fcsharp\u002Fdesktop-ui\u002Fdependency-properties-part1","01.csharp\u002F12.desktop-ui\u002F14.dependency-properties-part1",{"title":1463,"path":1464,"stem":1465},"Avalonia Property System — StyledProperty та DirectProperty","\u002Fcsharp\u002Fdesktop-ui\u002F14a.avalonia-property-system","01.csharp\u002F12.desktop-ui\u002F14a.avalonia-property-system",{"title":1467,"path":1468,"stem":1469},"Attached Properties — Властивості без меж","\u002Fcsharp\u002Fdesktop-ui\u002Fattached-properties","01.csharp\u002F12.desktop-ui\u002F15.attached-properties",{"title":1471,"path":1472,"stem":1473},"Routed Events — Маршрутизація подій у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Frouted-events","01.csharp\u002F12.desktop-ui\u002F16.routed-events",{"title":1475,"path":1476,"stem":1477},"Data Binding — Від Code-Behind до Декларативності","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part1","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part1",{"title":1479,"path":1480,"stem":1481},"INotifyPropertyChanged — Живе оновлення UI","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part2","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part2",{"title":1483,"path":1484,"stem":1485},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","\u002Fcsharp\u002Fdesktop-ui\u002F17a.avalonia-compiled-bindings","01.csharp\u002F12.desktop-ui\u002F17a.avalonia-compiled-bindings",{"title":1487,"path":1488,"stem":1489},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-advanced","01.csharp\u002F12.desktop-ui\u002F18.data-binding-advanced",{"title":1491,"path":1492,"stem":1493},"Value Converters — Перетворення типів даних у Data Binding","\u002Fcsharp\u002Fdesktop-ui\u002Fvalue-converters","01.csharp\u002F12.desktop-ui\u002F19.value-converters",{"title":1495,"path":1496,"stem":1497},"Data Templates — Візуалізація об'єктів у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-templates","01.csharp\u002F12.desktop-ui\u002F20.data-templates",{"title":1499,"path":1500,"stem":1501},"Collections Binding Part 1 — ObservableCollection та ItemsControl","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part1","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part1",{"title":1503,"path":1504,"stem":1505},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part2","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part2",{"title":1507,"path":1508,"stem":1509},"MVVM Pattern — Від Spaghetti Code до архітектури","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-pattern","01.csharp\u002F12.desktop-ui\u002F22.mvvm-pattern",{"title":1511,"path":1512,"stem":1513},"ViewModel Implementation — Від BaseViewModel до валідації","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-implementation","01.csharp\u002F12.desktop-ui\u002F23.viewmodel-implementation",{"title":1515,"path":1516,"stem":1517},"Commands — Від event handlers до декларативних команд","\u002Fcsharp\u002Fdesktop-ui\u002Fcommands","01.csharp\u002F12.desktop-ui\u002F24.commands",{"title":1519,"path":1520,"stem":1521},"MVVM Toolkit — MVVM без boilerplate через Source Generators","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-toolkit","01.csharp\u002F12.desktop-ui\u002F25.mvvm-toolkit",{"title":1523,"path":1524,"stem":1525},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","\u002Fcsharp\u002Fdesktop-ui\u002Fmessenger-pattern","01.csharp\u002F12.desktop-ui\u002F26.messenger-pattern",{"title":1527,"path":1528,"stem":1529},"Стилі WPF — CSS для десктопу","\u002Fcsharp\u002Fdesktop-ui\u002Fstyles-basics","01.csharp\u002F12.desktop-ui\u002F27.styles-basics",{"title":1531,"path":1532,"stem":1533},"CSS-like стилі Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F27a.avalonia-css-styling","01.csharp\u002F12.desktop-ui\u002F27a.avalonia-css-styling",{"title":1535,"path":1536,"stem":1537},"Control Templates — Частина 1. Концепція та TemplateBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part1","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part1",{"title":1539,"path":1540,"stem":1541},"Control Templates — Частина 2. Named Parts та ContentPresenter","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part2","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part2",{"title":1543,"path":1544,"stem":1545},"Control Themes в Avalonia — нова ера стилізації","\u002Fcsharp\u002Fdesktop-ui\u002F28a.avalonia-control-themes","01.csharp\u002F12.desktop-ui\u002F28a.avalonia-control-themes",{"title":1547,"path":1548,"stem":1549},"Triggers та Visual State Manager у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Ftriggers-visual-states","01.csharp\u002F12.desktop-ui\u002F29.triggers-visual-states",{"title":1551,"path":1552,"stem":1553},"Pseudo-classes в Avalonia — замість WPF Triggers","\u002Fcsharp\u002Fdesktop-ui\u002F29a.avalonia-pseudo-classes","01.csharp\u002F12.desktop-ui\u002F29a.avalonia-pseudo-classes",{"title":1555,"path":1556,"stem":1557},"Теми та ресурсні словники у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fresources-themes","01.csharp\u002F12.desktop-ui\u002F30.resources-themes",{"title":1559,"path":1560,"stem":1561},"Avalonia Themes — Fluent Design та система тематизації","\u002Fcsharp\u002Fdesktop-ui\u002F30a.avalonia-themes-fluent","01.csharp\u002F12.desktop-ui\u002F30a.avalonia-themes-fluent",{"title":1563,"path":1564,"stem":1565},"Контроли колекцій — глибоке занурення","\u002Fcsharp\u002Fdesktop-ui\u002Fcollection-controls","01.csharp\u002F12.desktop-ui\u002F31.collection-controls",{"title":1567,"path":1568,"stem":1569},"DataGrid — колонки та базове відображення","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part1","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part1",{"title":1571,"path":1572,"stem":1573},"DataGrid — сортування, фільтрація, редагування","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part2","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part2",{"title":1575,"path":1576,"stem":1577},"TreeView та GridView","\u002Fcsharp\u002Fdesktop-ui\u002Ftreeview-listview","01.csharp\u002F12.desktop-ui\u002F33.treeview-listview",{"title":1579,"path":1580,"stem":1581},"Меню, Toolbar, ContextMenu, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fmenus-toolbars","01.csharp\u002F12.desktop-ui\u002F34.menus-toolbars",{"title":1583,"path":1584,"stem":1585},"Навігація та керування вікнами. Частина 1: вікна та сторінки","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part1","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part1",{"title":1587,"path":1588,"stem":1589},"Навігація та керування вікнами. Частина 2: MVVM-навігація","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part2","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part2",{"title":1591,"path":1592,"stem":1593},"Avalonia — Навігація та діалоги","\u002Fcsharp\u002Fdesktop-ui\u002F35a.avalonia-navigation-dialogs","01.csharp\u002F12.desktop-ui\u002F35a.avalonia-navigation-dialogs",{"title":1595,"path":1596,"stem":1597},"Діалоги та File Pickers у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdialogs-file-pickers","01.csharp\u002F12.desktop-ui\u002F36.dialogs-file-pickers",{"title":1599,"path":1600,"stem":1601},"UserControl: компонентний підхід у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fuser-controls","01.csharp\u002F12.desktop-ui\u002F37.user-controls",{"title":1603,"path":1604,"stem":1605},"Custom Controls: Lookless Controls у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fcustom-controls","01.csharp\u002F12.desktop-ui\u002F38.custom-controls",{"title":1607,"path":1608,"stem":1609},"Avalonia TemplatedControl — Lookless Controls","\u002Fcsharp\u002Fdesktop-ui\u002F38a.avalonia-templated-controls","01.csharp\u002F12.desktop-ui\u002F38a.avalonia-templated-controls",{"title":1611,"path":1612,"stem":1613},"Анімації у WPF: Storyboard та Easing Functions","\u002Fcsharp\u002Fdesktop-ui\u002Fanimations-transitions","01.csharp\u002F12.desktop-ui\u002F39.animations-transitions",{"title":1615,"path":1616,"stem":1617},"Анімації в Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F39a.avalonia-animations","01.csharp\u002F12.desktop-ui\u002F39a.avalonia-animations",{"title":1619,"path":1620,"stem":1621},"2D Графіка та Мультимедіа у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fmedia-graphics","01.csharp\u002F12.desktop-ui\u002F40.media-graphics",{"title":1623,"path":1624,"stem":1625},"Dependency Injection у WPF та Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Fdi-integration","01.csharp\u002F12.desktop-ui\u002F41.di-integration",{"title":1627,"path":1628,"stem":1629},"SQLite та EF Core у десктопних додатках","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part1","01.csharp\u002F12.desktop-ui\u002F42.data-persistence-part1",{"title":1631,"path":1632,"stem":1633},"Repository Pattern та Unit of Work","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part2","01.csharp\u002F12.desktop-ui\u002F43.data-persistence-part2",{"title":1635,"path":1636,"stem":1637},"Тестування ViewModels","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-testing","01.csharp\u002F12.desktop-ui\u002F44.viewmodel-testing",{"title":1639,"path":1640,"stem":1641},"Avalonia Headless Testing — тестування UI без вікон","\u002Fcsharp\u002Fdesktop-ui\u002F44a.avalonia-headless-testing","01.csharp\u002F12.desktop-ui\u002F44a.avalonia-headless-testing",{"title":1643,"path":1644,"stem":1645},"Кросплатформна розробка з Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-cross-platform","01.csharp\u002F12.desktop-ui\u002F45.avalonia-cross-platform",{"title":1647,"path":1648,"stem":1649},"Пакування та розгортання Avalonia додатків","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F46.avalonia-packaging-deployment",{"title":1651,"path":1652,"stem":1653},"Розгортання WPF застосунків","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F47.wpf-packaging-deployment",{"title":1655,"icon":658,"path":1656,"stem":1657,"children":1658,"page":59},"Network Programming","\u002Fcsharp\u002Fnetwork-programming","01.csharp\u002F13.network-programming",[1659,1663,1667,1671,1675,1679,1683,1687,1691,1695,1699],{"title":1660,"path":1661,"stem":1662},"Основи комп'ютерних мереж","\u002Fcsharp\u002Fnetwork-programming\u002Ffoundations","01.csharp\u002F13.network-programming\u002F01.foundations",{"title":1664,"path":1665,"stem":1666},"Модель OSI та стек TCP\u002FIP","\u002Fcsharp\u002Fnetwork-programming\u002Fosi-model","01.csharp\u002F13.network-programming\u002F02.osi-model",{"title":1668,"path":1669,"stem":1670},"IP-протокол та адресація","\u002Fcsharp\u002Fnetwork-programming\u002Fip-addressing","01.csharp\u002F13.network-programming\u002F03.ip-addressing",{"title":1672,"path":1673,"stem":1674},"UDP — протокол без з'єднання","\u002Fcsharp\u002Fnetwork-programming\u002Fudp","01.csharp\u002F13.network-programming\u002F05.udp",{"title":1676,"path":1677,"stem":1678},"UDP Broadcast та Multicast","\u002Fcsharp\u002Fnetwork-programming\u002Fudp-broadcast-multicast","01.csharp\u002F13.network-programming\u002F06.udp-broadcast-multicast",{"title":1680,"path":1681,"stem":1682},"HTTP — протокол вебу","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-fundamentals","01.csharp\u002F13.network-programming\u002F07.http-fundamentals",{"title":1684,"path":1685,"stem":1686},"HttpListener — вбудований HTTP-сервер .NET","\u002Fcsharp\u002Fnetwork-programming\u002F07a.http-listener","01.csharp\u002F13.network-programming\u002F07a.http-listener",{"title":1688,"path":1689,"stem":1690},"HTTP Advanced — cookies, аутентифікація та HTTPS","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-advanced","01.csharp\u002F13.network-programming\u002F08.http-advanced",{"title":1692,"path":1693,"stem":1694},"SMTP та протоколи електронної пошти","\u002Fcsharp\u002Fnetwork-programming\u002Fsmtp","01.csharp\u002F13.network-programming\u002F09.smtp",{"title":1696,"path":1697,"stem":1698},"WebSocket — повнодуплексний протокол реального часу","\u002Fcsharp\u002Fnetwork-programming\u002Fwebsockets","01.csharp\u002F13.network-programming\u002F10.websockets",{"title":1700,"path":1701,"stem":1702},"TLS\u002FSSL — криптографічний захист мережевих з'єднань","\u002Fcsharp\u002Fnetwork-programming\u002Ftls-ssl","01.csharp\u002F13.network-programming\u002F11.tls-ssl",{"title":1704,"path":1705,"stem":1706},"C# & .NET: The Ultimate Roadmap","\u002Fcsharp\u002Froadmap","01.csharp\u002Froadmap",{"title":1708,"icon":1709,"path":1710,"stem":1711,"children":1712,"page":59},"C++","i-devicon-cplusplus","\u002Fcpp","02.cpp",[1713,1717,1721,1725,1729,1733,1737,1741,1745,1748,1752,1756,1760,1764,1768,1772,1776,1780,1784,1788,1792,1796,1800,1804,1808,1812,1816,1820,1824,1828,1832,1836,1840,1844,1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892,1896,1900,1904,1908,1912,1916,1920,1924,1928,1932,1936,1940,1944,1948,1952,1956,1960,1964,1968,1972,1976,1980,1984,1988,1992,1996,2000,2004,2008,2012,2016,2020,2024,2028,2032,2036,2040,2044,2048,2052,2056,2060,2064,2068,2072,2076,2080],{"title":1714,"path":1715,"stem":1716},"Вступ у програмування та алгоритми","\u002Fcpp\u002Fintro-algorithms","02.cpp\u002F01.intro-algorithms",{"title":1718,"path":1719,"stem":1720},"Code Style: угоди про оформлення коду","\u002Fcpp\u002Fcode-style","02.cpp\u002F02.code-style",{"title":1722,"path":1723,"stem":1724},"Середовище розробки та перший проєкт","\u002Fcpp\u002Fide-setup","02.cpp\u002F03.ide-setup",{"title":1726,"path":1727,"stem":1728},"Вивід даних на екран","\u002Fcpp\u002Fdata-output","02.cpp\u002F04.data-output",{"title":1730,"path":1731,"stem":1732},"Типи даних, змінні та константи","\u002Fcpp\u002Fdata-types-variables","02.cpp\u002F05.data-types-variables",{"title":1734,"path":1735,"stem":1736},"Ввід даних з клавіатури","\u002Fcpp\u002Fdata-input","02.cpp\u002F06.data-input",{"title":1738,"path":1739,"stem":1740},"Оператори, перетворення типів та логічні операції","\u002Fcpp\u002Foperators-type-conversion","02.cpp\u002F07.operators-type-conversion",{"title":1742,"path":1743,"stem":1744},"Цикли","\u002Fcpp\u002Floops","02.cpp\u002F08.loops",{"title":32,"path":1746,"stem":1747},"\u002Fcpp\u002Farrays","02.cpp\u002F09.arrays",{"title":1749,"path":1750,"stem":1751},"Алгоритми сортування та аналіз складності","\u002Fcpp\u002Fsorting","02.cpp\u002F10.sorting",{"title":1753,"path":1754,"stem":1755},"Алгоритми пошуку","\u002Fcpp\u002Fsearching","02.cpp\u002F11.searching",{"title":1757,"path":1758,"stem":1759},"Функції: основи","\u002Fcpp\u002Ffunctions-basics","02.cpp\u002F12.functions-basics",{"title":1761,"path":1762,"stem":1763},"Функції: прототипи, область видимості та додаткові можливості","\u002Fcpp\u002Ffunctions-scope","02.cpp\u002F13.functions-scope",{"title":1765,"path":1766,"stem":1767},"Функції: перевантаження та шаблони","\u002Fcpp\u002Ffunctions-overloading-templates","02.cpp\u002F14.functions-overloading-templates",{"title":1769,"path":1770,"stem":1771},"Вказівники: основи","\u002Fcpp\u002Fpointers-basics","02.cpp\u002F15.pointers-basics",{"title":1773,"path":1774,"stem":1775},"Посилання (References)","\u002Fcpp\u002Freferences","02.cpp\u002F16.references",{"title":1777,"path":1778,"stem":1779},"Вказівники, const і масиви","\u002Fcpp\u002Fpointers-const-arrays","02.cpp\u002F17.pointers-const-arrays",{"title":1781,"path":1782,"stem":1783},"Адресна арифметика","\u002Fcpp\u002Fpointer-arithmetic","02.cpp\u002F18.pointer-arithmetic",{"title":1785,"path":1786,"stem":1787},"Динамічна пам'ять","\u002Fcpp\u002Fdynamic-memory","02.cpp\u002F19.dynamic-memory",{"title":1789,"path":1790,"stem":1791},"Вказівники типу void","\u002Fcpp\u002Fvoid-pointers","02.cpp\u002F20.void-pointers",{"title":1793,"path":1794,"stem":1795},"Вказівники на вказівники","\u002Fcpp\u002Fpointers-to-pointers","02.cpp\u002F21.pointers-to-pointers",{"title":1797,"path":1798,"stem":1799},"Оператор доступу до членів через вказівник (->)","\u002Fcpp\u002Fmember-access-operator","02.cpp\u002F22.member-access-operator",{"title":1801,"path":1802,"stem":1803},"Цикл for-each (Range-based for)","\u002Fcpp\u002Fforeach-loop","02.cpp\u002F23.foreach-loop",{"title":1805,"path":1806,"stem":1807},"Вказівники на функції","\u002Fcpp\u002Ffunction-pointers","02.cpp\u002F24.function-pointers",{"title":1809,"path":1810,"stem":1811},"Лямбда-вирази","\u002Fcpp\u002Flambdas","02.cpp\u002F25.lambdas",{"title":1813,"path":1814,"stem":1815},"Лямбда-захоплення","\u002Fcpp\u002Flambda-captures","02.cpp\u002F26.lambda-captures",{"title":1817,"path":1818,"stem":1819},"Еліпсис","\u002Fcpp\u002Fellipsis","02.cpp\u002F27.ellipsis",{"title":1821,"path":1822,"stem":1823},"Безпечні альтернативи еліпсису","\u002Fcpp\u002F27a.ellipsis","02.cpp\u002F27a.ellipsis",{"title":1825,"path":1826,"stem":1827},"Аргументи командного рядка","\u002Fcpp\u002Fcommand-line-arguments","02.cpp\u002F28.command-line-arguments",{"title":1829,"path":1830,"stem":1831},"Перерахування (enum)","\u002Fcpp\u002Fenum","02.cpp\u002F29.enum",{"title":1833,"path":1834,"stem":1835},"Класи-перерахування (enum class)","\u002Fcpp\u002Fenum-class","02.cpp\u002F30.enum-class",{"title":1837,"path":1838,"stem":1839},"Псевдоніми типів (typedef і using)","\u002Fcpp\u002Ftype-aliases","02.cpp\u002F31.type-aliases",{"title":1841,"path":1842,"stem":1843},"Системи числення та двійкова арифметика","\u002Fcpp\u002Fnumber-systems","02.cpp\u002F32.number-systems",{"title":1845,"path":1846,"stem":1847},"Структури (struct): агрегування даних","\u002Fcpp\u002Fstruct","02.cpp\u002F33.struct",{"title":1849,"path":1850,"stem":1851},"Структури у функціях","\u002Fcpp\u002Fstruct-functions","02.cpp\u002F34.struct-functions",{"title":1853,"path":1854,"stem":1855},"Масиви структур і вкладені структури","\u002Fcpp\u002Fstruct-arrays","02.cpp\u002F35.struct-arrays",{"title":1857,"path":1858,"stem":1859},"Патерни struct та межі застосування","\u002Fcpp\u002Fstruct-patterns","02.cpp\u002F36.struct-patterns",{"title":1861,"path":1862,"stem":1863},"Символи та таблиця ASCII","\u002Fcpp\u002Fascii-characters","02.cpp\u002F37.ascii-characters",{"title":1865,"path":1866,"stem":1867},"Unicode та кодування UTF","\u002Fcpp\u002Funicode-utf","02.cpp\u002F38.unicode-utf",{"title":1869,"path":1870,"stem":1871},"C-style рядки","\u002Fcpp\u002Fc-strings","02.cpp\u002F39.c-strings",{"title":1873,"path":1874,"stem":1875},"Вступ до std::string","\u002Fcpp\u002Fstd-string-intro","02.cpp\u002F40.std-string-intro",{"title":1877,"path":1878,"stem":1879},"Довжина, ємність та доступ до символів std::string","\u002Fcpp\u002Fstd-string-capacity-access","02.cpp\u002F41.std-string-capacity-access",{"title":1881,"path":1882,"stem":1883},"Модифікація std::string: присвоювання, додавання, вставка, видалення та заміна","\u002Fcpp\u002Fstd-string-modification","02.cpp\u002F42.std-string-modification",{"title":1885,"path":1886,"stem":1887},"Пошук у std::string: find, npos та практичні патерни","\u002Fcpp\u002Fstd-string-search","02.cpp\u002F43.std-string-search",{"title":1889,"path":1890,"stem":1891},"std::string_view: невласницький погляд на рядок без копіювання","\u002Fcpp\u002Fstd-string-view","02.cpp\u002F44.std-string-view",{"title":1893,"path":1894,"stem":1895},"Об'єднання (union): один блок пам'яті, кілька інтерпретацій","\u002Fcpp\u002Funion","02.cpp\u002F45.union",{"title":1897,"path":1898,"stem":1899},"Організація коду: файли, препроцесор, простори імен","\u002Fcpp\u002Fmultifile-programs","02.cpp\u002F46.multifile-programs",{"title":1901,"path":1902,"stem":1903},"Робота з файлами: C-стиль (stdio.h)","\u002Fcpp\u002Fc-style-files","02.cpp\u002F47.c-style-files",{"title":1905,"path":1906,"stem":1907},"Робота з файлами: C++-стиль (fstream)","\u002Fcpp\u002Fcpp-style-files","02.cpp\u002F48.cpp-style-files",{"title":1909,"path":1910,"stem":1911},"Введення в ООП: від процедур до об'єктів","\u002Fcpp\u002Foop-intro","02.cpp\u002F49.oop-intro",{"title":1913,"path":1914,"stem":1915},"Класи, об'єкти та методи","\u002Fcpp\u002Fclasses-objects-methods","02.cpp\u002F50.classes-objects-methods",{"title":1917,"path":1918,"stem":1919},"Специфікатори доступу та інкапсуляція","\u002Fcpp\u002Faccess-specifiers-encapsulation","02.cpp\u002F51.access-specifiers-encapsulation",{"title":1921,"path":1922,"stem":1923},"Конструктори: гарантована ініціалізація об'єктів","\u002Fcpp\u002Fconstructors","02.cpp\u002F52.constructors",{"title":1925,"path":1926,"stem":1927},"Списки ініціалізації та делегуючі конструктори","\u002Fcpp\u002Finit-lists-delegating","02.cpp\u002F53.init-lists-delegating",{"title":1929,"path":1930,"stem":1931},"Деструктори та ідіома RAII","\u002Fcpp\u002Fdestructors-raii","02.cpp\u002F54.destructors-raii",{"title":1933,"path":1934,"stem":1935},"Прихований покажчик this","\u002Fcpp\u002Fthis-pointer","02.cpp\u002F55.this-pointer",{"title":1937,"path":1938,"stem":1939},"Розподіл класу на заголовковий файл та файл реалізації","\u002Fcpp\u002Fclass-header-source","02.cpp\u002F56.class-header-source",{"title":1941,"path":1942,"stem":1943},"Класи та const","\u002Fcpp\u002Fconst-classes","02.cpp\u002F57.const-classes",{"title":1945,"path":1946,"stem":1947},"Статичні члени класу","\u002Fcpp\u002Fstatic-members","02.cpp\u002F58.static-members",{"title":1949,"path":1950,"stem":1951},"Конструктор копіювання та запобігання копіюванню","\u002Fcpp\u002Fcopy-constructor","02.cpp\u002F59.copy-constructor",{"title":1953,"path":1954,"stem":1955},"Дружні функції та класи","\u002Fcpp\u002Ffriend-functions-classes","02.cpp\u002F60.friend-functions-classes",{"title":1957,"path":1958,"stem":1959},"Анонімні об'єкти та вкладені типи","\u002Fcpp\u002Fanonymous-objects-nested-types","02.cpp\u002F61.anonymous-objects-nested-types",{"title":1961,"path":1962,"stem":1963},"Вимірювання часу виконання: RAII-таймер","\u002Fcpp\u002Ftiming-raii-timer","02.cpp\u002F62.timing-raii-timer",{"title":1965,"path":1966,"stem":1967},"Перевантаження операторів: основи та арифметика","\u002Fcpp\u002Foperator-overloading-basics","02.cpp\u002F63.operator-overloading-basics",{"title":1969,"path":1970,"stem":1971},"Перевантаження операторів: методи класу та спеціальні оператори","\u002Fcpp\u002Foperator-overloading-methods","02.cpp\u002F64.operator-overloading-methods",{"title":1973,"path":1974,"stem":1975},"Приведення типів, explicit та delete","\u002Fcpp\u002Ftypecast-explicit-delete","02.cpp\u002F65.typecast-explicit-delete",{"title":1977,"path":1978,"stem":1979},"Глибоке копіювання та оператор присвоювання","\u002Fcpp\u002Fdeep-copy-assignment","02.cpp\u002F66.deep-copy-assignment",{"title":1981,"path":1982,"stem":1983},"Зв'язки між об'єктами: композиція та агрегація","\u002Fcpp\u002Fcomposition-aggregation","02.cpp\u002F67.composition-aggregation",{"title":1985,"path":1986,"stem":1987},"Асоціація та залежність між об","\u002Fcpp\u002Fassociation-dependency","02.cpp\u002F68.association-dependency",{"title":1989,"path":1990,"stem":1991},"Контейнерні класи та `std::initializer_list`","\u002Fcpp\u002Fcontainer-classes-initializer-list","02.cpp\u002F69.container-classes-initializer-list",{"title":1993,"path":1994,"stem":1995},"Вступ до наслідування: базові поняття та відношення «is-a»","\u002Fcpp\u002Finheritance-intro","02.cpp\u002F70.inheritance-intro",{"title":1997,"path":1998,"stem":1999},"Конструктори, деструктори та порядок ініціалізації в ієрархіях","\u002Fcpp\u002Finheritance-constructors-destructors","02.cpp\u002F71.inheritance-constructors-destructors",{"title":2001,"path":2002,"stem":2003},"Специфікатор `protected` та види наслідування","\u002Fcpp\u002Finheritance-access-specifiers","02.cpp\u002F72.inheritance-access-specifiers",{"title":2005,"path":2006,"stem":2007},"Перевизначення методів та приховування імен","\u002Fcpp\u002Fmethod-overriding-hiding","02.cpp\u002F73.method-overriding-hiding",{"title":2009,"path":2010,"stem":2011},"Множинне наслідування та проблема ромба","\u002Fcpp\u002Fmultiple-inheritance-diamond","02.cpp\u002F74.multiple-inheritance-diamond",{"title":2013,"path":2014,"stem":2015},"Вказівники на базовий клас та віртуальні функції","\u002Fcpp\u002Fvirtual-functions-polymorphism","02.cpp\u002F75.virtual-functions-polymorphism",{"title":2017,"path":2018,"stem":2019},"Специфікатори override, final та віртуальні деструктори","\u002Fcpp\u002Foverride-final-virtual-destructors","02.cpp\u002F76.override-final-virtual-destructors",{"title":2021,"path":2022,"stem":2023},"Внутрішні механізми поліморфізму: vtable, vptr та зв'язування","\u002Fcpp\u002Fvirtual-table-binding","02.cpp\u002F77.virtual-table-binding",{"title":2025,"path":2026,"stem":2027},"Абстрактні класи та чисті віртуальні функції","\u002Fcpp\u002Fabstract-classes-interfaces","02.cpp\u002F78.abstract-classes-interfaces",{"title":2029,"path":2030,"stem":2031},"Зрізка об'єктів, віртуальне наслідування та поліморфний вивід","\u002Fcpp\u002Fslicing-virtual-base-io","02.cpp\u002F79.slicing-virtual-base-io",{"title":2033,"path":2034,"stem":2035},"Динамічне приведення типів (`dynamic_cast`) та RTTI","\u002Fcpp\u002Fdynamic-cast-rtti","02.cpp\u002F80.dynamic-cast-rtti",{"title":2037,"path":2038,"stem":2039},"Шаблони функцій: узагальнене програмування","\u002Fcpp\u002Ffunction-templates","02.cpp\u002F81.function-templates",{"title":2041,"path":2042,"stem":2043},"Шаблони класів та NTTP-параметри","\u002Fcpp\u002Fclass-templates-nttp","02.cpp\u002F82.class-templates-nttp",{"title":2045,"path":2046,"stem":2047},"Повна (явна) спеціалізація шаблонів","\u002Fcpp\u002Ffull-template-specialization","02.cpp\u002F83.full-template-specialization",{"title":2049,"path":2050,"stem":2051},"Часткова спеціалізація шаблонів класів","\u002Fcpp\u002Fpartial-template-specialization","02.cpp\u002F84.partial-template-specialization",{"title":2053,"path":2054,"stem":2055},"Механізм винятків: try, catch, throw та розкручування стеку","\u002Fcpp\u002Fexceptions-basics","02.cpp\u002F85.exceptions-basics",{"title":2057,"path":2058,"stem":2059},"Ієрархія std::exception та власні класи винятків","\u002Fcpp\u002Fexception-hierarchy-custom","02.cpp\u002F86.exception-hierarchy-custom",{"title":2061,"path":2062,"stem":2063},"Повторне генерування винятків та функціональний try-блок","\u002Fcpp\u002Frethrow-function-try","02.cpp\u002F87.rethrow-function-try",{"title":2065,"path":2066,"stem":2067},"Безпека винятків, noexcept та деструктори","\u002Fcpp\u002Fsafety-noexcept","02.cpp\u002F88.safety-noexcept",{"title":2069,"path":2070,"stem":2071},"Стаття 89 — Власні структури даних: стек та черга","\u002Fcpp\u002Fcustom-stack-queue","02.cpp\u002F89.custom-stack-queue",{"title":2073,"path":2074,"stem":2075},"Стаття 90 — Власні структури даних: однозв'язаний список","\u002Fcpp\u002Flinked-list","02.cpp\u002F90.linked-list",{"title":2077,"path":2078,"stem":2079},"Стаття 91 — Власні структури даних: бінарне дерево пошуку","\u002Fcpp\u002Fbinary-search-tree","02.cpp\u002F91.binary-search-tree",{"title":2081,"path":2082,"stem":2083},"Стаття 92 — Власні структури даних: хеш-таблиця","\u002Fcpp\u002Fhash-table","02.cpp\u002F92.hash-table",{"title":2085,"icon":2086,"path":2087,"stem":2088,"children":2089,"page":59},"JavaScript","i-devicon-javascript","\u002Fjavascript","03.javascript",[2090,2116,2170,2192,2496,2534,2552],{"title":2091,"icon":2092,"path":2093,"stem":2094,"children":2095,"page":59},"Events","i-lucide-mouse-pointer-click","\u002Fjavascript\u002Fevents","03.javascript\u002F01.events",[2096,2100,2104,2108,2112],{"title":2097,"path":2098,"stem":2099},"Вступ до подій браузера","\u002Fjavascript\u002Fevents\u002Fintro","03.javascript\u002F01.events\u002F01.intro",{"title":2101,"path":2102,"stem":2103},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","\u002Fjavascript\u002Fevents\u002Fbubbling-capturing","03.javascript\u002F01.events\u002F02.bubbling-capturing",{"title":2105,"path":2106,"stem":2107},"Делегування подій (Event Delegation)","\u002Fjavascript\u002Fevents\u002Fdelegate-events","03.javascript\u002F01.events\u002F03.delegate-events",{"title":2109,"path":2110,"stem":2111},"Типові дії браузера та preventDefault()","\u002Fjavascript\u002Fevents\u002Fprevent-default","03.javascript\u002F01.events\u002F04.prevent-default",{"title":2113,"path":2114,"stem":2115},"Запуск користувацьких подій (Custom Events)","\u002Fjavascript\u002Fevents\u002Fcustom-events","03.javascript\u002F01.events\u002F05.custom-events",{"title":2117,"icon":2118,"path":2119,"stem":2120,"children":2121,"page":59},"Network","i-lucide-globe","\u002Fjavascript\u002Fnetwork","03.javascript\u002F02.network",[2122,2126,2130,2134,2138,2142,2146,2150,2154,2158,2162,2166],{"title":2123,"path":2124,"stem":2125},"Fetch API - Сучасний підхід до HTTP-запитів","\u002Fjavascript\u002Fnetwork\u002F01-fetch-api","03.javascript\u002F02.network\u002F01-fetch-api",{"title":2127,"path":2128,"stem":2129},"FormData - Робота з формами та файлами","\u002Fjavascript\u002Fnetwork\u002F02-formdata","03.javascript\u002F02.network\u002F02-formdata",{"title":2131,"path":2132,"stem":2133},"Відстеження прогресу завантаження","\u002Fjavascript\u002Fnetwork\u002F03-download-progress","03.javascript\u002F02.network\u002F03-download-progress",{"title":2135,"path":2136,"stem":2137},"Переривання fetch-запитів","\u002Fjavascript\u002Fnetwork\u002F04-abort-requests","03.javascript\u002F02.network\u002F04-abort-requests",{"title":2139,"path":2140,"stem":2141},"CORS - Запити між різними джерелами","\u002Fjavascript\u002Fnetwork\u002F05-cors","03.javascript\u002F02.network\u002F05-cors",{"title":2143,"path":2144,"stem":2145},"Fetch API - Повний довідник опцій","\u002Fjavascript\u002Fnetwork\u002F06-fetch-options","03.javascript\u002F02.network\u002F06-fetch-options",{"title":2147,"path":2148,"stem":2149},"URL Objects - Робота з посиланнями","\u002Fjavascript\u002Fnetwork\u002F07-url-objects","03.javascript\u002F02.network\u002F07-url-objects",{"title":2151,"path":2152,"stem":2153},"XMLHttpRequest - AJAX та низькорівневі запити","\u002Fjavascript\u002Fnetwork\u002F08-xmlhttprequest","03.javascript\u002F02.network\u002F08-xmlhttprequest",{"title":2155,"path":2156,"stem":2157},"Відновлюване завантаження файлів","\u002Fjavascript\u002Fnetwork\u002F09-resumable-upload","03.javascript\u002F02.network\u002F09-resumable-upload",{"title":2159,"path":2160,"stem":2161},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","\u002Fjavascript\u002Fnetwork\u002F10-cookies","03.javascript\u002F02.network\u002F10-cookies",{"title":2163,"path":2164,"stem":2165},"js-cookie: Керування Cookies без Болю","\u002Fjavascript\u002Fnetwork\u002F11-js-cookie","03.javascript\u002F02.network\u002F11-js-cookie",{"title":2167,"path":2168,"stem":2169},"Axios: Потужний HTTP-клієнт для JavaScript","\u002Fjavascript\u002Fnetwork\u002F12-axios","03.javascript\u002F02.network\u002F12-axios",{"title":2171,"icon":2172,"path":2173,"stem":2174,"children":2175,"page":59},"Bom","i-lucide-monitor","\u002Fjavascript\u002Fbom","03.javascript\u002F03.bom",[2176,2180,2184,2188],{"title":2177,"path":2178,"stem":2179},"LocalStorage, SessionStorage та patterns збереження даних","\u002Fjavascript\u002Fbom\u002F01-localstorage","03.javascript\u002F03.bom\u002F01-localstorage",{"title":2181,"path":2182,"stem":2183},"Location Object - Керування адресою сторінки","\u002Fjavascript\u002Fbom\u002F02-location-object","03.javascript\u002F03.bom\u002F02-location-object",{"title":2185,"path":2186,"stem":2187},"History API - Керування історією браузера","\u002Fjavascript\u002Fbom\u002F03-history-api","03.javascript\u002F03.bom\u002F03-history-api",{"title":2189,"path":2190,"stem":2191},"Navigator Object - Ідентифікація та Можливості Пристрою","\u002Fjavascript\u002Fbom\u002F04-navigator-object","03.javascript\u002F03.bom\u002F04-navigator-object",{"title":2193,"icon":2194,"path":2195,"stem":2196,"children":2197},"React","i-devicon-react","\u002Fjavascript\u002Freact","03.javascript\u002F04.react\u002Findex",[2198,2199,2203,2207,2211,2215,2278,2313,2465],{"title":2193,"path":2195,"stem":2196},{"title":2200,"path":2201,"stem":2202},"Робота з Формами в React","\u002Fjavascript\u002Freact\u002Freact-forms","03.javascript\u002F04.react\u002F01.react-forms",{"title":2204,"path":2205,"stem":2206},"React Hook Form: Професійна Робота з Формами","\u002Fjavascript\u002Freact\u002Freact-hook-form","03.javascript\u002F04.react\u002F02.react-hook-form",{"title":2208,"path":2209,"stem":2210},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","\u002Fjavascript\u002Freact\u002Freact-hook-form-new","03.javascript\u002F04.react\u002F02.react-hook-form-new",{"title":2212,"path":2213,"stem":2214},"Axios та React: Професійна Архітектура Запитів","\u002Fjavascript\u002Freact\u002Fdata-fetching-axios","03.javascript\u002F04.react\u002F03.data-fetching-axios",{"title":2216,"icon":132,"path":2217,"stem":2218,"children":2219},"Tanstack Query","\u002Fjavascript\u002Freact\u002Ftanstack-query","03.javascript\u002F04.react\u002F04.tanstack-query\u002Findex",[2220,2222,2226,2230,2234,2238,2242,2246,2250,2254,2258,2262,2266,2270,2274],{"title":2221,"path":2217,"stem":2218},"TanStack Query: Майстерність Керування Станом Сервера",{"title":2223,"path":2224,"stem":2225},"Парадигма Server State: Чому useEffect недостатньо","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-state-paradigm","03.javascript\u002F04.react\u002F04.tanstack-query\u002F01.server-state-paradigm",{"title":2227,"path":2228,"stem":2229},"Встановлення та Налаштування: Фундамент","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Finstallation-and-devtools","03.javascript\u002F04.react\u002F04.tanstack-query\u002F02.installation-and-devtools",{"title":2231,"path":2232,"stem":2233},"Основи Запитів та Магія Ключів","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fquery-basics-and-keys","03.javascript\u002F04.react\u002F04.tanstack-query\u002F03.query-basics-and-keys",{"title":2235,"path":2236,"stem":2237},"Синхронізація Даних: Життєвий Цикл Запиту","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fdata-synchronization","03.javascript\u002F04.react\u002F04.tanstack-query\u002F04.data-synchronization",{"title":2239,"path":2240,"stem":2241},"Мутації та Інвалідація: Зміна Даних","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fmutations-and-invalidation","03.javascript\u002F04.react\u002F04.tanstack-query\u002F05.mutations-and-invalidation",{"title":2243,"path":2244,"stem":2245},"Оптимістичні Оновлення: Швидше за Світло","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Foptimistic-updates","03.javascript\u002F04.react\u002F04.tanstack-query\u002F06.optimistic-updates",{"title":2247,"path":2248,"stem":2249},"Пагінація та Infinite Scroll","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fpagination-and-load-more","03.javascript\u002F04.react\u002F04.tanstack-query\u002F07.pagination-and-load-more",{"title":2251,"path":2252,"stem":2253},"Просунуті Патерни та Оптимізація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F04.tanstack-query\u002F08.advanced-patterns",{"title":2255,"path":2256,"stem":2257},"Архітектура та Best Practices","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Farchitecture-and-best-practices","03.javascript\u002F04.react\u002F04.tanstack-query\u002F09.architecture-and-best-practices",{"title":2259,"path":2260,"stem":2261},"Server-Side Rendering (SSR) та Гідратація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-side-rendering","03.javascript\u002F04.react\u002F04.tanstack-query\u002F10.server-side-rendering",{"title":2263,"path":2264,"stem":2265},"Стратегії Тестування","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Ftesting-strategies","03.javascript\u002F04.react\u002F04.tanstack-query\u002F11.testing-strategies",{"title":2267,"path":2268,"stem":2269},"Аутентифікація та Обробка Помилок","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fauthentication-and-errors","03.javascript\u002F04.react\u002F04.tanstack-query\u002F12.authentication-and-errors",{"title":2271,"path":2272,"stem":2273},"React Suspense та Майбутнє","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Freact-suspense","03.javascript\u002F04.react\u002F04.tanstack-query\u002F13.react-suspense",{"title":2275,"path":2276,"stem":2277},"Глибоке Занурення в Продуктивність","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fperformance-deep-dive","03.javascript\u002F04.react\u002F04.tanstack-query\u002F14.performance-deep-dive",{"title":2279,"icon":2194,"path":2280,"stem":2281,"children":2282},"React Router","\u002Fjavascript\u002Freact\u002Freact-router","03.javascript\u002F04.react\u002F05.react-router\u002Findex",[2283,2285,2289,2293,2297,2301,2305,2309],{"title":2284,"path":2280,"stem":2281},"React Router: Навігаційна система сучасного вебу",{"title":2286,"path":2287,"stem":2288},"Налаштування та Базовий Роутинг","\u002Fjavascript\u002Freact\u002Freact-router\u002Fsetup-and-basic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F01.setup-and-basic-routing",{"title":2290,"path":2291,"stem":2292},"Динамічна Навігація","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnavigation-and-links","03.javascript\u002F04.react\u002F05.react-router\u002F02.navigation-and-links",{"title":2294,"path":2295,"stem":2296},"Вкладені Маршрути та Макети","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnested-routes-and-layouts","03.javascript\u002F04.react\u002F05.react-router\u002F03.nested-routes-and-layouts",{"title":2298,"path":2299,"stem":2300},"Динамічні Маршрути та Параметри","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdynamic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F04.dynamic-routing",{"title":2302,"path":2303,"stem":2304},"Data APIs: Loaders та Actions","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdata-loading","03.javascript\u002F04.react\u002F05.react-router\u002F05.data-loading",{"title":2306,"path":2307,"stem":2308},"Просунуті Патерни","\u002Fjavascript\u002Freact\u002Freact-router\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F05.react-router\u002F06.advanced-patterns",{"title":2310,"path":2311,"stem":2312},"Legacy Routing: Компонентний підхід","\u002Fjavascript\u002Freact\u002Freact-router\u002Flegacy-routing","03.javascript\u002F04.react\u002F05.react-router\u002F07.legacy-routing",{"title":2314,"icon":132,"path":2315,"stem":2316,"children":2317},"Redux","\u002Fjavascript\u002Freact\u002Fredux","03.javascript\u002F04.react\u002F06.redux\u002Findex",[2318,2320,2336,2365,2374,2395,2411,2440],{"title":2319,"path":2315,"stem":2316},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2321,"stem":2322,"children":2323,"page":59},"\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals",[2324,2328,2332],{"title":2325,"path":2326,"stem":2327},"Вступ до State Management","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fintro-state-management","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F01.intro-state-management",{"title":2329,"path":2330,"stem":2331},"Філософія Redux та Три Принципи","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fredux-philosophy","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F02.redux-philosophy",{"title":2333,"path":2334,"stem":2335},"Чисті функції та Іммутабельність","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fpure-functions-immutability","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F03.pure-functions-immutability",{"title":2337,"icon":132,"path":2338,"stem":2339,"children":2340,"page":59},"Classic Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux",[2341,2345,2349,2353,2357,2361],{"title":2342,"path":2343,"stem":2344},"Створення Store (Classic Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fstore-setup","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F01.store-setup",{"title":2346,"path":2347,"stem":2348},"Actions, Constants та Action Creators","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Factions-constants","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F02.actions-constants",{"title":2350,"path":2351,"stem":2352},"Логіка Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freducers","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F03.reducers",{"title":2354,"path":2355,"stem":2356},"Комбінування Reducers (Root Reducer)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fdata-flow","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F04.data-flow",{"title":2358,"path":2359,"stem":2360},"Підключення до React (React-Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freact-redux-connection","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F05.react-redux-connection",{"title":2362,"path":2363,"stem":2364},"Middleware та Асинхронність (Redux Thunk)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fmiddleware-thunk","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F06.middleware-thunk",{"title":2366,"icon":132,"path":2367,"stem":2368,"children":2369,"page":59},"Transition To Rtk","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk",[2370],{"title":2371,"path":2372,"stem":2373},"Проблеми класичного Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk\u002Fproblems-with-classic","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk\u002F01.problems-with-classic",{"title":2375,"icon":132,"path":2376,"stem":2377,"children":2378,"page":59},"Redux Toolkit","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit",[2379,2383,2387,2391],{"title":2380,"path":2381,"stem":2382},"Налаштування Store з configureStore","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fconfigure-store","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F01.configure-store",{"title":2384,"path":2385,"stem":2386},"createSlice: Революція в Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fcreate-slice","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F02.create-slice",{"title":2388,"path":2389,"stem":2390},"Асинхронність з createAsyncThunk","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fasync-thunks","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F03.async-thunks",{"title":2392,"path":2393,"stem":2394},"04. Entity Adapter: Керування нормалізованим станом","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fentity-adapter","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F04.entity-adapter",{"title":2396,"icon":92,"path":2397,"stem":2398,"children":2399,"page":59},"Advanced","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced",[2400,2404,2408],{"title":2401,"path":2402,"stem":2403},"Мемоізація та Селектори: Повний Гайд по Reselect","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Fselectors-reselect","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F01.selectors-reselect",{"title":2405,"path":2406,"stem":2407},"RTK Query: Архітектура Серверного Кешу","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Frtk-query-intro","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F02.rtk-query-intro",{"title":2255,"path":2409,"stem":2410},"\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Farchitecture-best-practices","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F03.architecture-best-practices",{"title":2412,"icon":132,"path":2413,"stem":2414,"children":2415,"page":59},"Project Kanban","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban",[2416,2420,2424,2428,2432,2436],{"title":2417,"path":2418,"stem":2419},"Проєкт: Kanban Board (Trello Clone)","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fproject-overview","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F01.project-overview",{"title":2421,"path":2422,"stem":2423},"Налаштування та Типізація","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fsetup-and-types","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F02.setup-and-types",{"title":2425,"path":2426,"stem":2427},"Board Slice: Серце Дошки","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fboard-slice","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F03.board-slice",{"title":2429,"path":2430,"stem":2431},"Логіка Drag & Drop","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fdrag-and-drop-logic","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F04.drag-and-drop-logic",{"title":2433,"path":2434,"stem":2435},"Інтеграція з RTK Query","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Frtk-query-integration","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F05.rtk-query-integration",{"title":2437,"path":2438,"stem":2439},"Optimistic Updates","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Foptimistic-updates","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F06.optimistic-updates",{"title":2441,"icon":132,"path":2442,"stem":2443,"children":2444,"page":59},"Testing","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting","03.javascript\u002F04.react\u002F06.redux\u002F07.testing",[2445,2449,2453,2457,2461],{"title":2446,"path":2447,"stem":2448},"Тестування Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Fintro-testing","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F01.intro-testing",{"title":2450,"path":2451,"stem":2452},"Тестування Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-reducers","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F02.testing-reducers",{"title":2454,"path":2455,"stem":2456},"Тестування Селекторів","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-selectors","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F03.testing-selectors",{"title":2458,"path":2459,"stem":2460},"Тестування Компонентів (Integration)","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-components","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F04.testing-components",{"title":2462,"path":2463,"stem":2464},"Тестування Async Thunks","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-thunks","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F05.testing-thunks",{"title":2466,"icon":132,"path":2467,"stem":2468,"children":2469},"Ui Libraries","\u002Fjavascript\u002Freact\u002Fui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002Findex",[2470,2472,2476,2480,2484,2488,2492],{"title":2471,"path":2467,"stem":2468},"UI Бібліотеки в React",{"title":2473,"path":2474,"stem":2475},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fintroduction-to-ui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002F01.introduction-to-ui-libraries",{"title":2477,"path":2478,"stem":2479},"Філософія shadcn\u002Fui: \"Not a Component Library\"","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-philosophy","03.javascript\u002F04.react\u002F07.ui-libraries\u002F02.shadcn-philosophy",{"title":2481,"path":2482,"stem":2483},"Установка та Налаштування shadcn\u002Fui","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-installation","03.javascript\u002F04.react\u002F07.ui-libraries\u002F03.shadcn-installation",{"title":2485,"path":2486,"stem":2487},"Базові Компоненти shadcn\u002Fui: Фундамент Інтерфейсу","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-basics","03.javascript\u002F04.react\u002F07.ui-libraries\u002F04.shadcn-components-basics",{"title":2489,"path":2490,"stem":2491},"Компоненти Форм: Побудова Інтерактивних Form","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-forms","03.javascript\u002F04.react\u002F07.ui-libraries\u002F05.shadcn-components-forms",{"title":2493,"path":2494,"stem":2495},"Складні Компоненти: Dialog, Dropdown, Table та Command","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-advanced","03.javascript\u002F04.react\u002F07.ui-libraries\u002F06.shadcn-components-advanced",{"title":2497,"icon":2498,"path":2499,"stem":2500,"children":2501,"page":59},"TypeScript","i-devicon-typescript","\u002Fjavascript\u002Ftypescript","03.javascript\u002F05.typescript",[2502,2506,2510,2514,2518,2522,2526,2530],{"title":2503,"path":2504,"stem":2505},"TypeScript: Броня для вашого коду","\u002Fjavascript\u002Ftypescript\u002Fintro-and-basic-types","03.javascript\u002F05.typescript\u002F01.intro-and-basic-types",{"title":2507,"path":2508,"stem":2509},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","\u002Fjavascript\u002Ftypescript\u002Finterfaces-and-advanced-types","03.javascript\u002F05.typescript\u002F02.interfaces-and-advanced-types",{"title":2511,"path":2512,"stem":2513},"Алхімія Типів: Generics та Utility Types","\u002Fjavascript\u002Ftypescript\u002Fgenerics-and-utilities","03.javascript\u002F05.typescript\u002F03.generics-and-utilities",{"title":2515,"path":2516,"stem":2517},"Архітектура та Шаблони: Класи в TypeScript","\u002Fjavascript\u002Ftypescript\u002Fclasses-and-oop","03.javascript\u002F05.typescript\u002F04.classes-and-oop",{"title":2519,"path":2520,"stem":2521},"Продакшн та Екосистема: Advanced Config & Workflow","\u002Fjavascript\u002Ftypescript\u002Fadvanced-patterns-and-config","03.javascript\u002F05.typescript\u002F05.advanced-patterns-and-config",{"title":2523,"path":2524,"stem":2525},"TypeScript у світі React","\u002Fjavascript\u002Ftypescript\u002Freact-basics","03.javascript\u002F05.typescript\u002F06.react-basics",{"title":2527,"path":2528,"stem":2529},"React + TypeScript: Продвинуті патерни","\u002Fjavascript\u002Ftypescript\u002Freact-advanced","03.javascript\u002F05.typescript\u002F07.react-advanced",{"title":2531,"path":2532,"stem":2533},"React + TypeScript: Екосистема та бібліотеки","\u002Fjavascript\u002Ftypescript\u002Freact-ecosystem","03.javascript\u002F05.typescript\u002F08.react-ecosystem",{"title":2535,"icon":2536,"path":2537,"stem":2538,"children":2539,"page":59},"Асинхронність","i-lucide-refresh-cw","\u002Fjavascript\u002Fasync-programming","03.javascript\u002F06.async-programming",[2540,2544,2548],{"title":2541,"path":2542,"stem":2543},"Event Loop: серце асинхронності в JavaScript","\u002Fjavascript\u002Fasync-programming\u002Fevent-loop-fundamentals","03.javascript\u002F06.async-programming\u002F01.event-loop-fundamentals",{"title":2545,"path":2546,"stem":2547},"Task Queue та Microtask Queue: пріоритети виконання","\u002Fjavascript\u002Fasync-programming\u002Ftask-queues","03.javascript\u002F06.async-programming\u002F02.task-queues",{"title":2549,"path":2550,"stem":2551},"Асинхронні патерни: від Callbacks до Async\u002FAwait","\u002Fjavascript\u002Fasync-programming\u002Fasync-patterns","03.javascript\u002F06.async-programming\u002F03.async-patterns",{"title":2553,"path":2554,"stem":2555},"Atomic Design","\u002Fjavascript\u002Fatomic-design","03.javascript\u002F2.atomic-design",{"title":2557,"icon":2558,"path":2559,"stem":2560,"children":2561,"page":59},"Java","i-devicon-java","\u002Fjava","04.java",[2562,2565,2568,2572,2576,2580,2584],{"title":162,"path":2563,"stem":2564},"\u002Fjava\u002Fdata-mapper-part1","04.java\u002F01.data-mapper-part1",{"title":166,"path":2566,"stem":2567},"\u002Fjava\u002Fdata-mapper-part2","04.java\u002F02.data-mapper-part2",{"title":2569,"path":2570,"stem":2571},"Service Layer: Організація бізнес-логіки","\u002Fjava\u002Fservice-layer","04.java\u002F03.service-layer",{"title":2573,"path":2574,"stem":2575},"Rich Domain Model та State Pattern","\u002Fjava\u002Frich-domain-model","04.java\u002F04.rich-domain-model",{"title":2577,"path":2578,"stem":2579},"Патерни для складної бізнес-логіки","\u002Fjava\u002Fbusiness-logic-patterns","04.java\u002F05.business-logic-patterns",{"title":2581,"path":2582,"stem":2583},"Обробка помилок та валідація","\u002Fjava\u002Ferror-handling-validation","04.java\u002F06.error-handling-validation",{"title":2585,"path":2586,"stem":2587,"children":2588,"page":59},"Проектування баз даних","\u002Fjava\u002Fpr2","04.java\u002Fpr2",[2589,2593,2597,2601,2605,2609,2613,2617,2621,2625,2629,2633,2637,2641,2645,2649,2653,2657,2661,2665,2669,2673,2677,2681,2685,2689,2693,2697,2701,2705,2709,2713,2717,2721,2725,2729,2733],{"title":2590,"path":2591,"stem":2592},"Концептуальне моделювання: Мистецтво розуміння предметної області","\u002Fjava\u002Fpr2\u002Fconceptual-modeling","04.java\u002Fpr2\u002F01.conceptual-modeling",{"title":2594,"path":2595,"stem":2596},"Логічне моделювання: Від бізнес-ідей до структур даних","\u002Fjava\u002Fpr2\u002Flogical-modeling","04.java\u002Fpr2\u002F02.logical-modeling",{"title":2598,"path":2599,"stem":2600},"Нормалізація: Гігієна даних та боротьба з аномаліями","\u002Fjava\u002Fpr2\u002Fnormalization","04.java\u002Fpr2\u002F03.normalization",{"title":2602,"path":2603,"stem":2604},"Фізична схема: Від абстракції до DDL","\u002Fjava\u002Fpr2\u002Fphysical-schema","04.java\u002Fpr2\u002F04.physical-schema",{"title":2606,"path":2607,"stem":2608},"Архітектурна класифікація таблиць","\u002Fjava\u002Fpr2\u002Ftable-classification","04.java\u002Fpr2\u002F05.table-classification",{"title":2610,"path":2611,"stem":2612},"Database Migrations: Версіонування схеми з Flyway","\u002Fjava\u002Fpr2\u002Fdatabase-migrations","04.java\u002Fpr2\u002F06.database-migrations",{"title":2614,"path":2615,"stem":2616},"А що, якби це була не реляційна БД?","\u002Fjava\u002Fpr2\u002Fbeyond-relational","04.java\u002Fpr2\u002F07.beyond-relational",{"title":2618,"path":2619,"stem":2620},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","\u002Fjava\u002Fpr2\u002Fimpedance-mismatch","04.java\u002Fpr2\u002F09.impedance-mismatch",{"title":2622,"path":2623,"stem":2624},"JDBC: Перший контакт із базою даних","\u002Fjava\u002Fpr2\u002Fjdbc-fundamentals","04.java\u002Fpr2\u002F10.jdbc-fundamentals",{"title":2626,"path":2627,"stem":2628},"Якість коду: Spotless, SpotBugs та SonarQube","\u002Fjava\u002Fpr2\u002F10a.code-quality","04.java\u002Fpr2\u002F10a.code-quality",{"title":2630,"path":2631,"stem":2632},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","\u002Fjava\u002Fpr2\u002Fconnection-pool","04.java\u002Fpr2\u002F11.connection-pool",{"title":2634,"path":2635,"stem":2636},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","\u002Fjava\u002Fpr2\u002Frow-data-gateway","04.java\u002Fpr2\u002F12.row-data-gateway",{"title":2638,"path":2639,"stem":2640},"Table Data Gateway: Фасад таблиці як архітектурний відступ","\u002Fjava\u002Fpr2\u002Ftable-data-gateway","04.java\u002Fpr2\u002F13.table-data-gateway",{"title":2642,"path":2643,"stem":2644},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","\u002Fjava\u002Fpr2\u002Frepository-data-mapper","04.java\u002Fpr2\u002F14.repository-data-mapper",{"title":2646,"path":2647,"stem":2648},"Identity Map: Кешування сутностей у рамках сесії","\u002Fjava\u002Fpr2\u002Fidentity-map","04.java\u002Fpr2\u002F15.identity-map",{"title":2650,"path":2651,"stem":2652},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","\u002Fjava\u002Fpr2\u002Funit-of-work","04.java\u002Fpr2\u002F16.unit-of-work",{"title":2654,"path":2655,"stem":2656},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","\u002Fjava\u002Fpr2\u002Fstrategy-sql","04.java\u002Fpr2\u002F17.strategy-sql",{"title":2658,"path":2659,"stem":2660},"Proxy: Lazy Loading для One-To-Many колекцій","\u002Fjava\u002Fpr2\u002Fproxy-lazy-loading","04.java\u002Fpr2\u002F18.proxy-lazy-loading",{"title":2662,"path":2663,"stem":2664},"Generic Repository через Java Reflection: анотації та динамічний SQL","\u002Fjava\u002Fpr2\u002Fgeneric-repository-reflection","04.java\u002Fpr2\u002F19.generic-repository-reflection",{"title":2666,"path":2667,"stem":2668},"Specification Pattern: Композиція бізнес-правил для складних запитів","\u002Fjava\u002Fpr2\u002Fspecification-pattern","04.java\u002Fpr2\u002F20.specification-pattern",{"title":2670,"path":2671,"stem":2672},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","\u002Fjava\u002Fpr2\u002F20a.advanced-specifications","04.java\u002Fpr2\u002F20a.advanced-specifications",{"title":2674,"path":2675,"stem":2676},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","\u002Fjava\u002Fpr2\u002Fasynchronous-jdbc","04.java\u002Fpr2\u002F21.asynchronous-jdbc",{"title":2678,"path":2679,"stem":2680},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","\u002Fjava\u002Fpr2\u002Fintegration-testing-h2","04.java\u002Fpr2\u002F22.integration-testing-h2",{"title":2682,"path":2683,"stem":2684},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","\u002Fjava\u002Fpr2\u002Fintegration-testing-testcontainers","04.java\u002Fpr2\u002F23.integration-testing-testcontainers",{"title":2686,"path":2687,"stem":2688},"Google Guice: Впровадження залежностей у JavaFX-проєкті","\u002Fjava\u002Fpr2\u002Fdependency-injection-guice","04.java\u002Fpr2\u002F24.dependency-injection-guice",{"title":2690,"path":2691,"stem":2692},"JavaFX: Основи побудови графічних інтерфейсів","\u002Fjava\u002Fpr2\u002Fjavafx-fundamentals","04.java\u002Fpr2\u002F25.javafx-fundamentals",{"title":2694,"path":2695,"stem":2696},"Properties та Bindings: Реактивність у JavaFX","\u002Fjava\u002Fpr2\u002Fjavafx-properties-bindings","04.java\u002Fpr2\u002F26.javafx-properties-bindings",{"title":2698,"path":2699,"stem":2700},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","\u002Fjava\u002Fpr2\u002Fui-architecture-patterns","04.java\u002Fpr2\u002F27.ui-architecture-patterns",{"title":2702,"path":2703,"stem":2704},"MVVM на практиці: Побудова ViewModel","\u002Fjava\u002Fpr2\u002Fmvvm-viewmodel-implementation","04.java\u002Fpr2\u002F28.mvvm-viewmodel-implementation",{"title":2706,"path":2707,"stem":2708},"View та Controller: Зв'язування з ViewModel через FXML","\u002Fjava\u002Fpr2\u002Fmvvm-view-controller","04.java\u002Fpr2\u002F29.mvvm-view-controller",{"title":2710,"path":2711,"stem":2712},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","\u002Fjava\u002Fpr2\u002Fmvvm-guice-integration","04.java\u002Fpr2\u002F30.mvvm-guice-integration",{"title":2714,"path":2715,"stem":2716},"Валідація та обробка помилок у MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-validation-error-handling","04.java\u002Fpr2\u002F31.mvvm-validation-error-handling",{"title":2718,"path":2719,"stem":2720},"Навігація та управління екранами у JavaFX MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-navigation-screen-management","04.java\u002Fpr2\u002F32.mvvm-navigation-screen-management",{"title":2722,"path":2723,"stem":2724},"Тестування JavaFX MVVM-додатків","\u002Fjava\u002Fpr2\u002Fmvvm-testing","04.java\u002Fpr2\u002F33.mvvm-testing",{"title":2726,"path":2727,"stem":2728},"Стилізація та теми у JavaFX: CSS та User Experience","\u002Fjava\u002Fpr2\u002Fjavafx-styling-themes","04.java\u002Fpr2\u002F34.javafx-styling-themes",{"title":2730,"path":2731,"stem":2732},"AtlantaFX: Сучасні теми для JavaFX додатків","\u002Fjava\u002Fpr2\u002Fatlantafx-modern-themes","04.java\u002Fpr2\u002F35.atlantafx-modern-themes",{"title":2734,"path":2735,"stem":2736},"Пакування та розповсюдження JavaFX-додатків","\u002Fjava\u002Fpr2\u002Fjar-packaging-distribution","04.java\u002Fpr2\u002F36.jar-packaging-distribution",{"title":2738,"icon":2739,"path":2740,"stem":2741,"children":2742,"page":59},"Python","i-devicon-python","\u002Fpython","05.python",[2743,2747,2750,2754,2758,2762,2766,2770,2774,2778,2782,2786,2790,2794,2798,2859],{"title":2744,"path":2745,"stem":2746},"Модулі, Пакети та Віртуальні Середовища","\u002Fpython\u002Fmodules-packages-venv","05.python\u002F00.modules-packages-venv",{"title":71,"path":2748,"stem":2749},"\u002Fpython\u002Fclasses-objects","05.python\u002F01.classes-objects",{"title":2751,"path":2752,"stem":2753},"Інкапсуляція, Керування Доступом та Властивості","\u002Fpython\u002Fencapsulation","05.python\u002F02.encapsulation",{"title":2755,"path":2756,"stem":2757},"Наслідування, MRO та суперсила super()","\u002Fpython\u002Finheritance-mro","05.python\u002F03.inheritance-mro",{"title":2759,"path":2760,"stem":2761},"Абстракція — ABC проти Статичних Протоколів (PEP 544)","\u002Fpython\u002Fabstraction-protocols","05.python\u002F04.abstraction-protocols",{"title":2763,"path":2764,"stem":2765},"Магічні методи (Dunder) та Емуляція протоколів","\u002Fpython\u002Fdunder-methods","05.python\u002F05.dunder-methods",{"title":2767,"path":2768,"stem":2769},"Декоратори та Керування життєвим циклом методів","\u002Fpython\u002Fdecorators-static-class","05.python\u002F06.decorators-static-class",{"title":2771,"path":2772,"stem":2773},"Дескриптори — Магія доступу до атрибутів","\u002Fpython\u002Fdescriptors","05.python\u002F07.descriptors",{"title":2775,"path":2776,"stem":2777},"Метакласи — Динамічне створення класів під капотом CPython","\u002Fpython\u002Fmetaclasses","05.python\u002F08.metaclasses",{"title":2779,"path":2780,"stem":2781},"Dataclasses, NamedTuple та сучасні контейнери Python","\u002Fpython\u002Fmodern-containers","05.python\u002F09.modern-containers",{"title":2783,"path":2784,"stem":2785},"GIL та модель конкурентності CPython — фундамент перед потоками і процесами","\u002Fpython\u002Fgil-concurrency-intro","05.python\u002F11.gil-concurrency-intro",{"title":2787,"path":2788,"stem":2789},"Threading — конкурентність для I\u002FO-bound задач","\u002Fpython\u002Fthreading","05.python\u002F12.threading",{"title":2791,"path":2792,"stem":2793},"Multiprocessing — справжній паралелізм для CPU-bound задач","\u002Fpython\u002Fmultiprocessing","05.python\u002F13.multiprocessing",{"title":2795,"path":2796,"stem":2797},"asyncio — кооперативна конкурентність та event loop","\u002Fpython\u002Fasyncio","05.python\u002F14.asyncio",{"title":2799,"icon":92,"path":2800,"stem":2801,"children":2802,"page":59},"FastAPI","\u002Fpython\u002Ffastapi","05.python\u002Ffastapi",[2803,2807,2811,2815,2819,2823,2827,2831,2835,2839,2843,2847,2851,2855],{"title":2804,"path":2805,"stem":2806},"Глибокий Typing та Pydantic v2 — від анотацій до валідації","\u002Fpython\u002Ffastapi\u002Ftyping-pydantic","05.python\u002Ffastapi\u002F15.typing-pydantic",{"title":2808,"path":2809,"stem":2810},"WSGI, ASGI та Python Web-екосистема","\u002Fpython\u002Ffastapi\u002Fwsgi-asgi-ecosystem","05.python\u002Ffastapi\u002F16.wsgi-asgi-ecosystem",{"title":2812,"path":2813,"stem":2814},"FastAPI: Перший додаток, Uvicorn та OpenAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-intro","05.python\u002Ffastapi\u002F17.fastapi-intro",{"title":2816,"path":2817,"stem":2818},"Маршрутизація, параметри запитів та APIRouter","\u002Fpython\u002Ffastapi\u002Ffastapi-routing-params","05.python\u002Ffastapi\u002F18.fastapi-routing-params",{"title":2820,"path":2821,"stem":2822},"Pydantic v2 у FastAPI — схеми, валідація та серіалізація","\u002Fpython\u002Ffastapi\u002Ffastapi-pydantic-schemas","05.python\u002Ffastapi\u002F19.fastapi-pydantic-schemas",{"title":2824,"path":2825,"stem":2826},"Dependency Injection — серце архітектури FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-dependency-injection","05.python\u002Ffastapi\u002F20.fastapi-dependency-injection",{"title":2828,"path":2829,"stem":2830},"Обробка помилок, Middleware та CORS у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-errors-middleware","05.python\u002Ffastapi\u002F21.fastapi-errors-middleware",{"title":2832,"path":2833,"stem":2834},"SQLAlchemy 2.0 — ORM, Core та Async Engine","\u002Fpython\u002Ffastapi\u002Fsqlalchemy-orm","05.python\u002Ffastapi\u002F22.sqlalchemy-orm",{"title":2836,"path":2837,"stem":2838},"Alembic — міграції бази даних","\u002Fpython\u002Ffastapi\u002Falembic-migrations","05.python\u002Ffastapi\u002F23.alembic-migrations",{"title":2840,"path":2841,"stem":2842},"Repository Pattern та Clean Architecture у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-repository-clean-arch","05.python\u002Ffastapi\u002F24.fastapi-repository-clean-arch",{"title":2844,"path":2845,"stem":2846},"Автентифікація та авторизація — JWT, OAuth2, RBAC","\u002Fpython\u002Ffastapi\u002Ffastapi-auth-security","05.python\u002Ffastapi\u002F25.fastapi-auth-security",{"title":2848,"path":2849,"stem":2850},"Тестування FastAPI — pytest, httpx, fixtures","\u002Fpython\u002Ffastapi\u002Ffastapi-testing","05.python\u002Ffastapi\u002F26.fastapi-testing",{"title":2852,"path":2853,"stem":2854},"Async на повну — Background Tasks, WebSockets, SSE та Celery","\u002Fpython\u002Ffastapi\u002Ffastapi-async-advanced","05.python\u002Ffastapi\u002F27.fastapi-async-advanced",{"title":2856,"path":2857,"stem":2858},"Deployment — Docker, CI\u002FCD, Monitoring","\u002Fpython\u002Ffastapi\u002Ffastapi-deployment","05.python\u002Ffastapi\u002F28.fastapi-deployment",{"title":2860,"path":2861,"stem":2862},"[object Object]","\u002Fpython\u002Foop-plan","05.python\u002Foop-plan",{"title":2864,"icon":2865,"path":2866,"stem":2867,"children":2868,"page":59},"Бази даних","i-lucide-database","\u002Fdatabases","06.databases",[2869,2899,2922,2959,2988,3006,3040,3052,3061],{"title":2870,"icon":2871,"path":2872,"stem":2873,"children":2874,"page":59},"Intro","i-lucide-play","\u002Fdatabases\u002Fintro","06.databases\u002F01.intro",[2875,2879,2883,2887,2891,2895],{"title":2876,"path":2877,"stem":2878},"Введення в теорію баз даних","\u002Fdatabases\u002Fintro\u002Fintroduction-to-databases","06.databases\u002F01.intro\u002F01.introduction-to-databases",{"title":2880,"path":2881,"stem":2882},"Реляційна модель даних","\u002Fdatabases\u002Fintro\u002Frelational-model-theory","06.databases\u002F01.intro\u002F02.relational-model-theory",{"title":2884,"path":2885,"stem":2886},"ER-моделювання","\u002Fdatabases\u002Fintro\u002Fer-modeling","06.databases\u002F01.intro\u002F03.er-modeling",{"title":2888,"path":2889,"stem":2890},"Логічне проектування БД","\u002Fdatabases\u002Fintro\u002Flogical-schema","06.databases\u002F01.intro\u002F04.logical-schema",{"title":2892,"path":2893,"stem":2894},"Класифікація таблиць","\u002Fdatabases\u002Fintro\u002Ftable-classification","06.databases\u002F01.intro\u002F05.table-classification",{"title":2896,"path":2897,"stem":2898},"PlantUML для баз даних","\u002Fdatabases\u002Fintro\u002Fplantuml-diagrams","06.databases\u002F01.intro\u002F06.plantuml-diagrams",{"title":2900,"icon":2865,"path":2901,"stem":2902,"children":2903,"page":59},"MS SQL Server Start","\u002Fdatabases\u002Fms-sql-server-start","06.databases\u002F02.ms-sql-server-start",[2904,2908,2914,2918],{"title":2905,"path":2906,"stem":2907},"Типи даних у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fdata-types","06.databases\u002F02.ms-sql-server-start\u002F01.data-types",{"title":2909,"path":2910,"stem":2911,"children":2912},"Індекси у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-indexes","06.databases\u002F02.ms-sql-server-start\u002F02.sql-indexes",[2913],{"title":2909,"path":2910,"stem":2911},{"title":2915,"path":2916,"stem":2917},"Системні бази даних MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsystem-databases","06.databases\u002F02.ms-sql-server-start\u002F03.system-databases",{"title":2919,"path":2920,"stem":2921},"Огляд мови SQL та запитів","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-queries-overview","06.databases\u002F02.ms-sql-server-start\u002F04.sql-queries-overview",{"title":2923,"icon":2865,"path":2924,"stem":2925,"children":2926,"page":59},"SQL","\u002Fdatabases\u002Fsql","06.databases\u002F03.sql",[2927,2931,2935,2939,2943,2947,2951,2955],{"title":2928,"path":2929,"stem":2930},"Налаштування демонстраційної бази даних","\u002Fdatabases\u002Fsql\u002Fsample-database-setup","06.databases\u002F03.sql\u002F00.sample-database-setup",{"title":2932,"path":2933,"stem":2934},"DDL - Створення таблиць (CREATE TABLE)","\u002Fdatabases\u002Fsql\u002Fddl-create-table","06.databases\u002F03.sql\u002F01.ddl-create-table",{"title":2936,"path":2937,"stem":2938},"DDL - Зміна та видалення таблиць (ALTER, DROP)","\u002Fdatabases\u002Fsql\u002Fddl-alter-drop-table","06.databases\u002F03.sql\u002F02.ddl-alter-drop-table",{"title":2940,"path":2941,"stem":2942},"SELECT запити - Основи","\u002Fdatabases\u002Fsql\u002Fselect-queries-fundamentals","06.databases\u002F03.sql\u002F03.select-queries-fundamentals",{"title":2944,"path":2945,"stem":2946},"SELECT запити - Розширені можливості","\u002Fdatabases\u002Fsql\u002Fselect-queries-advanced","06.databases\u002F03.sql\u002F04.select-queries-advanced",{"title":2948,"path":2949,"stem":2950},"INSERT запити - Додавання даних","\u002Fdatabases\u002Fsql\u002Finsert-queries","06.databases\u002F03.sql\u002F05.insert-queries",{"title":2952,"path":2953,"stem":2954},"UPDATE та DELETE запити","\u002Fdatabases\u002Fsql\u002Fupdate-delete-queries","06.databases\u002F03.sql\u002F06.update-delete-queries",{"title":2956,"path":2957,"stem":2958},"Транзакції в SQL","\u002Fdatabases\u002Fsql\u002Ftransactions","06.databases\u002F03.sql\u002F07.transactions",{"title":2960,"icon":2865,"path":2961,"stem":2962,"children":2963,"page":59},"Multi Table Databases","\u002Fdatabases\u002Fmulti-table-databases","06.databases\u002F04.multi-table-databases",[2964,2968,2972,2976,2980,2984],{"title":2965,"path":2966,"stem":2967},"Зв'язки та нормалізація БД","\u002Fdatabases\u002Fmulti-table-databases\u002Frelationships-and-normalization","06.databases\u002F04.multi-table-databases\u002F00.relationships-and-normalization",{"title":2969,"path":2970,"stem":2971},"INNER JOIN - З'єднання таблиць","\u002Fdatabases\u002Fmulti-table-databases\u002Finner-join","06.databases\u002F04.multi-table-databases\u002F01.inner-join",{"title":2973,"path":2974,"stem":2975},"OUTER JOINs - LEFT, RIGHT, FULL","\u002Fdatabases\u002Fmulti-table-databases\u002Fouter-joins","06.databases\u002F04.multi-table-databases\u002F02.outer-joins",{"title":2977,"path":2978,"stem":2979},"CROSS та SELF JOINs","\u002Fdatabases\u002Fmulti-table-databases\u002Fcross-self-joins","06.databases\u002F04.multi-table-databases\u002F03.cross-self-joins",{"title":2981,"path":2982,"stem":2983},"Підзапити (Subqueries)","\u002Fdatabases\u002Fmulti-table-databases\u002Fsubqueries","06.databases\u002F04.multi-table-databases\u002F04.subqueries",{"title":2985,"path":2986,"stem":2987},"Агрегації з JOIN","\u002Fdatabases\u002Fmulti-table-databases\u002Faggregations-with-joins","06.databases\u002F04.multi-table-databases\u002F05.aggregations-with-joins",{"title":2989,"icon":2990,"path":2991,"stem":2992,"children":2993,"page":59},"Aggregate Functions","i-lucide-calculator","\u002Fdatabases\u002Faggregate-functions","06.databases\u002F05.aggregate-functions",[2994,2998,3002],{"title":2995,"path":2996,"stem":2997},"Функції агрегування в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fintroduction-aggregate-functions","06.databases\u002F05.aggregate-functions\u002F01.introduction-aggregate-functions",{"title":2999,"path":3000,"stem":3001},"Групування даних в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fgrouping-data","06.databases\u002F05.aggregate-functions\u002F02.grouping-data",{"title":3003,"path":3004,"stem":3005},"Підзапити з агрегатними функціями","\u002Fdatabases\u002Faggregate-functions\u002Fsubqueries-aggregates","06.databases\u002F05.aggregate-functions\u002F03.subqueries-aggregates",{"title":3007,"icon":3008,"path":3009,"stem":3010,"children":3011,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","\u002Fdatabases\u002Ftriggers-stored-procedures","06.databases\u002F07.triggers-stored-procedures",[3012,3016,3020,3024,3028,3032,3036],{"title":3013,"path":3014,"stem":3015},"DML-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fdml-triggers","06.databases\u002F07.triggers-stored-procedures\u002F01.dml-triggers",{"title":3017,"path":3018,"stem":3019},"DDL-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fddl-triggers","06.databases\u002F07.triggers-stored-procedures\u002F02.ddl-triggers",{"title":3021,"path":3022,"stem":3023},"Transact-SQL розширення","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransact-sql-extensions","06.databases\u002F07.triggers-stored-procedures\u002F03.transact-sql-extensions",{"title":3025,"path":3026,"stem":3027},"Транзакції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransactions","06.databases\u002F07.triggers-stored-procedures\u002F04.transactions",{"title":3029,"path":3030,"stem":3031},"Зберігаємі процедури","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fstored-procedures","06.databases\u002F07.triggers-stored-procedures\u002F05.stored-procedures",{"title":3033,"path":3034,"stem":3035},"Користувацькі функції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fuser-defined-functions","06.databases\u002F07.triggers-stored-procedures\u002F06.user-defined-functions",{"title":3037,"path":3038,"stem":3039},"Безпека баз даних","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fsecurity","06.databases\u002F07.triggers-stored-procedures\u002F08.security",{"title":3037,"icon":793,"path":3041,"stem":3042,"children":3043,"page":59},"\u002Fdatabases\u002Fsecurity","06.databases\u002F08.security",[3044,3048],{"title":3045,"path":3046,"stem":3047},"Вступ до безпеки баз даних","\u002Fdatabases\u002Fsecurity\u002Fintroduction","06.databases\u002F08.security\u002F01.introduction",{"title":3049,"path":3050,"stem":3051},"Системні представлення та метадані","\u002Fdatabases\u002Fsecurity\u002Fsystem-views","06.databases\u002F08.security\u002F02.system-views",{"title":3053,"icon":3054,"path":3055,"stem":3056,"children":3057,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","\u002Fdatabases\u002Fbackup-recovery","06.databases\u002F09.backup-recovery",[3058],{"title":3053,"path":3059,"stem":3060},"\u002Fdatabases\u002Fbackup-recovery\u002Fbackup-restore","06.databases\u002F09.backup-recovery\u002F01.backup-restore",{"title":3062,"icon":3063,"path":3064,"stem":3065,"children":3066,"page":59},"Повнотекстовий пошук","i-lucide-search","\u002Fdatabases\u002Ffull-text-search","06.databases\u002F10.full-text-search",[3067],{"title":3062,"path":3068,"stem":3069},"\u002Fdatabases\u002Ffull-text-search\u002Ffull-text-search","06.databases\u002F10.full-text-search\u002F01.full-text-search",{"title":3071,"icon":3072,"path":3073,"stem":3074,"children":3075,"page":59},"Tools","i-lucide-wrench","\u002Ftools","07.tools",[3076,3152],{"title":3077,"icon":3078,"path":3079,"stem":3080,"children":3081},"Docker","i-simple-icons-docker","\u002Ftools\u002Fdocker","07.tools\u002F01.docker\u002Findex",[3082,3084,3088,3092,3096,3100,3104,3108,3112,3116,3120,3124,3128,3132,3136,3140,3144,3148],{"title":3083,"path":3079,"stem":3080},"Docker: від нуля до production",{"title":3085,"path":3086,"stem":3087},"Контейнеризація — від проблеми до рішення","\u002Ftools\u002Fdocker\u002Fcontainerization-concept","07.tools\u002F01.docker\u002F01.containerization-concept",{"title":3089,"path":3090,"stem":3091},"Docker — що це і навіщо?","\u002Ftools\u002Fdocker\u002Fdocker-what-and-why","07.tools\u002F01.docker\u002F02.docker-what-and-why",{"title":3093,"path":3094,"stem":3095},"Архітектура Docker Engine","\u002Ftools\u002Fdocker\u002Fdocker-architecture","07.tools\u002F01.docker\u002F03.docker-architecture",{"title":3097,"path":3098,"stem":3099},"Встановлення Docker","\u002Ftools\u002Fdocker\u002Finstallation","07.tools\u002F01.docker\u002F04.installation",{"title":3101,"path":3102,"stem":3103},"Перший контейнер — docker run","\u002Ftools\u002Fdocker\u002Ffirst-container","07.tools\u002F01.docker\u002F05.first-container",{"title":3105,"path":3106,"stem":3107},"Життєвий цикл контейнера","\u002Ftools\u002Fdocker\u002Fcontainer-lifecycle","07.tools\u002F01.docker\u002F06.container-lifecycle",{"title":3109,"path":3110,"stem":3111},"Docker Images — фундаментальні концепції","\u002Ftools\u002Fdocker\u002Fdocker-images-fundamentals","07.tools\u002F01.docker\u002F07.docker-images-fundamentals",{"title":3113,"path":3114,"stem":3115},"Dockerfile — основи","\u002Ftools\u002Fdocker\u002Fdockerfile-basics","07.tools\u002F01.docker\u002F08.dockerfile-basics",{"title":3117,"path":3118,"stem":3119},"Dockerfile — просунуті техніки","\u002Ftools\u002Fdocker\u002Fdockerfile-advanced","07.tools\u002F01.docker\u002F09.dockerfile-advanced",{"title":3121,"path":3122,"stem":3123},"Build Context та кешування шарів","\u002Ftools\u002Fdocker\u002Fbuild-context-and-cache","07.tools\u002F01.docker\u002F10.build-context-and-cache",{"title":3125,"path":3126,"stem":3127},"Реєстри Docker-образів","\u002Ftools\u002Fdocker\u002Fimage-registries","07.tools\u002F01.docker\u002F11.image-registries",{"title":3129,"path":3130,"stem":3131},"Контейнеризація .NET додатків","\u002Ftools\u002Fdocker\u002Fdotnet-containerization","07.tools\u002F01.docker\u002F12.dotnet-containerization",{"title":3133,"path":3134,"stem":3135},"Томи та збереження даних","\u002Ftools\u002Fdocker\u002Fvolumes-and-data","07.tools\u002F01.docker\u002F13.volumes-and-data",{"title":3137,"path":3138,"stem":3139},"Основи мережі в Docker","\u002Ftools\u002Fdocker\u002Fnetworking-basics","07.tools\u002F01.docker\u002F14.networking-basics",{"title":3141,"path":3142,"stem":3143},"Змінні оточення та конфігурація","\u002Ftools\u002Fdocker\u002Fenvironment-and-configuration","07.tools\u002F01.docker\u002F15.environment-and-configuration",{"title":3145,"path":3146,"stem":3147},"Docker Compose — оркестрація контейнерів","\u002Ftools\u002Fdocker\u002Fdocker-compose-basics","07.tools\u002F01.docker\u002F16.docker-compose-basics",{"title":3149,"path":3150,"stem":3151},"Docker Compose — Multi-Service застосунки","\u002Ftools\u002Fdocker\u002Fcompose-multi-service","07.tools\u002F01.docker\u002F17.compose-multi-service",{"title":3153,"icon":3154,"path":3155,"stem":3156,"children":3157},"Kubernetes","simple-icons:kubernetes","\u002Ftools\u002Fkubernetes","07.tools\u002F02.kubernetes\u002Findex",[3158,3160,3164,3168,3172,3176,3180,3184,3188],{"title":3159,"path":3155,"stem":3156},"Kubernetes: від розробки до production",{"title":3161,"path":3162,"stem":3163},"Kubernetes — коли Docker Compose більше не вистачає","\u002Ftools\u002Fkubernetes\u002Fwhy-kubernetes","07.tools\u002F02.kubernetes\u002F01.why-kubernetes",{"title":3165,"path":3166,"stem":3167},"Архітектура Kubernetes — анатомія кластера","\u002Ftools\u002Fkubernetes\u002Fkubernetes-architecture","07.tools\u002F02.kubernetes\u002F02.kubernetes-architecture",{"title":3169,"path":3170,"stem":3171},"Локальне середовище — minikube, kind та k3s","\u002Ftools\u002Fkubernetes\u002Flocal-environment","07.tools\u002F02.kubernetes\u002F03.local-environment",{"title":3173,"path":3174,"stem":3175},"Pod — атомарна одиниця Kubernetes","\u002Ftools\u002Fkubernetes\u002Fpods-and-containers","07.tools\u002F02.kubernetes\u002F04.pods-and-containers",{"title":3177,"path":3178,"stem":3179},"Патерни використання Pod","\u002Ftools\u002Fkubernetes\u002Fpod-patterns","07.tools\u002F02.kubernetes\u002F05.pod-patterns",{"title":3181,"path":3182,"stem":3183},"Deployment — декларативне управління Pod","\u002Ftools\u002Fkubernetes\u002Fdeployment-basics","07.tools\u002F02.kubernetes\u002F06.deployment-basics",{"title":3185,"path":3186,"stem":3187},"Rolling Updates та управління життєвим циклом Deployment","\u002Ftools\u002Fkubernetes\u002Fdeployment-rolling-updates","07.tools\u002F02.kubernetes\u002F07.deployment-rolling-updates",{"title":3189,"path":3190,"stem":3191},"Service — мережева абстракція для Pod","\u002Ftools\u002Fkubernetes\u002Fservices-networking","07.tools\u002F02.kubernetes\u002F08.services-networking",{"title":3193,"icon":3194,"path":3195,"stem":3196,"children":3197,"page":59},"Software Engineering","i-lucide-code-2","\u002Fsoftware-engineering","09.software-engineering",[3198,3202,3206,3210,3214,3218,3222,3226,3230,3234,3238],{"title":3199,"path":3200,"stem":3201},"1. Аналіз предметної області. Експертні знання та складність","\u002Fsoftware-engineering\u002Fintro-subdomains","09.software-engineering\u002F01.intro-subdomains",{"title":3203,"path":3204,"stem":3205},"2. Обмежені контексти. Інтеграція обмежених контекстів","\u002Fsoftware-engineering\u002Fintegrating-limited-contexts","09.software-engineering\u002F02.integrating-limited-contexts",{"title":3207,"path":3208,"stem":3209},"3. Реалізація простої бізнес-логіки","\u002Fsoftware-engineering\u002Fsimple","09.software-engineering\u002F03.simple",{"title":3211,"path":3212,"stem":3213},"4. Опрацювання складної бізнес-логіки","\u002Fsoftware-engineering\u002Fcomplex-business-logic","09.software-engineering\u002F04.complex-business-logic",{"title":3215,"path":3216,"stem":3217},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","\u002Fsoftware-engineering\u002Fmodelling-the-time-factor","09.software-engineering\u002F05.modelling-the-time-factor",{"title":3219,"path":3220,"stem":3221},"6. Архітектурні патерни","\u002Fsoftware-engineering\u002Farchitectural-patterns","09.software-engineering\u002F06.architectural-patterns",{"title":3223,"path":3224,"stem":3225},"Паттерни взаємодії","\u002Fsoftware-engineering\u002Fpatterns-of-interaction","09.software-engineering\u002F07.patterns-of-interaction",{"title":3227,"path":3228,"stem":3229},"Евристика проєктування","\u002Fsoftware-engineering\u002Fdesign-heuristics","09.software-engineering\u002F08.design-heuristics",{"title":3231,"path":3232,"stem":3233},"Еволюція проєктних рішень","\u002Fsoftware-engineering\u002Fevolution-of-design-solutions","09.software-engineering\u002F09.evolution-of-design-solutions",{"title":3235,"path":3236,"stem":3237},"EventStorming","\u002Fsoftware-engineering\u002Feventstorming","09.software-engineering\u002F10.eventstorming",{"title":3239,"path":3240,"stem":3241},"DDD на практиці","\u002Fsoftware-engineering\u002Fddd-in-practice","09.software-engineering\u002F11.ddd-in-practice",{"title":3243,"icon":943,"path":3244,"stem":3245,"children":3246,"page":59},"DDD","\u002Fddd","10.ddd",[3247,3251,3255,3259,3263,3267,3271,3275,3279,3283,3287,3291,3295],{"title":3248,"path":3249,"stem":3250},"Аналіз предметної області","\u002Fddd\u002Fdomain-analysis","10.ddd\u002F01.domain-analysis",{"title":3252,"path":3253,"stem":3254},"Експертні знання про предметну область","\u002Fddd\u002Fdomain-expert-knowledge","10.ddd\u002F02.domain-expert-knowledge",{"title":3256,"path":3257,"stem":3258},"Як осмислити складність предметної області","\u002Fddd\u002Fmanaging-domain-complexity","10.ddd\u002F03.managing-domain-complexity",{"title":3260,"path":3261,"stem":3262},"Інтеграція обмежених контекстів","\u002Fddd\u002Fbounded-context-integration","10.ddd\u002F04.bounded-context-integration",{"title":3264,"path":3265,"stem":3266},"Реалізація простої бізнес-логіки","\u002Fddd\u002Fsimple-business-logic","10.ddd\u002F05.simple-business-logic",{"title":3268,"path":3269,"stem":3270},"Обробка складної бізнес-логіки","\u002Fddd\u002Fcomplex-business-logic","10.ddd\u002F06.complex-business-logic",{"title":3272,"path":3273,"stem":3274},"Моделювання фактора часу","\u002Fddd\u002Ftime-modeling","10.ddd\u002F07.time-modeling",{"title":3276,"path":3277,"stem":3278},"Глава 8. Архітектурні Патерни","\u002Fddd\u002Farchitectural-patterns","10.ddd\u002F08.architectural-patterns",{"title":3280,"path":3281,"stem":3282},"Глава 9. Патерни Взаємодії","\u002Fddd\u002Finteraction-patterns","10.ddd\u002F09.interaction-patterns",{"title":3284,"path":3285,"stem":3286},"Глава 10. Проектні Евристики","\u002Fddd\u002Fdesign-heuristics","10.ddd\u002F10.design-heuristics",{"title":3288,"path":3289,"stem":3290},"Глава 11. Еволюція Проектних Рішень","\u002Fddd\u002Fevolution-of-design-decisions","10.ddd\u002F11.evolution-of-design-decisions",{"title":3292,"path":3293,"stem":3294},"Глава 12. EventStorming","\u002Fddd\u002Fevent-storming","10.ddd\u002F12.event-storming",{"title":3296,"path":3297,"stem":3298},"Глава 13. DDD на Практиці","\u002Fddd\u002Fddd-in-practice","10.ddd\u002F13.ddd-in-practice",{"title":3300,"icon":3301,"path":3302,"stem":3303,"children":3304,"page":59},"Media Streaming","i-lucide-video","\u002Fmedia-streaming","11.media-streaming",[3305,3309,3313,3317,3321,3325,3329],{"title":3306,"path":3307,"stem":3308},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","\u002Fmedia-streaming\u002Fintroduction","11.media-streaming\u002F01.introduction",{"title":3310,"path":3311,"stem":3312},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","\u002Fmedia-streaming\u002Faudio-video-anatomy","11.media-streaming\u002F02.audio-video-anatomy",{"title":3314,"path":3315,"stem":3316},"03. The Gym: FFmpeg Deep Dive","\u002Fmedia-streaming\u002Fffmpeg-gym","11.media-streaming\u002F03.ffmpeg-gym",{"title":3318,"path":3319,"stem":3320},"04. HLS Protocol: HTTP Live Streaming у Деталях","\u002Fmedia-streaming\u002Fhls-protocol","11.media-streaming\u002F04.hls-protocol",{"title":3322,"path":3323,"stem":3324},"05. DASH Protocol: Відкритий Стандарт","\u002Fmedia-streaming\u002Fdash-protocol","11.media-streaming\u002F05.dash-protocol",{"title":3326,"path":3327,"stem":3328},"06. Масштабування: CDN та Adaptive Bitrate","\u002Fmedia-streaming\u002Fcdn-and-adaptive-bitrate","11.media-streaming\u002F06.cdn-and-adaptive-bitrate",{"title":3330,"path":3331,"stem":3332},"07. Війна із Затримкою (Latency)","\u002Fmedia-streaming\u002Frealtime-latency","11.media-streaming\u002F07.realtime-latency",{"title":3334,"icon":3335,"path":3336,"stem":3337,"children":3338,"page":59},"HTML & CSS","i-devicon-html5","\u002Fhtml-css","12.html-css",[3339,3343,3347,3351,3355,3359,3363,3367,3371,3375,3379,3383,3387,3391,3395,3399,3403,3407,3411,3415,3419,3423,3427,3431,3435,3439,3443,3447,3451,3455],{"title":3340,"path":3341,"stem":3342},"Вступ до HTML. Структура документа","\u002Fhtml-css\u002Fintro-html-structure","12.html-css\u002F01.intro-html-structure",{"title":3344,"path":3345,"stem":3346},"Форматування тексту в HTML","\u002Fhtml-css\u002Fhtml-text-formatting","12.html-css\u002F02.html-text-formatting",{"title":3348,"path":3349,"stem":3350},"Посилання та зображення в HTML","\u002Fhtml-css\u002Fhtml-links-images","12.html-css\u002F03.html-links-images",{"title":3352,"path":3353,"stem":3354},"Списки та таблиці в HTML","\u002Fhtml-css\u002Fhtml-lists-tables","12.html-css\u002F04.html-lists-tables",{"title":3356,"path":3357,"stem":3358},"Форми в HTML","\u002Fhtml-css\u002Fhtml-forms","12.html-css\u002F05.html-forms",{"title":3360,"path":3361,"stem":3362},"Семантичні елементи HTML5","\u002Fhtml-css\u002Fhtml-semantic-elements","12.html-css\u002F06.html-semantic-elements",{"title":3364,"path":3365,"stem":3366},"Мультимедіа та розширені елементи HTML","\u002Fhtml-css\u002Fhtml-multimedia-advanced","12.html-css\u002F07.html-multimedia-advanced",{"title":3368,"path":3369,"stem":3370},"Мікророзмітка та SEO в HTML","\u002Fhtml-css\u002Fhtml-microdata-seo","12.html-css\u002F08.html-microdata-seo",{"title":3372,"path":3373,"stem":3374},"Вступ до CSS. Селектори та специфічність","\u002Fhtml-css\u002Fcss-intro-selectors","12.html-css\u002F09.css-intro-selectors",{"title":3376,"path":3377,"stem":3378},"Блокова модель CSS. Відступи. Box Sizing","\u002Fhtml-css\u002Fcss-box-model","12.html-css\u002F10.css-box-model",{"title":3380,"path":3381,"stem":3382},"Розміри у CSS: повний довідник одиниць і ключових слів","\u002Fhtml-css\u002F10a.css-sizing","12.html-css\u002F10a.css-sizing",{"title":3384,"path":3385,"stem":3386},"Типографіка в CSS. Шрифти та текст","\u002Fhtml-css\u002Fcss-typography","12.html-css\u002F11.css-typography",{"title":3388,"path":3389,"stem":3390},"Кольори та фони в CSS","\u002Fhtml-css\u002Fcss-colors-backgrounds","12.html-css\u002F12.css-colors-backgrounds",{"title":3392,"path":3393,"stem":3394},"Тіні та фільтри в CSS","\u002Fhtml-css\u002F12b.css-shadows-filters","12.html-css\u002F12b.css-shadows-filters",{"title":3396,"path":3397,"stem":3398},"CSS Flexbox: Фундамент гнучких макетів","\u002Fhtml-css\u002Fcss-flexbox-fundamentals","12.html-css\u002F13.css-flexbox-fundamentals",{"title":3400,"path":3401,"stem":3402},"CSS Flexbox: Вирівнювання та Позиціонування","\u002Fhtml-css\u002Fcss-flexbox-alignment-sizing-and-patterns","12.html-css\u002F14.css-flexbox-alignment-sizing-and-patterns",{"title":3404,"path":3405,"stem":3406},"CSS Grid. Двовимірний макет. Частина 1","\u002Fhtml-css\u002Fcss-layout-grid","12.html-css\u002F15.css-layout-grid",{"title":3408,"path":3409,"stem":3410},"CSS Grid. Двовимірний макет. Частина 2","\u002Fhtml-css\u002Fcss-layout-grid-advanced","12.html-css\u002F16.css-layout-grid-advanced",{"title":3412,"path":3413,"stem":3414},"Позиціонування в CSS. Z-index. Stacking Context","\u002Fhtml-css\u002Fcss-positioning","12.html-css\u002F17.css-positioning",{"title":3416,"path":3417,"stem":3418},"CSS Анімації та Переходи","\u002Fhtml-css\u002Fcss-animations-transitions","12.html-css\u002F18.css-animations-transitions",{"title":3420,"path":3421,"stem":3422},"Адаптивний дизайн. Media Queries. Частина 1","\u002Fhtml-css\u002Fcss-responsive-media-queries","12.html-css\u002F19.css-responsive-media-queries",{"title":3424,"path":3425,"stem":3426},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","\u002Fhtml-css\u002Fcss-responsive-advanced","12.html-css\u002F20.css-responsive-advanced",{"title":3428,"path":3429,"stem":3430},"CSS Custom Properties. Методології. Сучасний CSS","\u002Fhtml-css\u002Fcss-variables-methodologies","12.html-css\u002F21.css-variables-methodologies",{"title":3432,"path":3433,"stem":3434},"Сучасний CSS 2023–2025: Нові можливості","\u002Fhtml-css\u002Fcss-modern-features","12.html-css\u002F22.css-modern-features",{"title":3436,"path":3437,"stem":3438},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","\u002Fhtml-css\u002F22a.css-nesting-modern-syntax","12.html-css\u002F22a.css-nesting-modern-syntax",{"title":3440,"path":3441,"stem":3442},"CSS для форм та інтерактивних станів","\u002Fhtml-css\u002Fcss-forms-interactive-states","12.html-css\u002F23.css-forms-interactive-states",{"title":3444,"path":3445,"stem":3446},"Доступність у CSS (CSS Accessibility)","\u002Fhtml-css\u002Fcss-accessibility","12.html-css\u002F24.css-accessibility",{"title":3448,"path":3449,"stem":3450},"CSS-функції та сучасні sizing primitives","\u002Fhtml-css\u002Fcss-functions-sizing","12.html-css\u002F25.css-functions-sizing",{"title":3452,"path":3453,"stem":3454},"Rendering Pipeline і CSS Performance","\u002Fhtml-css\u002Fcss-rendering-performance","12.html-css\u002F26.css-rendering-performance",{"title":3456,"path":3457,"stem":3458},"CSS Best Practices: типові ситуації та правильні рішення","\u002Fhtml-css\u002Fcss-best-practices","12.html-css\u002F27.css-best-practices",{"title":3460,"path":3461,"stem":3462,"children":3463,"page":59},"AWS","\u002Faws","13.aws",[3464,3468,3472,3476,3480,3484,3488,3492,3496,3500,3504,3508,3512,3516,3520,3524,3528,3532],{"title":3465,"path":3466,"stem":3467},"Реєстрація AWS акаунту та студентські програми","\u002Faws\u002Faccount-registration","13.aws\u002F00.account-registration",{"title":3469,"path":3470,"stem":3471},"Вступ до хмарних обчислень та AWS","\u002Faws\u002Fintroduction-to-cloud","13.aws\u002F01.introduction-to-cloud",{"title":3473,"path":3474,"stem":3475},"AWS IAM — Identity and Access Management","\u002Faws\u002Fiam","13.aws\u002F02.iam",{"title":3477,"path":3478,"stem":3479},"AWS IAM CLI — Довідник команд","\u002Faws\u002F02a.iam-doc","13.aws\u002F02a.iam-doc",{"title":3481,"path":3482,"stem":3483},"Docker та контейнеризація в AWS — ECR, ECS та Fargate","\u002Faws\u002Fdocker-ecs","13.aws\u002F03.docker-ecs",{"title":3485,"path":3486,"stem":3487},"AWS ECR \u002F ECS CLI — Довідник команд","\u002Faws\u002F03a.docker-ecs-doc","13.aws\u002F03a.docker-ecs-doc",{"title":3489,"path":3490,"stem":3491},"Amazon EC2 — Elastic Compute Cloud","\u002Faws\u002Fec2","13.aws\u002F04.ec2",{"title":3493,"path":3494,"stem":3495},"AWS EC2 CLI — Довідник команд","\u002Faws\u002F04a.ec2-doc","13.aws\u002F04a.ec2-doc",{"title":3497,"path":3498,"stem":3499},"Elastic Load Balancing та Auto Scaling","\u002Faws\u002Falb-asg","13.aws\u002F05.alb-asg",{"title":3501,"path":3502,"stem":3503},"Amazon S3 — Simple Storage Service","\u002Faws\u002Fs3","13.aws\u002F06.s3",{"title":3505,"path":3506,"stem":3507},"Amazon CloudFront — Content Delivery Network","\u002Faws\u002Fcloudfront","13.aws\u002F07.cloudfront",{"title":3509,"path":3510,"stem":3511},"Amazon RDS — Relational Database Service","\u002Faws\u002Frds","13.aws\u002F08.rds",{"title":3513,"path":3514,"stem":3515},"Amazon DynamoDB — NoSQL Database","\u002Faws\u002Fdynamodb","13.aws\u002F09.dynamodb",{"title":3517,"path":3518,"stem":3519},"AWS Lambda та Serverless Compute","\u002Faws\u002Flambda","13.aws\u002F10.lambda",{"title":3521,"path":3522,"stem":3523},"Amazon Bedrock - Foundation Models, RAG та Agents","\u002Faws\u002Fbedrock","13.aws\u002F22.bedrock",{"title":3525,"path":3526,"stem":3527},"Amazon Rekognition - Комп'ютерний зір","\u002Faws\u002Frekognition","13.aws\u002F23.rekognition",{"title":3529,"path":3530,"stem":3531},"Amazon Textract - Інтелектуальний аналіз документів","\u002Faws\u002Ftextract","13.aws\u002F24.textract",{"title":3533,"path":3534,"stem":3535},"Amazon Polly, Transcribe, Comprehend та Translate","\u002Faws\u002Faudio-nlp-services","13.aws\u002F25.audio-nlp-services",{"title":3537,"path":3538,"stem":3539,"children":3540,"page":59},"Ai Development","\u002Fai-development","14.ai-development",[3541],{"title":3542,"path":3543,"stem":3544,"children":3545,"page":59},"Foundations","\u002Fai-development\u002Ffoundations","14.ai-development\u002F00.foundations",[3546],{"title":3547,"path":3548,"stem":3549},"Модуль 0.1. Що таке Large Language Model (LLM)","\u002Fai-development\u002Ffoundations\u002Fwhat-is-llm","14.ai-development\u002F00.foundations\u002F01.what-is-llm",{"title":3551,"path":3552,"stem":3553,"children":3554,"page":59},"React Native","\u002Freact-native","15.react-native",[3555,3576],{"title":3556,"path":3557,"stem":3558,"children":3559,"page":59},"Version1","\u002Freact-native\u002Fversion1","15.react-native\u002Fversion1",[3560,3564,3568,3572],{"title":3561,"path":3562,"stem":3563},"Що таке React Native","\u002Freact-native\u002Fversion1\u002Fwhat-is-react-native","15.react-native\u002Fversion1\u002F01.what-is-react-native",{"title":3565,"path":3566,"stem":3567},"Як влаштований React Native «під капотом»","\u002Freact-native\u002Fversion1\u002Freact-native-architecture","15.react-native\u002Fversion1\u002F02.react-native-architecture",{"title":3569,"path":3570,"stem":3571},"Від React до React Native: ключові відмінності","\u002Freact-native\u002Fversion1\u002Freact-native-vs-react","15.react-native\u002Fversion1\u002F03.react-native-vs-react",{"title":3573,"path":3574,"stem":3575},"View та Text — фундаментальні будівельні блоки","\u002Freact-native\u002Fversion1\u002Fview-and-text","15.react-native\u002Fversion1\u002F04.view-and-text",{"title":3577,"path":3578,"stem":3579,"children":3580,"page":59},"Version2","\u002Freact-native\u002Fversion2","15.react-native\u002Fversion2",[3581,3585,3589,3593,3597,3601,3605,3609,3613,3617,3621,3625,3629,3633,3637,3641,3645,3649,3653,3657,3661,3665,3669,3673,3677,3681,3685],{"title":3582,"path":3583,"stem":3584},"React Native — навіщо він потрібен","\u002Freact-native\u002Fversion2\u002Fwhy-react-native","15.react-native\u002Fversion2\u002F01.why-react-native",{"title":3586,"path":3587,"stem":3588},"Налаштування середовища розробки","\u002Freact-native\u002Fversion2\u002Fenvironment-setup","15.react-native\u002Fversion2\u002F02.environment-setup",{"title":3590,"path":3591,"stem":3592},"Expo і класичний React Native — два шляхи одного фреймворку","\u002Freact-native\u002Fversion2\u002Fexpo-vs-cli-landscape","15.react-native\u002Fversion2\u002F03.expo-vs-cli-landscape",{"title":3594,"path":3595,"stem":3596},"Середовище Expo — перший запуск","\u002Freact-native\u002Fversion2\u002Fexpo-setup-and-tooling","15.react-native\u002Fversion2\u002F04.expo-setup-and-tooling",{"title":3598,"path":3599,"stem":3600},"Структура проєкту та перші UI-примітиви","\u002Freact-native\u002Fversion2\u002Fproject-structure-and-conventions","15.react-native\u002Fversion2\u002F05.project-structure-and-conventions",{"title":3602,"path":3603,"stem":3604},"Базові компоненти React Native","\u002Freact-native\u002Fversion2\u002Fcore-components","15.react-native\u002Fversion2\u002F06.core-components",{"title":3606,"path":3607,"stem":3608},"Flexbox і layout у React Native","\u002Freact-native\u002Fversion2\u002Fflexbox-and-layout","15.react-native\u002Fversion2\u002F07.flexbox-and-layout",{"title":3610,"path":3611,"stem":3612},"StyleSheet і теми (light \u002F dark)","\u002Freact-native\u002Fversion2\u002Fstylesheet-and-theming","15.react-native\u002Fversion2\u002F08.stylesheet-and-theming",{"title":3614,"path":3615,"stem":3616},"Списки та віртуалізація","\u002Freact-native\u002Fversion2\u002Flists-and-virtualization","15.react-native\u002Fversion2\u002F09.lists-and-virtualization",{"title":3618,"path":3619,"stem":3620},"Форми, ввід і валідація","\u002Freact-native\u002Fversion2\u002Fforms-input-validation","15.react-native\u002Fversion2\u002F10.forms-input-validation",{"title":3622,"path":3623,"stem":3624},"Основи Expo Router","\u002Freact-native\u002Fversion2\u002Fexpo-router-basics","15.react-native\u002Fversion2\u002F11.expo-router-basics",{"title":3626,"path":3627,"stem":3628},"Вкладені навігатори та модалки","\u002Freact-native\u002Fversion2\u002Fnested-navigators-and-modals","15.react-native\u002Fversion2\u002F12.nested-navigators-and-modals",{"title":3630,"path":3631,"stem":3632},"Deep linking і параметри маршрутів","\u002Freact-native\u002Fversion2\u002Fdeep-linking-and-params","15.react-native\u002Fversion2\u002F13.deep-linking-and-params",{"title":3634,"path":3635,"stem":3636},"Робота з мережею та життєвий цикл застосунку","\u002Freact-native\u002Fversion2\u002Fnetworking-and-app-lifecycle","15.react-native\u002Fversion2\u002F14.networking-and-app-lifecycle",{"title":3638,"path":3639,"stem":3640},"Redux Toolkit на мобільних платформах","\u002Freact-native\u002Fversion2\u002Fredux-toolkit-on-mobile","15.react-native\u002Fversion2\u002F15.redux-toolkit-on-mobile",{"title":3642,"path":3643,"stem":3644},"RTK Query на мобільних платформах","\u002Freact-native\u002Fversion2\u002Frtk-query-mobile","15.react-native\u002Fversion2\u002F16.rtk-query-mobile",{"title":3646,"path":3647,"stem":3648},"Автентифікація та Безпечне Зберігання","\u002Freact-native\u002Fversion2\u002Fauth-secure-storage","15.react-native\u002Fversion2\u002F18.auth-secure-storage",{"title":3650,"path":3651,"stem":3652},"Локальне сховище — AsyncStorage, MMKV, SQLite","\u002Freact-native\u002Fversion2\u002Flocal-storage-mmkv-sqlite","15.react-native\u002Fversion2\u002F19.local-storage-mmkv-sqlite",{"title":3654,"path":3655,"stem":3656},"Офлайн-First і синхронізація","\u002Freact-native\u002Fversion2\u002Foffline-first-sync","15.react-native\u002Fversion2\u002F20.offline-first-sync",{"title":3658,"path":3659,"stem":3660},"Камера, медіа та файлова система","\u002Freact-native\u002Fversion2\u002Fcamera-media-filesystem","15.react-native\u002Fversion2\u002F21.camera-media-filesystem",{"title":3662,"path":3663,"stem":3664},"Карти та геолокація","\u002Freact-native\u002Fversion2\u002Fmaps-and-location","15.react-native\u002Fversion2\u002F22.maps-and-location",{"title":3666,"path":3667,"stem":3668},"Жести та дотики","\u002Freact-native\u002Fversion2\u002Fgestures-and-touch","15.react-native\u002Fversion2\u002F23.gestures-and-touch",{"title":3670,"path":3671,"stem":3672},"Анімації з Reanimated","\u002Freact-native\u002Fversion2\u002Freanimated-animations","15.react-native\u002Fversion2\u002F24.reanimated-animations",{"title":3674,"path":3675,"stem":3676},"Push і локальні сповіщення","\u002Freact-native\u002Fversion2\u002Fpush-and-local-notifications","15.react-native\u002Fversion2\u002F25.push-and-local-notifications",{"title":3678,"path":3679,"stem":3680},"Інтернаціоналізація та доступність","\u002Freact-native\u002Fversion2\u002Faccessibility-and-i18n","15.react-native\u002Fversion2\u002F26.accessibility-and-i18n",{"title":3682,"path":3683,"stem":3684},"EAS Build та профілі збірки","\u002Freact-native\u002Fversion2\u002Feas-build-and-profiles","15.react-native\u002Fversion2\u002F29.eas-build-and-profiles",{"title":3686,"path":3687,"stem":3688},"Публікація в App Store та Google Play","\u002Freact-native\u002Fversion2\u002Fapp-store-play-release","15.react-native\u002Fversion2\u002F30.app-store-play-release",{"title":3690,"path":3691,"stem":3692,"children":3693,"page":59},"Ai Python","\u002Fai-python","16.ai-python",[3694,3703,3712,3721,3750,3776,3801,3834,3851,3864,3873,3886,3895,3916,3929,3950],{"title":3695,"path":3696,"stem":3697,"children":3698,"page":59},"Intro To Ai","\u002Fai-python\u002Fintro-to-ai","16.ai-python\u002F01.intro-to-ai",[3699],{"title":3700,"path":3701,"stem":3702},"Вступ до штучного інтелекту та машинного навчання","\u002Fai-python\u002Fintro-to-ai\u002Fintro-to-ai","16.ai-python\u002F01.intro-to-ai\u002F01.intro-to-ai",{"title":3704,"path":3705,"stem":3706,"children":3707,"page":59},"Jupyter Notebook","\u002Fai-python\u002Fjupyter-notebook","16.ai-python\u002F02.jupyter-notebook",[3708],{"title":3709,"path":3710,"stem":3711},"Знайомство з Jupyter Notebook та JupyterLab","\u002Fai-python\u002Fjupyter-notebook\u002Fjupyter-notebook","16.ai-python\u002F02.jupyter-notebook\u002F02.jupyter-notebook",{"title":3713,"path":3714,"stem":3715,"children":3716,"page":59},"Numpy Pandas","\u002Fai-python\u002Fnumpy-pandas","16.ai-python\u002F03.numpy-pandas",[3717],{"title":3718,"path":3719,"stem":3720},"Робота з даними з використанням NumPy і pandas","\u002Fai-python\u002Fnumpy-pandas\u002Fnumpy-pandas","16.ai-python\u002F03.numpy-pandas\u002F03.numpy-pandas",{"title":3722,"path":3723,"stem":3724,"children":3725,"page":59},"Statistics Basics","\u002Fai-python\u002Fstatistics-basics","16.ai-python\u002F04.statistics-basics",[3726,3730,3734,3738,3742,3746],{"title":3727,"path":3728,"stem":3729},"Випадкові величини та їх типи","\u002Fai-python\u002Fstatistics-basics\u002Frandom-variables","16.ai-python\u002F04.statistics-basics\u002F01.random-variables",{"title":3731,"path":3732,"stem":3733},"Міри центральної тенденції","\u002Fai-python\u002Fstatistics-basics\u002Fcentral-tendency","16.ai-python\u002F04.statistics-basics\u002F02.central-tendency",{"title":3735,"path":3736,"stem":3737},"Міри варіації (розсіювання)","\u002Fai-python\u002Fstatistics-basics\u002Fvariability","16.ai-python\u002F04.statistics-basics\u002F03.variability",{"title":3739,"path":3740,"stem":3741},"Розподіли випадкових величин","\u002Fai-python\u002Fstatistics-basics\u002Fdistributions","16.ai-python\u002F04.statistics-basics\u002F04.distributions",{"title":3743,"path":3744,"stem":3745},"Кореляція та взаємозв'язки","\u002Fai-python\u002Fstatistics-basics\u002Fcorrelation","16.ai-python\u002F04.statistics-basics\u002F05.correlation",{"title":3747,"path":3748,"stem":3749},"Викиди та аномалії","\u002Fai-python\u002Fstatistics-basics\u002Foutliers","16.ai-python\u002F04.statistics-basics\u002F06.outliers",{"title":3751,"path":3752,"stem":3753,"children":3754},"Візуалізація даних за допомогою matplotlib","\u002Fai-python\u002Fdata-visualization","16.ai-python\u002F05.data-visualization\u002Findex",[3755,3756,3760,3764,3768,3772],{"title":3751,"path":3752,"stem":3753},{"title":3757,"path":3758,"stem":3759},"Основи matplotlib — Figure, Axes та перший графік","\u002Fai-python\u002Fdata-visualization\u002Fmatplotlib-basics","16.ai-python\u002F05.data-visualization\u002F01.matplotlib-basics",{"title":3761,"path":3762,"stem":3763},"Візуалізація розподілів — гістограми та box plots","\u002Fai-python\u002Fdata-visualization\u002Fdistributions","16.ai-python\u002F05.data-visualization\u002F02.distributions",{"title":3765,"path":3766,"stem":3767},"Візуалізація залежностей — scatter plots та кореляція","\u002Fai-python\u002Fdata-visualization\u002Frelationships","16.ai-python\u002F05.data-visualization\u002F03.relationships",{"title":3769,"path":3770,"stem":3771},"Візуалізація категоріальних даних — bar charts та pie charts","\u002Fai-python\u002Fdata-visualization\u002Fcategorical","16.ai-python\u002F05.data-visualization\u002F04.categorical",{"title":3773,"path":3774,"stem":3775},"Композиції та стилізація — subplots та багатопанельні дашборди","\u002Fai-python\u002Fdata-visualization\u002Fsubplots-styling","16.ai-python\u002F05.data-visualization\u002F05.subplots-styling",{"title":3777,"path":3778,"stem":3779,"children":3780,"page":59},"Linear Regression","\u002Fai-python\u002Flinear-regression","16.ai-python\u002F06.linear-regression",[3781,3785,3789,3793,3797],{"title":3782,"path":3783,"stem":3784},"Регресійна задача та лінійна регресія з однією змінною","\u002Fai-python\u002Flinear-regression\u002Fregression-task","16.ai-python\u002F06.linear-regression\u002F01.regression-task",{"title":3786,"path":3787,"stem":3788},"Метрики якості регресії — як оцінити модель","\u002Fai-python\u002Flinear-regression\u002Fquality-metrics","16.ai-python\u002F06.linear-regression\u002F02.quality-metrics",{"title":3790,"path":3791,"stem":3792},"Градієнтний спуск — як модель навчається","\u002Fai-python\u002Flinear-regression\u002Fgradient-descent","16.ai-python\u002F06.linear-regression\u002F03.gradient-descent",{"title":3794,"path":3795,"stem":3796},"Множинна лінійна регресія — модель з багатьма ознаками","\u002Fai-python\u002Flinear-regression\u002Fmultiple-regression","16.ai-python\u002F06.linear-regression\u002F04.multiple-regression",{"title":3798,"path":3799,"stem":3800},"Практичні завдання та резюме модуля","\u002Fai-python\u002Flinear-regression\u002Fpractice","16.ai-python\u002F06.linear-regression\u002F05.practice",{"title":3802,"path":3803,"stem":3804,"children":3805,"page":59},"Practical Regression","\u002Fai-python\u002Fpractical-regression","16.ai-python\u002F07.practical-regression",[3806,3810,3814,3818,3822,3826,3830],{"title":3807,"path":3808,"stem":3809},"Знайомство з scikit-learn","\u002Fai-python\u002Fpractical-regression\u002Fintro-scikit-learn","16.ai-python\u002F07.practical-regression\u002F01.intro-scikit-learn",{"title":3811,"path":3812,"stem":3813},"Exploratory Data Analysis датасету Housing Prices","\u002Fai-python\u002Fpractical-regression\u002Fhousing-dataset-eda","16.ai-python\u002F07.practical-regression\u002F02.housing-dataset-eda",{"title":3815,"path":3816,"stem":3817},"Попередня обробка даних (Data Preprocessing)","\u002Fai-python\u002Fpractical-regression\u002Fdata-preprocessing","16.ai-python\u002F07.practical-regression\u002F03.data-preprocessing",{"title":3819,"path":3820,"stem":3821},"Train\u002FTest Split та перша повноцінна модель","\u002Fai-python\u002Fpractical-regression\u002Ftrain-test-split","16.ai-python\u002F07.practical-regression\u002F04.train-test-split",{"title":3823,"path":3824,"stem":3825},"Перенавчання та недонавчання","\u002Fai-python\u002Fpractical-regression\u002Foverfitting-underfitting","16.ai-python\u002F07.practical-regression\u002F05.overfitting-underfitting",{"title":3827,"path":3828,"stem":3829},"Крос-валідація (Cross-Validation)","\u002Fai-python\u002Fpractical-regression\u002Fcross-validation","16.ai-python\u002F07.practical-regression\u002F06.cross-validation",{"title":3831,"path":3832,"stem":3833},"Гіперпараметри та їх оптимізація","\u002Fai-python\u002Fpractical-regression\u002Fhyperparameter-tuning","16.ai-python\u002F07.practical-regression\u002F07.hyperparameter-tuning",{"title":3835,"path":3836,"stem":3837,"children":3838,"page":59},"Polynomial Regression","\u002Fai-python\u002Fpolynomial-regression","16.ai-python\u002F08.polynomial-regression",[3839,3843,3847],{"title":3840,"path":3841,"stem":3842},"Нелінійні залежності та поліноміальна регресія","\u002Fai-python\u002Fpolynomial-regression\u002Fnonlinear-patterns","16.ai-python\u002F08.polynomial-regression\u002F01.nonlinear-patterns",{"title":3844,"path":3845,"stem":3846},"Регуляризація в регресії — Ridge та Lasso","\u002Fai-python\u002Fpolynomial-regression\u002Fregularization-methods","16.ai-python\u002F08.polynomial-regression\u002F02.regularization-methods",{"title":3848,"path":3849,"stem":3850},"Практика — поліноміальна регресія та регуляризація на Housing Prices","\u002Fai-python\u002Fpolynomial-regression\u002Fhousing-prices-practice","16.ai-python\u002F08.polynomial-regression\u002F03.housing-prices-practice",{"title":3852,"path":3853,"stem":3854,"children":3855,"page":59},"Decision Trees Random Forest","\u002Fai-python\u002Fdecision-trees-random-forest","16.ai-python\u002F09.decision-trees-random-forest",[3856,3860],{"title":3857,"path":3858,"stem":3859},"Дерева рішень — моделі з прозорою та зрозумілою логікою","\u002Fai-python\u002Fdecision-trees-random-forest\u002Fdecision-trees","16.ai-python\u002F09.decision-trees-random-forest\u002F01.decision-trees",{"title":3861,"path":3862,"stem":3863},"Random Forest — ансамблі дерев рішень","\u002Fai-python\u002Fdecision-trees-random-forest\u002Frandom-forest","16.ai-python\u002F09.decision-trees-random-forest\u002F02.random-forest",{"title":3865,"path":3866,"stem":3867,"children":3868,"page":59},"Gradient Boosting","\u002Fai-python\u002Fgradient-boosting","16.ai-python\u002F10.gradient-boosting",[3869],{"title":3870,"path":3871,"stem":3872},"Градієнтний бустинг та XGBoost — послідовне виправлення помилок","\u002Fai-python\u002Fgradient-boosting\u002Fgradient-boosting-xgboost","16.ai-python\u002F10.gradient-boosting\u002F01.gradient-boosting-xgboost",{"title":3874,"path":3875,"stem":3876,"children":3877,"page":59},"Logistic Regression Classification","\u002Fai-python\u002Flogistic-regression-classification","16.ai-python\u002F11.logistic-regression-classification",[3878,3882],{"title":3879,"path":3880,"stem":3881},"Основи класифікації та логістична регресія","\u002Fai-python\u002Flogistic-regression-classification\u002Fclassification-basics","16.ai-python\u002F11.logistic-regression-classification\u002F01.classification-basics",{"title":3883,"path":3884,"stem":3885},"Метрики якості класифікації","\u002Fai-python\u002Flogistic-regression-classification\u002Fclassification-metrics","16.ai-python\u002F11.logistic-regression-classification\u002F02.classification-metrics",{"title":3887,"path":3888,"stem":3889,"children":3890,"page":59},"Neural Networks Basics","\u002Fai-python\u002Fneural-networks-basics","16.ai-python\u002F12.neural-networks-basics",[3891],{"title":3892,"path":3893,"stem":3894},"Вступ до нейронних мереж — від персептрона до багатошарових мереж","\u002Fai-python\u002Fneural-networks-basics\u002Fperceptron-to-mlp","16.ai-python\u002F12.neural-networks-basics\u002F01.perceptron-to-mlp",{"title":3896,"path":3897,"stem":3898,"children":3899,"page":59},"Pytorch Intro","\u002Fai-python\u002Fpytorch-intro","16.ai-python\u002F13.pytorch-intro",[3900,3904,3908,3912],{"title":3901,"path":3902,"stem":3903},"Тензори та автоматичне диференціювання в PyTorch","\u002Fai-python\u002Fpytorch-intro\u002Ftensors-and-autograd","16.ai-python\u002F13.pytorch-intro\u002F01.tensors-and-autograd",{"title":3905,"path":3906,"stem":3907},"Побудова та тренування нейронних мереж у PyTorch","\u002Fai-python\u002Fpytorch-intro\u002Fbuilding-and-training","16.ai-python\u002F13.pytorch-intro\u002F02.building-and-training",{"title":3909,"path":3910,"stem":3911},"Робота з даними та регуляризація в PyTorch","\u002Fai-python\u002Fpytorch-intro\u002Fdata-and-regularization","16.ai-python\u002F13.pytorch-intro\u002F03.data-and-regularization",{"title":3913,"path":3914,"stem":3915},"Просунуті теми PyTorch — GPU, оптимізація та deployment","\u002Fai-python\u002Fpytorch-intro\u002Fadvanced-topics","16.ai-python\u002F13.pytorch-intro\u002F04.advanced-topics",{"title":3917,"path":3918,"stem":3919,"children":3920,"page":59},"Activation Functions Heart Disease","\u002Fai-python\u002Factivation-functions-heart-disease","16.ai-python\u002F14.activation-functions-heart-disease",[3921,3925],{"title":3922,"path":3923,"stem":3924},"Функції активації та побудова нейронної мережі для передбачення вад серця","\u002Fai-python\u002Factivation-functions-heart-disease\u002Fheart-disease-prediction","16.ai-python\u002F14.activation-functions-heart-disease\u002F01.heart-disease-prediction",{"title":3926,"path":3927,"stem":3928},"Бонус — Повний приклад Student Performance Prediction","\u002Fai-python\u002Factivation-functions-heart-disease\u002Fstudent-performance-bonus","16.ai-python\u002F14.activation-functions-heart-disease\u002F02.student-performance-bonus",{"title":3930,"path":3931,"stem":3932,"children":3933,"page":59},"Cnn Image Classification","\u002Fai-python\u002Fcnn-image-classification","16.ai-python\u002F15.cnn-image-classification",[3934,3938,3942,3946],{"title":3935,"path":3936,"stem":3937},"Основи згорткових нейронних мереж — чому не MLP?","\u002Fai-python\u002Fcnn-image-classification\u002Fcnn-fundamentals","16.ai-python\u002F15.cnn-image-classification\u002F01.cnn-fundamentals",{"title":3939,"path":3940,"stem":3941},"Практична імплементація CNN для класифікації MNIST","\u002Fai-python\u002Fcnn-image-classification\u002Fmnist-classification","16.ai-python\u002F15.cnn-image-classification\u002F02.mnist-classification",{"title":3943,"path":3944,"stem":3945},"Глибші CNN та аугментація даних для CIFAR-10","\u002Fai-python\u002Fcnn-image-classification\u002Fcifar10-and-data-augmentation","16.ai-python\u002F15.cnn-image-classification\u002F03.cifar10-and-data-augmentation",{"title":3947,"path":3948,"stem":3949},"Transfer Learning та візуалізація внутрішніх представлень CNN","\u002Fai-python\u002Fcnn-image-classification\u002Ftransfer-learning-and-visualization","16.ai-python\u002F15.cnn-image-classification\u002F04.transfer-learning-and-visualization",{"title":3951,"path":3952,"stem":3953,"children":3954,"page":59},"Batching Normalization Deep Networks","\u002Fai-python\u002Fbatching-normalization-deep-networks","16.ai-python\u002F16.batching-normalization-deep-networks",[3955,3959,3963,3967,3971,3975,3979],{"title":3956,"path":3957,"stem":3958},"Mini-batch Gradient Descent та ефективна обробка даних","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fmini-batch-gradient-descent","16.ai-python\u002F16.batching-normalization-deep-networks\u002F01.mini-batch-gradient-descent",{"title":3960,"path":3961,"stem":3962},"Ініціалізація ваг — фундамент стабільного навчання","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fweight-initialization","16.ai-python\u002F16.batching-normalization-deep-networks\u002F02.weight-initialization",{"title":3964,"path":3965,"stem":3966},"Batch Normalization — прорив у глибокому навчанні","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fbatch-normalization","16.ai-python\u002F16.batching-normalization-deep-networks\u002F03.batch-normalization",{"title":3968,"path":3969,"stem":3970},"Dropout та комбіновані стратегії регуляризації","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fdropout-and-regularization","16.ai-python\u002F16.batching-normalization-deep-networks\u002F04.dropout-and-regularization",{"title":3972,"path":3973,"stem":3974},"Проєктування глибоких мереж — від теорії до практики","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fdeep-networks-architectures","16.ai-python\u002F16.batching-normalization-deep-networks\u002F05.deep-networks-architectures",{"title":3976,"path":3977,"stem":3978},"Планування learning rate — коли та як зменшувати крок навчання","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Flearning-rate-scheduling","16.ai-python\u002F16.batching-normalization-deep-networks\u002F06.learning-rate-scheduling",{"title":3980,"path":3981,"stem":3982},"Підсумковий проєкт — побудова сучасного класифікатора зображень","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Ffinal-project-deep-classifier","16.ai-python\u002F16.batching-normalization-deep-networks\u002F07.final-project-deep-classifier",{"title":3984,"icon":658,"path":3985,"stem":3986,"children":3987,"page":59},"Клієнт-серверна архітектура","\u002Fclient-server-architecture","17.client-server-architecture",[3988,3992,3996,4000,4004,4008,4012,4016,4020,4024,4028,4032,4036,4040,4044,4048,4052,4056],{"title":3989,"path":3990,"stem":3991},"Архітектура клієнт-серверних систем та основи Go","\u002Fclient-server-architecture\u002Fclient-server-architecture-and-go","17.client-server-architecture\u002F01.client-server-architecture-and-go",{"title":3993,"path":3994,"stem":3995},"Система типів та модель пам’яті мови Go","\u002Fclient-server-architecture\u002Ftype-system-and-memory-model","17.client-server-architecture\u002F02.type-system-and-memory-model",{"title":3997,"path":3998,"stem":3999},"Інтерфейси та модель конкурентності мови Go","\u002Fclient-server-architecture\u002Finterfaces-and-concurrency-model","17.client-server-architecture\u002F03.interfaces-and-concurrency-model",{"title":4001,"path":4002,"stem":4003},"Сокетне програмування та протокол TCP у Go","\u002Fclient-server-architecture\u002Ftcp-socket-programming","17.client-server-architecture\u002F04.tcp-socket-programming",{"title":4005,"path":4006,"stem":4007},"Протокол UDP та датаграмна передача даних","\u002Fclient-server-architecture\u002Fudp-datagram-transmission","17.client-server-architecture\u002F05.udp-datagram-transmission",{"title":4009,"path":4010,"stem":4011},"Криптографічний захист каналів зв","\u002Fclient-server-architecture\u002Ftls-cryptographic-channel-protection","17.client-server-architecture\u002F06.tls-cryptographic-channel-protection",{"title":4013,"path":4014,"stem":4015},"Прикладні мережеві протоколи DNS та SMTP","\u002Fclient-server-architecture\u002Fdns-smtp-application-protocols","17.client-server-architecture\u002F07.dns-smtp-application-protocols",{"title":4017,"path":4018,"stem":4019},"Протокол HTTP та стандартний вебсервер Go","\u002Fclient-server-architecture\u002Fhttp-protocol-and-web-server","17.client-server-architecture\u002F08.http-protocol-and-web-server",{"title":4021,"path":4022,"stem":4023},"Проєктування REST API та маршрутизація засобами Chi","\u002Fclient-server-architecture\u002Frest-api-design-and-chi-routing","17.client-server-architecture\u002F09.rest-api-design-and-chi-routing",{"title":4025,"path":4026,"stem":4027},"Лекція 10. Шарувата архітектура та конвеєри обробки запитів","\u002Fclient-server-architecture\u002Flayered-architecture-and-middleware-pipelines","17.client-server-architecture\u002F10.layered-architecture-and-middleware-pipelines",{"title":4029,"path":4030,"stem":4031},"Лекція 11. Архітектура та маршрутизація фреймворку Gin","\u002Fclient-server-architecture\u002Fgin-framework-architecture-and-routing","17.client-server-architecture\u002F11.gin-framework-architecture-and-routing",{"title":4033,"path":4034,"stem":4035},"Лекція 12. Валідація даних, безпека та документування у Gin","\u002Fclient-server-architecture\u002Fdata-validation-security-and-documentation-in-gin","17.client-server-architecture\u002F12.data-validation-security-and-documentation-in-gin",{"title":4037,"path":4038,"stem":4039},"Лекція 13. Взаємодія з реляційними СУБД та технології ORM","\u002Fclient-server-architecture\u002Frelational-databases-and-orm","17.client-server-architecture\u002F13.relational-databases-and-orm",{"title":4041,"path":4042,"stem":4043},"Потокова передача даних та протокол WebSocket","\u002Fclient-server-architecture\u002Fstreaming-data-and-websockets","17.client-server-architecture\u002F14.streaming-data-and-websockets",{"title":4045,"path":4046,"stem":4047},"Міжсервісна взаємодія на основі протоколу gRPC","\u002Fclient-server-architecture\u002Fgrpc-inter-service-communication","17.client-server-architecture\u002F15.grpc-inter-service-communication",{"title":4049,"path":4050,"stem":4051},"Асинхронний обмін повідомленнями та протоколи черг","\u002Fclient-server-architecture\u002Fasynchronous-message-exchange-and-queue-protocols","17.client-server-architecture\u002F16.asynchronous-message-exchange-and-queue-protocols",{"title":4053,"path":4054,"stem":4055},"Протоколи потокового медіа та технологія WebRTC","\u002Fclient-server-architecture\u002Fstreaming-media-protocols-and-webrtc","17.client-server-architecture\u002F17.streaming-media-protocols-and-webrtc",{"title":4057,"path":4058,"stem":4059},"Моніторинг, безпека та контейнеризація застосунків","\u002Fclient-server-architecture\u002Fmonitoring-security-and-containerization","17.client-server-architecture\u002F18.monitoring-security-and-containerization",{"title":4061,"icon":2118,"path":4062,"stem":4063,"children":4064,"page":59},"Розробка WEB-застосувань","\u002Fweb-development","18.web-development",[4065,4110,4148,4177,4206,4263,4313,4407,4473,4555,4588,4616,4658,4704,4747],{"title":4066,"icon":658,"path":4067,"stem":4068,"children":4069,"page":59},"Архітектура комп'ютерних мереж та мережеві протоколи","\u002Fweb-development\u002Fnetwork-architecture-and-protocols","18.web-development\u002F01.network-architecture-and-protocols",[4070,4074,4078,4082,4086,4090,4094,4098,4102,4106],{"title":4071,"path":4072,"stem":4073},"Еволюція веброзробки та модель клієнт-сервер","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fweb-evolution-client-server","18.web-development\u002F01.network-architecture-and-protocols\u002F01.web-evolution-client-server",{"title":4075,"path":4076,"stem":4077},"Еталонна модель OSI","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fosi-model","18.web-development\u002F01.network-architecture-and-protocols\u002F02.osi-model",{"title":4079,"path":4080,"stem":4081},"Стек протоколів TCP\u002FIP","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Ftcp-ip-stack","18.web-development\u002F01.network-architecture-and-protocols\u002F03.tcp-ip-stack",{"title":4083,"path":4084,"stem":4085},"IP-адресація, порти та сокети","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fip-addressing-ports-sockets","18.web-development\u002F01.network-architecture-and-protocols\u002F04.ip-addressing-ports-sockets",{"title":4087,"path":4088,"stem":4089},"Протоколи TCP та UDP","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Ftcp-udp-protocols","18.web-development\u002F01.network-architecture-and-protocols\u002F05.tcp-udp-protocols",{"title":4091,"path":4092,"stem":4093},"Система доменних імен DNS","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fdns-system","18.web-development\u002F01.network-architecture-and-protocols\u002F06.dns-system",{"title":4095,"path":4096,"stem":4097},"Протокол SMTP та поштові протоколи","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fsmtp-email-protocols","18.web-development\u002F01.network-architecture-and-protocols\u002F07.smtp-email-protocols",{"title":4099,"path":4100,"stem":4101},"Протокол WebSocket","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fwebsocket-protocol","18.web-development\u002F01.network-architecture-and-protocols\u002F08.websocket-protocol",{"title":4103,"path":4104,"stem":4105},"Протокол SSH","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fssh-protocol","18.web-development\u002F01.network-architecture-and-protocols\u002F09.ssh-protocol",{"title":4107,"path":4108,"stem":4109},"Огляд сучасних серверних технологій","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fmodern-backend-technologies","18.web-development\u002F01.network-architecture-and-protocols\u002F10.modern-backend-technologies",{"title":4111,"icon":4112,"path":4113,"stem":4114,"children":4115,"page":59},"Протокол HTTP та захищена передача даних HTTPS","i-lucide-lock","\u002Fweb-development\u002Fhttp-and-https","18.web-development\u002F02.http-and-https",[4116,4120,4124,4128,4132,4136,4140,4144],{"title":4117,"path":4118,"stem":4119},"Вступ до протоколу HTTP та еволюція веб-технологій","\u002Fweb-development\u002Fhttp-and-https\u002Fintroduction-and-evolution","18.web-development\u002F02.http-and-https\u002F01.introduction-and-evolution",{"title":4121,"path":4122,"stem":4123},"Анатомія HTTP-запиту","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-request-structure","18.web-development\u002F02.http-and-https\u002F02.http-request-structure",{"title":4125,"path":4126,"stem":4127},"Анатомія HTTP-відповіді","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-response-structure","18.web-development\u002F02.http-and-https\u002F03.http-response-structure",{"title":4129,"path":4130,"stem":4131},"Методи HTTP та їхня семантика","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-methods","18.web-development\u002F02.http-and-https\u002F04.http-methods",{"title":4133,"path":4134,"stem":4135},"Коди статусу HTTP та їхнє призначення","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-status-codes","18.web-development\u002F02.http-and-https\u002F05.http-status-codes",{"title":4137,"path":4138,"stem":4139},"HTTP-заголовки та їхнє призначення","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-headers","18.web-development\u002F02.http-and-https\u002F06.http-headers",{"title":4141,"path":4142,"stem":4143},"CORS та механізми безпеки HTTP","\u002Fweb-development\u002Fhttp-and-https\u002Fcors-and-security","18.web-development\u002F02.http-and-https\u002F07.cors-and-security",{"title":4145,"path":4146,"stem":4147},"HTTPS та криптографічні основи TLS","\u002Fweb-development\u002Fhttp-and-https\u002Fhttps-and-tls","18.web-development\u002F02.http-and-https\u002F08.https-and-tls",{"title":4149,"icon":2498,"path":4150,"stem":4151,"children":4152,"page":59},"Система типів мови TypeScript","\u002Fweb-development\u002Ftypescript-type-system","18.web-development\u002F03.typescript-type-system",[4153,4157,4161,4165,4169,4173],{"title":4154,"path":4155,"stem":4156},"Знайомство з TypeScript Compiler","\u002Fweb-development\u002Ftypescript-type-system\u002Fintroduction-compiler-config","18.web-development\u002F03.typescript-type-system\u002F01.introduction-compiler-config",{"title":4158,"path":4159,"stem":4160},"Примітивні типи даних: string, number, boolean","\u002Fweb-development\u002Ftypescript-type-system\u002Fprimitive-special-types","18.web-development\u002F03.typescript-type-system\u002F02.primitive-special-types",{"title":4162,"path":4163,"stem":4164},"Масиви, кортежі та об","\u002Fweb-development\u002Ftypescript-type-system\u002Farrays-tuples-objects","18.web-development\u002F03.typescript-type-system\u002F03.arrays-tuples-objects",{"title":4166,"path":4167,"stem":4168},"Псевдоніми типів, Union типи, Intersection типи та літеральні типи","\u002Fweb-development\u002Ftypescript-type-system\u002Funion-intersection-literals","18.web-development\u002F03.typescript-type-system\u002F04.union-intersection-literals",{"title":4170,"path":4171,"stem":4172},"Type Narrowing (звуження типів)","\u002Fweb-development\u002Ftypescript-type-system\u002Ftype-narrowing","18.web-development\u002F03.typescript-type-system\u002F05.type-narrowing",{"title":4174,"path":4175,"stem":4176},"Типізація функцій","\u002Fweb-development\u002Ftypescript-type-system\u002Ffunctions-typing","18.web-development\u002F03.typescript-type-system\u002F06.functions-typing",{"title":4178,"icon":2498,"path":4179,"stem":4180,"children":4181,"page":59},"Розширені засоби мови TypeScript","\u002Fweb-development\u002Ftypescript-advanced","18.web-development\u002F04.typescript-advanced",[4182,4186,4190,4194,4198,4202],{"title":4183,"path":4184,"stem":4185},"Interfaces та Classes","\u002Fweb-development\u002Ftypescript-advanced\u002Finterfaces-classes","18.web-development\u002F04.typescript-advanced\u002F01.interfaces-classes",{"title":4187,"path":4188,"stem":4189},"Основи Generic типів","\u002Fweb-development\u002Ftypescript-advanced\u002Fgenerics-fundamentals","18.web-development\u002F04.typescript-advanced\u002F02.generics-fundamentals",{"title":4191,"path":4192,"stem":4193},"Enums та Decorators","\u002Fweb-development\u002Ftypescript-advanced\u002Fenums-decorators","18.web-development\u002F04.typescript-advanced\u002F03.enums-decorators",{"title":4195,"path":4196,"stem":4197},"Utility Types","\u002Fweb-development\u002Ftypescript-advanced\u002Futility-types","18.web-development\u002F04.typescript-advanced\u002F04.utility-types",{"title":4199,"path":4200,"stem":4201},"Mapped та Conditional Types","\u002Fweb-development\u002Ftypescript-advanced\u002Fmapped-conditional-types","18.web-development\u002F04.typescript-advanced\u002F05.mapped-conditional-types",{"title":4203,"path":4204,"stem":4205},"Модулі та Strict Mode","\u002Fweb-development\u002Ftypescript-advanced\u002Fmodules-strict-mode","18.web-development\u002F04.typescript-advanced\u002F06.modules-strict-mode",{"title":4207,"icon":4208,"path":4209,"stem":4210,"children":4211,"page":59},"Платформа Node.js як середовище виконання","i-devicon-nodejs","\u002Fweb-development\u002Fnodejs-platform","18.web-development\u002F05.nodejs-platform",[4212,4216,4220,4224,4228,4232,4236,4240,4244,4248,4252,4255,4259],{"title":4213,"path":4214,"stem":4215},"Що таке Node.js та його архітектура","\u002Fweb-development\u002Fnodejs-platform\u002Fwhat-is-nodejs","18.web-development\u002F05.nodejs-platform\u002F01.what-is-nodejs",{"title":4217,"path":4218,"stem":4219},"Event Loop — серце Node.js","\u002Fweb-development\u002Fnodejs-platform\u002Fevent-loop","18.web-development\u002F05.nodejs-platform\u002F02.event-loop",{"title":4221,"path":4222,"stem":4223},"Модульні системи: CommonJS vs ES Modules","\u002Fweb-development\u002Fnodejs-platform\u002Fmodule-systems","18.web-development\u002F05.nodejs-platform\u002F03.module-systems",{"title":4225,"path":4226,"stem":4227},"npm та package.json","\u002Fweb-development\u002Fnodejs-platform\u002Fnpm-package-json","18.web-development\u002F05.nodejs-platform\u002F04.npm-package-json",{"title":4229,"path":4230,"stem":4231},"Глобальні об\\","\u002Fweb-development\u002Fnodejs-platform\u002Fglobal-objects","18.web-development\u002F05.nodejs-platform\u002F05.global-objects",{"title":4233,"path":4234,"stem":4235},"Вбудований модуль fs (File System)","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-fs","18.web-development\u002F05.nodejs-platform\u002F06.built-in-modules-fs",{"title":4237,"path":4238,"stem":4239},"Вбудований модуль path","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-path","18.web-development\u002F05.nodejs-platform\u002F07.built-in-modules-path",{"title":4241,"path":4242,"stem":4243},"Вбудований модуль http","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-http","18.web-development\u002F05.nodejs-platform\u002F08.built-in-modules-http",{"title":4245,"path":4246,"stem":4247},"Streams — потоки даних у Node.js","\u002Fweb-development\u002Fnodejs-platform\u002Fstreams","18.web-development\u002F05.nodejs-platform\u002F09.streams",{"title":4249,"path":4250,"stem":4251},"Вбудований модуль util (Utilities)","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-util","18.web-development\u002F05.nodejs-platform\u002F10.built-in-modules-util",{"title":3141,"path":4253,"stem":4254},"\u002Fweb-development\u002Fnodejs-platform\u002Fenvironment-variables","18.web-development\u002F05.nodejs-platform\u002F11.environment-variables",{"title":4256,"path":4257,"stem":4258},"Інструменти налагодження та розробки","\u002Fweb-development\u002Fnodejs-platform\u002Fdebugging-tools","18.web-development\u002F05.nodejs-platform\u002F12.debugging-tools",{"title":4260,"path":4261,"stem":4262},"Продуктивність та Best Practices","\u002Fweb-development\u002Fnodejs-platform\u002Fperformance-best-practices","18.web-development\u002F05.nodejs-platform\u002F13.performance-best-practices",{"title":4264,"icon":4265,"path":4266,"stem":4267,"children":4268,"page":59},"Асинхронне програмування у Node.js","i-lucide-clock","\u002Fweb-development\u002Fasync-programming-nodejs","18.web-development\u002F06.async-programming-nodejs",[4269,4273,4277,4281,4285,4289,4293,4297,4301,4305,4309],{"title":4270,"path":4271,"stem":4272},"Синхронний vs асинхронний код","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fsync-vs-async","18.web-development\u002F06.async-programming-nodejs\u002F01.sync-vs-async",{"title":4274,"path":4275,"stem":4276},"Callbacks — перша парадигма асинхронності","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fcallbacks","18.web-development\u002F06.async-programming-nodejs\u002F02.callbacks",{"title":4278,"path":4279,"stem":4280},"Promises — стандарт асинхронності","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fpromises","18.web-development\u002F06.async-programming-nodejs\u002F03.promises",{"title":4282,"path":4283,"stem":4284},"Promise комбінатори","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fpromise-combinators","18.web-development\u002F06.async-programming-nodejs\u002F04.promise-combinators",{"title":4286,"path":4287,"stem":4288},"Async\u002FAwait — синтаксичний цукор над Promises","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fasync-await","18.web-development\u002F06.async-programming-nodejs\u002F05.async-await",{"title":4290,"path":4291,"stem":4292},"Паралельне vs послідовне виконання","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fparallel-vs-sequential","18.web-development\u002F06.async-programming-nodejs\u002F06.parallel-vs-sequential",{"title":4294,"path":4295,"stem":4296},"EventEmitter — подієва модель Node.js","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fevent-emitter","18.web-development\u002F06.async-programming-nodejs\u002F07.event-emitter",{"title":4298,"path":4299,"stem":4300},"Worker Threads — багатопотоковість у Node.js","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fworker-threads","18.web-development\u002F06.async-programming-nodejs\u002F08.worker-threads",{"title":4302,"path":4303,"stem":4304},"Child Processes — запуск зовнішніх процесів","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fchild-processes","18.web-development\u002F06.async-programming-nodejs\u002F09.child-processes",{"title":4306,"path":4307,"stem":4308},"Обробка помилок у асинхронному коді","\u002Fweb-development\u002Fasync-programming-nodejs\u002Ferror-handling","18.web-development\u002F06.async-programming-nodejs\u002F10.error-handling",{"title":4310,"path":4311,"stem":4312},"Порівняння Node.js фреймворків","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fnodejs-frameworks","18.web-development\u002F06.async-programming-nodejs\u002F11.nodejs-frameworks",{"title":4314,"icon":4315,"path":4316,"stem":4317,"children":4318,"page":59},"Тема 7. Фреймворк NestJS. Контролери та маршрутизація","i-lucide-route","\u002Fweb-development\u002Fnestjs-controllers-routing","18.web-development\u002F07.nestjs-controllers-routing",[4319,4323,4327,4331,4335,4339,4343,4347,4351,4355,4359,4363,4367,4371,4375,4379,4383,4387,4391,4395,4399,4403],{"title":4320,"path":4321,"stem":4322},"Філософія та архітектура NestJS","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fnestjs-philosophy","18.web-development\u002F07.nestjs-controllers-routing\u002F01.nestjs-philosophy",{"title":4324,"path":4325,"stem":4326},"Три стовпи архітектури: модулі, контролери, провайдери","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fthree-pillars","18.web-development\u002F07.nestjs-controllers-routing\u002F02.three-pillars",{"title":4328,"path":4329,"stem":4330},"Інверсія керування (IoC) та ін","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fioc-and-di","18.web-development\u002F07.nestjs-controllers-routing\u002F03.ioc-and-di",{"title":4332,"path":4333,"stem":4334},"Встановлення NestJS CLI та створення проєкту","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fnestjs-cli-setup","18.web-development\u002F07.nestjs-controllers-routing\u002F04.nestjs-cli-setup",{"title":4336,"path":4337,"stem":4338},"Структура проєкту NestJS та конвенції","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fproject-structure","18.web-development\u002F07.nestjs-controllers-routing\u002F05.project-structure",{"title":4340,"path":4341,"stem":4342},"Контролери: основи маршрутизації","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fcontrollers-basics","18.web-development\u002F07.nestjs-controllers-routing\u002F06.controllers-basics",{"title":4344,"path":4345,"stem":4346},"Декоратори маршрутизації HTTP-методів","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Froute-decorators","18.web-development\u002F07.nestjs-controllers-routing\u002F07.route-decorators",{"title":4348,"path":4349,"stem":4350},"Параметри маршруту та декоратор @Param","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Froute-parameters","18.web-development\u002F07.nestjs-controllers-routing\u002F08.route-parameters",{"title":4352,"path":4353,"stem":4354},"Query-параметри та декоратор @Query","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fquery-parameters","18.web-development\u002F07.nestjs-controllers-routing\u002F09.query-parameters",{"title":4356,"path":4357,"stem":4358},"Тіло запиту та декоратор @Body","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Frequest-body","18.web-development\u002F07.nestjs-controllers-routing\u002F10.request-body",{"title":4360,"path":4361,"stem":4362},"Робота з заголовками запиту та відповіді","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fheaders-decorators","18.web-development\u002F07.nestjs-controllers-routing\u002F11.headers-decorators",{"title":4364,"path":4365,"stem":4366},"Data Transfer Objects (DTO): визначення та призначення","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fdto-introduction","18.web-development\u002F07.nestjs-controllers-routing\u002F12.dto-introduction",{"title":4368,"path":4369,"stem":4370},"Об'єкти Request та Response","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Frequest-response-objects","18.web-development\u002F07.nestjs-controllers-routing\u002F13.request-response-objects",{"title":4372,"path":4373,"stem":4374},"HTTP-статус коди та декоратор @HttpCode","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fhttp-status-codes","18.web-development\u002F07.nestjs-controllers-routing\u002F14.http-status-codes",{"title":4376,"path":4377,"stem":4378},"Перенаправлення (Redirects) та декоратор @Redirect","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fredirects","18.web-development\u002F07.nestjs-controllers-routing\u002F15.redirects",{"title":4380,"path":4381,"stem":4382},"Асинхронні обробники та Observables","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fasync-handlers","18.web-development\u002F07.nestjs-controllers-routing\u002F16.async-handlers",{"title":4384,"path":4385,"stem":4386},"Маршрутизація за піддоменами та версіонування API","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fsub-domain-routing","18.web-development\u002F07.nestjs-controllers-routing\u002F17.sub-domain-routing",{"title":4388,"path":4389,"stem":4390},"Області видимості контролерів (Scopes)","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fcontroller-scopes","18.web-development\u002F07.nestjs-controllers-routing\u002F18.controller-scopes",{"title":4392,"path":4393,"stem":4394},"Основні команди NestJS CLI","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fnestjs-cli-commands","18.web-development\u002F07.nestjs-controllers-routing\u002F19.nestjs-cli-commands",{"title":4396,"path":4397,"stem":4398},"Практичний приклад: створення CRUD-контролера","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fcrud-example","18.web-development\u002F07.nestjs-controllers-routing\u002F20.crud-example",{"title":4400,"path":4401,"stem":4402},"Тестування контролерів","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Ftesting-controllers","18.web-development\u002F07.nestjs-controllers-routing\u002F21.testing-controllers",{"title":4404,"path":4405,"stem":4406},"Best Practices для контролерів та маршрутизації","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fbest-practices","18.web-development\u002F07.nestjs-controllers-routing\u002F22.best-practices",{"title":4408,"icon":4409,"path":4410,"stem":4411,"children":4412,"page":59},"Тема 8. Провайдери, сервіси та модульна система NestJS","i-lucide-package","\u002Fweb-development\u002Fnestjs-providers-modules","18.web-development\u002F08.nestjs-providers-modules",[4413,4417,4421,4425,4429,4433,4437,4441,4445,4449,4453,4457,4461,4465,4469],{"title":4414,"path":4415,"stem":4416},"Концепція провайдерів у NestJS","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fproviders-concept","18.web-development\u002F08.nestjs-providers-modules\u002F01.providers-concept",{"title":4418,"path":4419,"stem":4420},"Сервіси: інкапсуляція бізнес-логіки","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fservices-business-logic","18.web-development\u002F08.nestjs-providers-modules\u002F02.services-business-logic",{"title":4422,"path":4423,"stem":4424},"Практика ін","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fdependency-injection-practice","18.web-development\u002F08.nestjs-providers-modules\u002F03.dependency-injection-practice",{"title":4426,"path":4427,"stem":4428},"Custom Providers: useValue, useClass, useFactory, useExisting","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fcustom-providers","18.web-development\u002F08.nestjs-providers-modules\u002F04.custom-providers",{"title":4430,"path":4431,"stem":4432},"Injection Tokens: рядкові та символьні токени","\u002Fweb-development\u002Fnestjs-providers-modules\u002Finjection-tokens","18.web-development\u002F08.nestjs-providers-modules\u002F05.injection-tokens",{"title":4434,"path":4435,"stem":4436},"Опціональні провайдери та декоратор @Optional","\u002Fweb-development\u002Fnestjs-providers-modules\u002Foptional-providers","18.web-development\u002F08.nestjs-providers-modules\u002F07.optional-providers",{"title":4438,"path":4439,"stem":4440},"Структура модулів: imports, controllers, providers, exports","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fmodules-structure","18.web-development\u002F08.nestjs-providers-modules\u002F08.modules-structure",{"title":4442,"path":4443,"stem":4444},"Feature Modules: ізоляція функціональності","\u002Fweb-development\u002Fnestjs-providers-modules\u002Ffeature-modules","18.web-development\u002F08.nestjs-providers-modules\u002F09.feature-modules",{"title":4446,"path":4447,"stem":4448},"Shared Modules: перевикористання провайдерів","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fshared-modules","18.web-development\u002F08.nestjs-providers-modules\u002F10.shared-modules",{"title":4450,"path":4451,"stem":4452},"Global Modules та декоратор @Global","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fglobal-modules","18.web-development\u002F08.nestjs-providers-modules\u002F11.global-modules",{"title":4454,"path":4455,"stem":4456},"Dynamic Modules: конфігурація під час виконання","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fdynamic-modules","18.web-development\u002F08.nestjs-providers-modules\u002F12.dynamic-modules",{"title":4458,"path":4459,"stem":4460},"Циклічні залежності та forwardRef","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fcircular-dependencies","18.web-development\u002F08.nestjs-providers-modules\u002F13.circular-dependencies",{"title":4462,"path":4463,"stem":4464},"Lifecycle Hooks: хуки життєвого циклу","\u002Fweb-development\u002Fnestjs-providers-modules\u002Flifecycle-hooks","18.web-development\u002F08.nestjs-providers-modules\u002F14.lifecycle-hooks",{"title":4466,"path":4467,"stem":4468},"ModuleRef: динамічний доступ до провайдерів","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fmodule-reference","18.web-development\u002F08.nestjs-providers-modules\u002F15.module-reference",{"title":4470,"path":4471,"stem":4472},"Тестування сервісів та провайдерів","\u002Fweb-development\u002Fnestjs-providers-modules\u002Ftesting-services","18.web-development\u002F08.nestjs-providers-modules\u002F16.testing-services",{"title":4474,"icon":4475,"path":4476,"stem":4477,"children":4478,"page":59},"Тема 9. Валідація даних та конвеєр обробки запитів у NestJS","i-lucide-filter","\u002Fweb-development\u002Fnestjs-validation-pipeline","18.web-development\u002F09.nestjs-validation-pipeline",[4479,4483,4487,4491,4495,4499,4503,4507,4511,4515,4519,4523,4527,4531,4535,4539,4543,4547,4551],{"title":4480,"path":4481,"stem":4482},"Request Pipeline: конвеєр обробки запитів","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Frequest-pipeline","18.web-development\u002F09.nestjs-validation-pipeline\u002F01.request-pipeline",{"title":4484,"path":4485,"stem":4486},"Pipes: концепція трансформації та валідації","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fpipes-concept","18.web-development\u002F09.nestjs-validation-pipeline\u002F02.pipes-concept",{"title":4488,"path":4489,"stem":4490},"Вбудовані pipes: ParseIntPipe, ParseBoolPipe, ParseUUIDPipe","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fbuilt-in-pipes","18.web-development\u002F09.nestjs-validation-pipeline\u002F03.built-in-pipes",{"title":4492,"path":4493,"stem":4494},"ValidationPipe: автоматична валідація DTO","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fvalidation-pipe","18.web-development\u002F09.nestjs-validation-pipeline\u002F04.validation-pipe",{"title":4496,"path":4497,"stem":4498},"Декоратори class-validator для DTO","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fclass-validator-decorators","18.web-development\u002F09.nestjs-validation-pipeline\u002F05.class-validator-decorators",{"title":4500,"path":4501,"stem":4502},"Створення кастомних валідаторів","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-validators","18.web-development\u002F09.nestjs-validation-pipeline\u002F06.custom-validators",{"title":4504,"path":4505,"stem":4506},"Створення власних Pipes","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-pipes","18.web-development\u002F09.nestjs-validation-pipeline\u002F07.custom-pipes",{"title":4508,"path":4509,"stem":4510},"Основи Middleware","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fmiddleware-basics","18.web-development\u002F09.nestjs-validation-pipeline\u002F08.middleware-basics",{"title":4512,"path":4513,"stem":4514},"Практичні приклади Middleware","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fmiddleware-examples","18.web-development\u002F09.nestjs-validation-pipeline\u002F09.middleware-examples",{"title":4516,"path":4517,"stem":4518},"Guards: захисні перехоплювачі доступу","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fguards-concept","18.web-development\u002F09.nestjs-validation-pipeline\u002F10.guards-concept",{"title":4520,"path":4521,"stem":4522},"Створення кастомних Guards","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-guards","18.web-development\u002F09.nestjs-validation-pipeline\u002F11.custom-guards",{"title":4524,"path":4525,"stem":4526},"Interceptors: перехоплення та трансформація відповідей","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Finterceptors-concept","18.web-development\u002F09.nestjs-validation-pipeline\u002F12.interceptors-concept",{"title":4528,"path":4529,"stem":4530},"Практичні приклади Interceptors","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Finterceptor-examples","18.web-development\u002F09.nestjs-validation-pipeline\u002F13.interceptor-examples",{"title":4532,"path":4533,"stem":4534},"Exception Filters: обробка виключень","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fexception-filters","18.web-development\u002F09.nestjs-validation-pipeline\u002F14.exception-filters",{"title":4536,"path":4537,"stem":4538},"Custom Exception Filters: спеціалізовані фільтри","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-exception-filters","18.web-development\u002F09.nestjs-validation-pipeline\u002F15.custom-exception-filters",{"title":4540,"path":4541,"stem":4542},"Execution Context: контекст виконання","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fexecution-context","18.web-development\u002F09.nestjs-validation-pipeline\u002F16.execution-context",{"title":4544,"path":4545,"stem":4546},"Custom Decorators: власні декоратори","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-decorators","18.web-development\u002F09.nestjs-validation-pipeline\u002F17.custom-decorators",{"title":4548,"path":4549,"stem":4550},"Global Components: глобальні компоненти","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fglobal-components","18.web-development\u002F09.nestjs-validation-pipeline\u002F18.global-components",{"title":4552,"path":4553,"stem":4554},"Pipeline Best Practices: найкращі практики","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fpipeline-best-practices","18.web-development\u002F09.nestjs-validation-pipeline\u002F19.pipeline-best-practices",{"title":4556,"icon":2865,"path":4557,"stem":4558,"children":4559,"page":59},"Інтеграція реляційних баз даних засобами TypeORM","\u002Fweb-development\u002Ftypeorm-integration","18.web-development\u002F10.typeorm-integration",[4560,4564,4568,4572,4576,4580,4584],{"title":4561,"path":4562,"stem":4563},"Робота з базами даних у Node.js через нативні драйвери","\u002Fweb-development\u002Ftypeorm-integration\u002F01-nodejs-database-drivers","18.web-development\u002F10.typeorm-integration\u002F01-nodejs-database-drivers",{"title":4565,"path":4566,"stem":4567},"ORM та знайомство з TypeORM","\u002Fweb-development\u002Ftypeorm-integration\u002F02-orm-and-typeorm-intro","18.web-development\u002F10.typeorm-integration\u002F02-orm-and-typeorm-intro",{"title":4569,"path":4570,"stem":4571},"Налаштування TypeORM у NestJS проєкті","\u002Fweb-development\u002Ftypeorm-integration\u002F03-typeorm-setup-nestjs","18.web-development\u002F10.typeorm-integration\u002F03-typeorm-setup-nestjs",{"title":4573,"path":4574,"stem":4575},"Entity класи та декоратори TypeORM","\u002Fweb-development\u002Ftypeorm-integration\u002F04-entities-and-decorators","18.web-development\u002F10.typeorm-integration\u002F04-entities-and-decorators",{"title":4577,"path":4578,"stem":4579},"Repository Pattern та CRUD операції","\u002Fweb-development\u002Ftypeorm-integration\u002F05-repository-pattern-crud","18.web-development\u002F10.typeorm-integration\u002F05-repository-pattern-crud",{"title":4581,"path":4582,"stem":4583},"Пагінація та фільтрація даних","\u002Fweb-development\u002Ftypeorm-integration\u002F06-pagination-filtering","18.web-development\u002F10.typeorm-integration\u002F06-pagination-filtering",{"title":4585,"path":4586,"stem":4587},"QueryBuilder для складних запитів","\u002Fweb-development\u002Ftypeorm-integration\u002F07-query-builder","18.web-development\u002F10.typeorm-integration\u002F07-query-builder",{"title":4589,"icon":4590,"path":4591,"stem":4592,"children":4593,"page":59},"Зв'язки між сутностями, міграції та оптимізація запитів","i-lucide-git-branch","\u002Fweb-development\u002Fentity-relations-migrations","18.web-development\u002F11.entity-relations-migrations",[4594,4598,4602,4606,4610],{"title":4595,"path":4596,"stem":4597},"Зв'язки One-to-Many та Many-to-One","\u002Fweb-development\u002Fentity-relations-migrations\u002F01-one-to-many-many-to-one","18.web-development\u002F11.entity-relations-migrations\u002F01-one-to-many-many-to-one",{"title":4599,"path":4600,"stem":4601},"Зв'язки One-to-One та Many-to-Many","\u002Fweb-development\u002Fentity-relations-migrations\u002F02-one-to-one-many-to-many","18.web-development\u002F11.entity-relations-migrations\u002F02-one-to-one-many-to-many",{"title":4603,"path":4604,"stem":4605},"Керування міграціями бази даних","\u002Fweb-development\u002Fentity-relations-migrations\u002F03-migrations-management","18.web-development\u002F11.entity-relations-migrations\u002F03-migrations-management",{"title":4607,"path":4608,"stem":4609},"Транзакції у TypeORM","\u002Fweb-development\u002Fentity-relations-migrations\u002F04-transactions","18.web-development\u002F11.entity-relations-migrations\u002F04-transactions",{"title":4611,"path":4612,"stem":4613,"children":4614},"Індекси та оптимізація запитів","\u002Fweb-development\u002Fentity-relations-migrations\u002F05-indexes-optimization","18.web-development\u002F11.entity-relations-migrations\u002F05-indexes-optimization",[4615],{"title":4611,"path":4612,"stem":4613},{"title":4617,"icon":4618,"path":4619,"stem":4620,"children":4621,"page":59},"Механізми автентифікації у вебзастосунках","i-lucide-key","\u002Fweb-development\u002Fauthentication","18.web-development\u002F12.authentication",[4622,4626,4630,4634,4638,4642,4646,4650,4654],{"title":4623,"path":4624,"stem":4625},"Автентифікація vs Авторизація","\u002Fweb-development\u002Fauthentication\u002F01-authentication-vs-authorization","18.web-development\u002F12.authentication\u002F01-authentication-vs-authorization",{"title":4627,"path":4628,"stem":4629},"Стратегії автентифікації","\u002Fweb-development\u002Fauthentication\u002F02-authentication-strategies","18.web-development\u002F12.authentication\u002F02-authentication-strategies",{"title":4631,"path":4632,"stem":4633},"Автентифікація на основі сесій","\u002Fweb-development\u002Fauthentication\u002F03-session-based-authentication","18.web-development\u002F12.authentication\u002F03-session-based-authentication",{"title":4635,"path":4636,"stem":4637},"Структура JSON Web Token (JWT)","\u002Fweb-development\u002Fauthentication\u002F04-jwt-structure","18.web-development\u002F12.authentication\u002F04-jwt-structure",{"title":4639,"path":4640,"stem":4641},"Інтеграція JWT у NestJS","\u002Fweb-development\u002Fauthentication\u002F05-nestjs-jwt-integration","18.web-development\u002F12.authentication\u002F05-nestjs-jwt-integration",{"title":4643,"path":4644,"stem":4645},"Passport.js стратегії у NestJS","\u002Fweb-development\u002Fauthentication\u002F06-passport-strategies","18.web-development\u002F12.authentication\u002F06-passport-strategies",{"title":4647,"path":4648,"stem":4649},"Безпечне зберігання паролів: хешування та криптографічний захист","\u002Fweb-development\u002Fauthentication\u002F07-password-hashing","18.web-development\u002F12.authentication\u002F07-password-hashing",{"title":4651,"path":4652,"stem":4653},"Refresh Token та безпечне оновлення доступу","\u002Fweb-development\u002Fauthentication\u002F08-refresh-tokens","18.web-development\u002F12.authentication\u002F08-refresh-tokens",{"title":4655,"path":4656,"stem":4657},"OAuth 2.0 та OpenID Connect: делегована автентифікація","\u002Fweb-development\u002Fauthentication\u002F09-oauth2-openid","18.web-development\u002F12.authentication\u002F09-oauth2-openid",{"title":4659,"icon":4660,"path":4661,"stem":4662,"children":4663,"page":59},"Авторизація та безпека серверних застосунків","i-lucide-shield","\u002Fweb-development\u002Fauthorization-security","18.web-development\u002F13.authorization-security",[4664,4668,4672,4676,4680,4684,4688,4692,4696,4700],{"title":4665,"path":4666,"stem":4667},"Моделі авторизації","\u002Fweb-development\u002Fauthorization-security\u002F01-authorization-models","18.web-development\u002F13.authorization-security\u002F01-authorization-models",{"title":4669,"path":4670,"stem":4671},"Реалізація RBAC у NestJS","\u002Fweb-development\u002Fauthorization-security\u002F02-rbac-implementation","18.web-development\u002F13.authorization-security\u002F02-rbac-implementation",{"title":4673,"path":4674,"stem":4675},"Permission-Based Access Control","\u002Fweb-development\u002Fauthorization-security\u002F03-permission-based-access","18.web-development\u002F13.authorization-security\u002F03-permission-based-access",{"title":4677,"path":4678,"stem":4679},"ABAC та бібліотека CASL","\u002Fweb-development\u002Fauthorization-security\u002F04-abac-casl","18.web-development\u002F13.authorization-security\u002F04-abac-casl",{"title":4681,"path":4682,"stem":4683},"Кастомні декоратори та метадані","\u002Fweb-development\u002Fauthorization-security\u002F05-custom-decorators-metadata","18.web-development\u002F13.authorization-security\u002F05-custom-decorators-metadata",{"title":4685,"path":4686,"stem":4687},"Конфігурація CORS","\u002Fweb-development\u002Fauthorization-security\u002F06-cors-configuration","18.web-development\u002F13.authorization-security\u002F06-cors-configuration",{"title":4689,"path":4690,"stem":4691},"Захист через HTTP Security Headers (Helmet)","\u002Fweb-development\u002Fauthorization-security\u002F07-helmet-security-headers","18.web-development\u002F13.authorization-security\u002F07-helmet-security-headers",{"title":4693,"path":4694,"stem":4695},"Rate Limiting та Throttling","\u002Fweb-development\u002Fauthorization-security\u002F08-rate-limiting-throttling","18.web-development\u002F13.authorization-security\u002F08-rate-limiting-throttling",{"title":4697,"path":4698,"stem":4699},"Захист від поширених атак","\u002Fweb-development\u002Fauthorization-security\u002F09-common-attacks-protection","18.web-development\u002F13.authorization-security\u002F09-common-attacks-protection",{"title":4701,"path":4702,"stem":4703},"HTTPS у продакшені","\u002Fweb-development\u002Fauthorization-security\u002F10-https-production","18.web-development\u002F13.authorization-security\u002F10-https-production",{"title":4705,"icon":871,"path":4706,"stem":4707,"children":4708,"page":59},"Комунікація у реальному часі, нотифікації та фонові задачі","\u002Fweb-development\u002Frealtime-notifications-jobs","18.web-development\u002F14.realtime-notifications-jobs",[4709,4713,4717,4721,4725,4729,4732,4735,4739,4743],{"title":4710,"path":4711,"stem":4712},"Методи комунікації у реальному часі","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F01-realtime-communication-methods","18.web-development\u002F14.realtime-notifications-jobs\u002F01-realtime-communication-methods",{"title":4714,"path":4715,"stem":4716},"WebSocket Gateway у NestJS","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F02-websocket-gateway","18.web-development\u002F14.realtime-notifications-jobs\u002F02-websocket-gateway",{"title":4718,"path":4719,"stem":4720},"Lifecycle та Rooms у WebSocket","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F03-websocket-lifecycle-rooms","18.web-development\u002F14.realtime-notifications-jobs\u002F03-websocket-lifecycle-rooms",{"title":4722,"path":4723,"stem":4724},"Server-Sent Events (SSE)","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F04-server-sent-events","18.web-development\u002F14.realtime-notifications-jobs\u002F04-server-sent-events",{"title":4726,"path":4727,"stem":4728},"Архітектура системи нотифікацій","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F05-notification-system","18.web-development\u002F14.realtime-notifications-jobs\u002F05-notification-system",{"title":904,"path":4730,"stem":4731},"\u002Fweb-development\u002Frealtime-notifications-jobs\u002F06-email-notifications","18.web-development\u002F14.realtime-notifications-jobs\u002F06-email-notifications",{"title":900,"path":4733,"stem":4734},"\u002Fweb-development\u002Frealtime-notifications-jobs\u002F07-web-push-notifications","18.web-development\u002F14.realtime-notifications-jobs\u002F07-web-push-notifications",{"title":4736,"path":4737,"stem":4738},"Фонові задачі та черги","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F08-background-jobs-queues","18.web-development\u002F14.realtime-notifications-jobs\u002F08-background-jobs-queues",{"title":4740,"path":4741,"stem":4742},"Планувальники та Cron jobs","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F09-schedulers-cron","18.web-development\u002F14.realtime-notifications-jobs\u002F09-schedulers-cron",{"title":4744,"path":4745,"stem":4746},"Практичні сценарії застосування","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F10-practical-scenarios","18.web-development\u002F14.realtime-notifications-jobs\u002F10-practical-scenarios",{"title":4748,"icon":4749,"path":4750,"stem":4751,"children":4752,"page":59},"Тестування, документування та розгортання застосунків","i-lucide-rocket","\u002Fweb-development\u002Ftesting-documentation-deployment","18.web-development\u002F15.testing-documentation-deployment",[4753,4757,4761,4765,4769,4773,4777,4781,4785,4789],{"title":4754,"path":4755,"stem":4756},"Піраміда тестування та стратегія","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F01-testing-pyramid-strategy","18.web-development\u002F15.testing-documentation-deployment\u002F01-testing-pyramid-strategy",{"title":4758,"path":4759,"stem":4760},"Основи Jest у NestJS","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F02-jest-nestjs-basics","18.web-development\u002F15.testing-documentation-deployment\u002F02-jest-nestjs-basics",{"title":4762,"path":4763,"stem":4764},"Unit-тестування сервісів","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F03-unit-testing-services","18.web-development\u002F15.testing-documentation-deployment\u002F03-unit-testing-services",{"title":4766,"path":4767,"stem":4768},"E2E тестування та покриття коду","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F04-e2e-testing-coverage","18.web-development\u002F15.testing-documentation-deployment\u002F04-e2e-testing-coverage",{"title":4770,"path":4771,"stem":4772},"Swagger, OpenAPI, Scalar та Arazzo","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F05-swagger-openapi","18.web-development\u002F15.testing-documentation-deployment\u002F05-swagger-openapi",{"title":4774,"path":4775,"stem":4776},"Керування конфігурацією","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F06-configuration-management","18.web-development\u002F15.testing-documentation-deployment\u002F06-configuration-management",{"title":4778,"path":4779,"stem":4780},"Docker та контейнеризація","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F07-docker-containerization","18.web-development\u002F15.testing-documentation-deployment\u002F07-docker-containerization",{"title":4782,"path":4783,"stem":4784},"CI\u002FCD Pipelines","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F08-cicd-pipelines","18.web-development\u002F15.testing-documentation-deployment\u002F08-cicd-pipelines",{"title":4786,"path":4787,"stem":4788},"Логування та моніторинг","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F09-logging-monitoring","18.web-development\u002F15.testing-documentation-deployment\u002F09-logging-monitoring",{"title":4790,"path":4791,"stem":4792},"Серіалізація та компресія","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F10-serialization-compression","18.web-development\u002F15.testing-documentation-deployment\u002F10-serialization-compression",{"title":4794,"icon":4795,"path":4796,"stem":4797,"children":4798,"page":59},"AI Core: мислення AI","i-lucide-brain","\u002Fai-core","19.ai-core",[4799,4840,4889,4927],{"title":4800,"icon":4749,"path":4801,"stem":4802,"children":4803,"page":59},"Вхід у професію AI Native Developer","\u002Fai-core\u002Fai-native-developer","19.ai-core\u002F01.ai-native-developer",[4804,4808,4812,4816,4820,4824,4828,4832,4836],{"title":4805,"path":4806,"stem":4807},"Вступ до професії розробника цифрових продуктів","\u002Fai-core\u002Fai-native-developer\u002Fdigital-products","19.ai-core\u002F01.ai-native-developer\u002F01.digital-products",{"title":4809,"path":4810,"stem":4811},"Професія розробника програмного забезпечення","\u002Fai-core\u002Fai-native-developer\u002Fsoftware-developer","19.ai-core\u002F01.ai-native-developer\u002F02.software-developer",{"title":4813,"path":4814,"stem":4815},"Професія AI Native Developer","\u002Fai-core\u002Fai-native-developer\u002Fai-native-developer","19.ai-core\u002F01.ai-native-developer\u002F03.ai-native-developer",{"title":4817,"path":4818,"stem":4819},"Вплив AI на розробку програмного забезпечення","\u002Fai-core\u002Fai-native-developer\u002Fai-impact-on-development","19.ai-core\u002F01.ai-native-developer\u002F04.ai-impact-on-development",{"title":4821,"path":4822,"stem":4823},"Життєвий цикл цифрового продукту","\u002Fai-core\u002Fai-native-developer\u002Fproduct-lifecycle","19.ai-core\u002F01.ai-native-developer\u002F05.product-lifecycle",{"title":4825,"path":4826,"stem":4827},"Застосування AI на етапах створення цифрового продукту","\u002Fai-core\u002Fai-native-developer\u002Fai-in-product-creation","19.ai-core\u002F01.ai-native-developer\u002F06.ai-in-product-creation",{"title":4829,"path":4830,"stem":4831},"Розподіл завдань між людиною та AI","\u002Fai-core\u002Fai-native-developer\u002Fhuman-vs-ai-tasks","19.ai-core\u002F01.ai-native-developer\u002F07.human-vs-ai-tasks",{"title":4833,"path":4834,"stem":4835},"Компетенції AI Native Developer","\u002Fai-core\u002Fai-native-developer\u002Fcompetencies","19.ai-core\u002F01.ai-native-developer\u002F08.competencies",{"title":4837,"path":4838,"stem":4839},"Карта професійного розвитку AI Native Developer","\u002Fai-core\u002Fai-native-developer\u002Fcareer-roadmap","19.ai-core\u002F01.ai-native-developer\u002F09.career-roadmap",{"title":4841,"icon":255,"path":4842,"stem":4843,"children":4844,"page":59},"Штучний інтелект і машинне навчання","\u002Fai-core\u002Fai-and-machine-learning","19.ai-core\u002F02.ai-and-machine-learning",[4845,4849,4853,4857,4861,4865,4869,4873,4877,4881,4885],{"title":4846,"path":4847,"stem":4848},"Штучний інтелект","\u002Fai-core\u002Fai-and-machine-learning\u002Fwhat-is-ai","19.ai-core\u002F02.ai-and-machine-learning\u002F01.what-is-ai",{"title":4850,"path":4851,"stem":4852},"Відмінність AI від інших програмних систем","\u002Fai-core\u002Fai-and-machine-learning\u002Fai-vs-automation","19.ai-core\u002F02.ai-and-machine-learning\u002F02.ai-vs-automation",{"title":4854,"path":4855,"stem":4856},"Завдання штучного інтелекту","\u002Fai-core\u002Fai-and-machine-learning\u002Fai-tasks","19.ai-core\u002F02.ai-and-machine-learning\u002F03.ai-tasks",{"title":4858,"path":4859,"stem":4860},"Machine Learning","\u002Fai-core\u002Fai-and-machine-learning\u002Fmachine-learning","19.ai-core\u002F02.ai-and-machine-learning\u002F04.machine-learning",{"title":4862,"path":4863,"stem":4864},"Machine Learning Workflow","\u002Fai-core\u002Fai-and-machine-learning\u002Fml-workflow","19.ai-core\u002F02.ai-and-machine-learning\u002F05.ml-workflow",{"title":4866,"path":4867,"stem":4868},"Типи машинного навчання","\u002Fai-core\u002Fai-and-machine-learning\u002Fml-types","19.ai-core\u002F02.ai-and-machine-learning\u002F06.ml-types",{"title":4870,"path":4871,"stem":4872},"Generative AI","\u002Fai-core\u002Fai-and-machine-learning\u002Fgenerative-ai","19.ai-core\u002F02.ai-and-machine-learning\u002F07.generative-ai",{"title":4874,"path":4875,"stem":4876},"Типи контенту, створюваного AI","\u002Fai-core\u002Fai-and-machine-learning\u002Fai-content-types","19.ai-core\u002F02.ai-and-machine-learning\u002F08.ai-content-types",{"title":4878,"path":4879,"stem":4880},"Великі мовні моделі","\u002Fai-core\u002Fai-and-machine-learning\u002Flarge-language-models","19.ai-core\u002F02.ai-and-machine-learning\u002F09.large-language-models",{"title":4882,"path":4883,"stem":4884},"Можливості та обмеження Generative AI","\u002Fai-core\u002Fai-and-machine-learning\u002Fcapabilities-and-limitations","19.ai-core\u002F02.ai-and-machine-learning\u002F10.capabilities-and-limitations",{"title":4886,"path":4887,"stem":4888},"Сучасні AI-асистенти","\u002Fai-core\u002Fai-and-machine-learning\u002Fmodern-ai-assistants","19.ai-core\u002F02.ai-and-machine-learning\u002F11.modern-ai-assistants",{"title":4890,"icon":4891,"path":4892,"stem":4893,"children":4894,"page":59},"Постановка завдань для AI: принципи створення промптів","i-lucide-message-square-text","\u002Fai-core\u002Fprompt-engineering","19.ai-core\u002F03.prompt-engineering",[4895,4899,4903,4907,4911,4915,4919,4923],{"title":4896,"path":4897,"stem":4898},"Промпт як інструмент","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-as-tool","19.ai-core\u002F03.prompt-engineering\u002F01.prompt-as-tool",{"title":4900,"path":4901,"stem":4902},"Принципи створення промптів","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-principles","19.ai-core\u002F03.prompt-engineering\u002F02.prompt-principles",{"title":4904,"path":4905,"stem":4906},"Структура промпта","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-structure","19.ai-core\u002F03.prompt-engineering\u002F03.prompt-structure",{"title":4908,"path":4909,"stem":4910},"Види промптів за типом задачі","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-types","19.ai-core\u002F03.prompt-engineering\u002F04.prompt-types",{"title":4912,"path":4913,"stem":4914},"Прийоми посилення промпта","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-enhancement","19.ai-core\u002F03.prompt-engineering\u002F05.prompt-enhancement",{"title":4916,"path":4917,"stem":4918},"Розбір і покращення промпта","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-analysis","19.ai-core\u002F03.prompt-engineering\u002F06.prompt-analysis",{"title":4920,"path":4921,"stem":4922},"Типові помилки під час створення промптів","\u002Fai-core\u002Fprompt-engineering\u002Fcommon-mistakes","19.ai-core\u002F03.prompt-engineering\u002F07.common-mistakes",{"title":4924,"path":4925,"stem":4926},"Практикум: особиста бібліотека промптів","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-library","19.ai-core\u002F03.prompt-engineering\u002F08.prompt-library",{"title":4928,"icon":793,"path":4929,"stem":4930,"children":4931,"page":59},"Перевірка результатів, безпека та AI Native Workflow","\u002Fai-core\u002Fverification-security-workflow","19.ai-core\u002F04.verification-security-workflow",[4932,4936,4940,4944,4948,4952,4956,4960,4964],{"title":4933,"path":4934,"stem":4935},"Межі можливостей AI","\u002Fai-core\u002Fverification-security-workflow\u002Fai-limitations","19.ai-core\u002F04.verification-security-workflow\u002F01.ai-limitations",{"title":4937,"path":4938,"stem":4939},"Галюцинації AI","\u002Fai-core\u002Fverification-security-workflow\u002Fai-hallucinations","19.ai-core\u002F04.verification-security-workflow\u002F02.ai-hallucinations",{"title":4941,"path":4942,"stem":4943},"Критичне мислення під час роботи з AI","\u002Fai-core\u002Fverification-security-workflow\u002Fcritical-thinking","19.ai-core\u002F04.verification-security-workflow\u002F03.critical-thinking",{"title":4945,"path":4946,"stem":4947},"Перевірка результатів роботи AI","\u002Fai-core\u002Fverification-security-workflow\u002Fresult-verification","19.ai-core\u002F04.verification-security-workflow\u002F04.result-verification",{"title":4949,"path":4950,"stem":4951},"Захист даних під час роботи з AI","\u002Fai-core\u002Fverification-security-workflow\u002Fdata-protection","19.ai-core\u002F04.verification-security-workflow\u002F05.data-protection",{"title":4953,"path":4954,"stem":4955},"Авторське право та навчальна доброчесність","\u002Fai-core\u002Fverification-security-workflow\u002Fcopyright-integrity","19.ai-core\u002F04.verification-security-workflow\u002F06.copyright-integrity",{"title":4957,"path":4958,"stem":4959},"Відповідальне використання AI","\u002Fai-core\u002Fverification-security-workflow\u002Fresponsible-use","19.ai-core\u002F04.verification-security-workflow\u002F07.responsible-use",{"title":4961,"path":4962,"stem":4963},"AI Native Workflow","\u002Fai-core\u002Fverification-security-workflow\u002Fai-native-workflow","19.ai-core\u002F04.verification-security-workflow\u002F08.ai-native-workflow",{"title":4965,"path":4966,"stem":4967},"Застосування AI Native Workflow під час створення цифрового продукту","\u002Fai-core\u002Fverification-security-workflow\u002Fworkflow-in-practice","19.ai-core\u002F04.verification-security-workflow\u002F09.workflow-in-practice",{"title":4969,"icon":4970,"path":4971,"stem":4972,"children":4973,"page":59},"AI Developer: Vibe Coding Sprint","i-lucide-code-xml","\u002Fai-developer","20.ai-developer",[4974,5000,5022,5043],{"title":4975,"icon":4976,"path":4977,"stem":4978,"children":4979,"page":59},"Проєктування і чорнова версія","i-lucide-pencil-ruler","\u002Fai-developer\u002Fdraft-creation","20.ai-developer\u002F01.draft-creation",[4980,4984,4988,4992,4996],{"title":4981,"path":4982,"stem":4983},"Парадигма Vibe Coding Workflow","\u002Fai-developer\u002Fdraft-creation\u002Fvibe-coding-workflow","20.ai-developer\u002F01.draft-creation\u002F01.vibe-coding-workflow",{"title":4985,"path":4986,"stem":4987},"AI-середовище швидкої розробки","\u002Fai-developer\u002Fdraft-creation\u002Fai-ide-environment","20.ai-developer\u002F01.draft-creation\u002F02.ai-ide-environment",{"title":4989,"path":4990,"stem":4991},"Користувацьке завдання та межі першої версії","\u002Fai-developer\u002Fdraft-creation\u002Fproduct-scope-and-mvp","20.ai-developer\u002F01.draft-creation\u002F03.product-scope-and-mvp",{"title":4993,"path":4994,"stem":4995},"Архітектура інтерфейсу та стани застосунку","\u002Fai-developer\u002Fdraft-creation\u002Fapplication-architecture-and-states","20.ai-developer\u002F01.draft-creation\u002F04.application-architecture-and-states",{"title":4997,"path":4998,"stem":4999},"Генерація та фіксація чорнової версії","\u002Fai-developer\u002Fdraft-creation\u002Fgenerating-draft-version","20.ai-developer\u002F01.draft-creation\u002F05.generating-draft-version",{"title":5001,"icon":5002,"path":5003,"stem":5004,"children":5005,"page":59},"Перша робоча версія","i-lucide-play-circle","\u002Fai-developer\u002Ffirst-working-version","20.ai-developer\u002F02.first-working-version",[5006,5010,5014,5018],{"title":5007,"path":5008,"stem":5009},"Уточнення контексту проєкту для AI","\u002Fai-developer\u002Ffirst-working-version\u002Frefining-project-context","20.ai-developer\u002F02.first-working-version\u002F01.refining-project-context",{"title":5011,"path":5012,"stem":5013},"Стратегія завдань для AI-розробки","\u002Fai-developer\u002Ffirst-working-version\u002Fai-prompting-strategy","20.ai-developer\u002F02.first-working-version\u002F02.ai-prompting-strategy",{"title":5015,"path":5016,"stem":5017},"Доопрацювання до першої робочої версії","\u002Fai-developer\u002Ffirst-working-version\u002Frefining-to-working-version","20.ai-developer\u002F02.first-working-version\u002F03.refining-to-working-version",{"title":5019,"path":5020,"stem":5021},"Перевірка першої робочої версії","\u002Fai-developer\u002Ffirst-working-version\u002Ftesting-first-working-version","20.ai-developer\u002F02.first-working-version\u002F04.testing-first-working-version",{"title":5023,"icon":3072,"path":5024,"stem":5025,"children":5026,"page":59},"Ітерації, дебагінг та відновлення","\u002Fai-developer\u002Fiteration-debugging-recovery","20.ai-developer\u002F03.iteration-debugging-recovery",[5027,5031,5035,5039],{"title":5028,"path":5029,"stem":5030},"Управління точковими змінами продукту","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fchange-management","20.ai-developer\u002F03.iteration-debugging-recovery\u002F01.change-management",{"title":5032,"path":5033,"stem":5034},"Надійність інтерфейсу та обробка помилок","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fscenario-reliability-and-errors","20.ai-developer\u002F03.iteration-debugging-recovery\u002F02.scenario-reliability-and-errors",{"title":5036,"path":5037,"stem":5038},"Керування контекстом і збереження версій","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fproject-context-and-versions","20.ai-developer\u002F03.iteration-debugging-recovery\u002F03.project-context-and-versions",{"title":5040,"path":5041,"stem":5042},"Збої AI-розробки, ліміти та аварійне відновлення","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fai-breakdowns-and-recovery","20.ai-developer\u002F03.iteration-debugging-recovery\u002F04.ai-breakdowns-and-recovery",{"title":5044,"icon":5045,"path":5046,"stem":5047,"children":5048,"page":59},"Перевірка та завершення продукту","i-lucide-check-check","\u002Fai-developer\u002Fverification-and-completion","20.ai-developer\u002F04.verification-and-completion",[5049,5053,5057],{"title":5050,"path":5051,"stem":5052},"Комплексний аудит та підсумкове тестування","\u002Fai-developer\u002Fverification-and-completion\u002Ffinal-product-testing","20.ai-developer\u002F04.verification-and-completion\u002F01.final-product-testing",{"title":5054,"path":5055,"stem":5056},"Підготовка підсумкової версії коду","\u002Fai-developer\u002Fverification-and-completion\u002Fpreparing-release-version","20.ai-developer\u002F04.verification-and-completion\u002F02.preparing-release-version",{"title":5058,"path":5059,"stem":5060},"Демонстрація, публікація та критерії оцінювання","\u002Fai-developer\u002Fverification-and-completion\u002Fproduct-showcase-and-publishing","20.ai-developer\u002F04.verification-and-completion\u002F03.product-showcase-and-publishing",{"title":5062,"path":5063,"stem":5064,"children":5065,"page":59},"Tailwind","\u002Ftailwind","21.tailwind",[5066,5070,5074,5078,5082,5086,5090,5094,5098,5102,5106,5110],{"title":5067,"path":5068,"stem":5069},"Що таке Tailwind CSS і навіщо він потрібен","\u002Ftailwind\u002Ftailwind-intro-philosophy","21.tailwind\u002F01.tailwind-intro-philosophy",{"title":5071,"path":5072,"stem":5073},"Встановлення та налаштування Tailwind CSS v4","\u002Ftailwind\u002Ftailwind-installation-setup","21.tailwind\u002F02.tailwind-installation-setup",{"title":5075,"path":5076,"stem":5077},"Utility-класи: основи та система Tailwind","\u002Ftailwind\u002Ftailwind-utility-classes-core","21.tailwind\u002F03.tailwind-utility-classes-core",{"title":5079,"path":5080,"stem":5081},"Layout: Flexbox та Grid через Tailwind","\u002Ftailwind\u002Ftailwind-flexbox-grid","21.tailwind\u002F04.tailwind-flexbox-grid",{"title":5083,"path":5084,"stem":5085},"Кастомізація теми через @theme у Tailwind v4","\u002Ftailwind\u002Ftailwind-theme-customization","21.tailwind\u002F05.tailwind-theme-customization",{"title":5087,"path":5088,"stem":5089},"Варіанти: hover, focus, responsive, dark mode та нові v4","\u002Ftailwind\u002Ftailwind-variants-states","21.tailwind\u002F06.tailwind-variants-states",{"title":5091,"path":5092,"stem":5093},"Типографіка та система кольорів у Tailwind v4","\u002Ftailwind\u002Ftailwind-typography-colors","21.tailwind\u002F07.tailwind-typography-colors",{"title":5095,"path":5096,"stem":5097},"Компоненти та повторюваність: @apply, @utility та патерни","\u002Ftailwind\u002Ftailwind-components-patterns","21.tailwind\u002F08.tailwind-components-patterns",{"title":5099,"path":5100,"stem":5101},"Темна тема та система дизайн-токенів у Tailwind v4","\u002Ftailwind\u002Ftailwind-dark-mode-theming","21.tailwind\u002F09.tailwind-dark-mode-theming",{"title":5103,"path":5104,"stem":5105},"Довільні значення та контейнерні запити у Tailwind v4","\u002Ftailwind\u002Ftailwind-arbitrary-container-queries","21.tailwind\u002F10.tailwind-arbitrary-container-queries",{"title":5107,"path":5108,"stem":5109},"Анімації, трансформації та 3D у Tailwind v4","\u002Ftailwind\u002Ftailwind-animations-transforms","21.tailwind\u002F11.tailwind-animations-transforms",{"title":5111,"path":5112,"stem":5113},"Tailwind CLI, PostCSS та інтеграція з фреймворками","\u002Ftailwind\u002Ftailwind-cli-tooling","21.tailwind\u002F12.tailwind-cli-tooling",{"title":5115,"path":5116,"stem":5117,"children":5118,"page":59},"React Typescript","\u002Freact-typescript","22.react-typescript",[5119,5144,5148,5152,5156,5160,5164,5168,5172],{"title":5120,"path":5121,"stem":5122,"children":5123,"page":59},"From Html To Project","\u002Freact-typescript\u002Ffrom-html-to-project","22.react-typescript\u002F01.from-html-to-project",[5124,5128,5132,5136,5140],{"title":5125,"path":5126,"stem":5127},"Від одного HTML-файлу до повноцінного проєкту","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fevolution","22.react-typescript\u002F01.from-html-to-project\u002F01.evolution",{"title":5129,"path":5130,"stem":5131},"Що таке React","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fwhat-is-react","22.react-typescript\u002F01.from-html-to-project\u002F02.what-is-react",{"title":5133,"path":5134,"stem":5135},"JSX та TSX — синтаксичний цукор, а не магія","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fjsx-tsx-syntax","22.react-typescript\u002F01.from-html-to-project\u002F03.jsx-tsx-syntax",{"title":5137,"path":5138,"stem":5139},"Компоненти — будівельні блоки інтерфейсу","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fcomponents","22.react-typescript\u002F01.from-html-to-project\u002F04.components",{"title":5141,"path":5142,"stem":5143},"JSX — розмітка всередині TypeScript","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fjsx-expressions","22.react-typescript\u002F01.from-html-to-project\u002F05.jsx-expressions",{"title":5145,"path":5146,"stem":5147},"Хук useReducer: структуроване управління складним станом","\u002Freact-typescript\u002Fuse-reducer","22.react-typescript\u002F23.use-reducer",{"title":5149,"path":5150,"stem":5151},"Context API: передавання даних без «пропс-дрілінгу»","\u002Freact-typescript\u002Fcontext-api","22.react-typescript\u002F24.context-api",{"title":5153,"path":5154,"stem":5155},"Синхронізація з ефектами: хук useEffect та функції очищення","\u002Freact-typescript\u002Fuse-effect","22.react-typescript\u002F27.use-effect",{"title":5157,"path":5158,"stem":5159},"Можливо, вам не потрібен ефект: антипатерни та альтернативи","\u002Freact-typescript\u002Fyou-might-not-need-effect","22.react-typescript\u002F28.you-might-not-need-effect",{"title":5161,"path":5162,"stem":5163},"Життєвий цикл реактивних ефектів: мислення процесами синхронізації","\u002Freact-typescript\u002Flifecycle-of-reactive-effects","22.react-typescript\u002F29.lifecycle-of-reactive-effects",{"title":5165,"path":5166,"stem":5167},"Розділення подій та ефектів: реактивна проти нереактивної логіки","\u002Freact-typescript\u002Fseparating-events-from-effects","22.react-typescript\u002F30.separating-events-from-effects",{"title":5169,"path":5170,"stem":5171},"Видалення зайвих залежностей ефектів та оптимізація синхронізації","\u002Freact-typescript\u002Fremoving-effect-dependencies","22.react-typescript\u002F31.removing-effect-dependencies",{"title":5173,"path":5174,"stem":5175},"Кастомні хуки: повторне використання та композиція реактивної логіки","\u002Freact-typescript\u002Fcustom-hooks","22.react-typescript\u002F32.custom-hooks",{"title":5177,"path":5178,"stem":5179},"Тестування компонентів діаграм та пісочниць","\u002Ftest-components","98.test-components",{"id":5181,"title":4053,"body":5182,"description":20937,"extension":20938,"links":20939,"meta":20940,"navigation":5505,"path":4054,"seo":20941,"stem":4055,"__hash__":20942},"docs\u002F17.client-server-architecture\u002F17.streaming-media-protocols-and-webrtc.md",{"type":5183,"value":5184,"toc":20870},"minimark",[5185,5189,5290,5293,5298,5314,5320,5332,5347,5349,5353,5364,5369,5372,5404,5412,5431,5437,5445,5448,5456,5469,5474,5477,5571,5578,5616,5623,5627,5631,5634,5731,5734,5776,5787,5789,5793,5796,5800,5803,5815,5823,5831,5839,5847,5859,5867,5871,5878,5885,5889,5896,5900,5903,6039,6044,6068,6072,6087,6092,6139,6144,6147,6152,6157,6168,6173,6180,6183,6186,6197,6199,6203,6206,6210,6219,6222,6474,6479,6488,6494,6554,6568,6644,6658,6707,6712,6806,6834,6838,6841,7337,7350,7364,7368,7374,8412,8419,8429,8431,8435,8445,8449,8552,8556,8563,8571,8579,8587,8595,8599,9162,9167,9219,9223,9231,9239,9251,9258,9260,9264,9274,9278,9285,9297,9317,9325,9333,9337,9340,9356,9374,9393,9413,9443,9458,9470,9676,9680,9686,9690,9705,9717,9729,9755,9902,9906,9913,9918,9935,9940,9943,9995,10000,10008,10040,10045,10056,10062,10070,10072,10076,10083,10106,10117,10128,10139,10155,10157,10161,10172,10176,10179,10203,10207,10238,10242,10245,11492,11503,11510,11514,11517,12718,12723,12749,12753,12760,12763,12876,12883,12964,12969,13134,13145,13153,13199,13206,13345,13350,13353,13758,13766,13772,14191,14199,14360,14365,14394,14404,14408,14411,15494,15499,15525,15535,15537,15541,15544,15548,15555,17591,17596,17637,17641,17648,17685,17688,18467,18471,18478,18496,18499,18875,18878,18880,18884,18888,18891,18929,18933,18940,19063,19076,19080,19083,19103,19106,19202,19204,19208,19328,19353,19355,19359,19363,19371,19379,19385,19393,19399,19402,19408,19411,19417,19429,19436,19440,19448,19464,19472,19590,19598,19617,19629,19757,19761,19773,20017,20025,20158,20166,20276,20287,20289,20293,20297,20303,20316,20322,20334,20339,20361,20367,20371,20379,20393,20398,20488,20503,20505,20509,20512,20517,20532,20536,20630,20634,20639,20660,20665,20695,20700,20723,20740,20742,20746,20824,20826,20867],[5186,5187,4053],"h1",{"id":5188},"протоколи-потокового-медіа-та-технологія-webrtc",[5190,5191,5192,5227],"card-group",{},[5193,5194,5197],"card",{"icon":5195,"title":5196},"i-lucide-target","🎯 Мета лекції",[5198,5199,5200,5204,5212,5215,5218,5221,5224],"ul",{},[5201,5202,5203],"li",{},"Зрозуміти фундаментальні відмінності між завантаженням файлів та потоковою передачею медіаконтенту.",[5201,5205,5206,5207,5211],{},"Опанувати архітектуру протоколу HTTP Live Streaming (HLS) та формат маніфестів ",[5208,5209,5210],"code",{},".m3u8",".",[5201,5213,5214],{},"Навчитися організовувати роздачу адаптивного відео з використанням різних рівнів якості.",[5201,5216,5217],{},"Дослідити альтернативний протокол DASH (Dynamic Adaptive Streaming over HTTP) та його переваги.",[5201,5219,5220],{},"Зрозуміти архітектуру технології WebRTC для прямого P2P зв'язку без проміжних серверів.",[5201,5222,5223],{},"Опанувати процес сигналізації, встановлення з'єднання через ICE та роль серверів STUN\u002FTURN.",[5201,5225,5226],{},"Реалізувати сервер потокового відео та систему відеочату на базі бібліотеки Pion WebRTC у Go.",[5193,5228,5230],{"icon":4618,"title":5229},"🔑 Ключові терміни",[5198,5231,5232,5239,5245,5254,5260,5266,5272,5278,5284],{},[5201,5233,5234,5238],{},[5235,5236,5237],"strong",{},"Streaming (потокова передача):"," технологія доставки медіаконтенту у вигляді послідовних фрагментів, що дозволяє розпочати відтворення до завершення завантаження всього файлу.",[5201,5240,5241,5244],{},[5235,5242,5243],{},"HLS (HTTP Live Streaming):"," протокол потокової передачі медіа, розроблений компанією Apple, що використовує звичайні HTTP-сервери.",[5201,5246,5247,5250,5251,5253],{},[5235,5248,5249],{},"Manifest (маніфест):"," текстовий файл формату ",[5208,5252,5210],{},", що містить метаінформацію про доступні медіасегменти та потоки.",[5201,5255,5256,5259],{},[5235,5257,5258],{},"Adaptive Bitrate (ABR, адаптивний бітрейт):"," технологія автоматичного вибору якості відео залежно від швидкості з'єднання користувача.",[5201,5261,5262,5265],{},[5235,5263,5264],{},"DASH (Dynamic Adaptive Streaming over HTTP):"," відкритий міжнародний стандарт адаптивного стрімінгу, альтернатива HLS.",[5201,5267,5268,5271],{},[5235,5269,5270],{},"WebRTC (Web Real-Time Communication):"," набір протоколів та API для прямого обміну аудіо, відео та даними між браузерами\u002Fзастосунками без посередників.",[5201,5273,5274,5277],{},[5235,5275,5276],{},"Signaling (сигналізація):"," процес обміну метаданими сесії (SDP) між учасниками перед встановленням прямого з'єднання.",[5201,5279,5280,5283],{},[5235,5281,5282],{},"ICE (Interactive Connectivity Establishment):"," фреймворк для визначення найкращого шляху з'єднання між двома вузлами через NAT.",[5201,5285,5286,5289],{},[5235,5287,5288],{},"STUN\u002FTURN:"," допоміжні сервери для визначення публічної IP-адреси клієнта (STUN) та проксування трафіку у випадку неможливості прямого з'єднання (TURN).",[5291,5292],"hr",{},[5294,5295,5297],"h2",{"id":5296},"світ-потокового-медіаконтенту-від-завантаження-до-стрімінгу","Світ потокового медіаконтенту: від завантаження до стрімінгу",[5299,5300,5301,5302,5305,5306,5309,5310,5313],"p",{},"У ранні роки розвитку інтернету споживання медіаконтенту здійснювалося переважно через повне завантаження файлу перед його відтворенням. Користувач ініціював завантаження відеофайлу формату ",[5208,5303,5304],{},".avi",", ",[5208,5307,5308],{},".mp4"," або ",[5208,5311,5312],{},".mov",", очікував завершення передачі усього обсягу даних (що могло тривати від хвилин до годин залежно від розміру та швидкості з'єднання), і лише після цього міг розпочати перегляд. Такий підхід мав цілу низку принципових обмежень: неможливість перегляду тривалих матеріалів без попереднього завершення завантаження, марнування дискового простору на клієнтській машині, відсутність адаптації до змін у пропускній здатності мережі та складність організації трансляцій у реальному часі (live streaming).",[5299,5315,5316,5319],{},[5235,5317,5318],{},"Потокова передача медіа (Media Streaming)"," радикально змінила парадигму доставки аудіо- та відеоконтенту. Замість завантаження монолітного файлу, медіапотік розділяється на невеликі самодостатні фрагменти (сегменти) тривалістю зазвичай від 2 до 10 секунд. Клієнтський програвач послідовно завантажує ці сегменти по HTTP, розпочинаючи відтворення вже після отримання першого фрагменту, паралельно завантажуючи наступні. Цей підхід забезпечує майже миттєвий старт відтворення, дозволяє здійснювати швидку перемотку вперед (достатньо завантажити сегмент із потрібної позиції без передачі проміжних даних), адаптувати якість під поточну швидкість з'єднання та здійснювати трансляції живих подій із затримкою, близькою до реального часу.",[5299,5321,5322,5323,5326,5327,5331],{},"Ключовою інновацією сучасних протоколів стрімінгу є концепція ",[5235,5324,5325],{},"адаптивного бітрейту (Adaptive Bitrate Streaming, ABR)",". Медіаконтент заздалегідь кодується у декілька варіантів якості (наприклад, 360p, 720p, 1080p, 4K), кожен із яких сегментується окремо. Програвач постійно вимірює доступну пропускну здатність мережі та поточний розмір буфера, динамічно обираючи оптимальну версію наступного сегменту. Якщо швидкість з'єднання знижується (наприклад, користувач перемістився у зону поганого покриття мобільної мережі), програвач автоматично перемикається на нижчу якість, запобігаючи затримкам та зупинкам відтворення (",[5328,5329,5330],"em",{},"buffering","). При покращенні умов система знову повертається до максимально можливої роздільної здатності. Завдяки цьому механізму забезпечується безперервний перегляд незалежно від нестабільності мережі, що є критичним фактором для позитивного користувацького досвіду.",[5333,5334,5335,5336,5309,5339,5342,5343,5346],"note",{},"На відміну від класичного протоколу ",[5235,5337,5338],{},"RTSP (Real-Time Streaming Protocol)",[5235,5340,5341],{},"RTMP (Real-Time Messaging Protocol)",", які працюють поверх TCP або окремих транспортних протоколів та вимагають встановлення спеціалізованих потокових серверів (наприклад, Wowza, Adobe Media Server), сучасні протоколи HTTP-стрімінгу (HLS, DASH) повністю базуються на звичайному протоколі ",[5235,5344,5345],{},"HTTP"," і можуть обслуговуватися стандартними вебсерверами (Nginx, Apache, Caddy) або навіть CDN (Content Delivery Networks). Це спрощує масштабування, кешування на проміжних вузлах та проходження через корпоративні брандмауери, які зазвичай блокують нестандартні порти, але дозволяють HTTP\u002FHTTPS-трафік.",[5291,5348],{},[5294,5350,5352],{"id":5351},"http-live-streaming-hls-архітектура-та-принципи-роботи","HTTP Live Streaming (HLS): Архітектура та принципи роботи",[5299,5354,5355,5356,5359,5360,5363],{},"Протокол ",[5235,5357,5358],{},"HTTP Live Streaming (HLS)"," був представлений компанією Apple у 2009 році як частина екосистеми iOS та macOS. Первісно розроблений для забезпечення якісного стрімінгу на мобільних пристроях із нестабільним з'єднанням, HLS швидко став де-факто стандартом у індустрії завдяки своїй простоті, надійності та універсальній підтримці у більшості браузерів та платформ. Специфікація протоколу зафіксована у документі ",[5235,5361,5362],{},"RFC 8216"," (HTTP Live Streaming, 2nd Edition).",[5365,5366,5368],"h3",{"id":5367},"компоненти-архітектури-hls","Компоненти архітектури HLS",[5299,5370,5371],{},"Система потокової передачі на базі HLS складається з трьох основних компонентів:",[5299,5373,5374,5377,5380,5381,5384,5385,5388,5389,5396,5397,5403],{},[5235,5375,5376],{},"1. Кодувальник та сегментатор (Encoder & Segmenter).",[5378,5379],"br",{},"\nВихідний медіаконтент (відеофайл або живий потік із камери) обробляється кодувальником, який виконує транскодування у сумісні формати (зазвичай H.264\u002FH.265 для відео та AAC для аудіо) на різних рівнях якості (бітрейтах). Після транскодування спеціалізований інструмент (наприклад, ",[5235,5382,5383],{},"FFmpeg"," з параметром ",[5208,5386,5387],{},"-hls_time",") розрізає кожен варіант на короткі медіасегменти фіксованої або змінної тривалості (зазвичай 6–10 секунд). Історично сегменти зберігалися у форматі ",[5235,5390,5391,5392,5395],{},"MPEG-2 Transport Stream (",[5208,5393,5394],{},".ts",")",", проте сучасні реалізації все частіше використовують контейнер ",[5235,5398,5399,5400,5395],{},"Fragmented MP4 (",[5208,5401,5402],{},".m4s",", який забезпечує кращу ефективність та сумісність.",[5299,5405,5406,5409,5411],{},[5235,5407,5408],{},"2. HTTP-сервер роздачі сегментів (Origin Server \u002F CDN).",[5378,5410],{},"\nЗгенеровані медіасегменти та файли маніфестів розміщуються на звичайному HTTP-сервері або мережі доставки контенту (CDN). Сервер не виконує жодної спеціалізованої логіки — він просто віддає статичні файли у відповідь на HTTP GET запити від клієнтів. Використання CDN дозволяє географічно розподілити навантаження, кешувати популярні сегменти на крайових серверах та зменшити затримки доставки.",[5299,5413,5414,5417,5419,5420,5305,5423,5426,5427,5430],{},[5235,5415,5416],{},"3. Клієнтський програвач (Media Player).",[5378,5418],{},"\nПрогравач на стороні клієнта (вбудований у браузер, мобільний застосунок або десктопний медіацентр) розбирає файл маніфесту, приймає рішення про вибір потрібного варіанту якості, послідовно завантажує медіасегменти через HTTP та подає їх на декодування і відтворення. Сучасні програвачі (наприклад, ",[5235,5421,5422],{},"Video.js",[5235,5424,5425],{},"hls.js",", нативна підтримка у Safari\u002FiOS, ",[5235,5428,5429],{},"ExoPlayer"," на Android) реалізують складну логіку адаптивного вибору бітрейту, передзавантаження сегментів у буфер, обробки помилок та перемикання між серверами у разі збоїв.",[5365,5432,5434,5435],{"id":5433},"формат-маніфесту-m3u8","Формат маніфесту ",[5208,5436,5210],{},[5299,5438,5439,5440,5444],{},"Центральним елементом протоколу HLS є текстовий файл маніфесту з розширенням ",[5235,5441,5442],{},[5208,5443,5210],{}," (кодування UTF-8 варіант формату M3U, який історично використовувався для списків відтворення аудіо). Маніфест містить метаінформацію про структуру потоку, доступні варіанти якості та посилання на медіасегменти.",[5299,5446,5447],{},"Існує два типи маніфестів:",[5299,5449,5450,5453,5455],{},[5235,5451,5452],{},"Master Playlist (головний список відтворення):",[5378,5454],{},"\nПерший файл, який запитує клієнт. Він містить посилання на окремі медіа-списки (Media Playlists) для кожного варіанту якості, а також інформацію про пропускну здатність, роздільну здатність та аудіодоріжки. Головний маніфест дозволяє програвачу оцінити доступні опції та обрати найбільш підходящу для початку відтворення.",[5299,5457,5458,5461,5463,5464,5309,5466,5468],{},[5235,5459,5460],{},"Media Playlist (медіа-список відтворення):",[5378,5462],{},"\nОкремий файл для конкретного варіанту якості, що містить упорядкований список посилань на медіасегменти (",[5208,5465,5394],{},[5208,5467,5402],{}," файли) із зазначенням тривалості кожного фрагменту. Медіа-список може бути статичним (для відео на вимогу, VOD) або динамічним (для живих трансляцій, коли нові сегменти додаються у реальному часі, а старі видаляються після закінчення актуальності).",[5470,5471,5473],"h4",{"id":5472},"приклад-головного-маніфесту-master-playlist","Приклад головного маніфесту (Master Playlist)",[5299,5475,5476],{},"Розглянемо структуру типового головного маніфесту для відео із трьома варіантами якості:",[5478,5479,5484],"pre",{"className":5480,"code":5481,"language":5482,"meta":5483,"style":5483},"language-m3u8 shiki shiki-themes light-plus dark-plus dark-plus","#EXTM3U\n#EXT-X-VERSION:3\n\n# Варіант 1: Низька якість (360p, 800 kbps)\n#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360,CODECS=\"avc1.42e00a,mp4a.40.2\"\n360p\u002Fplaylist.m3u8\n\n# Варіант 2: Середня якість (720p, 2000 kbps)\n#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=1280x720,CODECS=\"avc1.4d401f,mp4a.40.2\"\n720p\u002Fplaylist.m3u8\n\n# Варіант 3: Висока якість (1080p, 5000 kbps)\n#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,CODECS=\"avc1.640028,mp4a.40.2\"\n1080p\u002Fplaylist.m3u8\n","m3u8","",[5208,5485,5486,5494,5500,5507,5513,5519,5525,5530,5536,5542,5548,5553,5559,5565],{"__ignoreMap":5483},[5487,5488,5491],"span",{"class":5489,"line":5490},"line",1,[5487,5492,5493],{},"#EXTM3U\n",[5487,5495,5497],{"class":5489,"line":5496},2,[5487,5498,5499],{},"#EXT-X-VERSION:3\n",[5487,5501,5503],{"class":5489,"line":5502},3,[5487,5504,5506],{"emptyLinePlaceholder":5505},true,"\n",[5487,5508,5510],{"class":5489,"line":5509},4,[5487,5511,5512],{},"# Варіант 1: Низька якість (360p, 800 kbps)\n",[5487,5514,5516],{"class":5489,"line":5515},5,[5487,5517,5518],{},"#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360,CODECS=\"avc1.42e00a,mp4a.40.2\"\n",[5487,5520,5522],{"class":5489,"line":5521},6,[5487,5523,5524],{},"360p\u002Fplaylist.m3u8\n",[5487,5526,5528],{"class":5489,"line":5527},7,[5487,5529,5506],{"emptyLinePlaceholder":5505},[5487,5531,5533],{"class":5489,"line":5532},8,[5487,5534,5535],{},"# Варіант 2: Середня якість (720p, 2000 kbps)\n",[5487,5537,5539],{"class":5489,"line":5538},9,[5487,5540,5541],{},"#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=1280x720,CODECS=\"avc1.4d401f,mp4a.40.2\"\n",[5487,5543,5545],{"class":5489,"line":5544},10,[5487,5546,5547],{},"720p\u002Fplaylist.m3u8\n",[5487,5549,5551],{"class":5489,"line":5550},11,[5487,5552,5506],{"emptyLinePlaceholder":5505},[5487,5554,5556],{"class":5489,"line":5555},12,[5487,5557,5558],{},"# Варіант 3: Висока якість (1080p, 5000 kbps)\n",[5487,5560,5562],{"class":5489,"line":5561},13,[5487,5563,5564],{},"#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080,CODECS=\"avc1.640028,mp4a.40.2\"\n",[5487,5566,5568],{"class":5489,"line":5567},14,[5487,5569,5570],{},"1080p\u002Fplaylist.m3u8\n",[5299,5572,5573,5574,5577],{},"Кожен рядок ",[5208,5575,5576],{},"#EXT-X-STREAM-INF"," визначає окремий потік із ключовими параметрами:",[5198,5579,5580,5588,5600],{},[5201,5581,5582,5587],{},[5235,5583,5584],{},[5208,5585,5586],{},"BANDWIDTH"," — приблизна пропускна здатність у бітах за секунду, необхідна для плавного відтворення цього варіанту. Програвач використовує це значення для вибору оптимального потоку на основі вимірювань реальної швидкості з'єднання.",[5201,5589,5590,5595,5596,5599],{},[5235,5591,5592],{},[5208,5593,5594],{},"RESOLUTION"," — роздільна здатність відео у форматі ",[5208,5597,5598],{},"ширина×висота"," пікселів. Інформація корисна для адаптації під розмір екрану пристрою (немає сенсу завантажувати 4K на смартфон із екраном 720p).",[5201,5601,5602,5607,5608,5611,5612,5615],{},[5235,5603,5604],{},[5208,5605,5606],{},"CODECS"," — специфікація відео- та аудіокодеків у форматі RFC 6381. Значення ",[5208,5609,5610],{},"avc1"," позначає H.264 (Advanced Video Coding), а ",[5208,5613,5614],{},"mp4a.40.2"," — AAC-LC аудіокодек. Програвач перевіряє підтримку кодеків апаратним декодером пристрою перед вибором потоку.",[5299,5617,5618,5619,5622],{},"Після рядка з метаданими слідує відносний або абсолютний URL медіа-списку конкретного варіанту якості (наприклад, ",[5208,5620,5621],{},"360p\u002Fplaylist.m3u8",").",[5624,5625,5626],"tip",{},"Програвач зазвичай обирає початковий варіант якості на основі попередніх вимірювань швидкості (якщо доступні) або починає з середнього\u002Fнизького рівня для швидкого старту. Після завантаження перших кількох сегментів система аналізує фактичну швидкість завантаження та час заповнення буфера, приймаючи рішення про перемикання на вищу або нижчу якість для наступних сегментів.",[5470,5628,5630],{"id":5629},"приклад-медіа-списку-media-playlist-для-vod","Приклад медіа-списку (Media Playlist) для VOD",[5299,5632,5633],{},"Медіа-список конкретного варіанту якості містить упорядкований перелік медіасегментів:",[5478,5635,5637],{"className":5480,"code":5636,"language":5482,"meta":5483,"style":5483},"#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:10\n#EXT-X-MEDIA-SEQUENCE:0\n\n#EXTINF:10.0,\nsegment_0.ts\n#EXTINF:10.0,\nsegment_1.ts\n#EXTINF:10.0,\nsegment_2.ts\n#EXTINF:10.0,\nsegment_3.ts\n#EXTINF:10.0,\nsegment_4.ts\n#EXTINF:8.5,\nsegment_5.ts\n\n#EXT-X-ENDLIST\n",[5208,5638,5639,5643,5647,5652,5657,5661,5666,5671,5675,5680,5684,5689,5693,5698,5702,5708,5714,5720,5725],{"__ignoreMap":5483},[5487,5640,5641],{"class":5489,"line":5490},[5487,5642,5493],{},[5487,5644,5645],{"class":5489,"line":5496},[5487,5646,5499],{},[5487,5648,5649],{"class":5489,"line":5502},[5487,5650,5651],{},"#EXT-X-TARGETDURATION:10\n",[5487,5653,5654],{"class":5489,"line":5509},[5487,5655,5656],{},"#EXT-X-MEDIA-SEQUENCE:0\n",[5487,5658,5659],{"class":5489,"line":5515},[5487,5660,5506],{"emptyLinePlaceholder":5505},[5487,5662,5663],{"class":5489,"line":5521},[5487,5664,5665],{},"#EXTINF:10.0,\n",[5487,5667,5668],{"class":5489,"line":5527},[5487,5669,5670],{},"segment_0.ts\n",[5487,5672,5673],{"class":5489,"line":5532},[5487,5674,5665],{},[5487,5676,5677],{"class":5489,"line":5538},[5487,5678,5679],{},"segment_1.ts\n",[5487,5681,5682],{"class":5489,"line":5544},[5487,5683,5665],{},[5487,5685,5686],{"class":5489,"line":5550},[5487,5687,5688],{},"segment_2.ts\n",[5487,5690,5691],{"class":5489,"line":5555},[5487,5692,5665],{},[5487,5694,5695],{"class":5489,"line":5561},[5487,5696,5697],{},"segment_3.ts\n",[5487,5699,5700],{"class":5489,"line":5567},[5487,5701,5665],{},[5487,5703,5705],{"class":5489,"line":5704},15,[5487,5706,5707],{},"segment_4.ts\n",[5487,5709,5711],{"class":5489,"line":5710},16,[5487,5712,5713],{},"#EXTINF:8.5,\n",[5487,5715,5717],{"class":5489,"line":5716},17,[5487,5718,5719],{},"segment_5.ts\n",[5487,5721,5723],{"class":5489,"line":5722},18,[5487,5724,5506],{"emptyLinePlaceholder":5505},[5487,5726,5728],{"class":5489,"line":5727},19,[5487,5729,5730],{},"#EXT-X-ENDLIST\n",[5299,5732,5733],{},"Основні директиви медіа-списку:",[5198,5735,5736,5744,5752,5760,5768],{},[5201,5737,5738,5743],{},[5235,5739,5740],{},[5208,5741,5742],{},"#EXT-X-VERSION"," — версія специфікації HLS. Версія 3 є базовою та широко підтримується, новіші версії (4+) додають можливості шифрування, альтернативних аудіодоріжок та інших розширень.",[5201,5745,5746,5751],{},[5235,5747,5748],{},[5208,5749,5750],{},"#EXT-X-TARGETDURATION"," — максимальна тривалість одного сегменту в секундах (округлена до цілого числа). Програвач використовує це значення для визначення інтервалу оновлення списку при живих трансляціях.",[5201,5753,5754,5759],{},[5235,5755,5756],{},[5208,5757,5758],{},"#EXT-X-MEDIA-SEQUENCE"," — порядковий номер першого сегменту у списку. Для VOD зазвичай дорівнює 0, для живих трансляцій збільшується при видаленні старих сегментів зі списку.",[5201,5761,5762,5767],{},[5235,5763,5764],{},[5208,5765,5766],{},"#EXTINF"," — тривалість наступного сегменту у секундах (може бути дробовою). Кома після числа є обов'язковою частиною синтаксису, після неї може слідувати опціональний опис сегменту (рідко використовується).",[5201,5769,5770,5775],{},[5235,5771,5772],{},[5208,5773,5774],{},"#EXT-X-ENDLIST"," — маркер завершення статичного списку (VOD). Його наявність сигналізує програвачу, що медіапотік закінчений і нових сегментів не буде. У разі живих трансляцій цей тег відсутній, і програвач періодично перезавантажує маніфест для отримання нових сегментів.",[5333,5777,5778,5779,5782,5783,5786],{},"Для живих трансляцій (Live Streaming) медіа-список є ",[5235,5780,5781],{},"динамічним документом",", що постійно оновлюється сервером. Після відтворення останнього доступного сегменту програвач чекає приблизно одну тривалість сегменту, після чого повторно запитує маніфест. У новій версії з'являються свіжі сегменти, а найдавніші видаляються (зазвичай зберігається вікно з останніх 3–6 сегментів). Це створює ковзне вікно трансляції (",[5328,5784,5785],{},"sliding window",") та обмежує можливість перемотки назад до декількох десятків секунд.",[5291,5788],{},[5294,5790,5792],{"id":5791},"адаптивний-бітрейт-adaptive-bitrate-streaming","Адаптивний бітрейт (Adaptive Bitrate Streaming)",[5299,5794,5795],{},"Концепція адаптивного бітрейту є серцем сучасних систем стрімінгу. Замість використання фіксованого потоку якості, клієнтський програвач постійно оцінює поточні мережеві умови та динамічно змінює рівень якості для забезпечення безперервного відтворення.",[5365,5797,5799],{"id":5798},"алгоритм-адаптації","Алгоритм адаптації",[5299,5801,5802],{},"Типовий алгоритм адаптивного вибору бітрейту складається з наступних кроків:",[5299,5804,5805,5808,5810,5811,5814],{},[5235,5806,5807],{},"1. Ініціалізація та вибір стартової якості.",[5378,5809],{},"\nПри першому завантаженні програвач аналізує доступні варіанти у головному маніфесті. Якщо немає попередньої інформації про швидкість мережі (наприклад, перший запуск застосунку), зазвичай обирається середній або низький варіант для гарантованого швидкого старту. Деякі реалізації виконують короткий попередній тест швидкості (",[5328,5812,5813],{},"bandwidth probing","), завантажуючи невеликий тестовий сегмент та вимірюючи швидкість передачі.",[5299,5816,5817,5820,5822],{},[5235,5818,5819],{},"2. Вимірювання швидкості завантаження.",[5378,5821],{},"\nПісля завантаження кожного сегменту програвач фіксує реальний час завантаження та розмір отриманих даних, обчислюючи миттєву швидкість передачі. Для згладжування коливань зазвичай застосовується ковзне середнє (exponential moving average, EMA) за останніми декількома сегментами:",[5824,5825,5826,5827,5830],"math-formula",{},"\\text{Bandwidth}",[5328,5828,5829],{},"{\\text{est}} = \\alpha \\cdot \\text{Bandwidth}","{\\text{current}} + (1 - \\alpha) \\cdot \\text{Bandwidth}_{\\text{prev}}",[5299,5832,5833,5834,5838],{},"де ",[5824,5835],{":inline":5836,"tex":5837},"true","\\alpha"," — коефіцієнт згладжування (зазвичай 0.2–0.3).",[5299,5840,5841,5844,5846],{},[5235,5842,5843],{},"3. Аналіз стану буфера.",[5378,5845],{},"\nПрогравач постійно контролює розмір буфера передзавантажених сегментів (зазвичай вимірюється у секундах майбутнього відтворення). Якщо буфер наближається до критично низького рівня (менше 5–10 секунд), система може превентивно знизити якість для прискорення завантаження наступного сегменту. Навпаки, великий заповнений буфер (30+ секунд) дозволяє спробувати перемикання на вищу якість навіть при незначному покращенні швидкості.",[5299,5848,5849,5852,5854,5855,5858],{},[5235,5850,5851],{},"4. Прийняття рішення про перемикання.",[5378,5853],{},"\nНа основі оціненої пропускної здатності, поточного стану буфера та доступних варіантів у маніфесті алгоритм обирає оптимальний рівень якості для наступного сегменту. Перемикання відбувається на межі сегментів, що забезпечує плавність без артефактів. Сучасні алгоритми (наприклад, ",[5235,5856,5857],{},"BOLA"," — Buffer Occupancy based Lyapunov Algorithm) використовують теорію управління для досягнення оптимального балансу між якістю відео та стабільністю відтворення.",[5860,5861,5862,5863,5866],"warning",{},"Занадто агресивне перемикання між рівнями якості може призвести до негативного користувацького досвіду через часті зміни різкості картинки (",[5328,5864,5865],{},"quality oscillation","). Хороший алгоритм адаптації повинен мати певну гістерезисну затримку: для переходу на вищу якість вимагається стабільне підтвердження покращення умов протягом декількох сегментів, а для пониження — швидка реакція на погіршення для запобігання зупинкам буферизації.",[5365,5868,5870],{"id":5869},"переваги-адаптивного-підходу","Переваги адаптивного підходу",[5299,5872,5873,5874,5877],{},"Технологія адаптивного бітрейту вирішує одну з найскладніших проблем доставки медіа через інтернет: ",[5235,5875,5876],{},"непередбачувану та змінну природу мережевої пропускної здатності",". Користувач може розпочати перегляд відео вдома через швидке Wi-Fi з'єднання (50+ Mbps), продовжити у транспорті через 4G (5–20 Mbps із періодичними провалами), а завершити у зоні слабкого покриття з швидкістю менше 1 Mbps. Без адаптації такий сценарій призвів би до постійних зупинок та буферизації. З ABR система автоматично підлаштовується під кожний етап, забезпечуючи безперервність відтворення з максимально можливою якістю.",[5299,5879,5880,5881,5884],{},"Додатковою перевагою є ",[5235,5882,5883],{},"оптимізація споживання трафіку",". Користувачі з обмеженими тарифними планами автоматично отримують контент у стислому вигляді за умов повільного з'єднання, що зменшує витрати мобільного трафіку без необхідності ручного налаштування параметрів програвача.",[5365,5886,5888],{"id":5887},"глибоке-розуміння-буферизації-та-управління-якістю","Глибоке розуміння буферизації та управління якістю",[5299,5890,5891,5892,5895],{},"Ефективна робота адаптивного стрімінгу залежить не лише від вимірювання швидкості мережі, але й від інтелектуального управління ",[5235,5893,5894],{},"буфером передзавантаження"," — черги сегментів, які вже завантажені, але ще не відтворені.",[5470,5897,5899],{"id":5898},"анатомія-буфера-програвача","Анатомія буфера програвача",[5299,5901,5902],{},"Буфер програвача можна уявити як часову лінію майбутнього контенту:",[5904,5905,5907],"plant-uml",{"alt":5906},"Модель буфера відтворення",[5478,5908,5912],{"className":5909,"code":5910,"language":5911,"meta":5483,"style":5483},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #FFFFFF\n\nrectangle \"Відтворення\\n(Playback Position)\" as Playback #DBEAFE\nrectangle \"Буфер передзавантаження\\n30 секунд\" as Buffer #DCFCE7\nrectangle \"Критична зона\\n\u003C5 секунд\" as Critical #FEE2E2\nrectangle \"Зона комфорту\\n10-30 секунд\" as Comfort #FEF3C7\nrectangle \"Надлишковий буфер\\n>30 секунд\" as Excess #E2E8F0\n\nPlayback -right-> Critical : Ризик зупинки\nCritical -right-> Comfort : Стабільне відтворення\nComfort -right-> Excess : Марнування ресурсів\n\nnote bottom of Critical\n  Програвач агресивно знижує якість\n  для швидкого заповнення буфера\nend note\n\nnote bottom of Comfort\n  Оптимальна зона для експериментів\n  із вищою якістю\nend note\n\n@enduml\n","plantuml",[5208,5913,5914,5919,5924,5929,5933,5938,5943,5948,5953,5958,5962,5967,5972,5977,5981,5986,5991,5996,6001,6005,6011,6017,6023,6028,6033],{"__ignoreMap":5483},[5487,5915,5916],{"class":5489,"line":5490},[5487,5917,5918],{},"@startuml\n",[5487,5920,5921],{"class":5489,"line":5496},[5487,5922,5923],{},"skinparam style plain\n",[5487,5925,5926],{"class":5489,"line":5502},[5487,5927,5928],{},"skinparam backgroundColor #FFFFFF\n",[5487,5930,5931],{"class":5489,"line":5509},[5487,5932,5506],{"emptyLinePlaceholder":5505},[5487,5934,5935],{"class":5489,"line":5515},[5487,5936,5937],{},"rectangle \"Відтворення\\n(Playback Position)\" as Playback #DBEAFE\n",[5487,5939,5940],{"class":5489,"line":5521},[5487,5941,5942],{},"rectangle \"Буфер передзавантаження\\n30 секунд\" as Buffer #DCFCE7\n",[5487,5944,5945],{"class":5489,"line":5527},[5487,5946,5947],{},"rectangle \"Критична зона\\n\u003C5 секунд\" as Critical #FEE2E2\n",[5487,5949,5950],{"class":5489,"line":5532},[5487,5951,5952],{},"rectangle \"Зона комфорту\\n10-30 секунд\" as Comfort #FEF3C7\n",[5487,5954,5955],{"class":5489,"line":5538},[5487,5956,5957],{},"rectangle \"Надлишковий буфер\\n>30 секунд\" as Excess #E2E8F0\n",[5487,5959,5960],{"class":5489,"line":5544},[5487,5961,5506],{"emptyLinePlaceholder":5505},[5487,5963,5964],{"class":5489,"line":5550},[5487,5965,5966],{},"Playback -right-> Critical : Ризик зупинки\n",[5487,5968,5969],{"class":5489,"line":5555},[5487,5970,5971],{},"Critical -right-> Comfort : Стабільне відтворення\n",[5487,5973,5974],{"class":5489,"line":5561},[5487,5975,5976],{},"Comfort -right-> Excess : Марнування ресурсів\n",[5487,5978,5979],{"class":5489,"line":5567},[5487,5980,5506],{"emptyLinePlaceholder":5505},[5487,5982,5983],{"class":5489,"line":5704},[5487,5984,5985],{},"note bottom of Critical\n",[5487,5987,5988],{"class":5489,"line":5710},[5487,5989,5990],{},"  Програвач агресивно знижує якість\n",[5487,5992,5993],{"class":5489,"line":5716},[5487,5994,5995],{},"  для швидкого заповнення буфера\n",[5487,5997,5998],{"class":5489,"line":5722},[5487,5999,6000],{},"end note\n",[5487,6002,6003],{"class":5489,"line":5727},[5487,6004,5506],{"emptyLinePlaceholder":5505},[5487,6006,6008],{"class":5489,"line":6007},20,[5487,6009,6010],{},"note bottom of Comfort\n",[5487,6012,6014],{"class":5489,"line":6013},21,[5487,6015,6016],{},"  Оптимальна зона для експериментів\n",[5487,6018,6020],{"class":5489,"line":6019},22,[5487,6021,6022],{},"  із вищою якістю\n",[5487,6024,6026],{"class":5489,"line":6025},23,[5487,6027,6000],{},[5487,6029,6031],{"class":5489,"line":6030},24,[5487,6032,5506],{"emptyLinePlaceholder":5505},[5487,6034,6036],{"class":5489,"line":6035},25,[5487,6037,6038],{},"@enduml\n",[5299,6040,6041],{},[5235,6042,6043],{},"Три стани буфера:",[6045,6046,6047,6056,6062],"ol",{},[5201,6048,6049,6052,6053,6055],{},[5235,6050,6051],{},"Критичний рівень (\u003C5 секунд):"," Програвач знаходиться у режимі паніки. Навіть короткочасне уповільнення мережі призведе до зупинки відтворення (",[5328,6054,5330],{},"). Система негайно перемикається на найнижчу якість і максимально прискорює завантаження.",[5201,6057,6058,6061],{},[5235,6059,6060],{},"Зона комфорту (10–30 секунд):"," Оптимальний діапазон для роботи. Є достатній запас для поглинання коливань швидкості, і програвач може експериментувати з вищою якістю. Більшість алгоритмів ABR намагаються підтримувати буфер саме у цьому діапазоні.",[5201,6063,6064,6067],{},[5235,6065,6066],{},"Надлишковий буфер (>30 секунд):"," Марнування ресурсів. Якщо буфер занадто великий, програвач завантажує сегменти високої якості, які користувач можливо ніколи не побачить (наприклад, якщо закриє відео або перемотає вперед). Крім того, великий буфер уповільнює реакцію на зміни якості.",[5470,6069,6071],{"id":6070},"математична-модель-прийняття-рішень","Математична модель прийняття рішень",[5299,6073,6074,6075,6078,6079,6082,6083,6086],{},"Сучасні алгоритми ABR використовують складні евристики для балансування між ",[5235,6076,6077],{},"якістю"," (максимізація бітрейту), ",[5235,6080,6081],{},"стабільністю"," (мінімізація перемикань) та ",[5235,6084,6085],{},"швидкістю старту"," (мінімізація часу до початку відтворення). Розглянемо спрощену модель:",[5299,6088,6089],{},[5235,6090,6091],{},"Метрики для прийняття рішення:",[6045,6093,6094,6112,6126],{},[5201,6095,6096,6099,6100,6103,6104],{},[5235,6097,6098],{},"Оцінена пропускна здатність"," ",[5824,6101],{":inline":5836,"tex":6102},"B_{\\text{est}}",":",[5198,6105,6106,6109],{},[5201,6107,6108],{},"Обчислюється як ковзне середнє за останніми N сегментами",[5201,6110,6111],{},"Зазвичай беруться песимістичні оцінки (наприклад, 10-й перцентиль замість середнього)",[5201,6113,6114,6099,6117,6120,6121],{},[5235,6115,6116],{},"Поточний розмір буфера",[5824,6118],{":inline":5836,"tex":6119},"T_{\\text{buffer}}"," (у секундах):",[5198,6122,6123],{},[5201,6124,6125],{},"Вимірюється як різниця між позицією останнього завантаженого сегменту та поточною позицією відтворення",[5201,6127,6128,6099,6131,6103,6134],{},[5235,6129,6130],{},"Бітрейти доступних варіантів",[5824,6132],{":inline":5836,"tex":6133},"R_1, R_2, ..., R_n",[5198,6135,6136],{},[5201,6137,6138],{},"Сортовані за зростанням (від найнижчої до найвищої якості)",[5299,6140,6141],{},[5235,6142,6143],{},"Правило вибору наступного варіанту:",[5824,6145,6146],{},"R_{\\text{next}} = \\max { R_i \\mid R_i \\leq \\alpha \\cdot B_{\\text{est}} \\text{ та } T_{\\text{buffer}} > T_{\\text{min}} }",[5299,6148,5833,6149,6151],{},[5824,6150],{":inline":5836,"tex":5837}," — коефіцієнт безпеки (зазвичай 0.85–0.9), що залишає запас для поглинання коливань швидкості.",[5299,6153,6154],{},[5235,6155,6156],{},"Умова превентивного зниження якості:",[5299,6158,6159,6160,6163,6164,6167],{},"Якщо ",[5824,6161],{":inline":5836,"tex":6162},"T_{\\text{buffer}} \u003C T_{\\text{critical}}"," (наприклад, \u003C5 секунд), алгоритм ігнорує оцінку пропускної здатності та негайно перемикається на ",[5824,6165],{":inline":5836,"tex":6166},"R_1"," (найнижчу якість) для швидкого заповнення буфера.",[5299,6169,6170],{},[5235,6171,6172],{},"Гістерезис для запобігання осциляціям:",[5299,6174,6175,6176,6179],{},"Для переходу на вищу якість вимагається стабільне підтвердження протягом ",[5824,6177],{":inline":5836,"tex":6178},"K"," послідовних сегментів (зазвичай K=3):",[5824,6181,6182],{},"\\text{Upgrade якщо: } R_{i+1} \\leq \\alpha \\cdot B_{\\text{est}} \\text{ протягом } K \\text{ сегментів}",[5299,6184,6185],{},"Це запобігає ситуації, коли програвач постійно перемикається між 720p та 1080p через мінімальні коливання швидкості.",[5333,6187,6188,6189,6192,6193,6196],{},"Алгоритм ",[5235,6190,6191],{},"BOLA (Buffer Occupancy based Lyapunov Algorithm)",", розроблений дослідниками з University of Massachusetts, використовує теорію оптимального управління для максимізації якості при дотриманні обмежень на стабільність буфера. BOLA є референсною реалізацією у бібліотеці ",[5235,6194,6195],{},"dash.js"," та забезпечує кращу якість порівняно з простими евристиками, особливо у нестабільних мережах.",[5291,6198],{},[5294,6200,6202],{"id":6201},"реалізація-hls-сервера-у-go","Реалізація HLS-сервера у Go",[5299,6204,6205],{},"Тепер перейдемо до практичної реалізації HTTP-сервера для роздачі HLS-контенту на мові Go. Наша система складатиметься з двох основних компонентів: попередньої підготовки медіаконтенту за допомогою FFmpeg та вебсервера, що обслуговує маніфести і сегменти.",[5365,6207,6209],{"id":6208},"крок-1-підготовка-медіасегментів-за-допомогою-ffmpeg","Крок 1. Підготовка медіасегментів за допомогою FFmpeg",[5299,6211,6212,6214,6215,6218],{},[5235,6213,5383],{}," — універсальний інструмент командного рядка для обробки мультимедіа, який підтримує транскодування, сегментацію та генерацію маніфестів HLS. Припустимо, у нас є вихідний відеофайл ",[5208,6216,6217],{},"source.mp4",", який ми хочемо підготувати для стрімінгу у трьох варіантах якості.",[5299,6220,6221],{},"Команда для генерації HLS-потоку з адаптивним бітрейтом виглядає наступним чином:",[5478,6223,6227],{"className":6224,"code":6225,"language":6226,"meta":5483,"style":5483},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","ffmpeg -i source.mp4 \\\n  -filter_complex \\\n  \"[0:v]split=3[v1][v2][v3]; \\\n   [v1]scale=w=640:h=360[v1out]; \\\n   [v2]scale=w=1280:h=720[v2out]; \\\n   [v3]scale=w=1920:h=1080[v3out]\" \\\n  -map \"[v1out]\" -c:v:0 libx264 -b:v:0 800k -maxrate:v:0 856k -bufsize:v:0 1200k \\\n  -map \"[v2out]\" -c:v:1 libx264 -b:v:1 2000k -maxrate:v:1 2140k -bufsize:v:1 3000k \\\n  -map \"[v3out]\" -c:v:2 libx264 -b:v:2 5000k -maxrate:v:2 5350k -bufsize:v:2 7500k \\\n  -map a:0 -c:a aac -b:a 128k -ac 2 \\\n  -f hls \\\n  -hls_time 10 \\\n  -hls_playlist_type vod \\\n  -hls_segment_filename \"stream_%v\u002Fsegment_%03d.ts\" \\\n  -master_pl_name master.m3u8 \\\n  -var_stream_map \"v:0,a:0 v:1,a:0 v:2,a:0\" \\\n  stream_%v\u002Fplaylist.m3u8\n","bash",[5208,6228,6229,6247,6254,6262,6269,6276,6283,6317,6349,6381,6409,6419,6429,6439,6449,6459,6469],{"__ignoreMap":5483},[5487,6230,6231,6235,6239,6243],{"class":5489,"line":5490},[5487,6232,6234],{"class":6233},"s8Opu","ffmpeg",[5487,6236,6238],{"class":6237},"su1O8"," -i",[5487,6240,6242],{"class":6241},"sbdoH"," source.mp4",[5487,6244,6246],{"class":6245},"sjcCO"," \\\n",[5487,6248,6249,6252],{"class":5489,"line":5496},[5487,6250,6251],{"class":6237},"  -filter_complex",[5487,6253,6246],{"class":6245},[5487,6255,6256,6259],{"class":5489,"line":5502},[5487,6257,6258],{"class":6241},"  \"[0:v]split=3[v1][v2][v3]; ",[5487,6260,6261],{"class":6245},"\\\n",[5487,6263,6264,6267],{"class":5489,"line":5509},[5487,6265,6266],{"class":6241},"   [v1]scale=w=640:h=360[v1out]; ",[5487,6268,6261],{"class":6245},[5487,6270,6271,6274],{"class":5489,"line":5515},[5487,6272,6273],{"class":6241},"   [v2]scale=w=1280:h=720[v2out]; ",[5487,6275,6261],{"class":6245},[5487,6277,6278,6281],{"class":5489,"line":5521},[5487,6279,6280],{"class":6241},"   [v3]scale=w=1920:h=1080[v3out]\"",[5487,6282,6246],{"class":6245},[5487,6284,6285,6288,6291,6294,6297,6300,6303,6306,6309,6312,6315],{"class":5489,"line":5527},[5487,6286,6287],{"class":6237},"  -map",[5487,6289,6290],{"class":6241}," \"[v1out]\"",[5487,6292,6293],{"class":6237}," -c:v:0",[5487,6295,6296],{"class":6241}," libx264",[5487,6298,6299],{"class":6237}," -b:v:0",[5487,6301,6302],{"class":6241}," 800k",[5487,6304,6305],{"class":6237}," -maxrate:v:0",[5487,6307,6308],{"class":6241}," 856k",[5487,6310,6311],{"class":6237}," -bufsize:v:0",[5487,6313,6314],{"class":6241}," 1200k",[5487,6316,6246],{"class":6245},[5487,6318,6319,6321,6324,6327,6329,6332,6335,6338,6341,6344,6347],{"class":5489,"line":5532},[5487,6320,6287],{"class":6237},[5487,6322,6323],{"class":6241}," \"[v2out]\"",[5487,6325,6326],{"class":6237}," -c:v:1",[5487,6328,6296],{"class":6241},[5487,6330,6331],{"class":6237}," -b:v:1",[5487,6333,6334],{"class":6241}," 2000k",[5487,6336,6337],{"class":6237}," -maxrate:v:1",[5487,6339,6340],{"class":6241}," 2140k",[5487,6342,6343],{"class":6237}," -bufsize:v:1",[5487,6345,6346],{"class":6241}," 3000k",[5487,6348,6246],{"class":6245},[5487,6350,6351,6353,6356,6359,6361,6364,6367,6370,6373,6376,6379],{"class":5489,"line":5538},[5487,6352,6287],{"class":6237},[5487,6354,6355],{"class":6241}," \"[v3out]\"",[5487,6357,6358],{"class":6237}," -c:v:2",[5487,6360,6296],{"class":6241},[5487,6362,6363],{"class":6237}," -b:v:2",[5487,6365,6366],{"class":6241}," 5000k",[5487,6368,6369],{"class":6237}," -maxrate:v:2",[5487,6371,6372],{"class":6241}," 5350k",[5487,6374,6375],{"class":6237}," -bufsize:v:2",[5487,6377,6378],{"class":6241}," 7500k",[5487,6380,6246],{"class":6245},[5487,6382,6383,6385,6388,6391,6394,6397,6400,6403,6407],{"class":5489,"line":5544},[5487,6384,6287],{"class":6237},[5487,6386,6387],{"class":6241}," a:0",[5487,6389,6390],{"class":6237}," -c:a",[5487,6392,6393],{"class":6241}," aac",[5487,6395,6396],{"class":6237}," -b:a",[5487,6398,6399],{"class":6241}," 128k",[5487,6401,6402],{"class":6237}," -ac",[5487,6404,6406],{"class":6405},"sJj4R"," 2",[5487,6408,6246],{"class":6245},[5487,6410,6411,6414,6417],{"class":5489,"line":5550},[5487,6412,6413],{"class":6237},"  -f",[5487,6415,6416],{"class":6241}," hls",[5487,6418,6246],{"class":6245},[5487,6420,6421,6424,6427],{"class":5489,"line":5555},[5487,6422,6423],{"class":6237},"  -hls_time",[5487,6425,6426],{"class":6405}," 10",[5487,6428,6246],{"class":6245},[5487,6430,6431,6434,6437],{"class":5489,"line":5561},[5487,6432,6433],{"class":6237},"  -hls_playlist_type",[5487,6435,6436],{"class":6241}," vod",[5487,6438,6246],{"class":6245},[5487,6440,6441,6444,6447],{"class":5489,"line":5567},[5487,6442,6443],{"class":6237},"  -hls_segment_filename",[5487,6445,6446],{"class":6241}," \"stream_%v\u002Fsegment_%03d.ts\"",[5487,6448,6246],{"class":6245},[5487,6450,6451,6454,6457],{"class":5489,"line":5704},[5487,6452,6453],{"class":6237},"  -master_pl_name",[5487,6455,6456],{"class":6241}," master.m3u8",[5487,6458,6246],{"class":6245},[5487,6460,6461,6464,6467],{"class":5489,"line":5710},[5487,6462,6463],{"class":6237},"  -var_stream_map",[5487,6465,6466],{"class":6241}," \"v:0,a:0 v:1,a:0 v:2,a:0\"",[5487,6468,6246],{"class":6245},[5487,6470,6471],{"class":5489,"line":5716},[5487,6472,6473],{"class":6241},"  stream_%v\u002Fplaylist.m3u8\n",[5299,6475,6476],{},[5235,6477,6478],{},"Детальне пояснення ключових параметрів:",[5299,6480,6481],{},[5235,6482,6483,6484,6487],{},"Граф відеофільтрів (",[5208,6485,6486],{},"-filter_complex","):",[5299,6489,6490,6491,6493],{},"Параметр ",[5208,6492,6486],{}," визначає комплексний граф обробки медіапотоків, що дозволяє виконувати паралельні операції над одним джерелом:",[5198,6495,6496,6526,6545],{},[5201,6497,6498,6503,6504,6507,6508,6511,6512,6515,6516,5305,6519,5305,6522,6525],{},[5235,6499,6500],{},[5208,6501,6502],{},"[0:v]split=3[v1][v2][v3]"," — отримує вхідний відеопотік (позначений як ",[5208,6505,6506],{},"0:v",", де ",[5208,6509,6510],{},"0"," — індекс файлу, ",[5208,6513,6514],{},"v"," — відео) та розділяє його на три ідентичні копії, які позначаються мітками ",[5208,6517,6518],{},"[v1]",[5208,6520,6521],{},"[v2]",[5208,6523,6524],{},"[v3]",". Це необхідно, оскільки один і той самий вхідний потік не може бути використаний декілька разів у різних ланцюжках обробки.",[5201,6527,6528,6533,6534,6537,6538,6541,6542,5211],{},[5235,6529,6530],{},[5208,6531,6532],{},"[v1]scale=w=640:h=360[v1out]"," — застосовує фільтр масштабування до першої копії. Параметри ",[5208,6535,6536],{},"w"," (width, ширина) та ",[5208,6539,6540],{},"h"," (height, висота) визначають цільову роздільну здатність 640×360 пікселів (360p). Результат зберігається під міткою ",[5208,6543,6544],{},"[v1out]",[5201,6546,6547,6548,6550,6551,6553],{},"Аналогічно для ",[5208,6549,6521],{}," (720p) та ",[5208,6552,6524],{}," (1080p).",[5299,6555,6556],{},[5235,6557,6558,6559,5305,6562,5305,6565,6487],{},"Налаштування кодування відео (",[5208,6560,6561],{},"-map",[5208,6563,6564],{},"-c:v",[5208,6566,6567],{},"-b:v",[5198,6569,6570,6584,6608,6620,6632],{},[5201,6571,6572,6577,6578,6580,6581,6583],{},[5235,6573,6574],{},[5208,6575,6576],{},"-map \"[v1out]\""," — вказує FFmpeg використати відеопотік ",[5208,6579,6544],{}," (результат масштабування) як вхідний матеріал для кодування. Без явного ",[5208,6582,6561],{}," FFmpeg автоматично обирає лише один потік кожного типу.",[5201,6585,6586,6591,6592,6595,6596,6599,6600,6603,6604,6607],{},[5235,6587,6588],{},[5208,6589,6590],{},"-c:v:0 libx264"," — визначає відеокодек для першого вихідного потоку (індекс ",[5208,6593,6594],{},":0","). Значення ",[5208,6597,6598],{},"libx264"," активує програмну реалізацію кодеку H.264\u002FAVC через бібліотеку x264. Для апаратного прискорення можна використовувати ",[5208,6601,6602],{},"h264_nvenc"," (NVIDIA GPU) або ",[5208,6605,6606],{},"h264_videotoolbox"," (macOS).",[5201,6609,6610,6615,6616,6619],{},[5235,6611,6612],{},[5208,6613,6614],{},"-b:v:0 800k"," — встановлює ",[5235,6617,6618],{},"середній (target) бітрейт"," відеопотоку на 800 кілобіт\u002Fс. Кодек намагатиметься досягти цього значення у середньому по всьому відео, дозволяючи короткочасні відхилення для складних сцен.",[5201,6621,6622,6627,6628,6631],{},[5235,6623,6624],{},[5208,6625,6626],{},"-maxrate:v:0 856k"," — ",[5235,6629,6630],{},"максимальний бітрейт",", який не може бути перевищений навіть для найскладніших кадрів (зазвичай на 5–10% вище за середній). Це критично для стрімінгу, оскільки гарантує, що жодна частина відео не вимагатиме більшої пропускної здатності, ніж заявлено у маніфесті.",[5201,6633,6634,6639,6640,6643],{},[5235,6635,6636],{},[5208,6637,6638],{},"-bufsize:v:0 1200k"," — розмір ",[5235,6641,6642],{},"буфера верифікації відео (VBV buffer)"," у бітах. Визначає, скільки даних декодер може накопичити перед початком відтворення. Більший буфер дозволяє кращу якість для складних сцен, але збільшує затримку старту. Рекомендоване значення: 1.5× від максимального бітрейту.",[5299,6645,6646],{},[5235,6647,6648,6649,5305,6652,5305,6655,6487],{},"Аудіокодування (",[5208,6650,6651],{},"-map a",[5208,6653,6654],{},"-c:a",[5208,6656,6657],{},"-b:a",[5198,6659,6660,6668,6680,6688],{},[5201,6661,6662,6667],{},[5235,6663,6664],{},[5208,6665,6666],{},"-map a:0"," — обирає першу аудіодоріжку вхідного файлу. Якщо відео містить декілька аудіодоріжок (наприклад, різні мови), можна вибрати конкретну через індекс.",[5201,6669,6670,6675,6676,6679],{},[5235,6671,6672],{},[5208,6673,6674],{},"-c:a aac"," — використовує кодек ",[5235,6677,6678],{},"AAC (Advanced Audio Coding)"," для аудіо. AAC забезпечує кращу якість звуку порівняно з MP3 при тому самому бітрейті та універсально підтримується всіма платформами.",[5201,6681,6682,6687],{},[5235,6683,6684],{},[5208,6685,6686],{},"-b:a 128k"," — аудіобітрейт 128 кбіт\u002Fс. Це стандартне значення для стереозвуку прийнятної якості. Для музичного контенту можна підвищити до 192–256 кбіт\u002Fс, для мовлення — знизити до 64–96 кбіт\u002Fс.",[5201,6689,6690,6695,6696,6699,6700,6703,6704,5211],{},[5235,6691,6692],{},[5208,6693,6694],{},"-ac 2"," — кількість аудіоканалів (audio channels). Значення ",[5208,6697,6698],{},"2"," означає стерео. Для моно використовується ",[5208,6701,6702],{},"1",", для об'ємного звуку 5.1 — ",[5208,6705,6706],{},"6",[5299,6708,6709],{},[5235,6710,6711],{},"Параметри HLS-сегментації:",[5198,6713,6714,6725,6736,6757,6773,6781],{},[5201,6715,6716,6721,6722,6724],{},[5235,6717,6718],{},[5208,6719,6720],{},"-f hls"," — вихідний формат HLS. Вказує FFmpeg генерувати маніфести ",[5208,6723,5210],{}," та медіасегменти замість одного цілісного файлу.",[5201,6726,6727,6627,6732,6735],{},[5235,6728,6729],{},[5208,6730,6731],{},"-hls_time 10",[5235,6733,6734],{},"цільова тривалість"," одного сегменту в секундах. FFmpeg намагається розрізати відео на кордонах ключових кадрів (keyframes), тому реальна тривалість може трохи відрізнятися. Короткі сегменти (2–4 с) зменшують затримку live-трансляцій, довгі (10+ с) покращують ефективність кешування CDN.",[5201,6737,6738,6743,6744,6747,6748,6750,6751,6754,6755,5622],{},[5235,6739,6740],{},[5208,6741,6742],{},"-hls_playlist_type vod"," — тип списку відтворення. Значення ",[5208,6745,6746],{},"vod"," (Video on Demand) генерує статичний маніфест із тегом ",[5208,6749,5774],{},", що сигналізує завершеність контенту. Для live-трансляцій використовується ",[5208,6752,6753],{},"event"," (зростаючий список без видалення старих сегментів) або опускається параметр для створення ковзного вікна (",[5328,6756,5785],{},[5201,6758,6759,6764,6765,6768,6769,6772],{},[5235,6760,6761],{},[5208,6762,6763],{},"-hls_segment_filename \"stream_%v\u002Fsegment_%03d.ts\""," — шаблон імені файлів сегментів. ",[5208,6766,6767],{},"%v"," замінюється номером варіанту потоку (0, 1, 2), ",[5208,6770,6771],{},"%03d"," — послідовним номером сегменту з доповненням нулями до трьох цифр (001, 002, ..., 099, 100).",[5201,6774,6775,6780],{},[5235,6776,6777],{},[5208,6778,6779],{},"-master_pl_name master.m3u8"," — ім'я файлу головного маніфесту (Master Playlist), що міститиме метаінформацію про всі доступні варіанти якості.",[5201,6782,6783,6788,6789,6792,6793,6507,6796,6799,6800,6803,6804,5622],{},[5235,6784,6785],{},[5208,6786,6787],{},"-var_stream_map \"v:0,a:0 v:1,a:0 v:2,a:0\""," — визначає ",[5235,6790,6791],{},"зіставлення потоків"," для кожного варіанту. Формат: ",[5208,6794,6795],{},"v:X,a:Y",[5208,6797,6798],{},"X"," — індекс відеопотоку, ",[5208,6801,6802],{},"Y"," — індекс аудіопотоку. У нашому випадку всі три відеоваріанти використовують одну спільну аудіодоріжку (індекс ",[5208,6805,6510],{},[5299,6807,6808,6809,5305,6812,5305,6815,6818,6819,6822,6823,5305,6826,6829,6830,6833],{},"Після виконання команди FFmpeg створить директорії ",[5208,6810,6811],{},"stream_0\u002F",[5208,6813,6814],{},"stream_1\u002F",[5208,6816,6817],{},"stream_2\u002F"," із відповідними медіа-списками ",[5208,6820,6821],{},"playlist.m3u8"," та файлами сегментів ",[5208,6824,6825],{},"segment_000.ts",[5208,6827,6828],{},"segment_001.ts",", ..., а також головний маніфест ",[5208,6831,6832],{},"master.m3u8"," у кореневій директорії.",[5365,6835,6837],{"id":6836},"крок-2-простий-http-сервер-для-роздачі-hls-у-go","Крок 2. Простий HTTP-сервер для роздачі HLS у Go",[5299,6839,6840],{},"Після підготовки медіаконтенту нам потрібен HTTP-сервер, який обслуговуватиме запити клієнтських програвачів. Оскільки HLS базується на звичайних HTTP GET запитах до статичних файлів, найпростішою реалізацією є використання стандартного файлового обробника Go:",[5478,6842,6846],{"className":6843,"code":6844,"language":6845,"meta":5483,"style":5483},"language-go shiki shiki-themes light-plus dark-plus dark-plus","package main\n\nimport (\n    \"log\"\n    \"net\u002Fhttp\"\n)\n\nfunc main() {\n    \u002F\u002F Створюємо файловий обробник для директорії з HLS-контентом\n    fs := http.FileServer(http.Dir(\".\u002Fhls_content\"))\n    \n    \u002F\u002F Налаштовуємо маршрут для роздачі медіа\n    http.Handle(\"\u002Fstream\u002F\", http.StripPrefix(\"\u002Fstream\u002F\", fs))\n    \n    \u002F\u002F Додаємо заголовки CORS для дозволу вебклієнтів з інших доменів\n    http.HandleFunc(\"\u002F\", func(w http.ResponseWriter, r *http.Request) {\n        w.Header().Set(\"Access-Control-Allow-Origin\", \"*\")\n        w.Header().Set(\"Access-Control-Allow-Methods\", \"GET, OPTIONS\")\n        w.Header().Set(\"Access-Control-Allow-Headers\", \"Content-Type\")\n        \n        if r.Method == http.MethodOptions {\n            w.WriteHeader(http.StatusOK)\n            return\n        }\n        \n        fs.ServeHTTP(w, r)\n    })\n    \n    log.Println(\"HLS streaming server запущено на порту :8080\")\n    log.Println(\"Головний маніфест доступний за адресою: http:\u002F\u002Flocalhost:8080\u002Fstream\u002Fmaster.m3u8\")\n    \n    if err := http.ListenAndServe(\":8080\", nil); err != nil {\n        log.Fatalf(\"Помилка запуску сервера: %v\", err)\n    }\n}\n","go",[5208,6847,6848,6857,6861,6871,6876,6881,6886,6890,6901,6907,6942,6947,6952,6987,6991,6996,7044,7072,7096,7120,7125,7151,7172,7177,7182,7186,7207,7213,7218,7236,7252,7257,7298,7325,7331],{"__ignoreMap":5483},[5487,6849,6850,6853],{"class":5489,"line":5490},[5487,6851,6852],{"class":6237},"package",[5487,6854,6856],{"class":6855},"sN1BT"," main\n",[5487,6858,6859],{"class":5489,"line":5496},[5487,6860,5506],{"emptyLinePlaceholder":5505},[5487,6862,6863,6867],{"class":5489,"line":5502},[5487,6864,6866],{"class":6865},"s8xlr","import",[5487,6868,6870],{"class":6869},"sHH4Y"," (\n",[5487,6872,6873],{"class":5489,"line":5509},[5487,6874,6875],{"class":6241},"    \"log\"\n",[5487,6877,6878],{"class":5489,"line":5515},[5487,6879,6880],{"class":6241},"    \"net\u002Fhttp\"\n",[5487,6882,6883],{"class":5489,"line":5521},[5487,6884,6885],{"class":6869},")\n",[5487,6887,6888],{"class":5489,"line":5527},[5487,6889,5506],{"emptyLinePlaceholder":5505},[5487,6891,6892,6895,6898],{"class":5489,"line":5532},[5487,6893,6894],{"class":6237},"func",[5487,6896,6897],{"class":6233}," main",[5487,6899,6900],{"class":6869},"() {\n",[5487,6902,6903],{"class":5489,"line":5538},[5487,6904,6906],{"class":6905},"spJ8K","    \u002F\u002F Створюємо файловий обробник для директорії з HLS-контентом\n",[5487,6908,6909,6913,6916,6919,6921,6924,6927,6929,6931,6934,6936,6939],{"class":5489,"line":5544},[5487,6910,6912],{"class":6911},"siwwj","    fs",[5487,6914,6915],{"class":6869}," := ",[5487,6917,6918],{"class":6911},"http",[5487,6920,5211],{"class":6869},[5487,6922,6923],{"class":6233},"FileServer",[5487,6925,6926],{"class":6869},"(",[5487,6928,6918],{"class":6911},[5487,6930,5211],{"class":6869},[5487,6932,6933],{"class":6233},"Dir",[5487,6935,6926],{"class":6869},[5487,6937,6938],{"class":6241},"\".\u002Fhls_content\"",[5487,6940,6941],{"class":6869},"))\n",[5487,6943,6944],{"class":5489,"line":5550},[5487,6945,6946],{"class":6869},"    \n",[5487,6948,6949],{"class":5489,"line":5555},[5487,6950,6951],{"class":6905},"    \u002F\u002F Налаштовуємо маршрут для роздачі медіа\n",[5487,6953,6954,6957,6959,6962,6964,6967,6969,6971,6973,6976,6978,6980,6982,6985],{"class":5489,"line":5561},[5487,6955,6956],{"class":6911},"    http",[5487,6958,5211],{"class":6869},[5487,6960,6961],{"class":6233},"Handle",[5487,6963,6926],{"class":6869},[5487,6965,6966],{"class":6241},"\"\u002Fstream\u002F\"",[5487,6968,5305],{"class":6869},[5487,6970,6918],{"class":6911},[5487,6972,5211],{"class":6869},[5487,6974,6975],{"class":6233},"StripPrefix",[5487,6977,6926],{"class":6869},[5487,6979,6966],{"class":6241},[5487,6981,5305],{"class":6869},[5487,6983,6984],{"class":6911},"fs",[5487,6986,6941],{"class":6869},[5487,6988,6989],{"class":5489,"line":5567},[5487,6990,6946],{"class":6869},[5487,6992,6993],{"class":5489,"line":5704},[5487,6994,6995],{"class":6905},"    \u002F\u002F Додаємо заголовки CORS для дозволу вебклієнтів з інших доменів\n",[5487,6997,6998,7000,7002,7005,7007,7010,7012,7014,7016,7018,7021,7023,7026,7028,7031,7034,7036,7038,7041],{"class":5489,"line":5710},[5487,6999,6956],{"class":6911},[5487,7001,5211],{"class":6869},[5487,7003,7004],{"class":6233},"HandleFunc",[5487,7006,6926],{"class":6869},[5487,7008,7009],{"class":6241},"\"\u002F\"",[5487,7011,5305],{"class":6869},[5487,7013,6894],{"class":6237},[5487,7015,6926],{"class":6869},[5487,7017,6536],{"class":6911},[5487,7019,7020],{"class":6855}," http",[5487,7022,5211],{"class":6869},[5487,7024,7025],{"class":6855},"ResponseWriter",[5487,7027,5305],{"class":6869},[5487,7029,7030],{"class":6911},"r",[5487,7032,7033],{"class":6869}," *",[5487,7035,6918],{"class":6855},[5487,7037,5211],{"class":6869},[5487,7039,7040],{"class":6855},"Request",[5487,7042,7043],{"class":6869},") {\n",[5487,7045,7046,7049,7051,7054,7057,7060,7062,7065,7067,7070],{"class":5489,"line":5716},[5487,7047,7048],{"class":6911},"        w",[5487,7050,5211],{"class":6869},[5487,7052,7053],{"class":6233},"Header",[5487,7055,7056],{"class":6869},"().",[5487,7058,7059],{"class":6233},"Set",[5487,7061,6926],{"class":6869},[5487,7063,7064],{"class":6241},"\"Access-Control-Allow-Origin\"",[5487,7066,5305],{"class":6869},[5487,7068,7069],{"class":6241},"\"*\"",[5487,7071,6885],{"class":6869},[5487,7073,7074,7076,7078,7080,7082,7084,7086,7089,7091,7094],{"class":5489,"line":5722},[5487,7075,7048],{"class":6911},[5487,7077,5211],{"class":6869},[5487,7079,7053],{"class":6233},[5487,7081,7056],{"class":6869},[5487,7083,7059],{"class":6233},[5487,7085,6926],{"class":6869},[5487,7087,7088],{"class":6241},"\"Access-Control-Allow-Methods\"",[5487,7090,5305],{"class":6869},[5487,7092,7093],{"class":6241},"\"GET, OPTIONS\"",[5487,7095,6885],{"class":6869},[5487,7097,7098,7100,7102,7104,7106,7108,7110,7113,7115,7118],{"class":5489,"line":5727},[5487,7099,7048],{"class":6911},[5487,7101,5211],{"class":6869},[5487,7103,7053],{"class":6233},[5487,7105,7056],{"class":6869},[5487,7107,7059],{"class":6233},[5487,7109,6926],{"class":6869},[5487,7111,7112],{"class":6241},"\"Access-Control-Allow-Headers\"",[5487,7114,5305],{"class":6869},[5487,7116,7117],{"class":6241},"\"Content-Type\"",[5487,7119,6885],{"class":6869},[5487,7121,7122],{"class":5489,"line":6007},[5487,7123,7124],{"class":6869},"        \n",[5487,7126,7127,7130,7133,7135,7138,7141,7143,7145,7148],{"class":5489,"line":6013},[5487,7128,7129],{"class":6865},"        if",[5487,7131,7132],{"class":6911}," r",[5487,7134,5211],{"class":6869},[5487,7136,7137],{"class":6911},"Method",[5487,7139,7140],{"class":6869}," == ",[5487,7142,6918],{"class":6911},[5487,7144,5211],{"class":6869},[5487,7146,7147],{"class":6911},"MethodOptions",[5487,7149,7150],{"class":6869}," {\n",[5487,7152,7153,7156,7158,7161,7163,7165,7167,7170],{"class":5489,"line":6019},[5487,7154,7155],{"class":6911},"            w",[5487,7157,5211],{"class":6869},[5487,7159,7160],{"class":6233},"WriteHeader",[5487,7162,6926],{"class":6869},[5487,7164,6918],{"class":6911},[5487,7166,5211],{"class":6869},[5487,7168,7169],{"class":6911},"StatusOK",[5487,7171,6885],{"class":6869},[5487,7173,7174],{"class":5489,"line":6025},[5487,7175,7176],{"class":6865},"            return\n",[5487,7178,7179],{"class":5489,"line":6030},[5487,7180,7181],{"class":6869},"        }\n",[5487,7183,7184],{"class":5489,"line":6035},[5487,7185,7124],{"class":6869},[5487,7187,7189,7192,7194,7197,7199,7201,7203,7205],{"class":5489,"line":7188},26,[5487,7190,7191],{"class":6911},"        fs",[5487,7193,5211],{"class":6869},[5487,7195,7196],{"class":6233},"ServeHTTP",[5487,7198,6926],{"class":6869},[5487,7200,6536],{"class":6911},[5487,7202,5305],{"class":6869},[5487,7204,7030],{"class":6911},[5487,7206,6885],{"class":6869},[5487,7208,7210],{"class":5489,"line":7209},27,[5487,7211,7212],{"class":6869},"    })\n",[5487,7214,7216],{"class":5489,"line":7215},28,[5487,7217,6946],{"class":6869},[5487,7219,7221,7224,7226,7229,7231,7234],{"class":5489,"line":7220},29,[5487,7222,7223],{"class":6911},"    log",[5487,7225,5211],{"class":6869},[5487,7227,7228],{"class":6233},"Println",[5487,7230,6926],{"class":6869},[5487,7232,7233],{"class":6241},"\"HLS streaming server запущено на порту :8080\"",[5487,7235,6885],{"class":6869},[5487,7237,7239,7241,7243,7245,7247,7250],{"class":5489,"line":7238},30,[5487,7240,7223],{"class":6911},[5487,7242,5211],{"class":6869},[5487,7244,7228],{"class":6233},[5487,7246,6926],{"class":6869},[5487,7248,7249],{"class":6241},"\"Головний маніфест доступний за адресою: http:\u002F\u002Flocalhost:8080\u002Fstream\u002Fmaster.m3u8\"",[5487,7251,6885],{"class":6869},[5487,7253,7255],{"class":5489,"line":7254},31,[5487,7256,6946],{"class":6869},[5487,7258,7260,7263,7266,7268,7270,7272,7275,7277,7280,7282,7285,7288,7291,7294,7296],{"class":5489,"line":7259},32,[5487,7261,7262],{"class":6865},"    if",[5487,7264,7265],{"class":6911}," err",[5487,7267,6915],{"class":6869},[5487,7269,6918],{"class":6911},[5487,7271,5211],{"class":6869},[5487,7273,7274],{"class":6233},"ListenAndServe",[5487,7276,6926],{"class":6869},[5487,7278,7279],{"class":6241},"\":8080\"",[5487,7281,5305],{"class":6869},[5487,7283,7284],{"class":6237},"nil",[5487,7286,7287],{"class":6869},"); ",[5487,7289,7290],{"class":6911},"err",[5487,7292,7293],{"class":6869}," != ",[5487,7295,7284],{"class":6237},[5487,7297,7150],{"class":6869},[5487,7299,7301,7304,7306,7309,7311,7314,7316,7319,7321,7323],{"class":5489,"line":7300},33,[5487,7302,7303],{"class":6911},"        log",[5487,7305,5211],{"class":6869},[5487,7307,7308],{"class":6233},"Fatalf",[5487,7310,6926],{"class":6869},[5487,7312,7313],{"class":6241},"\"Помилка запуску сервера: ",[5487,7315,6767],{"class":6911},[5487,7317,7318],{"class":6241},"\"",[5487,7320,5305],{"class":6869},[5487,7322,7290],{"class":6911},[5487,7324,6885],{"class":6869},[5487,7326,7328],{"class":5489,"line":7327},34,[5487,7329,7330],{"class":6869},"    }\n",[5487,7332,7334],{"class":5489,"line":7333},35,[5487,7335,7336],{"class":6869},"}\n",[5299,7338,7339,7340,7342,7343,7345,7346,7349],{},"Цей мінімалістичний сервер виконує роздачу файлів маніфестів (",[5208,7341,5210],{},") та медіасегментів (",[5208,7344,5394],{},") із директорії ",[5208,7347,7348],{},".\u002Fhls_content",". Заголовки CORS дозволяють браузерам завантажувати контент із довільних веб-застосунків, що критично для тестування через різні домени.",[5624,7351,7352,7353,5309,7356,7359,7360,7363],{},"У продакшн-середовищах рекомендується використовувати спеціалізовані вебсервери ",[5235,7354,7355],{},"Nginx",[5235,7357,7358],{},"Caddy"," для роздачі статичних медіафайлів. Вони забезпечують значно вищу продуктивність, вбудовану підтримку кешування, стискання (gzip, Brotli), обмеження швидкості (",[5328,7361,7362],{},"rate limiting",") та інтеграцію з CDN. Go-сервер може залишатися для динамічної генерації маніфестів або проксування запитів до джерел контенту.",[5365,7365,7367],{"id":7366},"крок-3-вдосконалений-сервер-із-динамічною-генерацією-маніфестів","Крок 3. Вдосконалений сервер із динамічною генерацією маніфестів",[5299,7369,7370,7371,7373],{},"Для живих трансляцій або ситуацій, коли медіасегменти генеруються в реальному часі, може знадобитися динамічна побудова медіа-списків. Наступний приклад демонструє генерацію ",[5208,7372,5210],{}," маніфесту на льоту:",[5478,7375,7377],{"className":6843,"code":7376,"language":6845,"meta":5483,"style":5483},"package main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \"net\u002Fhttp\"\n    \"os\"\n    \"path\u002Ffilepath\"\n    \"sort\"\n    \"strings\"\n)\n\nconst (\n    segmentDuration = 10.0 \u002F\u002F Тривалість одного сегменту в секундах\n    baseURL         = \"\u002Fstream\u002F\"\n)\n\n\u002F\u002F generateMediaPlaylist створює медіа-список для конкретного варіанту якості\nfunc generateMediaPlaylist(w http.ResponseWriter, quality string) {\n    segmentsDir := filepath.Join(\".\u002Fhls_content\", quality)\n    \n    \u002F\u002F Зчитуємо список файлів сегментів із директорії\n    files, err := os.ReadDir(segmentsDir)\n    if err != nil {\n        http.Error(w, \"Помилка читання директорії сегментів\", http.StatusInternalServerError)\n        return\n    }\n    \n    \u002F\u002F Фільтруємо та сортуємо .ts файли\n    var segments []string\n    for _, file := range files {\n        if !file.IsDir() && strings.HasSuffix(file.Name(), \".ts\") {\n            segments = append(segments, file.Name())\n        }\n    }\n    sort.Strings(segments)\n    \n    \u002F\u002F Формуємо маніфест у форматі HLS\n    w.Header().Set(\"Content-Type\", \"application\u002Fvnd.apple.mpegurl\")\n    w.Header().Set(\"Access-Control-Allow-Origin\", \"*\")\n    \n    fmt.Fprintf(w, \"#EXTM3U\\n\")\n    fmt.Fprintf(w, \"#EXT-X-VERSION:3\\n\")\n    fmt.Fprintf(w, \"#EXT-X-TARGETDURATION:%d\\n\", int(segmentDuration)+1)\n    fmt.Fprintf(w, \"#EXT-X-MEDIA-SEQUENCE:0\\n\\n\")\n    \n    for _, seg := range segments {\n        fmt.Fprintf(w, \"#EXTINF:%.1f,\\n\", segmentDuration)\n        fmt.Fprintf(w, \"%s\u002F%s\\n\", quality, seg)\n    }\n    \n    fmt.Fprintf(w, \"#EXT-X-ENDLIST\\n\")\n}\n\nfunc main() {\n    \u002F\u002F Роздача статичних медіасегментів\n    fs := http.FileServer(http.Dir(\".\u002Fhls_content\"))\n    http.Handle(\"\u002Fstream\u002F\", http.StripPrefix(\"\u002Fstream\u002F\", fs))\n    \n    \u002F\u002F Динамічна генерація медіа-списків\n    http.HandleFunc(\"\u002Fplaylist\u002F360p.m3u8\", func(w http.ResponseWriter, r *http.Request) {\n        generateMediaPlaylist(w, \"stream_0\")\n    })\n    http.HandleFunc(\"\u002Fplaylist\u002F720p.m3u8\", func(w http.ResponseWriter, r *http.Request) {\n        generateMediaPlaylist(w, \"stream_1\")\n    })\n    http.HandleFunc(\"\u002Fplaylist\u002F1080p.m3u8\", func(w http.ResponseWriter, r *http.Request) {\n        generateMediaPlaylist(w, \"stream_2\")\n    })\n    \n    log.Println(\"Розширений HLS-сервер запущено на :8080\")\n    log.Fatal(http.ListenAndServe(\":8080\", nil))\n}\n",[5208,7378,7379,7385,7389,7395,7400,7404,7408,7413,7418,7423,7428,7432,7436,7443,7458,7469,7473,7477,7482,7509,7534,7538,7543,7569,7581,7611,7616,7620,7624,7629,7643,7666,7708,7734,7738,7742,7759,7764,7770,7795,7818,7823,7850,7874,7916,7941,7946,7966,8001,8040,8045,8050,8074,8079,8084,8093,8099,8126,8157,8162,8168,8210,8227,8232,8274,8290,8295,8337,8353,8358,8363,8379,8407],{"__ignoreMap":5483},[5487,7380,7381,7383],{"class":5489,"line":5490},[5487,7382,6852],{"class":6237},[5487,7384,6856],{"class":6855},[5487,7386,7387],{"class":5489,"line":5496},[5487,7388,5506],{"emptyLinePlaceholder":5505},[5487,7390,7391,7393],{"class":5489,"line":5502},[5487,7392,6866],{"class":6865},[5487,7394,6870],{"class":6869},[5487,7396,7397],{"class":5489,"line":5509},[5487,7398,7399],{"class":6241},"    \"fmt\"\n",[5487,7401,7402],{"class":5489,"line":5515},[5487,7403,6875],{"class":6241},[5487,7405,7406],{"class":5489,"line":5521},[5487,7407,6880],{"class":6241},[5487,7409,7410],{"class":5489,"line":5527},[5487,7411,7412],{"class":6241},"    \"os\"\n",[5487,7414,7415],{"class":5489,"line":5532},[5487,7416,7417],{"class":6241},"    \"path\u002Ffilepath\"\n",[5487,7419,7420],{"class":5489,"line":5538},[5487,7421,7422],{"class":6241},"    \"sort\"\n",[5487,7424,7425],{"class":5489,"line":5544},[5487,7426,7427],{"class":6241},"    \"strings\"\n",[5487,7429,7430],{"class":5489,"line":5550},[5487,7431,6885],{"class":6869},[5487,7433,7434],{"class":5489,"line":5555},[5487,7435,5506],{"emptyLinePlaceholder":5505},[5487,7437,7438,7441],{"class":5489,"line":5561},[5487,7439,7440],{"class":6237},"const",[5487,7442,6870],{"class":6869},[5487,7444,7445,7449,7452,7455],{"class":5489,"line":5567},[5487,7446,7448],{"class":7447},"s-QsJ","    segmentDuration",[5487,7450,7451],{"class":6869}," = ",[5487,7453,7454],{"class":6405},"10.0",[5487,7456,7457],{"class":6905}," \u002F\u002F Тривалість одного сегменту в секундах\n",[5487,7459,7460,7463,7466],{"class":5489,"line":5704},[5487,7461,7462],{"class":7447},"    baseURL",[5487,7464,7465],{"class":6869},"         = ",[5487,7467,7468],{"class":6241},"\"\u002Fstream\u002F\"\n",[5487,7470,7471],{"class":5489,"line":5710},[5487,7472,6885],{"class":6869},[5487,7474,7475],{"class":5489,"line":5716},[5487,7476,5506],{"emptyLinePlaceholder":5505},[5487,7478,7479],{"class":5489,"line":5722},[5487,7480,7481],{"class":6905},"\u002F\u002F generateMediaPlaylist створює медіа-список для конкретного варіанту якості\n",[5487,7483,7484,7486,7489,7491,7493,7495,7497,7499,7501,7504,7507],{"class":5489,"line":5727},[5487,7485,6894],{"class":6237},[5487,7487,7488],{"class":6233}," generateMediaPlaylist",[5487,7490,6926],{"class":6869},[5487,7492,6536],{"class":6911},[5487,7494,7020],{"class":6855},[5487,7496,5211],{"class":6869},[5487,7498,7025],{"class":6855},[5487,7500,5305],{"class":6869},[5487,7502,7503],{"class":6911},"quality",[5487,7505,7506],{"class":6855}," string",[5487,7508,7043],{"class":6869},[5487,7510,7511,7514,7516,7519,7521,7524,7526,7528,7530,7532],{"class":5489,"line":6007},[5487,7512,7513],{"class":6911},"    segmentsDir",[5487,7515,6915],{"class":6869},[5487,7517,7518],{"class":6911},"filepath",[5487,7520,5211],{"class":6869},[5487,7522,7523],{"class":6233},"Join",[5487,7525,6926],{"class":6869},[5487,7527,6938],{"class":6241},[5487,7529,5305],{"class":6869},[5487,7531,7503],{"class":6911},[5487,7533,6885],{"class":6869},[5487,7535,7536],{"class":5489,"line":6013},[5487,7537,6946],{"class":6869},[5487,7539,7540],{"class":5489,"line":6019},[5487,7541,7542],{"class":6905},"    \u002F\u002F Зчитуємо список файлів сегментів із директорії\n",[5487,7544,7545,7548,7550,7552,7554,7557,7559,7562,7564,7567],{"class":5489,"line":6025},[5487,7546,7547],{"class":6911},"    files",[5487,7549,5305],{"class":6869},[5487,7551,7290],{"class":6911},[5487,7553,6915],{"class":6869},[5487,7555,7556],{"class":6911},"os",[5487,7558,5211],{"class":6869},[5487,7560,7561],{"class":6233},"ReadDir",[5487,7563,6926],{"class":6869},[5487,7565,7566],{"class":6911},"segmentsDir",[5487,7568,6885],{"class":6869},[5487,7570,7571,7573,7575,7577,7579],{"class":5489,"line":6030},[5487,7572,7262],{"class":6865},[5487,7574,7265],{"class":6911},[5487,7576,7293],{"class":6869},[5487,7578,7284],{"class":6237},[5487,7580,7150],{"class":6869},[5487,7582,7583,7586,7588,7591,7593,7595,7597,7600,7602,7604,7606,7609],{"class":5489,"line":6035},[5487,7584,7585],{"class":6911},"        http",[5487,7587,5211],{"class":6869},[5487,7589,7590],{"class":6233},"Error",[5487,7592,6926],{"class":6869},[5487,7594,6536],{"class":6911},[5487,7596,5305],{"class":6869},[5487,7598,7599],{"class":6241},"\"Помилка читання директорії сегментів\"",[5487,7601,5305],{"class":6869},[5487,7603,6918],{"class":6911},[5487,7605,5211],{"class":6869},[5487,7607,7608],{"class":6911},"StatusInternalServerError",[5487,7610,6885],{"class":6869},[5487,7612,7613],{"class":5489,"line":7188},[5487,7614,7615],{"class":6865},"        return\n",[5487,7617,7618],{"class":5489,"line":7209},[5487,7619,7330],{"class":6869},[5487,7621,7622],{"class":5489,"line":7215},[5487,7623,6946],{"class":6869},[5487,7625,7626],{"class":5489,"line":7220},[5487,7627,7628],{"class":6905},"    \u002F\u002F Фільтруємо та сортуємо .ts файли\n",[5487,7630,7631,7634,7637,7640],{"class":5489,"line":7238},[5487,7632,7633],{"class":6237},"    var",[5487,7635,7636],{"class":6911}," segments",[5487,7638,7639],{"class":6869}," []",[5487,7641,7642],{"class":6855},"string\n",[5487,7644,7645,7648,7651,7653,7656,7658,7661,7664],{"class":5489,"line":7254},[5487,7646,7647],{"class":6865},"    for",[5487,7649,7650],{"class":6911}," _",[5487,7652,5305],{"class":6869},[5487,7654,7655],{"class":6911},"file",[5487,7657,6915],{"class":6869},[5487,7659,7660],{"class":6865},"range",[5487,7662,7663],{"class":6911}," files",[5487,7665,7150],{"class":6869},[5487,7667,7668,7670,7673,7675,7677,7680,7683,7686,7688,7691,7693,7695,7697,7700,7703,7706],{"class":5489,"line":7259},[5487,7669,7129],{"class":6865},[5487,7671,7672],{"class":6869}," !",[5487,7674,7655],{"class":6911},[5487,7676,5211],{"class":6869},[5487,7678,7679],{"class":6233},"IsDir",[5487,7681,7682],{"class":6869},"() && ",[5487,7684,7685],{"class":6911},"strings",[5487,7687,5211],{"class":6869},[5487,7689,7690],{"class":6233},"HasSuffix",[5487,7692,6926],{"class":6869},[5487,7694,7655],{"class":6911},[5487,7696,5211],{"class":6869},[5487,7698,7699],{"class":6233},"Name",[5487,7701,7702],{"class":6869},"(), ",[5487,7704,7705],{"class":6241},"\".ts\"",[5487,7707,7043],{"class":6869},[5487,7709,7710,7713,7715,7718,7720,7723,7725,7727,7729,7731],{"class":5489,"line":7300},[5487,7711,7712],{"class":6911},"            segments",[5487,7714,7451],{"class":6869},[5487,7716,7717],{"class":6233},"append",[5487,7719,6926],{"class":6869},[5487,7721,7722],{"class":6911},"segments",[5487,7724,5305],{"class":6869},[5487,7726,7655],{"class":6911},[5487,7728,5211],{"class":6869},[5487,7730,7699],{"class":6233},[5487,7732,7733],{"class":6869},"())\n",[5487,7735,7736],{"class":5489,"line":7327},[5487,7737,7181],{"class":6869},[5487,7739,7740],{"class":5489,"line":7333},[5487,7741,7330],{"class":6869},[5487,7743,7745,7748,7750,7753,7755,7757],{"class":5489,"line":7744},36,[5487,7746,7747],{"class":6911},"    sort",[5487,7749,5211],{"class":6869},[5487,7751,7752],{"class":6233},"Strings",[5487,7754,6926],{"class":6869},[5487,7756,7722],{"class":6911},[5487,7758,6885],{"class":6869},[5487,7760,7762],{"class":5489,"line":7761},37,[5487,7763,6946],{"class":6869},[5487,7765,7767],{"class":5489,"line":7766},38,[5487,7768,7769],{"class":6905},"    \u002F\u002F Формуємо маніфест у форматі HLS\n",[5487,7771,7773,7776,7778,7780,7782,7784,7786,7788,7790,7793],{"class":5489,"line":7772},39,[5487,7774,7775],{"class":6911},"    w",[5487,7777,5211],{"class":6869},[5487,7779,7053],{"class":6233},[5487,7781,7056],{"class":6869},[5487,7783,7059],{"class":6233},[5487,7785,6926],{"class":6869},[5487,7787,7117],{"class":6241},[5487,7789,5305],{"class":6869},[5487,7791,7792],{"class":6241},"\"application\u002Fvnd.apple.mpegurl\"",[5487,7794,6885],{"class":6869},[5487,7796,7798,7800,7802,7804,7806,7808,7810,7812,7814,7816],{"class":5489,"line":7797},40,[5487,7799,7775],{"class":6911},[5487,7801,5211],{"class":6869},[5487,7803,7053],{"class":6233},[5487,7805,7056],{"class":6869},[5487,7807,7059],{"class":6233},[5487,7809,6926],{"class":6869},[5487,7811,7064],{"class":6241},[5487,7813,5305],{"class":6869},[5487,7815,7069],{"class":6241},[5487,7817,6885],{"class":6869},[5487,7819,7821],{"class":5489,"line":7820},41,[5487,7822,6946],{"class":6869},[5487,7824,7826,7829,7831,7834,7836,7838,7840,7843,7846,7848],{"class":5489,"line":7825},42,[5487,7827,7828],{"class":6911},"    fmt",[5487,7830,5211],{"class":6869},[5487,7832,7833],{"class":6233},"Fprintf",[5487,7835,6926],{"class":6869},[5487,7837,6536],{"class":6911},[5487,7839,5305],{"class":6869},[5487,7841,7842],{"class":6241},"\"#EXTM3U",[5487,7844,7845],{"class":6245},"\\n",[5487,7847,7318],{"class":6241},[5487,7849,6885],{"class":6869},[5487,7851,7853,7855,7857,7859,7861,7863,7865,7868,7870,7872],{"class":5489,"line":7852},43,[5487,7854,7828],{"class":6911},[5487,7856,5211],{"class":6869},[5487,7858,7833],{"class":6233},[5487,7860,6926],{"class":6869},[5487,7862,6536],{"class":6911},[5487,7864,5305],{"class":6869},[5487,7866,7867],{"class":6241},"\"#EXT-X-VERSION:3",[5487,7869,7845],{"class":6245},[5487,7871,7318],{"class":6241},[5487,7873,6885],{"class":6869},[5487,7875,7877,7879,7881,7883,7885,7887,7889,7892,7895,7897,7899,7901,7904,7906,7909,7912,7914],{"class":5489,"line":7876},44,[5487,7878,7828],{"class":6911},[5487,7880,5211],{"class":6869},[5487,7882,7833],{"class":6233},[5487,7884,6926],{"class":6869},[5487,7886,6536],{"class":6911},[5487,7888,5305],{"class":6869},[5487,7890,7891],{"class":6241},"\"#EXT-X-TARGETDURATION:",[5487,7893,7894],{"class":6911},"%d",[5487,7896,7845],{"class":6245},[5487,7898,7318],{"class":6241},[5487,7900,5305],{"class":6869},[5487,7902,7903],{"class":6855},"int",[5487,7905,6926],{"class":6869},[5487,7907,7908],{"class":6911},"segmentDuration",[5487,7910,7911],{"class":6869},")+",[5487,7913,6702],{"class":6405},[5487,7915,6885],{"class":6869},[5487,7917,7919,7921,7923,7925,7927,7929,7931,7934,7937,7939],{"class":5489,"line":7918},45,[5487,7920,7828],{"class":6911},[5487,7922,5211],{"class":6869},[5487,7924,7833],{"class":6233},[5487,7926,6926],{"class":6869},[5487,7928,6536],{"class":6911},[5487,7930,5305],{"class":6869},[5487,7932,7933],{"class":6241},"\"#EXT-X-MEDIA-SEQUENCE:0",[5487,7935,7936],{"class":6245},"\\n\\n",[5487,7938,7318],{"class":6241},[5487,7940,6885],{"class":6869},[5487,7942,7944],{"class":5489,"line":7943},46,[5487,7945,6946],{"class":6869},[5487,7947,7949,7951,7953,7955,7958,7960,7962,7964],{"class":5489,"line":7948},47,[5487,7950,7647],{"class":6865},[5487,7952,7650],{"class":6911},[5487,7954,5305],{"class":6869},[5487,7956,7957],{"class":6911},"seg",[5487,7959,6915],{"class":6869},[5487,7961,7660],{"class":6865},[5487,7963,7636],{"class":6911},[5487,7965,7150],{"class":6869},[5487,7967,7969,7972,7974,7976,7978,7980,7982,7985,7988,7991,7993,7995,7997,7999],{"class":5489,"line":7968},48,[5487,7970,7971],{"class":6911},"        fmt",[5487,7973,5211],{"class":6869},[5487,7975,7833],{"class":6233},[5487,7977,6926],{"class":6869},[5487,7979,6536],{"class":6911},[5487,7981,5305],{"class":6869},[5487,7983,7984],{"class":6241},"\"#EXTINF:",[5487,7986,7987],{"class":6911},"%.1f",[5487,7989,7990],{"class":6241},",",[5487,7992,7845],{"class":6245},[5487,7994,7318],{"class":6241},[5487,7996,5305],{"class":6869},[5487,7998,7908],{"class":6911},[5487,8000,6885],{"class":6869},[5487,8002,8004,8006,8008,8010,8012,8014,8016,8018,8021,8024,8026,8028,8030,8032,8034,8036,8038],{"class":5489,"line":8003},49,[5487,8005,7971],{"class":6911},[5487,8007,5211],{"class":6869},[5487,8009,7833],{"class":6233},[5487,8011,6926],{"class":6869},[5487,8013,6536],{"class":6911},[5487,8015,5305],{"class":6869},[5487,8017,7318],{"class":6241},[5487,8019,8020],{"class":6911},"%s",[5487,8022,8023],{"class":6241},"\u002F",[5487,8025,8020],{"class":6911},[5487,8027,7845],{"class":6245},[5487,8029,7318],{"class":6241},[5487,8031,5305],{"class":6869},[5487,8033,7503],{"class":6911},[5487,8035,5305],{"class":6869},[5487,8037,7957],{"class":6911},[5487,8039,6885],{"class":6869},[5487,8041,8043],{"class":5489,"line":8042},50,[5487,8044,7330],{"class":6869},[5487,8046,8048],{"class":5489,"line":8047},51,[5487,8049,6946],{"class":6869},[5487,8051,8053,8055,8057,8059,8061,8063,8065,8068,8070,8072],{"class":5489,"line":8052},52,[5487,8054,7828],{"class":6911},[5487,8056,5211],{"class":6869},[5487,8058,7833],{"class":6233},[5487,8060,6926],{"class":6869},[5487,8062,6536],{"class":6911},[5487,8064,5305],{"class":6869},[5487,8066,8067],{"class":6241},"\"#EXT-X-ENDLIST",[5487,8069,7845],{"class":6245},[5487,8071,7318],{"class":6241},[5487,8073,6885],{"class":6869},[5487,8075,8077],{"class":5489,"line":8076},53,[5487,8078,7336],{"class":6869},[5487,8080,8082],{"class":5489,"line":8081},54,[5487,8083,5506],{"emptyLinePlaceholder":5505},[5487,8085,8087,8089,8091],{"class":5489,"line":8086},55,[5487,8088,6894],{"class":6237},[5487,8090,6897],{"class":6233},[5487,8092,6900],{"class":6869},[5487,8094,8096],{"class":5489,"line":8095},56,[5487,8097,8098],{"class":6905},"    \u002F\u002F Роздача статичних медіасегментів\n",[5487,8100,8102,8104,8106,8108,8110,8112,8114,8116,8118,8120,8122,8124],{"class":5489,"line":8101},57,[5487,8103,6912],{"class":6911},[5487,8105,6915],{"class":6869},[5487,8107,6918],{"class":6911},[5487,8109,5211],{"class":6869},[5487,8111,6923],{"class":6233},[5487,8113,6926],{"class":6869},[5487,8115,6918],{"class":6911},[5487,8117,5211],{"class":6869},[5487,8119,6933],{"class":6233},[5487,8121,6926],{"class":6869},[5487,8123,6938],{"class":6241},[5487,8125,6941],{"class":6869},[5487,8127,8129,8131,8133,8135,8137,8139,8141,8143,8145,8147,8149,8151,8153,8155],{"class":5489,"line":8128},58,[5487,8130,6956],{"class":6911},[5487,8132,5211],{"class":6869},[5487,8134,6961],{"class":6233},[5487,8136,6926],{"class":6869},[5487,8138,6966],{"class":6241},[5487,8140,5305],{"class":6869},[5487,8142,6918],{"class":6911},[5487,8144,5211],{"class":6869},[5487,8146,6975],{"class":6233},[5487,8148,6926],{"class":6869},[5487,8150,6966],{"class":6241},[5487,8152,5305],{"class":6869},[5487,8154,6984],{"class":6911},[5487,8156,6941],{"class":6869},[5487,8158,8160],{"class":5489,"line":8159},59,[5487,8161,6946],{"class":6869},[5487,8163,8165],{"class":5489,"line":8164},60,[5487,8166,8167],{"class":6905},"    \u002F\u002F Динамічна генерація медіа-списків\n",[5487,8169,8171,8173,8175,8177,8179,8182,8184,8186,8188,8190,8192,8194,8196,8198,8200,8202,8204,8206,8208],{"class":5489,"line":8170},61,[5487,8172,6956],{"class":6911},[5487,8174,5211],{"class":6869},[5487,8176,7004],{"class":6233},[5487,8178,6926],{"class":6869},[5487,8180,8181],{"class":6241},"\"\u002Fplaylist\u002F360p.m3u8\"",[5487,8183,5305],{"class":6869},[5487,8185,6894],{"class":6237},[5487,8187,6926],{"class":6869},[5487,8189,6536],{"class":6911},[5487,8191,7020],{"class":6855},[5487,8193,5211],{"class":6869},[5487,8195,7025],{"class":6855},[5487,8197,5305],{"class":6869},[5487,8199,7030],{"class":6911},[5487,8201,7033],{"class":6869},[5487,8203,6918],{"class":6855},[5487,8205,5211],{"class":6869},[5487,8207,7040],{"class":6855},[5487,8209,7043],{"class":6869},[5487,8211,8213,8216,8218,8220,8222,8225],{"class":5489,"line":8212},62,[5487,8214,8215],{"class":6233},"        generateMediaPlaylist",[5487,8217,6926],{"class":6869},[5487,8219,6536],{"class":6911},[5487,8221,5305],{"class":6869},[5487,8223,8224],{"class":6241},"\"stream_0\"",[5487,8226,6885],{"class":6869},[5487,8228,8230],{"class":5489,"line":8229},63,[5487,8231,7212],{"class":6869},[5487,8233,8235,8237,8239,8241,8243,8246,8248,8250,8252,8254,8256,8258,8260,8262,8264,8266,8268,8270,8272],{"class":5489,"line":8234},64,[5487,8236,6956],{"class":6911},[5487,8238,5211],{"class":6869},[5487,8240,7004],{"class":6233},[5487,8242,6926],{"class":6869},[5487,8244,8245],{"class":6241},"\"\u002Fplaylist\u002F720p.m3u8\"",[5487,8247,5305],{"class":6869},[5487,8249,6894],{"class":6237},[5487,8251,6926],{"class":6869},[5487,8253,6536],{"class":6911},[5487,8255,7020],{"class":6855},[5487,8257,5211],{"class":6869},[5487,8259,7025],{"class":6855},[5487,8261,5305],{"class":6869},[5487,8263,7030],{"class":6911},[5487,8265,7033],{"class":6869},[5487,8267,6918],{"class":6855},[5487,8269,5211],{"class":6869},[5487,8271,7040],{"class":6855},[5487,8273,7043],{"class":6869},[5487,8275,8277,8279,8281,8283,8285,8288],{"class":5489,"line":8276},65,[5487,8278,8215],{"class":6233},[5487,8280,6926],{"class":6869},[5487,8282,6536],{"class":6911},[5487,8284,5305],{"class":6869},[5487,8286,8287],{"class":6241},"\"stream_1\"",[5487,8289,6885],{"class":6869},[5487,8291,8293],{"class":5489,"line":8292},66,[5487,8294,7212],{"class":6869},[5487,8296,8298,8300,8302,8304,8306,8309,8311,8313,8315,8317,8319,8321,8323,8325,8327,8329,8331,8333,8335],{"class":5489,"line":8297},67,[5487,8299,6956],{"class":6911},[5487,8301,5211],{"class":6869},[5487,8303,7004],{"class":6233},[5487,8305,6926],{"class":6869},[5487,8307,8308],{"class":6241},"\"\u002Fplaylist\u002F1080p.m3u8\"",[5487,8310,5305],{"class":6869},[5487,8312,6894],{"class":6237},[5487,8314,6926],{"class":6869},[5487,8316,6536],{"class":6911},[5487,8318,7020],{"class":6855},[5487,8320,5211],{"class":6869},[5487,8322,7025],{"class":6855},[5487,8324,5305],{"class":6869},[5487,8326,7030],{"class":6911},[5487,8328,7033],{"class":6869},[5487,8330,6918],{"class":6855},[5487,8332,5211],{"class":6869},[5487,8334,7040],{"class":6855},[5487,8336,7043],{"class":6869},[5487,8338,8340,8342,8344,8346,8348,8351],{"class":5489,"line":8339},68,[5487,8341,8215],{"class":6233},[5487,8343,6926],{"class":6869},[5487,8345,6536],{"class":6911},[5487,8347,5305],{"class":6869},[5487,8349,8350],{"class":6241},"\"stream_2\"",[5487,8352,6885],{"class":6869},[5487,8354,8356],{"class":5489,"line":8355},69,[5487,8357,7212],{"class":6869},[5487,8359,8361],{"class":5489,"line":8360},70,[5487,8362,6946],{"class":6869},[5487,8364,8366,8368,8370,8372,8374,8377],{"class":5489,"line":8365},71,[5487,8367,7223],{"class":6911},[5487,8369,5211],{"class":6869},[5487,8371,7228],{"class":6233},[5487,8373,6926],{"class":6869},[5487,8375,8376],{"class":6241},"\"Розширений HLS-сервер запущено на :8080\"",[5487,8378,6885],{"class":6869},[5487,8380,8382,8384,8386,8389,8391,8393,8395,8397,8399,8401,8403,8405],{"class":5489,"line":8381},72,[5487,8383,7223],{"class":6911},[5487,8385,5211],{"class":6869},[5487,8387,8388],{"class":6233},"Fatal",[5487,8390,6926],{"class":6869},[5487,8392,6918],{"class":6911},[5487,8394,5211],{"class":6869},[5487,8396,7274],{"class":6233},[5487,8398,6926],{"class":6869},[5487,8400,7279],{"class":6241},[5487,8402,5305],{"class":6869},[5487,8404,7284],{"class":6237},[5487,8406,6941],{"class":6869},[5487,8408,8410],{"class":5489,"line":8409},73,[5487,8411,7336],{"class":6869},[5299,8413,8414,8415,8418],{},"У цій реалізації функція ",[5208,8416,8417],{},"generateMediaPlaylist"," сканує директорію з медіасегментами та динамічно формує маніфест із актуальним списком доступних фрагментів. Це дозволяє додавати нові сегменти без перезапуску сервера — підхід, критичний для живих трансляцій.",[5333,8420,8421,8422,8425,8426,8428],{},"Для справжніх систем live streaming необхідно реалізувати логіку ",[5235,8423,8424],{},"ковзного вікна"," (sliding window): після додавання нового сегменту найстаріші видаляються зі списку, а значення ",[5208,8427,5758],{}," збільшується. Програвач періодично перезавантажує маніфест (зазвичай раз на тривалість одного сегменту) та завантажує нові фрагменти у міру їх появи. Затримка між реальною подією та її відображенням у програвача становить приблизно 2–3 тривалості сегменту (наприклад, 20–30 секунд при 10-секундних сегментах).",[5291,8430],{},[5294,8432,8434],{"id":8433},"dash-альтернативний-стандарт-адаптивного-стрімінгу","DASH: альтернативний стандарт адаптивного стрімінгу",[5299,8436,5355,8437,8440,8441,8444],{},[5235,8438,8439],{},"DASH (Dynamic Adaptive Streaming over HTTP)",", стандартизований консорціумом MPEG як ",[5235,8442,8443],{},"ISO\u002FIEC 23009-1",", є відкритою та незалежною від виробника альтернативою HLS. На відміну від HLS, який історично був зв'язаний з екосистемою Apple, DASH був розроблений як міжнародний стандарт із підтримкою провідних технологічних компаній (Google, Microsoft, Netflix, Adobe).",[5365,8446,8448],{"id":8447},"ключові-відмінності-між-hls-та-dash","Ключові відмінності між HLS та DASH",[8450,8451,8452,8468],"table",{},[8453,8454,8455],"thead",{},[8456,8457,8458,8462,8465],"tr",{},[8459,8460,8461],"th",{},"Аспект",[8459,8463,8464],{},"HLS (Apple)",[8459,8466,8467],{},"DASH (MPEG)",[8469,8470,8471,8492,8513,8526,8539],"tbody",{},[8456,8472,8473,8479,8485],{},[8474,8475,8476],"td",{},[5235,8477,8478],{},"Формат маніфесту",[8474,8480,8481,8482,8484],{},"Текстовий файл ",[5208,8483,5210],{}," (розширений M3U)",[8474,8486,8487,8488,8491],{},"XML-документ ",[5208,8489,8490],{},".mpd"," (Media Presentation Description)",[8456,8493,8494,8499,8507],{},[8474,8495,8496],{},[5235,8497,8498],{},"Формат сегментів",[8474,8500,8501,8502,8504,8505,5395],{},"MPEG-2 TS (",[5208,8503,5394],{},") або fMP4 (",[5208,8506,5402],{},[8474,8508,8509,8510,8512],{},"Переважно fMP4 (",[5208,8511,5402],{},"), також підтримка WebM",[8456,8514,8515,8520,8523],{},[8474,8516,8517],{},[5235,8518,8519],{},"Стандартизація",[8474,8521,8522],{},"RFC 8216 (IETF), контрольований Apple",[8474,8524,8525],{},"ISO\u002FIEC 23009-1, відкритий консорціум MPEG",[8456,8527,8528,8533,8536],{},[8474,8529,8530],{},[5235,8531,8532],{},"Підтримка браузерів",[8474,8534,8535],{},"Нативна в Safari\u002FiOS, через бібліотеки hls.js у інших",[8474,8537,8538],{},"Нативна через Media Source Extensions (MSE)",[8456,8540,8541,8546,8549],{},[8474,8542,8543],{},[5235,8544,8545],{},"Шифрування контенту",[8474,8547,8548],{},"AES-128, Sample-AES, FairPlay DRM",[8474,8550,8551],{},"MPEG-CENC (Common Encryption), Widevine, PlayReady",[5365,8553,8555],{"id":8554},"архітектура-dash-та-формат-mpd","Архітектура DASH та формат MPD",[5299,8557,8558,8559,8562],{},"Центральним елементом DASH є ",[5235,8560,8561],{},"Media Presentation Description (MPD)"," — XML-файл, що описує структуру медіапрезентації. MPD містить ієрархію елементів:",[5299,8564,8565,8568,8570],{},[5235,8566,8567],{},"1. Period (Період).",[5378,8569],{},"\nЧасовий інтервал презентації. Одна презентація може складатися з декількох періодів (наприклад, основний контент та рекламні вставки).",[5299,8572,8573,8576,8578],{},[5235,8574,8575],{},"2. AdaptationSet (Набір адаптації).",[5378,8577],{},"\nГрупа представлень одного типу медіа (відео, аудіо, субтитри) на різних рівнях якості. Клієнт може перемикатися між представленнями всередині одного набору адаптації.",[5299,8580,8581,8584,8586],{},[5235,8582,8583],{},"3. Representation (Представлення).",[5378,8585],{},"\nКонкретний варіант кодування (роздільна здатність, бітрейт, кодек). Кожне представлення містить посилання на список сегментів через шаблон URL або явний перелік.",[5299,8588,8589,8592,8594],{},[5235,8590,8591],{},"4. Segment (Сегмент).",[5378,8593],{},"\nОкремий файл медіаданих фіксованої або змінної тривалості, зазвичай у форматі Fragmented MP4.",[5470,8596,8598],{"id":8597},"приклад-спрощеного-mpd-маніфесту","Приклад спрощеного MPD-маніфесту",[5478,8600,8604],{"className":8601,"code":8602,"language":8603,"meta":5483,"style":5483},"language-xml shiki shiki-themes light-plus dark-plus dark-plus","\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003CMPD xmlns=\"urn:mpeg:dash:schema:mpd:2011\" \n     type=\"static\" \n     mediaPresentationDuration=\"PT10M30S\"\n     minBufferTime=\"PT5S\">\n  \n  \u003CPeriod id=\"0\" start=\"PT0S\">\n    \u003C!-- Відео AdaptationSet -->\n    \u003CAdaptationSet mimeType=\"video\u002Fmp4\" codecs=\"avc1.4d401f\" \n                   width=\"1920\" height=\"1080\" frameRate=\"30\">\n      \n      \u003C!-- Представлення 1080p, 5 Mbps -->\n      \u003CRepresentation id=\"video_1080p\" bandwidth=\"5000000\">\n        \u003CSegmentTemplate timescale=\"1000\" duration=\"10000\" \n                         initialization=\"init_1080p.mp4\" \n                         media=\"segment_1080p_$Number$.m4s\" \n                         startNumber=\"1\"\u002F>\n      \u003C\u002FRepresentation>\n      \n      \u003C!-- Представлення 720p, 2 Mbps -->\n      \u003CRepresentation id=\"video_720p\" bandwidth=\"2000000\" \n                      width=\"1280\" height=\"720\">\n        \u003CSegmentTemplate timescale=\"1000\" duration=\"10000\" \n                         initialization=\"init_720p.mp4\" \n                         media=\"segment_720p_$Number$.m4s\" \n                         startNumber=\"1\"\u002F>\n      \u003C\u002FRepresentation>\n      \n    \u003C\u002FAdaptationSet>\n    \n    \u003C!-- Аудіо AdaptationSet -->\n    \u003CAdaptationSet mimeType=\"audio\u002Fmp4\" codecs=\"mp4a.40.2\" lang=\"uk\">\n      \u003CRepresentation id=\"audio\" bandwidth=\"128000\">\n        \u003CSegmentTemplate timescale=\"1000\" duration=\"10000\" \n                         initialization=\"init_audio.mp4\" \n                         media=\"segment_audio_$Number$.m4s\" \n                         startNumber=\"1\"\u002F>\n      \u003C\u002FRepresentation>\n    \u003C\u002FAdaptationSet>\n    \n  \u003C\u002FPeriod>\n\u003C\u002FMPD>\n","xml",[5208,8605,8606,8637,8656,8668,8678,8691,8696,8722,8727,8753,8781,8786,8791,8816,8842,8854,8866,8879,8888,8892,8897,8919,8938,8958,8969,8980,8990,8998,9002,9011,9015,9020,9050,9072,9092,9103,9114,9124,9132,9140,9144,9153],{"__ignoreMap":5483},[5487,8607,8608,8612,8615,8619,8622,8626,8629,8631,8634],{"class":5489,"line":5490},[5487,8609,8611],{"class":8610},"s0P7L","\u003C?",[5487,8613,8603],{"class":8614},"sKtos",[5487,8616,8618],{"class":8617},"sa4r_"," version",[5487,8620,8621],{"class":6869},"=",[5487,8623,8625],{"class":8624},"su9tN","\"1.0\"",[5487,8627,8628],{"class":8617}," encoding",[5487,8630,8621],{"class":6869},[5487,8632,8633],{"class":8624},"\"UTF-8\"",[5487,8635,8636],{"class":8610},"?>\n",[5487,8638,8639,8642,8645,8648,8650,8653],{"class":5489,"line":5496},[5487,8640,8641],{"class":8610},"\u003C",[5487,8643,8644],{"class":8614},"MPD",[5487,8646,8647],{"class":8617}," xmlns",[5487,8649,8621],{"class":6869},[5487,8651,8652],{"class":8624},"\"urn:mpeg:dash:schema:mpd:2011\"",[5487,8654,8655],{"class":6869}," \n",[5487,8657,8658,8661,8663,8666],{"class":5489,"line":5502},[5487,8659,8660],{"class":8617},"     type",[5487,8662,8621],{"class":6869},[5487,8664,8665],{"class":8624},"\"static\"",[5487,8667,8655],{"class":6869},[5487,8669,8670,8673,8675],{"class":5489,"line":5509},[5487,8671,8672],{"class":8617},"     mediaPresentationDuration",[5487,8674,8621],{"class":6869},[5487,8676,8677],{"class":8624},"\"PT10M30S\"\n",[5487,8679,8680,8683,8685,8688],{"class":5489,"line":5515},[5487,8681,8682],{"class":8617},"     minBufferTime",[5487,8684,8621],{"class":6869},[5487,8686,8687],{"class":8624},"\"PT5S\"",[5487,8689,8690],{"class":8610},">\n",[5487,8692,8693],{"class":5489,"line":5521},[5487,8694,8695],{"class":6869},"  \n",[5487,8697,8698,8701,8704,8707,8709,8712,8715,8717,8720],{"class":5489,"line":5527},[5487,8699,8700],{"class":8610},"  \u003C",[5487,8702,8703],{"class":8614},"Period",[5487,8705,8706],{"class":8617}," id",[5487,8708,8621],{"class":6869},[5487,8710,8711],{"class":8624},"\"0\"",[5487,8713,8714],{"class":8617}," start",[5487,8716,8621],{"class":6869},[5487,8718,8719],{"class":8624},"\"PT0S\"",[5487,8721,8690],{"class":8610},[5487,8723,8724],{"class":5489,"line":5532},[5487,8725,8726],{"class":6905},"    \u003C!-- Відео AdaptationSet -->\n",[5487,8728,8729,8732,8735,8738,8740,8743,8746,8748,8751],{"class":5489,"line":5538},[5487,8730,8731],{"class":8610},"    \u003C",[5487,8733,8734],{"class":8614},"AdaptationSet",[5487,8736,8737],{"class":8617}," mimeType",[5487,8739,8621],{"class":6869},[5487,8741,8742],{"class":8624},"\"video\u002Fmp4\"",[5487,8744,8745],{"class":8617}," codecs",[5487,8747,8621],{"class":6869},[5487,8749,8750],{"class":8624},"\"avc1.4d401f\"",[5487,8752,8655],{"class":6869},[5487,8754,8755,8758,8760,8763,8766,8768,8771,8774,8776,8779],{"class":5489,"line":5544},[5487,8756,8757],{"class":8617},"                   width",[5487,8759,8621],{"class":6869},[5487,8761,8762],{"class":8624},"\"1920\"",[5487,8764,8765],{"class":8617}," height",[5487,8767,8621],{"class":6869},[5487,8769,8770],{"class":8624},"\"1080\"",[5487,8772,8773],{"class":8617}," frameRate",[5487,8775,8621],{"class":6869},[5487,8777,8778],{"class":8624},"\"30\"",[5487,8780,8690],{"class":8610},[5487,8782,8783],{"class":5489,"line":5550},[5487,8784,8785],{"class":6869},"      \n",[5487,8787,8788],{"class":5489,"line":5555},[5487,8789,8790],{"class":6905},"      \u003C!-- Представлення 1080p, 5 Mbps -->\n",[5487,8792,8793,8796,8799,8801,8803,8806,8809,8811,8814],{"class":5489,"line":5561},[5487,8794,8795],{"class":8610},"      \u003C",[5487,8797,8798],{"class":8614},"Representation",[5487,8800,8706],{"class":8617},[5487,8802,8621],{"class":6869},[5487,8804,8805],{"class":8624},"\"video_1080p\"",[5487,8807,8808],{"class":8617}," bandwidth",[5487,8810,8621],{"class":6869},[5487,8812,8813],{"class":8624},"\"5000000\"",[5487,8815,8690],{"class":8610},[5487,8817,8818,8821,8824,8827,8829,8832,8835,8837,8840],{"class":5489,"line":5567},[5487,8819,8820],{"class":8610},"        \u003C",[5487,8822,8823],{"class":8614},"SegmentTemplate",[5487,8825,8826],{"class":8617}," timescale",[5487,8828,8621],{"class":6869},[5487,8830,8831],{"class":8624},"\"1000\"",[5487,8833,8834],{"class":8617}," duration",[5487,8836,8621],{"class":6869},[5487,8838,8839],{"class":8624},"\"10000\"",[5487,8841,8655],{"class":6869},[5487,8843,8844,8847,8849,8852],{"class":5489,"line":5704},[5487,8845,8846],{"class":8617},"                         initialization",[5487,8848,8621],{"class":6869},[5487,8850,8851],{"class":8624},"\"init_1080p.mp4\"",[5487,8853,8655],{"class":6869},[5487,8855,8856,8859,8861,8864],{"class":5489,"line":5710},[5487,8857,8858],{"class":8617},"                         media",[5487,8860,8621],{"class":6869},[5487,8862,8863],{"class":8624},"\"segment_1080p_$Number$.m4s\"",[5487,8865,8655],{"class":6869},[5487,8867,8868,8871,8873,8876],{"class":5489,"line":5716},[5487,8869,8870],{"class":8617},"                         startNumber",[5487,8872,8621],{"class":6869},[5487,8874,8875],{"class":8624},"\"1\"",[5487,8877,8878],{"class":8610},"\u002F>\n",[5487,8880,8881,8884,8886],{"class":5489,"line":5722},[5487,8882,8883],{"class":8610},"      \u003C\u002F",[5487,8885,8798],{"class":8614},[5487,8887,8690],{"class":8610},[5487,8889,8890],{"class":5489,"line":5727},[5487,8891,8785],{"class":6869},[5487,8893,8894],{"class":5489,"line":6007},[5487,8895,8896],{"class":6905},"      \u003C!-- Представлення 720p, 2 Mbps -->\n",[5487,8898,8899,8901,8903,8905,8907,8910,8912,8914,8917],{"class":5489,"line":6013},[5487,8900,8795],{"class":8610},[5487,8902,8798],{"class":8614},[5487,8904,8706],{"class":8617},[5487,8906,8621],{"class":6869},[5487,8908,8909],{"class":8624},"\"video_720p\"",[5487,8911,8808],{"class":8617},[5487,8913,8621],{"class":6869},[5487,8915,8916],{"class":8624},"\"2000000\"",[5487,8918,8655],{"class":6869},[5487,8920,8921,8924,8926,8929,8931,8933,8936],{"class":5489,"line":6019},[5487,8922,8923],{"class":8617},"                      width",[5487,8925,8621],{"class":6869},[5487,8927,8928],{"class":8624},"\"1280\"",[5487,8930,8765],{"class":8617},[5487,8932,8621],{"class":6869},[5487,8934,8935],{"class":8624},"\"720\"",[5487,8937,8690],{"class":8610},[5487,8939,8940,8942,8944,8946,8948,8950,8952,8954,8956],{"class":5489,"line":6025},[5487,8941,8820],{"class":8610},[5487,8943,8823],{"class":8614},[5487,8945,8826],{"class":8617},[5487,8947,8621],{"class":6869},[5487,8949,8831],{"class":8624},[5487,8951,8834],{"class":8617},[5487,8953,8621],{"class":6869},[5487,8955,8839],{"class":8624},[5487,8957,8655],{"class":6869},[5487,8959,8960,8962,8964,8967],{"class":5489,"line":6030},[5487,8961,8846],{"class":8617},[5487,8963,8621],{"class":6869},[5487,8965,8966],{"class":8624},"\"init_720p.mp4\"",[5487,8968,8655],{"class":6869},[5487,8970,8971,8973,8975,8978],{"class":5489,"line":6035},[5487,8972,8858],{"class":8617},[5487,8974,8621],{"class":6869},[5487,8976,8977],{"class":8624},"\"segment_720p_$Number$.m4s\"",[5487,8979,8655],{"class":6869},[5487,8981,8982,8984,8986,8988],{"class":5489,"line":7188},[5487,8983,8870],{"class":8617},[5487,8985,8621],{"class":6869},[5487,8987,8875],{"class":8624},[5487,8989,8878],{"class":8610},[5487,8991,8992,8994,8996],{"class":5489,"line":7209},[5487,8993,8883],{"class":8610},[5487,8995,8798],{"class":8614},[5487,8997,8690],{"class":8610},[5487,8999,9000],{"class":5489,"line":7215},[5487,9001,8785],{"class":6869},[5487,9003,9004,9007,9009],{"class":5489,"line":7220},[5487,9005,9006],{"class":8610},"    \u003C\u002F",[5487,9008,8734],{"class":8614},[5487,9010,8690],{"class":8610},[5487,9012,9013],{"class":5489,"line":7238},[5487,9014,6946],{"class":6869},[5487,9016,9017],{"class":5489,"line":7254},[5487,9018,9019],{"class":6905},"    \u003C!-- Аудіо AdaptationSet -->\n",[5487,9021,9022,9024,9026,9028,9030,9033,9035,9037,9040,9043,9045,9048],{"class":5489,"line":7259},[5487,9023,8731],{"class":8610},[5487,9025,8734],{"class":8614},[5487,9027,8737],{"class":8617},[5487,9029,8621],{"class":6869},[5487,9031,9032],{"class":8624},"\"audio\u002Fmp4\"",[5487,9034,8745],{"class":8617},[5487,9036,8621],{"class":6869},[5487,9038,9039],{"class":8624},"\"mp4a.40.2\"",[5487,9041,9042],{"class":8617}," lang",[5487,9044,8621],{"class":6869},[5487,9046,9047],{"class":8624},"\"uk\"",[5487,9049,8690],{"class":8610},[5487,9051,9052,9054,9056,9058,9060,9063,9065,9067,9070],{"class":5489,"line":7300},[5487,9053,8795],{"class":8610},[5487,9055,8798],{"class":8614},[5487,9057,8706],{"class":8617},[5487,9059,8621],{"class":6869},[5487,9061,9062],{"class":8624},"\"audio\"",[5487,9064,8808],{"class":8617},[5487,9066,8621],{"class":6869},[5487,9068,9069],{"class":8624},"\"128000\"",[5487,9071,8690],{"class":8610},[5487,9073,9074,9076,9078,9080,9082,9084,9086,9088,9090],{"class":5489,"line":7327},[5487,9075,8820],{"class":8610},[5487,9077,8823],{"class":8614},[5487,9079,8826],{"class":8617},[5487,9081,8621],{"class":6869},[5487,9083,8831],{"class":8624},[5487,9085,8834],{"class":8617},[5487,9087,8621],{"class":6869},[5487,9089,8839],{"class":8624},[5487,9091,8655],{"class":6869},[5487,9093,9094,9096,9098,9101],{"class":5489,"line":7333},[5487,9095,8846],{"class":8617},[5487,9097,8621],{"class":6869},[5487,9099,9100],{"class":8624},"\"init_audio.mp4\"",[5487,9102,8655],{"class":6869},[5487,9104,9105,9107,9109,9112],{"class":5489,"line":7744},[5487,9106,8858],{"class":8617},[5487,9108,8621],{"class":6869},[5487,9110,9111],{"class":8624},"\"segment_audio_$Number$.m4s\"",[5487,9113,8655],{"class":6869},[5487,9115,9116,9118,9120,9122],{"class":5489,"line":7761},[5487,9117,8870],{"class":8617},[5487,9119,8621],{"class":6869},[5487,9121,8875],{"class":8624},[5487,9123,8878],{"class":8610},[5487,9125,9126,9128,9130],{"class":5489,"line":7766},[5487,9127,8883],{"class":8610},[5487,9129,8798],{"class":8614},[5487,9131,8690],{"class":8610},[5487,9133,9134,9136,9138],{"class":5489,"line":7772},[5487,9135,9006],{"class":8610},[5487,9137,8734],{"class":8614},[5487,9139,8690],{"class":8610},[5487,9141,9142],{"class":5489,"line":7797},[5487,9143,6946],{"class":6869},[5487,9145,9146,9149,9151],{"class":5489,"line":7820},[5487,9147,9148],{"class":8610},"  \u003C\u002F",[5487,9150,8703],{"class":8614},[5487,9152,8690],{"class":8610},[5487,9154,9155,9158,9160],{"class":5489,"line":7825},[5487,9156,9157],{"class":8610},"\u003C\u002F",[5487,9159,8644],{"class":8614},[5487,9161,8690],{"class":8610},[5299,9163,9164],{},[5235,9165,9166],{},"Пояснення ключових атрибутів:",[5198,9168,9169,9180,9188,9196,9211],{},[5201,9170,9171,9176,9177,5211],{},[5235,9172,9173],{},[5208,9174,9175],{},"type=\"static\""," — статична презентація (VOD). Для живих трансляцій використовується ",[5208,9178,9179],{},"type=\"dynamic\"",[5201,9181,9182,9187],{},[5235,9183,9184],{},[5208,9185,9186],{},"mediaPresentationDuration=\"PT10M30S\""," — загальна тривалість контенту у форматі ISO 8601 (10 хвилин 30 секунд).",[5201,9189,9190,9195],{},[5235,9191,9192],{},[5208,9193,9194],{},"minBufferTime=\"PT5S\""," — мінімальний рекомендований розмір буфера клієнта (5 секунд).",[5201,9197,9198,9202,9203,9206,9207,9210],{},[5235,9199,9200],{},[5208,9201,8823],{}," — шаблон для формування URL сегментів. Змінна ",[5208,9204,9205],{},"$Number$"," замінюється послідовним номером сегменту, ",[5208,9208,9209],{},"initialization"," вказує на спеціальний ініціалізаційний фрагмент fMP4, що містить заголовки контейнера.",[5201,9212,9213,9218],{},[5235,9214,9215],{},[5208,9216,9217],{},"timescale=\"1000\" duration=\"10000\""," — часова шкала у мілісекундах та тривалість сегменту (10000 мс = 10 секунд).",[5365,9220,9222],{"id":9221},"переваги-dash","Переваги DASH",[5299,9224,9225,9228,9230],{},[5235,9226,9227],{},"Відкритість та незалежність від виробника.",[5378,9229],{},"\nНа відміну від HLS, який був створений Apple і довгий час залишався нестандартизованим, DASH є справжнім міжнародним стандартом, що гарантує інтероперабельність між різними платформами та постачальниками обладнання.",[5299,9232,9233,9236,9238],{},[5235,9234,9235],{},"Гнучкість у виборі кодеків та контейнерів.",[5378,9237],{},"\nDASH підтримує широкий спектр форматів: H.264, H.265 (HEVC), VP9, AV1 для відео; AAC, Opus для аудіо; контейнери fMP4, WebM. Це дозволяє оптимізувати баланс між якістю, розміром файлів та вимогами до декодування залежно від цільової платформи.",[5299,9240,9241,9244,9246,9247,9250],{},[5235,9242,9243],{},"Просунуті можливості захисту контенту.",[5378,9245],{},"\nDASH використовує стандарт ",[5235,9248,9249],{},"Common Encryption (CENC)",", що дозволяє зашифрувати контент один раз та розподіляти його через різні DRM-системи (Widevine, PlayReady, FairPlay) без потреби у множинному кодуванні.",[5860,9252,9253,9254,9257],{},"Хоча DASH теоретично є кращим технічним рішенням, ",[5235,9255,9256],{},"HLS досі домінує у реальному світі"," через історичну підтримку в iOS\u002FSafari та масову інфраструктуру постачальників CDN, налаштовану під HLS. Багато стрімінгових платформ використовують гібридний підхід: HLS для Apple-пристроїв та DASH для Android\u002FWeb через MSE.",[5291,9259],{},[5294,9261,9263],{"id":9262},"webrtc-революція-прямого-звязку-в-реальному-часі","WebRTC: революція прямого зв'язку в реальному часі",[5299,9265,9266,9267,9270,9271,5211],{},"Протоколи HLS та DASH чудово підходять для доставки попередньо записаного контенту або live-трансляцій із прийнятною затримкою (15–30 секунд). Проте для інтерактивних сценаріїв використання — ",[5235,9268,9269],{},"відеоконференцій, дистанційного робочого столу, онлайн-ігор, телемедицини"," — така затримка є неприйнятною. Саме для цих задач було розроблено технологію ",[5235,9272,9273],{},"WebRTC (Web Real-Time Communication)",[5365,9275,9277],{"id":9276},"філософія-та-архітектурні-принципи-webrtc","Філософія та архітектурні принципи WebRTC",[5299,9279,9280,9281,9284],{},"WebRTC є не одним протоколом, а цілою ",[5235,9282,9283],{},"екосистемою стандартів",", що визначають набір протоколів, API та механізмів для прямого обміну медіаданими та довільною інформацією між користувацькими агентами (браузерами або нативними застосунками) з мінімальною затримкою. Ключові філософські принципи технології:",[5299,9286,9287,9290,9292,9293,9296],{},[5235,9288,9289],{},"1. Прямий зв'язок P2P (Peer-to-Peer).",[5378,9291],{},"\nНа відміну від клієнт-серверної архітектури HLS\u002FDASH, де медіаконтент завжди проходить через HTTP-сервер, WebRTC прагне встановити ",[5235,9294,9295],{},"пряме з'єднання між кінцевими учасниками"," без посередників. Це радикально знижує затримку (латентність може досягати 100–200 мс замість секунд) та зменшує навантаження на центральні сервери. Медіапотоки передаються напряму з камери\u002Fмікрофона одного користувача до динаміків\u002Fекрана іншого.",[5299,9298,9299,9302,9304,9305,9308,9309,9312,9313,9316],{},[5235,9300,9301],{},"2. Безпека за замовчуванням.",[5378,9303],{},"\nВсі медіапотоки та дані, що передаються через WebRTC, обов'язково ",[5235,9306,9307],{},"шифруються"," за допомогою протоколів ",[5235,9310,9311],{},"DTLS (Datagram Transport Layer Security)"," та ",[5235,9314,9315],{},"SRTP (Secure Real-time Transport Protocol)",". Ключі шифрування обмінюються у процесі встановлення з'єднання, і немає можливості вимкнути шифрування — воно є невід'ємною частиною стека.",[5299,9318,9319,9322,9324],{},[5235,9320,9321],{},"3. Адаптація до мережевих умов у реальному часі.",[5378,9323],{},"\nWebRTC постійно моніторить якість з'єднання (втрати пакетів, джиттер, затримку) та динамічно адаптує параметри кодування медіа (бітрейт, роздільну здатність, частоту кадрів), щоб забезпечити найкращу можливу якість при поточних умовах.",[5299,9326,9327,9330,9332],{},[5235,9328,9329],{},"4. Універсальність та інтероперабельність.",[5378,9331],{},"\nСтандарти WebRTC підтримуються консорціумом W3C (API для JavaScript у браузерах) та IETF (мережеві протоколи). Це гарантує, що з'єднання може бути встановлене між різними типами клієнтів: Chrome на Windows, Safari на iOS, мобільний застосунок на Android, серверний компонент на Go, десктопна програма на Electron тощо.",[5365,9334,9336],{"id":9335},"компоненти-архітектури-webrtc","Компоненти архітектури WebRTC",[5299,9338,9339],{},"Для успішного встановлення P2P-з'єднання необхідна взаємодія декількох компонентів:",[5299,9341,9342,9345,9347,9348,9351,9352,9355],{},[5235,9343,9344],{},"1. Signaling Server (Сервер сигналізації).",[5378,9346],{},"\nНезважаючи на концепцію прямого зв'єднання, учасники повинні спочатку якось обмінятися метаінформацією про свої можливості, мережеві адреси та параметри сесії. ",[5235,9349,9350],{},"Сигналізація"," — це процес передачі цих даних через зовнішній канал зв'язку (зазвичай WebSocket або HTTP Long Polling). Важливо розуміти, що специфікація WebRTC ",[5235,9353,9354],{},"навмисно не стандартизує"," протокол сигналізації, залишаючи розробникам свободу вибору (WebSocket, MQTT, навіть ручне копіювання через месенджер). Однак найпопулярнішим підходом є використання WebSocket-сервера, що пересилає повідомлення між учасниками.",[5299,9357,9358,9361,9363,9366,9367,9370,9371,6103],{},[5235,9359,9360],{},"2. Протокол SDP (Session Description Protocol).",[5378,9362],{},[5235,9364,9365],{},"SDP"," — це текстовий формат опису параметрів медіасесії, стандартизований у ",[5235,9368,9369],{},"RFC 4566",". SDP-повідомлення містить інформацію про підтримувані кодеки (H.264, VP8, VP9, Opus), типи медіа (аудіо, відео, дані), порти та IP-адреси кандидатів для з'єднання, а також криптографічні відбитки сертифікатів для DTLS. Типовий процес встановлення з'єднання називається ",[5235,9372,9373],{},"Offer\u002FAnswer Model",[5198,9375,9376,9383,9390],{},[5201,9377,9378,9379,9382],{},"Ініціатор (Caller) створює ",[5235,9380,9381],{},"Offer"," — SDP-опис зі списком своїх можливостей та пропозицією параметрів сесії.",[5201,9384,9385,9386,9389],{},"Відповідач (Callee) отримує Offer, аналізує його та формує ",[5235,9387,9388],{},"Answer"," — SDP-відповідь зі своїми підтримуваними параметрами та узгодженими налаштуваннями.",[5201,9391,9392],{},"Обидва учасники обмінюються цими SDP-описами через сервер сигналізації.",[5299,9394,9395,9398,9400,9401,9404,9405,9408,9409,9412],{},[5235,9396,9397],{},"3. ICE (Interactive Connectivity Establishment).",[5378,9399],{},"\nПісля обміну SDP-описами потрібно визначити фактичну мережеву топологію та знайти оптимальний шлях з'єднання. Більшість користувачів знаходяться за ",[5235,9402,9403],{},"NAT (Network Address Translation)"," або корпоративними брандмауерами, які приховують справжню IP-адресу пристрою та блокують вхідні з'єднання. Фреймворк ",[5235,9406,9407],{},"ICE (RFC 8445)"," вирішує цю проблему шляхом збору ",[5235,9410,9411],{},"кандидатів з'єднання (ICE candidates)"," — можливих мережевих адрес, через які може бути встановлене з'єднання:",[5198,9414,9415,9424,9433],{},[5201,9416,9417,9420,9421,5622],{},[5235,9418,9419],{},"Host candidates:"," локальні IP-адреси пристрою (приватні адреси локальної мережі, наприклад, ",[5208,9422,9423],{},"192.168.1.100",[5201,9425,9426,9429,9430,5211],{},[5235,9427,9428],{},"Server-reflexive candidates (srflx):"," публічна IP-адреса, отримана через запит до ",[5235,9431,9432],{},"STUN-сервера",[5201,9434,9435,9438,9439,9442],{},[5235,9436,9437],{},"Relay candidates:"," адреса ",[5235,9440,9441],{},"TURN-сервера",", що виконує роль ретранслятора трафіку у випадку неможливості прямого з'єднання.",[5299,9444,9445,9448,9450,9453,9454,9457],{},[5235,9446,9447],{},"4. STUN Server (Session Traversal Utilities for NAT).",[5378,9449],{},[5235,9451,9452],{},"STUN"," — це простий протокол, що дозволяє клієнту дізнатися свою публічну IP-адресу та тип NAT, через який він під'єднаний до інтернету. Клієнт надсилає запит до публічного STUN-сервера (наприклад, ",[5208,9455,9456],{},"stun:stun.l.google.com:19302","), і сервер відповідає IP-адресою та портом, з якого було отримано запит. Ця інформація додається до списку ICE-кандидатів та передається іншому учаснику через сигналізацію.",[5299,9459,9460,9463,9465,9466,9469],{},[5235,9461,9462],{},"5. TURN Server (Traversal Using Relays around NAT).",[5378,9464],{},"\nУ деяких мережевих конфігураціях (симетричний NAT, строгі корпоративні firewall) пряме з'єднання P2P неможливе навіть із знанням публічних адрес. У таких випадках використовується ",[5235,9467,9468],{},"TURN-сервер"," — публічно доступний сервер-ретранслятор, через який проксується весь медіатрафік. TURN гарантує з'єднання у 100% випадків, але ціною збільшення затримки, навантаження на сервер та споживання пропускної здатності. Статистика показує, що приблизно 8–15% з'єднань потребують TURN через обмеження NAT.",[5904,9471,9473],{"alt":9472},"Процес встановлення WebRTC-з'єднання",[5478,9474,9476],{"className":5909,"code":9475,"language":5911,"meta":5483,"style":5483},"@startuml\nskinparam style plain\nskinparam backgroundColor #FFFFFF\nautonumber\n\nactor \"Користувач A\\n(Caller)\" as UserA #DBEAFE\nparticipant \"Signaling Server\\n(WebSocket)\" as Signal #FEF3C7\nactor \"Користувач B\\n(Callee)\" as UserB #DCFCE7\nparticipant \"STUN Server\" as STUN #E2E8F0\nparticipant \"TURN Server\" as TURN #FEE2E2\n\nUserA -> UserA : Збір локальних медіапотоків\\n(камера, мікрофон)\nUserA -> UserA : Створення PeerConnection\nUserA -> STUN : STUN Binding Request\nSTUN -> UserA : Відповідь із публічною IP\n\nUserA -> UserA : Генерація SDP Offer\\nта збір ICE candidates\nUserA -> Signal : Відправка Offer + ICE candidates\nSignal -> UserB : Пересилання Offer\n\nUserB -> UserB : Створення PeerConnection\nUserB -> STUN : STUN Binding Request\nSTUN -> UserB : Відповідь із публічною IP\n\nUserB -> UserB : Генерація SDP Answer\\nта збір ICE candidates\nUserB -> Signal : Відправка Answer + ICE candidates\nSignal -> UserA : Пересилання Answer\n\nUserA \u003C-> UserB : Спроби встановлення\\nпрямого з'єднання (ICE connectivity checks)\n\nalt Пряме з'єднання успішне\n    UserA \u003C-> UserB : **DTLS Handshake**\\nобмін криптографічними ключами\n    UserA \u003C-> UserB : **SRTP Media Stream**\\n(аудіо, відео)\nelse Пряме з'єднання неможливе\n    UserA -> TURN : Запит на створення Relay\n    TURN -> UserA : Виділення Relay Candidate\n    UserA -> Signal : Відправка TURN Candidate\n    Signal -> UserB : Пересилання\n    UserA \u003C-> TURN \u003C-> UserB : Ретрансляція медіатрафіку\nend\n\n@enduml\n",[5208,9477,9478,9482,9486,9490,9495,9499,9504,9509,9514,9519,9524,9528,9533,9538,9543,9548,9552,9557,9562,9567,9571,9576,9581,9586,9590,9595,9600,9605,9609,9614,9618,9623,9628,9633,9638,9643,9648,9653,9658,9663,9668,9672],{"__ignoreMap":5483},[5487,9479,9480],{"class":5489,"line":5490},[5487,9481,5918],{},[5487,9483,9484],{"class":5489,"line":5496},[5487,9485,5923],{},[5487,9487,9488],{"class":5489,"line":5502},[5487,9489,5928],{},[5487,9491,9492],{"class":5489,"line":5509},[5487,9493,9494],{},"autonumber\n",[5487,9496,9497],{"class":5489,"line":5515},[5487,9498,5506],{"emptyLinePlaceholder":5505},[5487,9500,9501],{"class":5489,"line":5521},[5487,9502,9503],{},"actor \"Користувач A\\n(Caller)\" as UserA #DBEAFE\n",[5487,9505,9506],{"class":5489,"line":5527},[5487,9507,9508],{},"participant \"Signaling Server\\n(WebSocket)\" as Signal #FEF3C7\n",[5487,9510,9511],{"class":5489,"line":5532},[5487,9512,9513],{},"actor \"Користувач B\\n(Callee)\" as UserB #DCFCE7\n",[5487,9515,9516],{"class":5489,"line":5538},[5487,9517,9518],{},"participant \"STUN Server\" as STUN #E2E8F0\n",[5487,9520,9521],{"class":5489,"line":5544},[5487,9522,9523],{},"participant \"TURN Server\" as TURN #FEE2E2\n",[5487,9525,9526],{"class":5489,"line":5550},[5487,9527,5506],{"emptyLinePlaceholder":5505},[5487,9529,9530],{"class":5489,"line":5555},[5487,9531,9532],{},"UserA -> UserA : Збір локальних медіапотоків\\n(камера, мікрофон)\n",[5487,9534,9535],{"class":5489,"line":5561},[5487,9536,9537],{},"UserA -> UserA : Створення PeerConnection\n",[5487,9539,9540],{"class":5489,"line":5567},[5487,9541,9542],{},"UserA -> STUN : STUN Binding Request\n",[5487,9544,9545],{"class":5489,"line":5704},[5487,9546,9547],{},"STUN -> UserA : Відповідь із публічною IP\n",[5487,9549,9550],{"class":5489,"line":5710},[5487,9551,5506],{"emptyLinePlaceholder":5505},[5487,9553,9554],{"class":5489,"line":5716},[5487,9555,9556],{},"UserA -> UserA : Генерація SDP Offer\\nта збір ICE candidates\n",[5487,9558,9559],{"class":5489,"line":5722},[5487,9560,9561],{},"UserA -> Signal : Відправка Offer + ICE candidates\n",[5487,9563,9564],{"class":5489,"line":5727},[5487,9565,9566],{},"Signal -> UserB : Пересилання Offer\n",[5487,9568,9569],{"class":5489,"line":6007},[5487,9570,5506],{"emptyLinePlaceholder":5505},[5487,9572,9573],{"class":5489,"line":6013},[5487,9574,9575],{},"UserB -> UserB : Створення PeerConnection\n",[5487,9577,9578],{"class":5489,"line":6019},[5487,9579,9580],{},"UserB -> STUN : STUN Binding Request\n",[5487,9582,9583],{"class":5489,"line":6025},[5487,9584,9585],{},"STUN -> UserB : Відповідь із публічною IP\n",[5487,9587,9588],{"class":5489,"line":6030},[5487,9589,5506],{"emptyLinePlaceholder":5505},[5487,9591,9592],{"class":5489,"line":6035},[5487,9593,9594],{},"UserB -> UserB : Генерація SDP Answer\\nта збір ICE candidates\n",[5487,9596,9597],{"class":5489,"line":7188},[5487,9598,9599],{},"UserB -> Signal : Відправка Answer + ICE candidates\n",[5487,9601,9602],{"class":5489,"line":7209},[5487,9603,9604],{},"Signal -> UserA : Пересилання Answer\n",[5487,9606,9607],{"class":5489,"line":7215},[5487,9608,5506],{"emptyLinePlaceholder":5505},[5487,9610,9611],{"class":5489,"line":7220},[5487,9612,9613],{},"UserA \u003C-> UserB : Спроби встановлення\\nпрямого з'єднання (ICE connectivity checks)\n",[5487,9615,9616],{"class":5489,"line":7238},[5487,9617,5506],{"emptyLinePlaceholder":5505},[5487,9619,9620],{"class":5489,"line":7254},[5487,9621,9622],{},"alt Пряме з'єднання успішне\n",[5487,9624,9625],{"class":5489,"line":7259},[5487,9626,9627],{},"    UserA \u003C-> UserB : **DTLS Handshake**\\nобмін криптографічними ключами\n",[5487,9629,9630],{"class":5489,"line":7300},[5487,9631,9632],{},"    UserA \u003C-> UserB : **SRTP Media Stream**\\n(аудіо, відео)\n",[5487,9634,9635],{"class":5489,"line":7327},[5487,9636,9637],{},"else Пряме з'єднання неможливе\n",[5487,9639,9640],{"class":5489,"line":7333},[5487,9641,9642],{},"    UserA -> TURN : Запит на створення Relay\n",[5487,9644,9645],{"class":5489,"line":7744},[5487,9646,9647],{},"    TURN -> UserA : Виділення Relay Candidate\n",[5487,9649,9650],{"class":5489,"line":7761},[5487,9651,9652],{},"    UserA -> Signal : Відправка TURN Candidate\n",[5487,9654,9655],{"class":5489,"line":7766},[5487,9656,9657],{},"    Signal -> UserB : Пересилання\n",[5487,9659,9660],{"class":5489,"line":7772},[5487,9661,9662],{},"    UserA \u003C-> TURN \u003C-> UserB : Ретрансляція медіатрафіку\n",[5487,9664,9665],{"class":5489,"line":7797},[5487,9666,9667],{},"end\n",[5487,9669,9670],{"class":5489,"line":7820},[5487,9671,5506],{"emptyLinePlaceholder":5505},[5487,9673,9674],{"class":5489,"line":7825},[5487,9675,6038],{},[5365,9677,9679],{"id":9678},"детальний-розбір-типів-nat-та-процесу-ice","Детальний розбір типів NAT та процесу ICE",[5299,9681,9682,9683,9685],{},"Один із найскладніших аспектів WebRTC — це встановлення прямого з'єднання між вузлами, що знаходяться за ",[5235,9684,9403],{},". Розуміння типів NAT критично важливе для діагностики проблем із підключенням та налаштування інфраструктури STUN\u002FTURN.",[5470,9687,9689],{"id":9688},"типи-nat-та-їх-вплив-на-зєднання","Типи NAT та їх вплив на з'єднання",[5299,9691,9692,9695,9697,9698,6099,9701,9704],{},[5235,9693,9694],{},"1. Full Cone NAT (найпростіший випадок):",[5378,9696],{},"\nПісля того, як внутрішній хост надіслав пакет назовні через певний порт, NAT створює ",[5235,9699,9700],{},"постійне відображення",[5208,9702,9703],{},"приватна_IP:порт → публічна_IP:порт",". Будь-який зовнішній хост може надіслати пакет на цю публічну адресу, і він буде переспрямований на внутрішній хост. Це найсприятливіший тип для P2P з'єднань — достатньо STUN для визначення публічної адреси.",[5299,9706,9707,9710,9712,9713,9716],{},[5235,9708,9709],{},"2. Address-Restricted Cone NAT:",[5378,9711],{},"\nNAT дозволяє вхідні пакети лише від тих зовнішніх IP-адрес, на які внутрішній хост вже надсилав пакети. Порт відправника не має значення. Для встановлення з'єднання обидві сторони повинні спочатку надіслати пакети одна одній (",[5235,9714,9715],{},"simultaneous open","). ICE автоматично виконує цей процес через connectivity checks.",[5299,9718,9719,9722,9724,9725,9728],{},[5235,9720,9721],{},"3. Port-Restricted Cone NAT:",[5378,9723],{},"\nНайсуворіший варіант Cone NAT: вхідні пакети дозволені лише від конкретної пари ",[5208,9726,9727],{},"зовнішня_IP:порт",", на яку було надіслано вихідний пакет. Потребує точної координації портів через ICE.",[5299,9730,9731,9734,9736,9737,9740,9741,9312,9744,9747,9748,9312,9751,9754],{},[5235,9732,9733],{},"4. Symmetric NAT (найскладніший випадок):",[5378,9735],{},"\nNAT створює ",[5235,9738,9739],{},"різні публічні порти"," для кожного зовнішнього адресата. Якщо внутрішній хост надсилає пакети на ",[5208,9742,9743],{},"A.A.A.A:1111",[5208,9745,9746],{},"B.B.B.B:2222"," з одного локального порту, NAT виділяє два різні публічні порти (наприклад, ",[5208,9749,9750],{},":40000",[5208,9752,9753],{},":40001","). Це робить пряме з'єднання практично неможливим без TURN, оскільки адреса, отримана через STUN, буде відрізнятися від адреси, яку бачить інший учасник.",[5904,9756,9758],{"alt":9757},"Типи NAT та можливості встановлення з'єднання",[5478,9759,9761],{"className":5909,"code":9760,"language":5911,"meta":5483,"style":5483},"@startuml\nskinparam style plain\nskinparam backgroundColor #FFFFFF\n\ncomponent \"Клієнт A\\n192.168.1.100:5000\" as ClientA #DBEAFE\ncomponent \"NAT A\\nТип: Symmetric\" as NatA #FEE2E2\ncomponent \"STUN Server\" as STUN #F1F5F9\ncomponent \"TURN Server\" as TURN #FEF3C7\ncomponent \"NAT B\\nТип: Port-Restricted Cone\" as NatB #FEF3C7\ncomponent \"Клієнт B\\n10.0.0.50:6000\" as ClientB #DCFCE7\n\nClientA -right-> NatA : Вихідний пакет\nNatA -right-> STUN : З порту 203.0.113.10:40000\nSTUN --> NatA : \"Ваша адреса:\\n203.0.113.10:40000\"\n\nClientB -left-> NatB : Вихідний пакет\nNatB -left-> STUN : З порту 198.51.100.5:50000\nSTUN --> NatB : \"Ваша адреса:\\n198.51.100.5:50000\"\n\nNatA -down-> TURN : Не можу з'єднатися\\nбезпосередньо через\\nSymmetric NAT\nTURN -down-> NatB : Ретрансляція трафіку\n\nnote bottom of NatA\n  Symmetric NAT змінює порт\n  для кожного адресата,\n  тому пряме з'єднання\n  неможливе\nend note\n\n@enduml\n",[5208,9762,9763,9767,9771,9775,9779,9784,9789,9794,9799,9804,9809,9813,9818,9823,9828,9832,9837,9842,9847,9851,9856,9861,9865,9870,9875,9880,9885,9890,9894,9898],{"__ignoreMap":5483},[5487,9764,9765],{"class":5489,"line":5490},[5487,9766,5918],{},[5487,9768,9769],{"class":5489,"line":5496},[5487,9770,5923],{},[5487,9772,9773],{"class":5489,"line":5502},[5487,9774,5928],{},[5487,9776,9777],{"class":5489,"line":5509},[5487,9778,5506],{"emptyLinePlaceholder":5505},[5487,9780,9781],{"class":5489,"line":5515},[5487,9782,9783],{},"component \"Клієнт A\\n192.168.1.100:5000\" as ClientA #DBEAFE\n",[5487,9785,9786],{"class":5489,"line":5521},[5487,9787,9788],{},"component \"NAT A\\nТип: Symmetric\" as NatA #FEE2E2\n",[5487,9790,9791],{"class":5489,"line":5527},[5487,9792,9793],{},"component \"STUN Server\" as STUN #F1F5F9\n",[5487,9795,9796],{"class":5489,"line":5532},[5487,9797,9798],{},"component \"TURN Server\" as TURN #FEF3C7\n",[5487,9800,9801],{"class":5489,"line":5538},[5487,9802,9803],{},"component \"NAT B\\nТип: Port-Restricted Cone\" as NatB #FEF3C7\n",[5487,9805,9806],{"class":5489,"line":5544},[5487,9807,9808],{},"component \"Клієнт B\\n10.0.0.50:6000\" as ClientB #DCFCE7\n",[5487,9810,9811],{"class":5489,"line":5550},[5487,9812,5506],{"emptyLinePlaceholder":5505},[5487,9814,9815],{"class":5489,"line":5555},[5487,9816,9817],{},"ClientA -right-> NatA : Вихідний пакет\n",[5487,9819,9820],{"class":5489,"line":5561},[5487,9821,9822],{},"NatA -right-> STUN : З порту 203.0.113.10:40000\n",[5487,9824,9825],{"class":5489,"line":5567},[5487,9826,9827],{},"STUN --> NatA : \"Ваша адреса:\\n203.0.113.10:40000\"\n",[5487,9829,9830],{"class":5489,"line":5704},[5487,9831,5506],{"emptyLinePlaceholder":5505},[5487,9833,9834],{"class":5489,"line":5710},[5487,9835,9836],{},"ClientB -left-> NatB : Вихідний пакет\n",[5487,9838,9839],{"class":5489,"line":5716},[5487,9840,9841],{},"NatB -left-> STUN : З порту 198.51.100.5:50000\n",[5487,9843,9844],{"class":5489,"line":5722},[5487,9845,9846],{},"STUN --> NatB : \"Ваша адреса:\\n198.51.100.5:50000\"\n",[5487,9848,9849],{"class":5489,"line":5727},[5487,9850,5506],{"emptyLinePlaceholder":5505},[5487,9852,9853],{"class":5489,"line":6007},[5487,9854,9855],{},"NatA -down-> TURN : Не можу з'єднатися\\nбезпосередньо через\\nSymmetric NAT\n",[5487,9857,9858],{"class":5489,"line":6013},[5487,9859,9860],{},"TURN -down-> NatB : Ретрансляція трафіку\n",[5487,9862,9863],{"class":5489,"line":6019},[5487,9864,5506],{"emptyLinePlaceholder":5505},[5487,9866,9867],{"class":5489,"line":6025},[5487,9868,9869],{},"note bottom of NatA\n",[5487,9871,9872],{"class":5489,"line":6030},[5487,9873,9874],{},"  Symmetric NAT змінює порт\n",[5487,9876,9877],{"class":5489,"line":6035},[5487,9878,9879],{},"  для кожного адресата,\n",[5487,9881,9882],{"class":5489,"line":7188},[5487,9883,9884],{},"  тому пряме з'єднання\n",[5487,9886,9887],{"class":5489,"line":7209},[5487,9888,9889],{},"  неможливе\n",[5487,9891,9892],{"class":5489,"line":7215},[5487,9893,6000],{},[5487,9895,9896],{"class":5489,"line":7220},[5487,9897,5506],{"emptyLinePlaceholder":5505},[5487,9899,9900],{"class":5489,"line":7238},[5487,9901,6038],{},[5470,9903,9905],{"id":9904},"процес-збору-та-перевірки-ice-кандидатів","Процес збору та перевірки ICE-кандидатів",[5299,9907,9908,9909,9912],{},"Фреймворк ",[5235,9910,9911],{},"ICE (Interactive Connectivity Establishment)"," автоматизує процес пошуку робочого шляху з'єднання через послідовність спроб:",[5299,9914,9915],{},[5235,9916,9917],{},"Фаза 1. Збір кандидатів (Candidate Gathering):",[6045,9919,9920,9925,9930],{},[5201,9921,9922,9924],{},[5235,9923,9419],{}," Клієнт перераховує всі локальні мережеві інтерфейси (Ethernet, Wi-Fi, VPN) та додає їх IP-адреси як кандидати. Працює для з'єднань у локальній мережі.",[5201,9926,9927,9929],{},[5235,9928,9428],{}," Клієнт надсилає STUN Binding Request на публічний STUN-сервер. Сервер відповідає пакетом, що містить IP-адресу та порт, з яких було отримано запит — це публічна адреса клієнта після NAT.",[5201,9931,9932,9934],{},[5235,9933,9437],{}," Якщо налаштований TURN-сервер, клієнт запитує виділення адреси для ретрансляції. TURN резервує порт та повідомляє клієнту публічну адресу виділеного relay.",[5299,9936,9937],{},[5235,9938,9939],{},"Фаза 2. Обмін кандидатами (Candidate Exchange):",[5299,9941,9942],{},"Зібрані кандидати серіалізуються у формат JSON та передаються іншому учаснику через сервер сигналізації:",[5478,9944,9948],{"className":9945,"code":9946,"language":9947,"meta":5483,"style":5483},"language-json shiki shiki-themes light-plus dark-plus dark-plus","{\n  \"candidate\": \"candidate:1 1 UDP 2130706431 192.168.1.100 5000 typ host\",\n  \"sdpMid\": \"0\",\n  \"sdpMLineIndex\": 0\n}\n","json",[5208,9949,9950,9955,9970,9981,9991],{"__ignoreMap":5483},[5487,9951,9952],{"class":5489,"line":5490},[5487,9953,9954],{"class":6869},"{\n",[5487,9956,9957,9961,9964,9967],{"class":5489,"line":5496},[5487,9958,9960],{"class":9959},"sLwNe","  \"candidate\"",[5487,9962,9963],{"class":6869},": ",[5487,9965,9966],{"class":6241},"\"candidate:1 1 UDP 2130706431 192.168.1.100 5000 typ host\"",[5487,9968,9969],{"class":6869},",\n",[5487,9971,9972,9975,9977,9979],{"class":5489,"line":5502},[5487,9973,9974],{"class":9959},"  \"sdpMid\"",[5487,9976,9963],{"class":6869},[5487,9978,8711],{"class":6241},[5487,9980,9969],{"class":6869},[5487,9982,9983,9986,9988],{"class":5489,"line":5509},[5487,9984,9985],{"class":9959},"  \"sdpMLineIndex\"",[5487,9987,9963],{"class":6869},[5487,9989,9990],{"class":6405},"0\n",[5487,9992,9993],{"class":5489,"line":5515},[5487,9994,7336],{"class":6869},[5299,9996,9997],{},[5235,9998,9999],{},"Структура рядка candidate:",[5478,10001,10006],{"className":10002,"code":10004,"language":10005},[10003],"language-text","candidate:\u003Cfoundation> \u003Ccomponent> \u003Cprotocol> \u003Cpriority> \u003CIP> \u003Cport> typ \u003Ctype> [raddr \u003Crelated-IP>] [rport \u003Crelated-port>]\n","text",[5208,10007,10004],{"__ignoreMap":5483},[5198,10009,10010,10016,10022,10028,10034],{},[5201,10011,10012,10015],{},[5235,10013,10014],{},"foundation:"," Ідентифікатор сімейства кандидатів (кандидати з однаковим foundation мають однакову базову адресу).",[5201,10017,10018,10021],{},[5235,10019,10020],{},"component:"," 1 для RTP, 2 для RTCP (у сучасних реалізаціях RTCP мультиплексується поверх RTP, тому завжди 1).",[5201,10023,10024,10027],{},[5235,10025,10026],{},"protocol:"," UDP, TCP, TLS.",[5201,10029,10030,10033],{},[5235,10031,10032],{},"priority:"," Числовий пріоритет (вищий = кращий). Host candidates мають найвищий пріоритет, relay — найнижчий.",[5201,10035,10036,10039],{},[5235,10037,10038],{},"type:"," host, srflx, relay, prflx (peer reflexive — виявлений під час connectivity checks).",[5299,10041,10042],{},[5235,10043,10044],{},"Фаза 3. Перевірка підключення (Connectivity Checks):",[5299,10046,10047,10048,10051,10052,10055],{},"Після отримання кандидатів обох сторін ICE формує ",[5235,10049,10050],{},"матрицю перевірок"," (всі можливі пари кандидатів A→B). Пари сортуються за пріоритетом, і система починає надсилати STUN Binding Requests між ними. Перша пара, що успішно обмінялася STUN-пакетами в обох напрямках, стає ",[5235,10053,10054],{},"номінованою"," (nominated) та використовується для передачі медіа.",[5478,10057,10060],{"className":10058,"code":10059,"language":10005},[10003],"Приклад порядку перевірок (від найкращих до найгірших):\n1. Host A ↔ Host B          (локальна мережа, найкраща латентність)\n2. Host A ↔ srflx B         (один за NAT)\n3. srflx A ↔ srflx B        (обидва за NAT, пряме з'єднання)\n4. srflx A ↔ relay B        (один через TURN)\n5. relay A ↔ relay B        (обидва через TURN, найгірша латентність)\n",[5208,10061,10059],{"__ignoreMap":5483},[10063,10064,10065,10066,10069],"caution",{},"Процес ICE може тривати від 1 до 10 секунд залежно від кількості мережевих інтерфейсів та складності NAT. Для прискорення використовується ",[5235,10067,10068],{},"Trickle ICE"," (RFC 8838): кандидати передаються та перевіряються по мірі їх генерації, без очікування завершення повного збору. Це дозволяє розпочати встановлення з'єднання на 2–5 секунд раніше.",[5291,10071],{},[5365,10073,10075],{"id":10074},"протоколи-транспортного-рівня-webrtc","Протоколи транспортного рівня WebRTC",[5299,10077,10078,10079,10082],{},"На відміну від HTTP-стрімінгу, що працює поверх TCP, WebRTC використовує ",[5235,10080,10081],{},"UDP"," як основний транспортний протокол. Вибір UDP обумовлений критичними вимогами до затримки: для інтерактивної комунікації краще втратити кілька кадрів відео, ніж чекати повторної передачі пакета через TCP. Поверх UDP працюють наступні спеціалізовані протоколи:",[5299,10084,10085,10088,10090,10093,10094,10097,10098,10101,10102,10105],{},[5235,10086,10087],{},"1. RTP (Real-time Transport Protocol).",[5378,10089],{},[5235,10091,10092],{},"RTP (RFC 3550)"," — основний протокол доставки медіаданих у реальному часі. Кожен пакет RTP містить часову мітку (",[5328,10095,10096],{},"timestamp","), порядковий номер (",[5328,10099,10100],{},"sequence number","), тип корисного навантаження (",[5328,10103,10104],{},"payload type",", наприклад, VP8, Opus) та власне закодовані медіафрагменти. Приймач використовує порядкові номери для детектування втрачених пакетів та часові мітки для синхронізації відтворення аудіо та відео.",[5299,10107,10108,10111,10113,10116],{},[5235,10109,10110],{},"2. SRTP (Secure RTP).",[5378,10112],{},[5235,10114,10115],{},"SRTP (RFC 3711)"," — розширення RTP із вбудованим шифруванням корисного навантаження та аутентифікацією пакетів. Кожен RTP-пакет шифрується алгоритмом AES, що гарантує конфіденційність медіа навіть при перехопленні трафіку.",[5299,10118,10119,10122,10124,10127],{},[5235,10120,10121],{},"3. RTCP (RTP Control Protocol).",[5378,10123],{},[5235,10125,10126],{},"RTCP"," — супровідний протокол для передачі статистики якості з'єднання: кількості отриманих\u002Fвтрачених пакетів, джиттеру (варіації затримки), round-trip time (RTT). Ця інформація використовується алгоритмами адаптивного контролю бітрейту для динамічної зміни параметрів кодування.",[5299,10129,10130,10133,10135,10138],{},[5235,10131,10132],{},"4. DTLS (Datagram Transport Layer Security).",[5378,10134],{},[5235,10136,10137],{},"DTLS (RFC 6347)"," — адаптація протоколу TLS для датаграмної передачі по UDP. DTLS виконує криптографічне рукостискання на початку сесії, обмінюється сертифікатами та узгоджує ключі шифрування для SRTP. Відбитки сертифікатів (fingerprints) передаються у SDP-описах для взаємної автентифікації учасників.",[5299,10140,10141,10144,10146,10147,10150,10151,10154],{},[5235,10142,10143],{},"5. SCTP (Stream Control Transmission Protocol) поверх DTLS.",[5378,10145],{},"\nДля передачі довільних даних (не медіа) WebRTC використовує ",[5235,10148,10149],{},"DataChannels"," — канали даних, що працюють поверх протоколу ",[5235,10152,10153],{},"SCTP",", інкапсульованого у DTLS. DataChannels дозволяють обмінюватися текстовими повідомленнями, файлами або ігровими командами із низькою затримкою та опціональною гарантією доставки.",[5291,10156],{},[5294,10158,10160],{"id":10159},"реалізація-webrtc-у-go-бібліотека-pion","Реалізація WebRTC у Go: Бібліотека Pion",[5299,10162,10163,10164,10167,10168,10171],{},"Екосистема Go має повнофункціональну чисту реалізацію стека WebRTC — проєкт ",[5235,10165,10166],{},"Pion"," (",[5208,10169,10170],{},"github.com\u002Fpion\u002Fwebrtc","). На відміну від обгорток навколо нативної бібліотеки libwebrtc (C++), Pion повністю написаний на Go, що забезпечує чудову інтеграцію з екосистемою мови, простоту налагодження та кросплатформну збірку без залежностей від C-бібліотек.",[5365,10173,10175],{"id":10174},"архітектура-серверного-компоненту-webrtc","Архітектура серверного компоненту WebRTC",[5299,10177,10178],{},"Для побудови системи відеочату або трансляцій на базі WebRTC потрібні три основні компоненти:",[6045,10180,10181,10187,10197],{},[5201,10182,10183,10186],{},[5235,10184,10185],{},"Signaling Server"," — сервер сигналізації для обміну SDP-описами та ICE-кандидатами між учасниками.",[5201,10188,10189,10192,10193,10196],{},[5235,10190,10191],{},"WebRTC Peer"," — об'єкт ",[5208,10194,10195],{},"webrtc.PeerConnection",", що керує медіапотоками та з'єднаннями.",[5201,10198,10199,10202],{},[5235,10200,10201],{},"Media Source\u002FSink"," — джерело медіаконтенту (камера, файл, синтетичний потік) або приймач для запису\u002Fтрансляції.",[5365,10204,10206],{"id":10205},"крок-1-встановлення-бібліотеки-pion-webrtc","Крок 1. Встановлення бібліотеки Pion WebRTC",[10208,10209,10210],"tabs",{},[10211,10212,10214],"tabs-item",{"label":10213},"Go Modules",[5478,10215,10217],{"className":6224,"code":10216,"language":6226,"meta":5483,"style":5483},"go get github.com\u002Fpion\u002Fwebrtc\u002Fv4\ngo get github.com\u002Fgorilla\u002Fwebsocket\n",[5208,10218,10219,10229],{"__ignoreMap":5483},[5487,10220,10221,10223,10226],{"class":5489,"line":5490},[5487,10222,6845],{"class":6233},[5487,10224,10225],{"class":6241}," get",[5487,10227,10228],{"class":6241}," github.com\u002Fpion\u002Fwebrtc\u002Fv4\n",[5487,10230,10231,10233,10235],{"class":5489,"line":5496},[5487,10232,6845],{"class":6233},[5487,10234,10225],{"class":6241},[5487,10236,10237],{"class":6241}," github.com\u002Fgorilla\u002Fwebsocket\n",[5365,10239,10241],{"id":10240},"крок-2-простий-сервер-сигналізації-на-websocket","Крок 2. Простий сервер сигналізації на WebSocket",[5299,10243,10244],{},"Першим кроком створимо WebSocket-сервер, що виконує роль посередника для обміну SDP-повідомленнями між двома учасниками:",[5478,10246,10248],{"className":6843,"code":10247,"language":6845,"meta":5483,"style":5483},"package main\n\nimport (\n    \"encoding\u002Fjson\"\n    \"log\"\n    \"net\u002Fhttp\"\n    \"sync\"\n\n    \"github.com\u002Fgorilla\u002Fwebsocket\"\n)\n\nvar upgrader = websocket.Upgrader{\n    CheckOrigin: func(r *http.Request) bool {\n        return true \u002F\u002F У продакшні перевіряйте походження запитів!\n    },\n}\n\n\u002F\u002F SignalingMessage представляє повідомлення сигналізації\ntype SignalingMessage struct {\n    Type      string          `json:\"type\"`      \u002F\u002F \"offer\", \"answer\", \"ice-candidate\"\n    SDP       string          `json:\"sdp,omitempty\"`\n    Candidate json.RawMessage `json:\"candidate,omitempty\"`\n    Target    string          `json:\"target,omitempty\"` \u002F\u002F ID цільового клієнта\n}\n\n\u002F\u002F SignalingServer керує WebSocket-з'єднаннями учасників\ntype SignalingServer struct {\n    clients map[string]*websocket.Conn\n    mu      sync.RWMutex\n}\n\nfunc NewSignalingServer() *SignalingServer {\n    return &SignalingServer{\n        clients: make(map[string]*websocket.Conn),\n    }\n}\n\n\u002F\u002F HandleWebSocket обробляє нове WebSocket-з'єднання\nfunc (s *SignalingServer) HandleWebSocket(w http.ResponseWriter, r *http.Request) {\n    conn, err := upgrader.Upgrade(w, r, nil)\n    if err != nil {\n        log.Printf(\"Помилка оновлення з'єднання: %v\", err)\n        return\n    }\n    defer conn.Close()\n\n    \u002F\u002F Генеруємо унікальний ID клієнта (в реальності — автентифікація)\n    clientID := r.URL.Query().Get(\"id\")\n    if clientID == \"\" {\n        log.Println(\"Відсутній параметр ID клієнта\")\n        return\n    }\n\n    \u002F\u002F Реєструємо клієнта\n    s.mu.Lock()\n    s.clients[clientID] = conn\n    s.mu.Unlock()\n    log.Printf(\"Клієнт %s підключився\", clientID)\n\n    defer func() {\n        s.mu.Lock()\n        delete(s.clients, clientID)\n        s.mu.Unlock()\n        log.Printf(\"Клієнт %s від'єднався\", clientID)\n    }()\n\n    \u002F\u002F Читаємо повідомлення та пересилаємо цільовому клієнту\n    for {\n        var msg SignalingMessage\n        if err := conn.ReadJSON(&msg); err != nil {\n            log.Printf(\"Помилка читання повідомлення: %v\", err)\n            break\n        }\n\n        log.Printf(\"Отримано %s від %s для %s\", msg.Type, clientID, msg.Target)\n\n        \u002F\u002F Пересилаємо повідомлення цільовому учаснику\n        s.mu.RLock()\n        targetConn, exists := s.clients[msg.Target]\n        s.mu.RUnlock()\n\n        if exists {\n            if err := targetConn.WriteJSON(msg); err != nil {\n                log.Printf(\"Помилка відправки до %s: %v\", msg.Target, err)\n            }\n        } else {\n            log.Printf(\"Цільовий клієнт %s не знайдено\", msg.Target)\n        }\n    }\n}\n\nfunc main() {\n    server := NewSignalingServer()\n\n    http.HandleFunc(\"\u002Fsignal\", server.HandleWebSocket)\n    http.Handle(\"\u002F\", http.FileServer(http.Dir(\".\u002Fstatic\")))\n\n    log.Println(\"Signaling Server запущено на :8080\")\n    log.Fatal(http.ListenAndServe(\":8080\", nil))\n}\n",[5208,10249,10250,10256,10260,10266,10271,10275,10279,10284,10288,10293,10297,10301,10321,10350,10361,10366,10370,10374,10379,10392,10406,10417,10433,10447,10451,10455,10460,10471,10495,10508,10512,10516,10531,10543,10574,10578,10582,10586,10591,10634,10667,10679,10703,10707,10711,10727,10731,10736,10767,10781,10796,10800,10804,10808,10813,10830,10850,10865,10889,10893,10902,10917,10937,10951,10974,10979,10983,10988,10994,11005,11037,11061,11066,11070,11075,11127,11132,11138,11154,11184,11200,11205,11215,11247,11284,11290,11301,11330,11335,11340,11345,11350,11359,11372,11377,11402,11439,11444,11460,11487],{"__ignoreMap":5483},[5487,10251,10252,10254],{"class":5489,"line":5490},[5487,10253,6852],{"class":6237},[5487,10255,6856],{"class":6855},[5487,10257,10258],{"class":5489,"line":5496},[5487,10259,5506],{"emptyLinePlaceholder":5505},[5487,10261,10262,10264],{"class":5489,"line":5502},[5487,10263,6866],{"class":6865},[5487,10265,6870],{"class":6869},[5487,10267,10268],{"class":5489,"line":5509},[5487,10269,10270],{"class":6241},"    \"encoding\u002Fjson\"\n",[5487,10272,10273],{"class":5489,"line":5515},[5487,10274,6875],{"class":6241},[5487,10276,10277],{"class":5489,"line":5521},[5487,10278,6880],{"class":6241},[5487,10280,10281],{"class":5489,"line":5527},[5487,10282,10283],{"class":6241},"    \"sync\"\n",[5487,10285,10286],{"class":5489,"line":5532},[5487,10287,5506],{"emptyLinePlaceholder":5505},[5487,10289,10290],{"class":5489,"line":5538},[5487,10291,10292],{"class":6241},"    \"github.com\u002Fgorilla\u002Fwebsocket\"\n",[5487,10294,10295],{"class":5489,"line":5544},[5487,10296,6885],{"class":6869},[5487,10298,10299],{"class":5489,"line":5550},[5487,10300,5506],{"emptyLinePlaceholder":5505},[5487,10302,10303,10306,10309,10311,10314,10316,10319],{"class":5489,"line":5555},[5487,10304,10305],{"class":6237},"var",[5487,10307,10308],{"class":6911}," upgrader",[5487,10310,7451],{"class":6869},[5487,10312,10313],{"class":6855},"websocket",[5487,10315,5211],{"class":6869},[5487,10317,10318],{"class":6855},"Upgrader",[5487,10320,9954],{"class":6869},[5487,10322,10323,10326,10328,10330,10332,10334,10336,10338,10340,10342,10345,10348],{"class":5489,"line":5561},[5487,10324,10325],{"class":6911},"    CheckOrigin",[5487,10327,9963],{"class":6869},[5487,10329,6894],{"class":6237},[5487,10331,6926],{"class":6869},[5487,10333,7030],{"class":6911},[5487,10335,7033],{"class":6869},[5487,10337,6918],{"class":6855},[5487,10339,5211],{"class":6869},[5487,10341,7040],{"class":6855},[5487,10343,10344],{"class":6869},") ",[5487,10346,10347],{"class":6855},"bool",[5487,10349,7150],{"class":6869},[5487,10351,10352,10355,10358],{"class":5489,"line":5567},[5487,10353,10354],{"class":6865},"        return",[5487,10356,10357],{"class":6237}," true",[5487,10359,10360],{"class":6905}," \u002F\u002F У продакшні перевіряйте походження запитів!\n",[5487,10362,10363],{"class":5489,"line":5704},[5487,10364,10365],{"class":6869},"    },\n",[5487,10367,10368],{"class":5489,"line":5710},[5487,10369,7336],{"class":6869},[5487,10371,10372],{"class":5489,"line":5716},[5487,10373,5506],{"emptyLinePlaceholder":5505},[5487,10375,10376],{"class":5489,"line":5722},[5487,10377,10378],{"class":6905},"\u002F\u002F SignalingMessage представляє повідомлення сигналізації\n",[5487,10380,10381,10384,10387,10390],{"class":5489,"line":5727},[5487,10382,10383],{"class":6237},"type",[5487,10385,10386],{"class":6855}," SignalingMessage",[5487,10388,10389],{"class":6237}," struct",[5487,10391,7150],{"class":6869},[5487,10393,10394,10397,10400,10403],{"class":5489,"line":6007},[5487,10395,10396],{"class":6911},"    Type",[5487,10398,10399],{"class":6855},"      string",[5487,10401,10402],{"class":6241},"          `json:\"type\"`",[5487,10404,10405],{"class":6905},"      \u002F\u002F \"offer\", \"answer\", \"ice-candidate\"\n",[5487,10407,10408,10411,10414],{"class":5489,"line":6013},[5487,10409,10410],{"class":6911},"    SDP",[5487,10412,10413],{"class":6855},"       string",[5487,10415,10416],{"class":6241},"          `json:\"sdp,omitempty\"`\n",[5487,10418,10419,10422,10425,10427,10430],{"class":5489,"line":6019},[5487,10420,10421],{"class":6911},"    Candidate",[5487,10423,10424],{"class":6855}," json",[5487,10426,5211],{"class":6869},[5487,10428,10429],{"class":6855},"RawMessage",[5487,10431,10432],{"class":6241}," `json:\"candidate,omitempty\"`\n",[5487,10434,10435,10438,10441,10444],{"class":5489,"line":6025},[5487,10436,10437],{"class":6911},"    Target",[5487,10439,10440],{"class":6855},"    string",[5487,10442,10443],{"class":6241},"          `json:\"target,omitempty\"`",[5487,10445,10446],{"class":6905}," \u002F\u002F ID цільового клієнта\n",[5487,10448,10449],{"class":5489,"line":6030},[5487,10450,7336],{"class":6869},[5487,10452,10453],{"class":5489,"line":6035},[5487,10454,5506],{"emptyLinePlaceholder":5505},[5487,10456,10457],{"class":5489,"line":7188},[5487,10458,10459],{"class":6905},"\u002F\u002F SignalingServer керує WebSocket-з'єднаннями учасників\n",[5487,10461,10462,10464,10467,10469],{"class":5489,"line":7209},[5487,10463,10383],{"class":6237},[5487,10465,10466],{"class":6855}," SignalingServer",[5487,10468,10389],{"class":6237},[5487,10470,7150],{"class":6869},[5487,10472,10473,10476,10479,10482,10485,10488,10490,10492],{"class":5489,"line":7215},[5487,10474,10475],{"class":6911},"    clients",[5487,10477,10478],{"class":6237}," map",[5487,10480,10481],{"class":6869},"[",[5487,10483,10484],{"class":6855},"string",[5487,10486,10487],{"class":6869},"]*",[5487,10489,10313],{"class":6855},[5487,10491,5211],{"class":6869},[5487,10493,10494],{"class":6855},"Conn\n",[5487,10496,10497,10500,10503,10505],{"class":5489,"line":7220},[5487,10498,10499],{"class":6911},"    mu",[5487,10501,10502],{"class":6855},"      sync",[5487,10504,5211],{"class":6869},[5487,10506,10507],{"class":6855},"RWMutex\n",[5487,10509,10510],{"class":5489,"line":7238},[5487,10511,7336],{"class":6869},[5487,10513,10514],{"class":5489,"line":7254},[5487,10515,5506],{"emptyLinePlaceholder":5505},[5487,10517,10518,10520,10523,10526,10529],{"class":5489,"line":7259},[5487,10519,6894],{"class":6237},[5487,10521,10522],{"class":6233}," NewSignalingServer",[5487,10524,10525],{"class":6869},"() *",[5487,10527,10528],{"class":6855},"SignalingServer",[5487,10530,7150],{"class":6869},[5487,10532,10533,10536,10539,10541],{"class":5489,"line":7300},[5487,10534,10535],{"class":6865},"    return",[5487,10537,10538],{"class":6869}," &",[5487,10540,10528],{"class":6855},[5487,10542,9954],{"class":6869},[5487,10544,10545,10548,10550,10553,10555,10558,10560,10562,10564,10566,10568,10571],{"class":5489,"line":7327},[5487,10546,10547],{"class":6911},"        clients",[5487,10549,9963],{"class":6869},[5487,10551,10552],{"class":6233},"make",[5487,10554,6926],{"class":6869},[5487,10556,10557],{"class":6237},"map",[5487,10559,10481],{"class":6869},[5487,10561,10484],{"class":6855},[5487,10563,10487],{"class":6869},[5487,10565,10313],{"class":6855},[5487,10567,5211],{"class":6869},[5487,10569,10570],{"class":6855},"Conn",[5487,10572,10573],{"class":6869},"),\n",[5487,10575,10576],{"class":5489,"line":7333},[5487,10577,7330],{"class":6869},[5487,10579,10580],{"class":5489,"line":7744},[5487,10581,7336],{"class":6869},[5487,10583,10584],{"class":5489,"line":7761},[5487,10585,5506],{"emptyLinePlaceholder":5505},[5487,10587,10588],{"class":5489,"line":7766},[5487,10589,10590],{"class":6905},"\u002F\u002F HandleWebSocket обробляє нове WebSocket-з'єднання\n",[5487,10592,10593,10595,10597,10600,10603,10605,10607,10610,10612,10614,10616,10618,10620,10622,10624,10626,10628,10630,10632],{"class":5489,"line":7772},[5487,10594,6894],{"class":6237},[5487,10596,10167],{"class":6869},[5487,10598,10599],{"class":6911},"s ",[5487,10601,10602],{"class":6869},"*",[5487,10604,10528],{"class":6855},[5487,10606,10344],{"class":6869},[5487,10608,10609],{"class":6233},"HandleWebSocket",[5487,10611,6926],{"class":6869},[5487,10613,6536],{"class":6911},[5487,10615,7020],{"class":6855},[5487,10617,5211],{"class":6869},[5487,10619,7025],{"class":6855},[5487,10621,5305],{"class":6869},[5487,10623,7030],{"class":6911},[5487,10625,7033],{"class":6869},[5487,10627,6918],{"class":6855},[5487,10629,5211],{"class":6869},[5487,10631,7040],{"class":6855},[5487,10633,7043],{"class":6869},[5487,10635,10636,10639,10641,10643,10645,10648,10650,10653,10655,10657,10659,10661,10663,10665],{"class":5489,"line":7797},[5487,10637,10638],{"class":6911},"    conn",[5487,10640,5305],{"class":6869},[5487,10642,7290],{"class":6911},[5487,10644,6915],{"class":6869},[5487,10646,10647],{"class":6911},"upgrader",[5487,10649,5211],{"class":6869},[5487,10651,10652],{"class":6233},"Upgrade",[5487,10654,6926],{"class":6869},[5487,10656,6536],{"class":6911},[5487,10658,5305],{"class":6869},[5487,10660,7030],{"class":6911},[5487,10662,5305],{"class":6869},[5487,10664,7284],{"class":6237},[5487,10666,6885],{"class":6869},[5487,10668,10669,10671,10673,10675,10677],{"class":5489,"line":7820},[5487,10670,7262],{"class":6865},[5487,10672,7265],{"class":6911},[5487,10674,7293],{"class":6869},[5487,10676,7284],{"class":6237},[5487,10678,7150],{"class":6869},[5487,10680,10681,10683,10685,10688,10690,10693,10695,10697,10699,10701],{"class":5489,"line":7825},[5487,10682,7303],{"class":6911},[5487,10684,5211],{"class":6869},[5487,10686,10687],{"class":6233},"Printf",[5487,10689,6926],{"class":6869},[5487,10691,10692],{"class":6241},"\"Помилка оновлення з'єднання: ",[5487,10694,6767],{"class":6911},[5487,10696,7318],{"class":6241},[5487,10698,5305],{"class":6869},[5487,10700,7290],{"class":6911},[5487,10702,6885],{"class":6869},[5487,10704,10705],{"class":5489,"line":7852},[5487,10706,7615],{"class":6865},[5487,10708,10709],{"class":5489,"line":7876},[5487,10710,7330],{"class":6869},[5487,10712,10713,10716,10719,10721,10724],{"class":5489,"line":7918},[5487,10714,10715],{"class":6865},"    defer",[5487,10717,10718],{"class":6911}," conn",[5487,10720,5211],{"class":6869},[5487,10722,10723],{"class":6233},"Close",[5487,10725,10726],{"class":6869},"()\n",[5487,10728,10729],{"class":5489,"line":7943},[5487,10730,5506],{"emptyLinePlaceholder":5505},[5487,10732,10733],{"class":5489,"line":7948},[5487,10734,10735],{"class":6905},"    \u002F\u002F Генеруємо унікальний ID клієнта (в реальності — автентифікація)\n",[5487,10737,10738,10741,10743,10745,10747,10750,10752,10755,10757,10760,10762,10765],{"class":5489,"line":7968},[5487,10739,10740],{"class":6911},"    clientID",[5487,10742,6915],{"class":6869},[5487,10744,7030],{"class":6911},[5487,10746,5211],{"class":6869},[5487,10748,10749],{"class":6911},"URL",[5487,10751,5211],{"class":6869},[5487,10753,10754],{"class":6233},"Query",[5487,10756,7056],{"class":6869},[5487,10758,10759],{"class":6233},"Get",[5487,10761,6926],{"class":6869},[5487,10763,10764],{"class":6241},"\"id\"",[5487,10766,6885],{"class":6869},[5487,10768,10769,10771,10774,10776,10779],{"class":5489,"line":8003},[5487,10770,7262],{"class":6865},[5487,10772,10773],{"class":6911}," clientID",[5487,10775,7140],{"class":6869},[5487,10777,10778],{"class":6241},"\"\"",[5487,10780,7150],{"class":6869},[5487,10782,10783,10785,10787,10789,10791,10794],{"class":5489,"line":8042},[5487,10784,7303],{"class":6911},[5487,10786,5211],{"class":6869},[5487,10788,7228],{"class":6233},[5487,10790,6926],{"class":6869},[5487,10792,10793],{"class":6241},"\"Відсутній параметр ID клієнта\"",[5487,10795,6885],{"class":6869},[5487,10797,10798],{"class":5489,"line":8047},[5487,10799,7615],{"class":6865},[5487,10801,10802],{"class":5489,"line":8052},[5487,10803,7330],{"class":6869},[5487,10805,10806],{"class":5489,"line":8076},[5487,10807,5506],{"emptyLinePlaceholder":5505},[5487,10809,10810],{"class":5489,"line":8081},[5487,10811,10812],{"class":6905},"    \u002F\u002F Реєструємо клієнта\n",[5487,10814,10815,10818,10820,10823,10825,10828],{"class":5489,"line":8086},[5487,10816,10817],{"class":6911},"    s",[5487,10819,5211],{"class":6869},[5487,10821,10822],{"class":6911},"mu",[5487,10824,5211],{"class":6869},[5487,10826,10827],{"class":6233},"Lock",[5487,10829,10726],{"class":6869},[5487,10831,10832,10834,10836,10839,10841,10844,10847],{"class":5489,"line":8095},[5487,10833,10817],{"class":6911},[5487,10835,5211],{"class":6869},[5487,10837,10838],{"class":6911},"clients",[5487,10840,10481],{"class":6869},[5487,10842,10843],{"class":6911},"clientID",[5487,10845,10846],{"class":6869},"] = ",[5487,10848,10849],{"class":6911},"conn\n",[5487,10851,10852,10854,10856,10858,10860,10863],{"class":5489,"line":8101},[5487,10853,10817],{"class":6911},[5487,10855,5211],{"class":6869},[5487,10857,10822],{"class":6911},[5487,10859,5211],{"class":6869},[5487,10861,10862],{"class":6233},"Unlock",[5487,10864,10726],{"class":6869},[5487,10866,10867,10869,10871,10873,10875,10878,10880,10883,10885,10887],{"class":5489,"line":8128},[5487,10868,7223],{"class":6911},[5487,10870,5211],{"class":6869},[5487,10872,10687],{"class":6233},[5487,10874,6926],{"class":6869},[5487,10876,10877],{"class":6241},"\"Клієнт ",[5487,10879,8020],{"class":6911},[5487,10881,10882],{"class":6241}," підключився\"",[5487,10884,5305],{"class":6869},[5487,10886,10843],{"class":6911},[5487,10888,6885],{"class":6869},[5487,10890,10891],{"class":5489,"line":8159},[5487,10892,5506],{"emptyLinePlaceholder":5505},[5487,10894,10895,10897,10900],{"class":5489,"line":8164},[5487,10896,10715],{"class":6865},[5487,10898,10899],{"class":6237}," func",[5487,10901,6900],{"class":6869},[5487,10903,10904,10907,10909,10911,10913,10915],{"class":5489,"line":8170},[5487,10905,10906],{"class":6911},"        s",[5487,10908,5211],{"class":6869},[5487,10910,10822],{"class":6911},[5487,10912,5211],{"class":6869},[5487,10914,10827],{"class":6233},[5487,10916,10726],{"class":6869},[5487,10918,10919,10922,10924,10927,10929,10931,10933,10935],{"class":5489,"line":8212},[5487,10920,10921],{"class":6233},"        delete",[5487,10923,6926],{"class":6869},[5487,10925,10926],{"class":6911},"s",[5487,10928,5211],{"class":6869},[5487,10930,10838],{"class":6911},[5487,10932,5305],{"class":6869},[5487,10934,10843],{"class":6911},[5487,10936,6885],{"class":6869},[5487,10938,10939,10941,10943,10945,10947,10949],{"class":5489,"line":8229},[5487,10940,10906],{"class":6911},[5487,10942,5211],{"class":6869},[5487,10944,10822],{"class":6911},[5487,10946,5211],{"class":6869},[5487,10948,10862],{"class":6233},[5487,10950,10726],{"class":6869},[5487,10952,10953,10955,10957,10959,10961,10963,10965,10968,10970,10972],{"class":5489,"line":8234},[5487,10954,7303],{"class":6911},[5487,10956,5211],{"class":6869},[5487,10958,10687],{"class":6233},[5487,10960,6926],{"class":6869},[5487,10962,10877],{"class":6241},[5487,10964,8020],{"class":6911},[5487,10966,10967],{"class":6241}," від'єднався\"",[5487,10969,5305],{"class":6869},[5487,10971,10843],{"class":6911},[5487,10973,6885],{"class":6869},[5487,10975,10976],{"class":5489,"line":8276},[5487,10977,10978],{"class":6869},"    }()\n",[5487,10980,10981],{"class":5489,"line":8292},[5487,10982,5506],{"emptyLinePlaceholder":5505},[5487,10984,10985],{"class":5489,"line":8297},[5487,10986,10987],{"class":6905},"    \u002F\u002F Читаємо повідомлення та пересилаємо цільовому клієнту\n",[5487,10989,10990,10992],{"class":5489,"line":8339},[5487,10991,7647],{"class":6865},[5487,10993,7150],{"class":6869},[5487,10995,10996,10999,11002],{"class":5489,"line":8355},[5487,10997,10998],{"class":6237},"        var",[5487,11000,11001],{"class":6911}," msg",[5487,11003,11004],{"class":6855}," SignalingMessage\n",[5487,11006,11007,11009,11011,11013,11016,11018,11021,11024,11027,11029,11031,11033,11035],{"class":5489,"line":8360},[5487,11008,7129],{"class":6865},[5487,11010,7265],{"class":6911},[5487,11012,6915],{"class":6869},[5487,11014,11015],{"class":6911},"conn",[5487,11017,5211],{"class":6869},[5487,11019,11020],{"class":6233},"ReadJSON",[5487,11022,11023],{"class":6869},"(&",[5487,11025,11026],{"class":6911},"msg",[5487,11028,7287],{"class":6869},[5487,11030,7290],{"class":6911},[5487,11032,7293],{"class":6869},[5487,11034,7284],{"class":6237},[5487,11036,7150],{"class":6869},[5487,11038,11039,11042,11044,11046,11048,11051,11053,11055,11057,11059],{"class":5489,"line":8365},[5487,11040,11041],{"class":6911},"            log",[5487,11043,5211],{"class":6869},[5487,11045,10687],{"class":6233},[5487,11047,6926],{"class":6869},[5487,11049,11050],{"class":6241},"\"Помилка читання повідомлення: ",[5487,11052,6767],{"class":6911},[5487,11054,7318],{"class":6241},[5487,11056,5305],{"class":6869},[5487,11058,7290],{"class":6911},[5487,11060,6885],{"class":6869},[5487,11062,11063],{"class":5489,"line":8381},[5487,11064,11065],{"class":6865},"            break\n",[5487,11067,11068],{"class":5489,"line":8409},[5487,11069,7181],{"class":6869},[5487,11071,11073],{"class":5489,"line":11072},74,[5487,11074,5506],{"emptyLinePlaceholder":5505},[5487,11076,11078,11080,11082,11084,11086,11089,11091,11094,11096,11099,11101,11103,11105,11107,11109,11112,11114,11116,11118,11120,11122,11125],{"class":5489,"line":11077},75,[5487,11079,7303],{"class":6911},[5487,11081,5211],{"class":6869},[5487,11083,10687],{"class":6233},[5487,11085,6926],{"class":6869},[5487,11087,11088],{"class":6241},"\"Отримано ",[5487,11090,8020],{"class":6911},[5487,11092,11093],{"class":6241}," від ",[5487,11095,8020],{"class":6911},[5487,11097,11098],{"class":6241}," для ",[5487,11100,8020],{"class":6911},[5487,11102,7318],{"class":6241},[5487,11104,5305],{"class":6869},[5487,11106,11026],{"class":6911},[5487,11108,5211],{"class":6869},[5487,11110,11111],{"class":6911},"Type",[5487,11113,5305],{"class":6869},[5487,11115,10843],{"class":6911},[5487,11117,5305],{"class":6869},[5487,11119,11026],{"class":6911},[5487,11121,5211],{"class":6869},[5487,11123,11124],{"class":6911},"Target",[5487,11126,6885],{"class":6869},[5487,11128,11130],{"class":5489,"line":11129},76,[5487,11131,5506],{"emptyLinePlaceholder":5505},[5487,11133,11135],{"class":5489,"line":11134},77,[5487,11136,11137],{"class":6905},"        \u002F\u002F Пересилаємо повідомлення цільовому учаснику\n",[5487,11139,11141,11143,11145,11147,11149,11152],{"class":5489,"line":11140},78,[5487,11142,10906],{"class":6911},[5487,11144,5211],{"class":6869},[5487,11146,10822],{"class":6911},[5487,11148,5211],{"class":6869},[5487,11150,11151],{"class":6233},"RLock",[5487,11153,10726],{"class":6869},[5487,11155,11157,11160,11162,11165,11167,11169,11171,11173,11175,11177,11179,11181],{"class":5489,"line":11156},79,[5487,11158,11159],{"class":6911},"        targetConn",[5487,11161,5305],{"class":6869},[5487,11163,11164],{"class":6911},"exists",[5487,11166,6915],{"class":6869},[5487,11168,10926],{"class":6911},[5487,11170,5211],{"class":6869},[5487,11172,10838],{"class":6911},[5487,11174,10481],{"class":6869},[5487,11176,11026],{"class":6911},[5487,11178,5211],{"class":6869},[5487,11180,11124],{"class":6911},[5487,11182,11183],{"class":6869},"]\n",[5487,11185,11187,11189,11191,11193,11195,11198],{"class":5489,"line":11186},80,[5487,11188,10906],{"class":6911},[5487,11190,5211],{"class":6869},[5487,11192,10822],{"class":6911},[5487,11194,5211],{"class":6869},[5487,11196,11197],{"class":6233},"RUnlock",[5487,11199,10726],{"class":6869},[5487,11201,11203],{"class":5489,"line":11202},81,[5487,11204,5506],{"emptyLinePlaceholder":5505},[5487,11206,11208,11210,11213],{"class":5489,"line":11207},82,[5487,11209,7129],{"class":6865},[5487,11211,11212],{"class":6911}," exists",[5487,11214,7150],{"class":6869},[5487,11216,11218,11221,11223,11225,11228,11230,11233,11235,11237,11239,11241,11243,11245],{"class":5489,"line":11217},83,[5487,11219,11220],{"class":6865},"            if",[5487,11222,7265],{"class":6911},[5487,11224,6915],{"class":6869},[5487,11226,11227],{"class":6911},"targetConn",[5487,11229,5211],{"class":6869},[5487,11231,11232],{"class":6233},"WriteJSON",[5487,11234,6926],{"class":6869},[5487,11236,11026],{"class":6911},[5487,11238,7287],{"class":6869},[5487,11240,7290],{"class":6911},[5487,11242,7293],{"class":6869},[5487,11244,7284],{"class":6237},[5487,11246,7150],{"class":6869},[5487,11248,11250,11253,11255,11257,11259,11262,11264,11266,11268,11270,11272,11274,11276,11278,11280,11282],{"class":5489,"line":11249},84,[5487,11251,11252],{"class":6911},"                log",[5487,11254,5211],{"class":6869},[5487,11256,10687],{"class":6233},[5487,11258,6926],{"class":6869},[5487,11260,11261],{"class":6241},"\"Помилка відправки до ",[5487,11263,8020],{"class":6911},[5487,11265,9963],{"class":6241},[5487,11267,6767],{"class":6911},[5487,11269,7318],{"class":6241},[5487,11271,5305],{"class":6869},[5487,11273,11026],{"class":6911},[5487,11275,5211],{"class":6869},[5487,11277,11124],{"class":6911},[5487,11279,5305],{"class":6869},[5487,11281,7290],{"class":6911},[5487,11283,6885],{"class":6869},[5487,11285,11287],{"class":5489,"line":11286},85,[5487,11288,11289],{"class":6869},"            }\n",[5487,11291,11293,11296,11299],{"class":5489,"line":11292},86,[5487,11294,11295],{"class":6869},"        } ",[5487,11297,11298],{"class":6865},"else",[5487,11300,7150],{"class":6869},[5487,11302,11304,11306,11308,11310,11312,11315,11317,11320,11322,11324,11326,11328],{"class":5489,"line":11303},87,[5487,11305,11041],{"class":6911},[5487,11307,5211],{"class":6869},[5487,11309,10687],{"class":6233},[5487,11311,6926],{"class":6869},[5487,11313,11314],{"class":6241},"\"Цільовий клієнт ",[5487,11316,8020],{"class":6911},[5487,11318,11319],{"class":6241}," не знайдено\"",[5487,11321,5305],{"class":6869},[5487,11323,11026],{"class":6911},[5487,11325,5211],{"class":6869},[5487,11327,11124],{"class":6911},[5487,11329,6885],{"class":6869},[5487,11331,11333],{"class":5489,"line":11332},88,[5487,11334,7181],{"class":6869},[5487,11336,11338],{"class":5489,"line":11337},89,[5487,11339,7330],{"class":6869},[5487,11341,11343],{"class":5489,"line":11342},90,[5487,11344,7336],{"class":6869},[5487,11346,11348],{"class":5489,"line":11347},91,[5487,11349,5506],{"emptyLinePlaceholder":5505},[5487,11351,11353,11355,11357],{"class":5489,"line":11352},92,[5487,11354,6894],{"class":6237},[5487,11356,6897],{"class":6233},[5487,11358,6900],{"class":6869},[5487,11360,11362,11365,11367,11370],{"class":5489,"line":11361},93,[5487,11363,11364],{"class":6911},"    server",[5487,11366,6915],{"class":6869},[5487,11368,11369],{"class":6233},"NewSignalingServer",[5487,11371,10726],{"class":6869},[5487,11373,11375],{"class":5489,"line":11374},94,[5487,11376,5506],{"emptyLinePlaceholder":5505},[5487,11378,11380,11382,11384,11386,11388,11391,11393,11396,11398,11400],{"class":5489,"line":11379},95,[5487,11381,6956],{"class":6911},[5487,11383,5211],{"class":6869},[5487,11385,7004],{"class":6233},[5487,11387,6926],{"class":6869},[5487,11389,11390],{"class":6241},"\"\u002Fsignal\"",[5487,11392,5305],{"class":6869},[5487,11394,11395],{"class":6911},"server",[5487,11397,5211],{"class":6869},[5487,11399,10609],{"class":6911},[5487,11401,6885],{"class":6869},[5487,11403,11405,11407,11409,11411,11413,11415,11417,11419,11421,11423,11425,11427,11429,11431,11433,11436],{"class":5489,"line":11404},96,[5487,11406,6956],{"class":6911},[5487,11408,5211],{"class":6869},[5487,11410,6961],{"class":6233},[5487,11412,6926],{"class":6869},[5487,11414,7009],{"class":6241},[5487,11416,5305],{"class":6869},[5487,11418,6918],{"class":6911},[5487,11420,5211],{"class":6869},[5487,11422,6923],{"class":6233},[5487,11424,6926],{"class":6869},[5487,11426,6918],{"class":6911},[5487,11428,5211],{"class":6869},[5487,11430,6933],{"class":6233},[5487,11432,6926],{"class":6869},[5487,11434,11435],{"class":6241},"\".\u002Fstatic\"",[5487,11437,11438],{"class":6869},")))\n",[5487,11440,11442],{"class":5489,"line":11441},97,[5487,11443,5506],{"emptyLinePlaceholder":5505},[5487,11445,11447,11449,11451,11453,11455,11458],{"class":5489,"line":11446},98,[5487,11448,7223],{"class":6911},[5487,11450,5211],{"class":6869},[5487,11452,7228],{"class":6233},[5487,11454,6926],{"class":6869},[5487,11456,11457],{"class":6241},"\"Signaling Server запущено на :8080\"",[5487,11459,6885],{"class":6869},[5487,11461,11463,11465,11467,11469,11471,11473,11475,11477,11479,11481,11483,11485],{"class":5489,"line":11462},99,[5487,11464,7223],{"class":6911},[5487,11466,5211],{"class":6869},[5487,11468,8388],{"class":6233},[5487,11470,6926],{"class":6869},[5487,11472,6918],{"class":6911},[5487,11474,5211],{"class":6869},[5487,11476,7274],{"class":6233},[5487,11478,6926],{"class":6869},[5487,11480,7279],{"class":6241},[5487,11482,5305],{"class":6869},[5487,11484,7284],{"class":6237},[5487,11486,6941],{"class":6869},[5487,11488,11490],{"class":5489,"line":11489},100,[5487,11491,7336],{"class":6869},[5299,11493,11494,11495,11498,11499,11502],{},"Цей базовий сервер приймає WebSocket-з'єднання, реєструє клієнтів за унікальними ID та пересилає повідомлення сигналізації між ними. У реальних застосунках сюди додається автентифікація, кімнати (",[5328,11496,11497],{},"rooms","), присутність (",[5328,11500,11501],{},"presence",") та обробка груп учасників.",[5333,11504,11505,11506,11509],{},"Протокол сигналізації ",[5235,11507,11508],{},"не стандартизований"," специфікацією WebRTC. Розробники можуть використовувати будь-який механізм обміну повідомленнями: WebSocket, Server-Sent Events, HTTP Long Polling, MQTT, навіть ручну вставку SDP через месенджер (для тестування). Важливо лише забезпечити надійну доставку SDP Offer, Answer та ICE-кандидатів між учасниками.",[5365,11511,11513],{"id":11512},"крок-3-створення-webrtc-peer-connection-на-сервері","Крок 3. Створення WebRTC Peer Connection на сервері",[5299,11515,11516],{},"Тепер реалізуємо серверну частину WebRTC, що приймає вхідне з'єднання від браузерного клієнта та транслює відео із файлу або живого джерела:",[5478,11518,11520],{"className":6843,"code":11519,"language":6845,"meta":5483,"style":5483},"package main\n\nimport (\n    \"encoding\u002Fjson\"\n    \"fmt\"\n    \"log\"\n\n    \"github.com\u002Fpion\u002Fwebrtc\u002Fv4\"\n)\n\n\u002F\u002F CreatePeerConnection створює та налаштовує WebRTC PeerConnection\nfunc CreatePeerConnection() (*webrtc.PeerConnection, error) {\n    \u002F\u002F Конфігурація ICE серверів (STUN\u002FTURN)\n    config := webrtc.Configuration{\n        ICEServers: []webrtc.ICEServer{\n            {\n                URLs: []string{\"stun:stun.l.google.com:19302\"},\n            },\n            \u002F\u002F Для продакшну додайте власний TURN-сервер:\n            \u002F\u002F {\n            \u002F\u002F     URLs:       []string{\"turn:turn.example.com:3478\"},\n            \u002F\u002F     Username:   \"user\",\n            \u002F\u002F     Credential: \"password\",\n            \u002F\u002F },\n        },\n    }\n\n    \u002F\u002F Створюємо новий PeerConnection\n    peerConnection, err := webrtc.NewPeerConnection(config)\n    if err != nil {\n        return nil, fmt.Errorf(\"помилка створення PeerConnection: %w\", err)\n    }\n\n    \u002F\u002F Обробник події підключення\n    peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {\n        log.Printf(\"Стан з'єднання змінився: %s\", state.String())\n        \n        if state == webrtc.PeerConnectionStateFailed {\n            log.Println(\"З'єднання не вдалося встановити\")\n        } else if state == webrtc.PeerConnectionStateConnected {\n            log.Println(\"WebRTC з'єднання успішно встановлено!\")\n        }\n    })\n\n    \u002F\u002F Обробник ICE-кандидатів\n    peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) {\n        if candidate == nil {\n            return\n        }\n        \n        \u002F\u002F Серіалізуємо кандидата у JSON\n        candidateJSON, err := json.Marshal(candidate.ToJSON())\n        if err != nil {\n            log.Printf(\"Помилка серіалізації ICE-кандидата: %v\", err)\n            return\n        }\n        \n        log.Printf(\"Згенеровано ICE-кандидат: %s\", string(candidateJSON))\n        \u002F\u002F Тут відправте candidateJSON клієнту через сервер сигналізації\n    })\n\n    \u002F\u002F Обробник вхідних медіапотоків (якщо peer відправляє відео)\n    peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {\n        log.Printf(\"Отримано віддалений трек: ID=%s, StreamID=%s, Codec=%s\",\n            track.ID(), track.StreamID(), track.Codec().MimeType)\n\n        \u002F\u002F Читаємо RTP-пакети із треку (для запису або ретрансляції)\n        for {\n            rtpPacket, _, readErr := track.ReadRTP()\n            if readErr != nil {\n                log.Printf(\"Помилка читання RTP: %v\", readErr)\n                return\n            }\n            \n            \u002F\u002F Обробка пакетів: запис у файл, пересилка іншим учасникам тощо\n            _ = rtpPacket\n        }\n    })\n\n    return peerConnection, nil\n}\n\n\u002F\u002F HandleOffer обробляє SDP Offer від клієнта та генерує Answer\nfunc HandleOffer(pc *webrtc.PeerConnection, offerSDP string) (string, error) {\n    \u002F\u002F Розбираємо отриманий SDP Offer\n    offer := webrtc.SessionDescription{\n        Type: webrtc.SDPTypeOffer,\n        SDP:  offerSDP,\n    }\n\n    \u002F\u002F Встановлюємо віддалений опис (Remote Description)\n    if err := pc.SetRemoteDescription(offer); err != nil {\n        return \"\", fmt.Errorf(\"помилка встановлення Remote Description: %w\", err)\n    }\n\n    \u002F\u002F Створюємо відповідь (Answer)\n    answer, err := pc.CreateAnswer(nil)\n    if err != nil {\n        return \"\", fmt.Errorf(\"помилка створення Answer: %w\", err)\n    }\n\n    \u002F\u002F Встановлюємо локальний опис (Local Description)\n    if err := pc.SetLocalDescription(answer); err != nil {\n        return \"\", fmt.Errorf(\"помилка встановлення Local Description: %w\", err)\n    }\n\n    return answer.SDP, nil\n}\n",[5208,11521,11522,11528,11532,11538,11542,11546,11550,11554,11559,11563,11567,11572,11597,11602,11618,11635,11640,11658,11663,11668,11673,11678,11683,11688,11693,11698,11702,11706,11711,11736,11748,11781,11785,11789,11794,11822,11850,11854,11872,11887,11909,11924,11928,11932,11936,11941,11970,11983,11987,11991,11995,12000,12029,12041,12064,12068,12072,12076,12104,12109,12113,12117,12122,12165,12194,12229,12233,12238,12245,12271,12284,12307,12312,12316,12321,12326,12336,12340,12344,12348,12360,12364,12368,12373,12411,12416,12432,12448,12460,12464,12468,12473,12503,12533,12537,12541,12546,12570,12582,12611,12615,12620,12626,12657,12687,12692,12697,12713],{"__ignoreMap":5483},[5487,11523,11524,11526],{"class":5489,"line":5490},[5487,11525,6852],{"class":6237},[5487,11527,6856],{"class":6855},[5487,11529,11530],{"class":5489,"line":5496},[5487,11531,5506],{"emptyLinePlaceholder":5505},[5487,11533,11534,11536],{"class":5489,"line":5502},[5487,11535,6866],{"class":6865},[5487,11537,6870],{"class":6869},[5487,11539,11540],{"class":5489,"line":5509},[5487,11541,10270],{"class":6241},[5487,11543,11544],{"class":5489,"line":5515},[5487,11545,7399],{"class":6241},[5487,11547,11548],{"class":5489,"line":5521},[5487,11549,6875],{"class":6241},[5487,11551,11552],{"class":5489,"line":5527},[5487,11553,5506],{"emptyLinePlaceholder":5505},[5487,11555,11556],{"class":5489,"line":5532},[5487,11557,11558],{"class":6241},"    \"github.com\u002Fpion\u002Fwebrtc\u002Fv4\"\n",[5487,11560,11561],{"class":5489,"line":5538},[5487,11562,6885],{"class":6869},[5487,11564,11565],{"class":5489,"line":5544},[5487,11566,5506],{"emptyLinePlaceholder":5505},[5487,11568,11569],{"class":5489,"line":5550},[5487,11570,11571],{"class":6905},"\u002F\u002F CreatePeerConnection створює та налаштовує WebRTC PeerConnection\n",[5487,11573,11574,11576,11579,11582,11585,11587,11590,11592,11595],{"class":5489,"line":5555},[5487,11575,6894],{"class":6237},[5487,11577,11578],{"class":6233}," CreatePeerConnection",[5487,11580,11581],{"class":6869},"() (*",[5487,11583,11584],{"class":6855},"webrtc",[5487,11586,5211],{"class":6869},[5487,11588,11589],{"class":6855},"PeerConnection",[5487,11591,5305],{"class":6869},[5487,11593,11594],{"class":6855},"error",[5487,11596,7043],{"class":6869},[5487,11598,11599],{"class":5489,"line":5561},[5487,11600,11601],{"class":6905},"    \u002F\u002F Конфігурація ICE серверів (STUN\u002FTURN)\n",[5487,11603,11604,11607,11609,11611,11613,11616],{"class":5489,"line":5567},[5487,11605,11606],{"class":6911},"    config",[5487,11608,6915],{"class":6869},[5487,11610,11584],{"class":6855},[5487,11612,5211],{"class":6869},[5487,11614,11615],{"class":6855},"Configuration",[5487,11617,9954],{"class":6869},[5487,11619,11620,11623,11626,11628,11630,11633],{"class":5489,"line":5704},[5487,11621,11622],{"class":6911},"        ICEServers",[5487,11624,11625],{"class":6869},": []",[5487,11627,11584],{"class":6855},[5487,11629,5211],{"class":6869},[5487,11631,11632],{"class":6855},"ICEServer",[5487,11634,9954],{"class":6869},[5487,11636,11637],{"class":5489,"line":5710},[5487,11638,11639],{"class":6869},"            {\n",[5487,11641,11642,11645,11647,11649,11652,11655],{"class":5489,"line":5716},[5487,11643,11644],{"class":6911},"                URLs",[5487,11646,11625],{"class":6869},[5487,11648,10484],{"class":6855},[5487,11650,11651],{"class":6869},"{",[5487,11653,11654],{"class":6241},"\"stun:stun.l.google.com:19302\"",[5487,11656,11657],{"class":6869},"},\n",[5487,11659,11660],{"class":5489,"line":5722},[5487,11661,11662],{"class":6869},"            },\n",[5487,11664,11665],{"class":5489,"line":5727},[5487,11666,11667],{"class":6905},"            \u002F\u002F Для продакшну додайте власний TURN-сервер:\n",[5487,11669,11670],{"class":5489,"line":6007},[5487,11671,11672],{"class":6905},"            \u002F\u002F {\n",[5487,11674,11675],{"class":5489,"line":6013},[5487,11676,11677],{"class":6905},"            \u002F\u002F     URLs:       []string{\"turn:turn.example.com:3478\"},\n",[5487,11679,11680],{"class":5489,"line":6019},[5487,11681,11682],{"class":6905},"            \u002F\u002F     Username:   \"user\",\n",[5487,11684,11685],{"class":5489,"line":6025},[5487,11686,11687],{"class":6905},"            \u002F\u002F     Credential: \"password\",\n",[5487,11689,11690],{"class":5489,"line":6030},[5487,11691,11692],{"class":6905},"            \u002F\u002F },\n",[5487,11694,11695],{"class":5489,"line":6035},[5487,11696,11697],{"class":6869},"        },\n",[5487,11699,11700],{"class":5489,"line":7188},[5487,11701,7330],{"class":6869},[5487,11703,11704],{"class":5489,"line":7209},[5487,11705,5506],{"emptyLinePlaceholder":5505},[5487,11707,11708],{"class":5489,"line":7215},[5487,11709,11710],{"class":6905},"    \u002F\u002F Створюємо новий PeerConnection\n",[5487,11712,11713,11716,11718,11720,11722,11724,11726,11729,11731,11734],{"class":5489,"line":7220},[5487,11714,11715],{"class":6911},"    peerConnection",[5487,11717,5305],{"class":6869},[5487,11719,7290],{"class":6911},[5487,11721,6915],{"class":6869},[5487,11723,11584],{"class":6911},[5487,11725,5211],{"class":6869},[5487,11727,11728],{"class":6233},"NewPeerConnection",[5487,11730,6926],{"class":6869},[5487,11732,11733],{"class":6911},"config",[5487,11735,6885],{"class":6869},[5487,11737,11738,11740,11742,11744,11746],{"class":5489,"line":7238},[5487,11739,7262],{"class":6865},[5487,11741,7265],{"class":6911},[5487,11743,7293],{"class":6869},[5487,11745,7284],{"class":6237},[5487,11747,7150],{"class":6869},[5487,11749,11750,11752,11755,11757,11760,11762,11765,11767,11770,11773,11775,11777,11779],{"class":5489,"line":7254},[5487,11751,10354],{"class":6865},[5487,11753,11754],{"class":6237}," nil",[5487,11756,5305],{"class":6869},[5487,11758,11759],{"class":6911},"fmt",[5487,11761,5211],{"class":6869},[5487,11763,11764],{"class":6233},"Errorf",[5487,11766,6926],{"class":6869},[5487,11768,11769],{"class":6241},"\"помилка створення PeerConnection: ",[5487,11771,11772],{"class":6911},"%w",[5487,11774,7318],{"class":6241},[5487,11776,5305],{"class":6869},[5487,11778,7290],{"class":6911},[5487,11780,6885],{"class":6869},[5487,11782,11783],{"class":5489,"line":7259},[5487,11784,7330],{"class":6869},[5487,11786,11787],{"class":5489,"line":7300},[5487,11788,5506],{"emptyLinePlaceholder":5505},[5487,11790,11791],{"class":5489,"line":7327},[5487,11792,11793],{"class":6905},"    \u002F\u002F Обробник події підключення\n",[5487,11795,11796,11798,11800,11803,11805,11807,11809,11812,11815,11817,11820],{"class":5489,"line":7333},[5487,11797,11715],{"class":6911},[5487,11799,5211],{"class":6869},[5487,11801,11802],{"class":6233},"OnConnectionStateChange",[5487,11804,6926],{"class":6869},[5487,11806,6894],{"class":6237},[5487,11808,6926],{"class":6869},[5487,11810,11811],{"class":6911},"state",[5487,11813,11814],{"class":6855}," webrtc",[5487,11816,5211],{"class":6869},[5487,11818,11819],{"class":6855},"PeerConnectionState",[5487,11821,7043],{"class":6869},[5487,11823,11824,11826,11828,11830,11832,11835,11837,11839,11841,11843,11845,11848],{"class":5489,"line":7744},[5487,11825,7303],{"class":6911},[5487,11827,5211],{"class":6869},[5487,11829,10687],{"class":6233},[5487,11831,6926],{"class":6869},[5487,11833,11834],{"class":6241},"\"Стан з'єднання змінився: ",[5487,11836,8020],{"class":6911},[5487,11838,7318],{"class":6241},[5487,11840,5305],{"class":6869},[5487,11842,11811],{"class":6911},[5487,11844,5211],{"class":6869},[5487,11846,11847],{"class":6233},"String",[5487,11849,7733],{"class":6869},[5487,11851,11852],{"class":5489,"line":7761},[5487,11853,7124],{"class":6869},[5487,11855,11856,11858,11861,11863,11865,11867,11870],{"class":5489,"line":7766},[5487,11857,7129],{"class":6865},[5487,11859,11860],{"class":6911}," state",[5487,11862,7140],{"class":6869},[5487,11864,11584],{"class":6911},[5487,11866,5211],{"class":6869},[5487,11868,11869],{"class":6911},"PeerConnectionStateFailed",[5487,11871,7150],{"class":6869},[5487,11873,11874,11876,11878,11880,11882,11885],{"class":5489,"line":7772},[5487,11875,11041],{"class":6911},[5487,11877,5211],{"class":6869},[5487,11879,7228],{"class":6233},[5487,11881,6926],{"class":6869},[5487,11883,11884],{"class":6241},"\"З'єднання не вдалося встановити\"",[5487,11886,6885],{"class":6869},[5487,11888,11889,11891,11893,11896,11898,11900,11902,11904,11907],{"class":5489,"line":7797},[5487,11890,11295],{"class":6869},[5487,11892,11298],{"class":6865},[5487,11894,11895],{"class":6865}," if",[5487,11897,11860],{"class":6911},[5487,11899,7140],{"class":6869},[5487,11901,11584],{"class":6911},[5487,11903,5211],{"class":6869},[5487,11905,11906],{"class":6911},"PeerConnectionStateConnected",[5487,11908,7150],{"class":6869},[5487,11910,11911,11913,11915,11917,11919,11922],{"class":5489,"line":7820},[5487,11912,11041],{"class":6911},[5487,11914,5211],{"class":6869},[5487,11916,7228],{"class":6233},[5487,11918,6926],{"class":6869},[5487,11920,11921],{"class":6241},"\"WebRTC з'єднання успішно встановлено!\"",[5487,11923,6885],{"class":6869},[5487,11925,11926],{"class":5489,"line":7825},[5487,11927,7181],{"class":6869},[5487,11929,11930],{"class":5489,"line":7852},[5487,11931,7212],{"class":6869},[5487,11933,11934],{"class":5489,"line":7876},[5487,11935,5506],{"emptyLinePlaceholder":5505},[5487,11937,11938],{"class":5489,"line":7918},[5487,11939,11940],{"class":6905},"    \u002F\u002F Обробник ICE-кандидатів\n",[5487,11942,11943,11945,11947,11950,11952,11954,11956,11959,11961,11963,11965,11968],{"class":5489,"line":7943},[5487,11944,11715],{"class":6911},[5487,11946,5211],{"class":6869},[5487,11948,11949],{"class":6233},"OnICECandidate",[5487,11951,6926],{"class":6869},[5487,11953,6894],{"class":6237},[5487,11955,6926],{"class":6869},[5487,11957,11958],{"class":6911},"candidate",[5487,11960,7033],{"class":6869},[5487,11962,11584],{"class":6855},[5487,11964,5211],{"class":6869},[5487,11966,11967],{"class":6855},"ICECandidate",[5487,11969,7043],{"class":6869},[5487,11971,11972,11974,11977,11979,11981],{"class":5489,"line":7948},[5487,11973,7129],{"class":6865},[5487,11975,11976],{"class":6911}," candidate",[5487,11978,7140],{"class":6869},[5487,11980,7284],{"class":6237},[5487,11982,7150],{"class":6869},[5487,11984,11985],{"class":5489,"line":7968},[5487,11986,7176],{"class":6865},[5487,11988,11989],{"class":5489,"line":8003},[5487,11990,7181],{"class":6869},[5487,11992,11993],{"class":5489,"line":8042},[5487,11994,7124],{"class":6869},[5487,11996,11997],{"class":5489,"line":8047},[5487,11998,11999],{"class":6905},"        \u002F\u002F Серіалізуємо кандидата у JSON\n",[5487,12001,12002,12005,12007,12009,12011,12013,12015,12018,12020,12022,12024,12027],{"class":5489,"line":8052},[5487,12003,12004],{"class":6911},"        candidateJSON",[5487,12006,5305],{"class":6869},[5487,12008,7290],{"class":6911},[5487,12010,6915],{"class":6869},[5487,12012,9947],{"class":6911},[5487,12014,5211],{"class":6869},[5487,12016,12017],{"class":6233},"Marshal",[5487,12019,6926],{"class":6869},[5487,12021,11958],{"class":6911},[5487,12023,5211],{"class":6869},[5487,12025,12026],{"class":6233},"ToJSON",[5487,12028,7733],{"class":6869},[5487,12030,12031,12033,12035,12037,12039],{"class":5489,"line":8076},[5487,12032,7129],{"class":6865},[5487,12034,7265],{"class":6911},[5487,12036,7293],{"class":6869},[5487,12038,7284],{"class":6237},[5487,12040,7150],{"class":6869},[5487,12042,12043,12045,12047,12049,12051,12054,12056,12058,12060,12062],{"class":5489,"line":8081},[5487,12044,11041],{"class":6911},[5487,12046,5211],{"class":6869},[5487,12048,10687],{"class":6233},[5487,12050,6926],{"class":6869},[5487,12052,12053],{"class":6241},"\"Помилка серіалізації ICE-кандидата: ",[5487,12055,6767],{"class":6911},[5487,12057,7318],{"class":6241},[5487,12059,5305],{"class":6869},[5487,12061,7290],{"class":6911},[5487,12063,6885],{"class":6869},[5487,12065,12066],{"class":5489,"line":8086},[5487,12067,7176],{"class":6865},[5487,12069,12070],{"class":5489,"line":8095},[5487,12071,7181],{"class":6869},[5487,12073,12074],{"class":5489,"line":8101},[5487,12075,7124],{"class":6869},[5487,12077,12078,12080,12082,12084,12086,12089,12091,12093,12095,12097,12099,12102],{"class":5489,"line":8128},[5487,12079,7303],{"class":6911},[5487,12081,5211],{"class":6869},[5487,12083,10687],{"class":6233},[5487,12085,6926],{"class":6869},[5487,12087,12088],{"class":6241},"\"Згенеровано ICE-кандидат: ",[5487,12090,8020],{"class":6911},[5487,12092,7318],{"class":6241},[5487,12094,5305],{"class":6869},[5487,12096,10484],{"class":6855},[5487,12098,6926],{"class":6869},[5487,12100,12101],{"class":6911},"candidateJSON",[5487,12103,6941],{"class":6869},[5487,12105,12106],{"class":5489,"line":8159},[5487,12107,12108],{"class":6905},"        \u002F\u002F Тут відправте candidateJSON клієнту через сервер сигналізації\n",[5487,12110,12111],{"class":5489,"line":8164},[5487,12112,7212],{"class":6869},[5487,12114,12115],{"class":5489,"line":8170},[5487,12116,5506],{"emptyLinePlaceholder":5505},[5487,12118,12119],{"class":5489,"line":8212},[5487,12120,12121],{"class":6905},"    \u002F\u002F Обробник вхідних медіапотоків (якщо peer відправляє відео)\n",[5487,12123,12124,12126,12128,12131,12133,12135,12137,12140,12142,12144,12146,12149,12151,12154,12156,12158,12160,12163],{"class":5489,"line":8229},[5487,12125,11715],{"class":6911},[5487,12127,5211],{"class":6869},[5487,12129,12130],{"class":6233},"OnTrack",[5487,12132,6926],{"class":6869},[5487,12134,6894],{"class":6237},[5487,12136,6926],{"class":6869},[5487,12138,12139],{"class":6911},"track",[5487,12141,7033],{"class":6869},[5487,12143,11584],{"class":6855},[5487,12145,5211],{"class":6869},[5487,12147,12148],{"class":6855},"TrackRemote",[5487,12150,5305],{"class":6869},[5487,12152,12153],{"class":6911},"receiver",[5487,12155,7033],{"class":6869},[5487,12157,11584],{"class":6855},[5487,12159,5211],{"class":6869},[5487,12161,12162],{"class":6855},"RTPReceiver",[5487,12164,7043],{"class":6869},[5487,12166,12167,12169,12171,12173,12175,12178,12180,12183,12185,12188,12190,12192],{"class":5489,"line":8234},[5487,12168,7303],{"class":6911},[5487,12170,5211],{"class":6869},[5487,12172,10687],{"class":6233},[5487,12174,6926],{"class":6869},[5487,12176,12177],{"class":6241},"\"Отримано віддалений трек: ID=",[5487,12179,8020],{"class":6911},[5487,12181,12182],{"class":6241},", StreamID=",[5487,12184,8020],{"class":6911},[5487,12186,12187],{"class":6241},", Codec=",[5487,12189,8020],{"class":6911},[5487,12191,7318],{"class":6241},[5487,12193,9969],{"class":6869},[5487,12195,12196,12199,12201,12204,12206,12208,12210,12213,12215,12217,12219,12222,12224,12227],{"class":5489,"line":8276},[5487,12197,12198],{"class":6911},"            track",[5487,12200,5211],{"class":6869},[5487,12202,12203],{"class":6233},"ID",[5487,12205,7702],{"class":6869},[5487,12207,12139],{"class":6911},[5487,12209,5211],{"class":6869},[5487,12211,12212],{"class":6233},"StreamID",[5487,12214,7702],{"class":6869},[5487,12216,12139],{"class":6911},[5487,12218,5211],{"class":6869},[5487,12220,12221],{"class":6233},"Codec",[5487,12223,7056],{"class":6869},[5487,12225,12226],{"class":6911},"MimeType",[5487,12228,6885],{"class":6869},[5487,12230,12231],{"class":5489,"line":8292},[5487,12232,5506],{"emptyLinePlaceholder":5505},[5487,12234,12235],{"class":5489,"line":8297},[5487,12236,12237],{"class":6905},"        \u002F\u002F Читаємо RTP-пакети із треку (для запису або ретрансляції)\n",[5487,12239,12240,12243],{"class":5489,"line":8339},[5487,12241,12242],{"class":6865},"        for",[5487,12244,7150],{"class":6869},[5487,12246,12247,12250,12252,12255,12257,12260,12262,12264,12266,12269],{"class":5489,"line":8355},[5487,12248,12249],{"class":6911},"            rtpPacket",[5487,12251,5305],{"class":6869},[5487,12253,12254],{"class":6911},"_",[5487,12256,5305],{"class":6869},[5487,12258,12259],{"class":6911},"readErr",[5487,12261,6915],{"class":6869},[5487,12263,12139],{"class":6911},[5487,12265,5211],{"class":6869},[5487,12267,12268],{"class":6233},"ReadRTP",[5487,12270,10726],{"class":6869},[5487,12272,12273,12275,12278,12280,12282],{"class":5489,"line":8360},[5487,12274,11220],{"class":6865},[5487,12276,12277],{"class":6911}," readErr",[5487,12279,7293],{"class":6869},[5487,12281,7284],{"class":6237},[5487,12283,7150],{"class":6869},[5487,12285,12286,12288,12290,12292,12294,12297,12299,12301,12303,12305],{"class":5489,"line":8365},[5487,12287,11252],{"class":6911},[5487,12289,5211],{"class":6869},[5487,12291,10687],{"class":6233},[5487,12293,6926],{"class":6869},[5487,12295,12296],{"class":6241},"\"Помилка читання RTP: ",[5487,12298,6767],{"class":6911},[5487,12300,7318],{"class":6241},[5487,12302,5305],{"class":6869},[5487,12304,12259],{"class":6911},[5487,12306,6885],{"class":6869},[5487,12308,12309],{"class":5489,"line":8381},[5487,12310,12311],{"class":6865},"                return\n",[5487,12313,12314],{"class":5489,"line":8409},[5487,12315,11289],{"class":6869},[5487,12317,12318],{"class":5489,"line":11072},[5487,12319,12320],{"class":6869},"            \n",[5487,12322,12323],{"class":5489,"line":11077},[5487,12324,12325],{"class":6905},"            \u002F\u002F Обробка пакетів: запис у файл, пересилка іншим учасникам тощо\n",[5487,12327,12328,12331,12333],{"class":5489,"line":11129},[5487,12329,12330],{"class":6911},"            _",[5487,12332,7451],{"class":6869},[5487,12334,12335],{"class":6911},"rtpPacket\n",[5487,12337,12338],{"class":5489,"line":11134},[5487,12339,7181],{"class":6869},[5487,12341,12342],{"class":5489,"line":11140},[5487,12343,7212],{"class":6869},[5487,12345,12346],{"class":5489,"line":11156},[5487,12347,5506],{"emptyLinePlaceholder":5505},[5487,12349,12350,12352,12355,12357],{"class":5489,"line":11186},[5487,12351,10535],{"class":6865},[5487,12353,12354],{"class":6911}," peerConnection",[5487,12356,5305],{"class":6869},[5487,12358,12359],{"class":6237},"nil\n",[5487,12361,12362],{"class":5489,"line":11202},[5487,12363,7336],{"class":6869},[5487,12365,12366],{"class":5489,"line":11207},[5487,12367,5506],{"emptyLinePlaceholder":5505},[5487,12369,12370],{"class":5489,"line":11217},[5487,12371,12372],{"class":6905},"\u002F\u002F HandleOffer обробляє SDP Offer від клієнта та генерує Answer\n",[5487,12374,12375,12377,12380,12382,12385,12387,12389,12391,12393,12395,12398,12400,12403,12405,12407,12409],{"class":5489,"line":11249},[5487,12376,6894],{"class":6237},[5487,12378,12379],{"class":6233}," HandleOffer",[5487,12381,6926],{"class":6869},[5487,12383,12384],{"class":6911},"pc",[5487,12386,7033],{"class":6869},[5487,12388,11584],{"class":6855},[5487,12390,5211],{"class":6869},[5487,12392,11589],{"class":6855},[5487,12394,5305],{"class":6869},[5487,12396,12397],{"class":6911},"offerSDP",[5487,12399,7506],{"class":6855},[5487,12401,12402],{"class":6869},") (",[5487,12404,10484],{"class":6855},[5487,12406,5305],{"class":6869},[5487,12408,11594],{"class":6855},[5487,12410,7043],{"class":6869},[5487,12412,12413],{"class":5489,"line":11286},[5487,12414,12415],{"class":6905},"    \u002F\u002F Розбираємо отриманий SDP Offer\n",[5487,12417,12418,12421,12423,12425,12427,12430],{"class":5489,"line":11292},[5487,12419,12420],{"class":6911},"    offer",[5487,12422,6915],{"class":6869},[5487,12424,11584],{"class":6855},[5487,12426,5211],{"class":6869},[5487,12428,12429],{"class":6855},"SessionDescription",[5487,12431,9954],{"class":6869},[5487,12433,12434,12437,12439,12441,12443,12446],{"class":5489,"line":11303},[5487,12435,12436],{"class":6911},"        Type",[5487,12438,9963],{"class":6869},[5487,12440,11584],{"class":6911},[5487,12442,5211],{"class":6869},[5487,12444,12445],{"class":6911},"SDPTypeOffer",[5487,12447,9969],{"class":6869},[5487,12449,12450,12453,12456,12458],{"class":5489,"line":11332},[5487,12451,12452],{"class":6911},"        SDP",[5487,12454,12455],{"class":6869},":  ",[5487,12457,12397],{"class":6911},[5487,12459,9969],{"class":6869},[5487,12461,12462],{"class":5489,"line":11337},[5487,12463,7330],{"class":6869},[5487,12465,12466],{"class":5489,"line":11342},[5487,12467,5506],{"emptyLinePlaceholder":5505},[5487,12469,12470],{"class":5489,"line":11347},[5487,12471,12472],{"class":6905},"    \u002F\u002F Встановлюємо віддалений опис (Remote Description)\n",[5487,12474,12475,12477,12479,12481,12483,12485,12488,12490,12493,12495,12497,12499,12501],{"class":5489,"line":11352},[5487,12476,7262],{"class":6865},[5487,12478,7265],{"class":6911},[5487,12480,6915],{"class":6869},[5487,12482,12384],{"class":6911},[5487,12484,5211],{"class":6869},[5487,12486,12487],{"class":6233},"SetRemoteDescription",[5487,12489,6926],{"class":6869},[5487,12491,12492],{"class":6911},"offer",[5487,12494,7287],{"class":6869},[5487,12496,7290],{"class":6911},[5487,12498,7293],{"class":6869},[5487,12500,7284],{"class":6237},[5487,12502,7150],{"class":6869},[5487,12504,12505,12507,12510,12512,12514,12516,12518,12520,12523,12525,12527,12529,12531],{"class":5489,"line":11361},[5487,12506,10354],{"class":6865},[5487,12508,12509],{"class":6241}," \"\"",[5487,12511,5305],{"class":6869},[5487,12513,11759],{"class":6911},[5487,12515,5211],{"class":6869},[5487,12517,11764],{"class":6233},[5487,12519,6926],{"class":6869},[5487,12521,12522],{"class":6241},"\"помилка встановлення Remote Description: ",[5487,12524,11772],{"class":6911},[5487,12526,7318],{"class":6241},[5487,12528,5305],{"class":6869},[5487,12530,7290],{"class":6911},[5487,12532,6885],{"class":6869},[5487,12534,12535],{"class":5489,"line":11374},[5487,12536,7330],{"class":6869},[5487,12538,12539],{"class":5489,"line":11379},[5487,12540,5506],{"emptyLinePlaceholder":5505},[5487,12542,12543],{"class":5489,"line":11404},[5487,12544,12545],{"class":6905},"    \u002F\u002F Створюємо відповідь (Answer)\n",[5487,12547,12548,12551,12553,12555,12557,12559,12561,12564,12566,12568],{"class":5489,"line":11441},[5487,12549,12550],{"class":6911},"    answer",[5487,12552,5305],{"class":6869},[5487,12554,7290],{"class":6911},[5487,12556,6915],{"class":6869},[5487,12558,12384],{"class":6911},[5487,12560,5211],{"class":6869},[5487,12562,12563],{"class":6233},"CreateAnswer",[5487,12565,6926],{"class":6869},[5487,12567,7284],{"class":6237},[5487,12569,6885],{"class":6869},[5487,12571,12572,12574,12576,12578,12580],{"class":5489,"line":11446},[5487,12573,7262],{"class":6865},[5487,12575,7265],{"class":6911},[5487,12577,7293],{"class":6869},[5487,12579,7284],{"class":6237},[5487,12581,7150],{"class":6869},[5487,12583,12584,12586,12588,12590,12592,12594,12596,12598,12601,12603,12605,12607,12609],{"class":5489,"line":11462},[5487,12585,10354],{"class":6865},[5487,12587,12509],{"class":6241},[5487,12589,5305],{"class":6869},[5487,12591,11759],{"class":6911},[5487,12593,5211],{"class":6869},[5487,12595,11764],{"class":6233},[5487,12597,6926],{"class":6869},[5487,12599,12600],{"class":6241},"\"помилка створення Answer: ",[5487,12602,11772],{"class":6911},[5487,12604,7318],{"class":6241},[5487,12606,5305],{"class":6869},[5487,12608,7290],{"class":6911},[5487,12610,6885],{"class":6869},[5487,12612,12613],{"class":5489,"line":11489},[5487,12614,7330],{"class":6869},[5487,12616,12618],{"class":5489,"line":12617},101,[5487,12619,5506],{"emptyLinePlaceholder":5505},[5487,12621,12623],{"class":5489,"line":12622},102,[5487,12624,12625],{"class":6905},"    \u002F\u002F Встановлюємо локальний опис (Local Description)\n",[5487,12627,12629,12631,12633,12635,12637,12639,12642,12644,12647,12649,12651,12653,12655],{"class":5489,"line":12628},103,[5487,12630,7262],{"class":6865},[5487,12632,7265],{"class":6911},[5487,12634,6915],{"class":6869},[5487,12636,12384],{"class":6911},[5487,12638,5211],{"class":6869},[5487,12640,12641],{"class":6233},"SetLocalDescription",[5487,12643,6926],{"class":6869},[5487,12645,12646],{"class":6911},"answer",[5487,12648,7287],{"class":6869},[5487,12650,7290],{"class":6911},[5487,12652,7293],{"class":6869},[5487,12654,7284],{"class":6237},[5487,12656,7150],{"class":6869},[5487,12658,12660,12662,12664,12666,12668,12670,12672,12674,12677,12679,12681,12683,12685],{"class":5489,"line":12659},104,[5487,12661,10354],{"class":6865},[5487,12663,12509],{"class":6241},[5487,12665,5305],{"class":6869},[5487,12667,11759],{"class":6911},[5487,12669,5211],{"class":6869},[5487,12671,11764],{"class":6233},[5487,12673,6926],{"class":6869},[5487,12675,12676],{"class":6241},"\"помилка встановлення Local Description: ",[5487,12678,11772],{"class":6911},[5487,12680,7318],{"class":6241},[5487,12682,5305],{"class":6869},[5487,12684,7290],{"class":6911},[5487,12686,6885],{"class":6869},[5487,12688,12690],{"class":5489,"line":12689},105,[5487,12691,7330],{"class":6869},[5487,12693,12695],{"class":5489,"line":12694},106,[5487,12696,5506],{"emptyLinePlaceholder":5505},[5487,12698,12700,12702,12705,12707,12709,12711],{"class":5489,"line":12699},107,[5487,12701,10535],{"class":6865},[5487,12703,12704],{"class":6911}," answer",[5487,12706,5211],{"class":6869},[5487,12708,9365],{"class":6911},[5487,12710,5305],{"class":6869},[5487,12712,12359],{"class":6237},[5487,12714,12716],{"class":5489,"line":12715},108,[5487,12717,7336],{"class":6869},[5299,12719,12720],{},[5235,12721,12722],{},"Основні кроки роботи з PeerConnection:",[6045,12724,12725,12731,12737,12743],{},[5201,12726,12727,12730],{},[5235,12728,12729],{},"Створення конфігурації"," із адресами STUN\u002FTURN серверів.",[5201,12732,12733,12736],{},[5235,12734,12735],{},"Реєстрація обробників подій",": зміна стану з'єднання, нові ICE-кандидати, вхідні медіатреки.",[5201,12738,12739,12742],{},[5235,12740,12741],{},"Обробка SDP Offer:"," встановлення віддаленого опису, генерація Answer, встановлення локального опису.",[5201,12744,12745,12748],{},[5235,12746,12747],{},"Передача ICE-кандидатів"," клієнту через сервер сигналізації у міру їх генерації.",[5365,12750,12752],{"id":12751},"детальний-розбір-ключових-типів-та-методів-pion-webrtc","Детальний розбір ключових типів та методів Pion WebRTC",[5470,12754,12756,12757],{"id":12755},"структура-webrtcconfiguration","Структура ",[5208,12758,12759],{},"webrtc.Configuration",[5299,12761,12762],{},"Об'єкт конфігурації визначає параметри створення з'єднання та містить наступні поля:",[5478,12764,12766],{"className":6843,"code":12765,"language":6845,"meta":5483,"style":5483},"type Configuration struct {\n    \u002F\u002F ICEServers - список STUN\u002FTURN серверів для збору кандидатів\n    ICEServers []ICEServer\n    \n    \u002F\u002F ICETransportPolicy - політика вибору ICE-кандидатів:\n    \u002F\u002F - ICETransportPolicyAll (за замовчуванням): використовувати всі типи\n    \u002F\u002F - ICETransportPolicyRelay: лише через TURN (для примусового проксування)\n    ICETransportPolicy ICETransportPolicy\n    \n    \u002F\u002F BundlePolicy - політика об'єднання медіапотоків у один транспорт:\n    \u002F\u002F - BundlePolicyBalanced: баланс між продуктивністю та надійністю\n    \u002F\u002F - BundlePolicyMaxBundle: всі потоки через одне з'єднання (економія портів)\n    BundlePolicy BundlePolicy\n    \n    \u002F\u002F Certificates - масив сертифікатів DTLS для автентифікації\n    \u002F\u002F (автоматично генеруються, якщо не вказані)\n    Certificates []Certificate\n}\n",[5208,12767,12768,12779,12784,12794,12798,12803,12808,12813,12821,12825,12830,12835,12840,12848,12852,12857,12862,12872],{"__ignoreMap":5483},[5487,12769,12770,12772,12775,12777],{"class":5489,"line":5490},[5487,12771,10383],{"class":6237},[5487,12773,12774],{"class":6855}," Configuration",[5487,12776,10389],{"class":6237},[5487,12778,7150],{"class":6869},[5487,12780,12781],{"class":5489,"line":5496},[5487,12782,12783],{"class":6905},"    \u002F\u002F ICEServers - список STUN\u002FTURN серверів для збору кандидатів\n",[5487,12785,12786,12789,12791],{"class":5489,"line":5502},[5487,12787,12788],{"class":6911},"    ICEServers",[5487,12790,7639],{"class":6869},[5487,12792,12793],{"class":6855},"ICEServer\n",[5487,12795,12796],{"class":5489,"line":5509},[5487,12797,6946],{"class":6869},[5487,12799,12800],{"class":5489,"line":5515},[5487,12801,12802],{"class":6905},"    \u002F\u002F ICETransportPolicy - політика вибору ICE-кандидатів:\n",[5487,12804,12805],{"class":5489,"line":5521},[5487,12806,12807],{"class":6905},"    \u002F\u002F - ICETransportPolicyAll (за замовчуванням): використовувати всі типи\n",[5487,12809,12810],{"class":5489,"line":5527},[5487,12811,12812],{"class":6905},"    \u002F\u002F - ICETransportPolicyRelay: лише через TURN (для примусового проксування)\n",[5487,12814,12815,12818],{"class":5489,"line":5532},[5487,12816,12817],{"class":6911},"    ICETransportPolicy",[5487,12819,12820],{"class":6855}," ICETransportPolicy\n",[5487,12822,12823],{"class":5489,"line":5538},[5487,12824,6946],{"class":6869},[5487,12826,12827],{"class":5489,"line":5544},[5487,12828,12829],{"class":6905},"    \u002F\u002F BundlePolicy - політика об'єднання медіапотоків у один транспорт:\n",[5487,12831,12832],{"class":5489,"line":5550},[5487,12833,12834],{"class":6905},"    \u002F\u002F - BundlePolicyBalanced: баланс між продуктивністю та надійністю\n",[5487,12836,12837],{"class":5489,"line":5555},[5487,12838,12839],{"class":6905},"    \u002F\u002F - BundlePolicyMaxBundle: всі потоки через одне з'єднання (економія портів)\n",[5487,12841,12842,12845],{"class":5489,"line":5561},[5487,12843,12844],{"class":6911},"    BundlePolicy",[5487,12846,12847],{"class":6855}," BundlePolicy\n",[5487,12849,12850],{"class":5489,"line":5567},[5487,12851,6946],{"class":6869},[5487,12853,12854],{"class":5489,"line":5704},[5487,12855,12856],{"class":6905},"    \u002F\u002F Certificates - масив сертифікатів DTLS для автентифікації\n",[5487,12858,12859],{"class":5489,"line":5710},[5487,12860,12861],{"class":6905},"    \u002F\u002F (автоматично генеруються, якщо не вказані)\n",[5487,12863,12864,12867,12869],{"class":5489,"line":5716},[5487,12865,12866],{"class":6911},"    Certificates",[5487,12868,7639],{"class":6869},[5487,12870,12871],{"class":6855},"Certificate\n",[5487,12873,12874],{"class":5489,"line":5722},[5487,12875,7336],{"class":6869},[5299,12877,12878,12882],{},[5235,12879,12756,12880],{},[5208,12881,11632],{}," описує STUN або TURN сервер:",[5478,12884,12886],{"className":6843,"code":12885,"language":6845,"meta":5483,"style":5483},"type ICEServer struct {\n    URLs       []string \u002F\u002F Список URL серверів (можна вказати резервні)\n    Username   string   \u002F\u002F Ім'я користувача (для TURN з автентифікацією)\n    Credential string   \u002F\u002F Пароль або токен доступу\n    \n    \u002F\u002F CredentialType - тип автентифікації:\n    \u002F\u002F - ICECredentialTypePassword (за замовчуванням)\n    \u002F\u002F - ICECredentialTypeOauth (токен OAuth для TURN)\n    CredentialType ICECredentialType\n}\n",[5208,12887,12888,12899,12912,12923,12933,12937,12942,12947,12952,12960],{"__ignoreMap":5483},[5487,12889,12890,12892,12895,12897],{"class":5489,"line":5490},[5487,12891,10383],{"class":6237},[5487,12893,12894],{"class":6855}," ICEServer",[5487,12896,10389],{"class":6237},[5487,12898,7150],{"class":6869},[5487,12900,12901,12904,12907,12909],{"class":5489,"line":5496},[5487,12902,12903],{"class":6911},"    URLs",[5487,12905,12906],{"class":6869},"       []",[5487,12908,10484],{"class":6855},[5487,12910,12911],{"class":6905}," \u002F\u002F Список URL серверів (можна вказати резервні)\n",[5487,12913,12914,12917,12920],{"class":5489,"line":5502},[5487,12915,12916],{"class":6911},"    Username",[5487,12918,12919],{"class":6855},"   string",[5487,12921,12922],{"class":6905},"   \u002F\u002F Ім'я користувача (для TURN з автентифікацією)\n",[5487,12924,12925,12928,12930],{"class":5489,"line":5509},[5487,12926,12927],{"class":6911},"    Credential",[5487,12929,7506],{"class":6855},[5487,12931,12932],{"class":6905},"   \u002F\u002F Пароль або токен доступу\n",[5487,12934,12935],{"class":5489,"line":5515},[5487,12936,6946],{"class":6869},[5487,12938,12939],{"class":5489,"line":5521},[5487,12940,12941],{"class":6905},"    \u002F\u002F CredentialType - тип автентифікації:\n",[5487,12943,12944],{"class":5489,"line":5527},[5487,12945,12946],{"class":6905},"    \u002F\u002F - ICECredentialTypePassword (за замовчуванням)\n",[5487,12948,12949],{"class":5489,"line":5532},[5487,12950,12951],{"class":6905},"    \u002F\u002F - ICECredentialTypeOauth (токен OAuth для TURN)\n",[5487,12953,12954,12957],{"class":5489,"line":5538},[5487,12955,12956],{"class":6911},"    CredentialType",[5487,12958,12959],{"class":6855}," ICECredentialType\n",[5487,12961,12962],{"class":5489,"line":5544},[5487,12963,7336],{"class":6869},[5299,12965,12966],{},[5235,12967,12968],{},"Приклад розширеної конфігурації:",[5478,12970,12972],{"className":6843,"code":12971,"language":6845,"meta":5483,"style":5483},"config := webrtc.Configuration{\n    ICEServers: []webrtc.ICEServer{\n        {\n            URLs: []string{\n                \"stun:stun.l.google.com:19302\",\n                \"stun:stun1.l.google.com:19302\", \u002F\u002F Резервний STUN\n            },\n        },\n        {\n            URLs:       []string{\"turn:turn.example.com:3478\"},\n            Username:   \"myuser\",\n            Credential: \"mypassword\",\n        },\n    },\n    ICETransportPolicy: webrtc.ICETransportPolicyAll,\n    BundlePolicy:       webrtc.BundlePolicyMaxBundle, \u002F\u002F Мінімізація портів\n}\n",[5208,12973,12974,12988,13002,13007,13018,13025,13035,13039,13043,13047,13063,13076,13088,13092,13096,13111,13130],{"__ignoreMap":5483},[5487,12975,12976,12978,12980,12982,12984,12986],{"class":5489,"line":5490},[5487,12977,11733],{"class":6911},[5487,12979,6915],{"class":6869},[5487,12981,11584],{"class":6855},[5487,12983,5211],{"class":6869},[5487,12985,11615],{"class":6855},[5487,12987,9954],{"class":6869},[5487,12989,12990,12992,12994,12996,12998,13000],{"class":5489,"line":5496},[5487,12991,12788],{"class":6911},[5487,12993,11625],{"class":6869},[5487,12995,11584],{"class":6855},[5487,12997,5211],{"class":6869},[5487,12999,11632],{"class":6855},[5487,13001,9954],{"class":6869},[5487,13003,13004],{"class":5489,"line":5502},[5487,13005,13006],{"class":6869},"        {\n",[5487,13008,13009,13012,13014,13016],{"class":5489,"line":5509},[5487,13010,13011],{"class":6911},"            URLs",[5487,13013,11625],{"class":6869},[5487,13015,10484],{"class":6855},[5487,13017,9954],{"class":6869},[5487,13019,13020,13023],{"class":5489,"line":5515},[5487,13021,13022],{"class":6241},"                \"stun:stun.l.google.com:19302\"",[5487,13024,9969],{"class":6869},[5487,13026,13027,13030,13032],{"class":5489,"line":5521},[5487,13028,13029],{"class":6241},"                \"stun:stun1.l.google.com:19302\"",[5487,13031,5305],{"class":6869},[5487,13033,13034],{"class":6905},"\u002F\u002F Резервний STUN\n",[5487,13036,13037],{"class":5489,"line":5527},[5487,13038,11662],{"class":6869},[5487,13040,13041],{"class":5489,"line":5532},[5487,13042,11697],{"class":6869},[5487,13044,13045],{"class":5489,"line":5538},[5487,13046,13006],{"class":6869},[5487,13048,13049,13051,13054,13056,13058,13061],{"class":5489,"line":5544},[5487,13050,13011],{"class":6911},[5487,13052,13053],{"class":6869},":       []",[5487,13055,10484],{"class":6855},[5487,13057,11651],{"class":6869},[5487,13059,13060],{"class":6241},"\"turn:turn.example.com:3478\"",[5487,13062,11657],{"class":6869},[5487,13064,13065,13068,13071,13074],{"class":5489,"line":5550},[5487,13066,13067],{"class":6911},"            Username",[5487,13069,13070],{"class":6869},":   ",[5487,13072,13073],{"class":6241},"\"myuser\"",[5487,13075,9969],{"class":6869},[5487,13077,13078,13081,13083,13086],{"class":5489,"line":5555},[5487,13079,13080],{"class":6911},"            Credential",[5487,13082,9963],{"class":6869},[5487,13084,13085],{"class":6241},"\"mypassword\"",[5487,13087,9969],{"class":6869},[5487,13089,13090],{"class":5489,"line":5561},[5487,13091,11697],{"class":6869},[5487,13093,13094],{"class":5489,"line":5567},[5487,13095,10365],{"class":6869},[5487,13097,13098,13100,13102,13104,13106,13109],{"class":5489,"line":5704},[5487,13099,12817],{"class":6911},[5487,13101,9963],{"class":6869},[5487,13103,11584],{"class":6911},[5487,13105,5211],{"class":6869},[5487,13107,13108],{"class":6911},"ICETransportPolicyAll",[5487,13110,9969],{"class":6869},[5487,13112,13113,13115,13118,13120,13122,13125,13127],{"class":5489,"line":5710},[5487,13114,12844],{"class":6911},[5487,13116,13117],{"class":6869},":       ",[5487,13119,11584],{"class":6911},[5487,13121,5211],{"class":6869},[5487,13123,13124],{"class":6911},"BundlePolicyMaxBundle",[5487,13126,5305],{"class":6869},[5487,13128,13129],{"class":6905},"\u002F\u002F Мінімізація портів\n",[5487,13131,13132],{"class":5489,"line":5716},[5487,13133,7336],{"class":6869},[5470,13135,13137,13138,13141,13142],{"id":13136},"метод-addtrack-та-структура-rtpsender","Метод ",[5208,13139,13140],{},"AddTrack()"," та структура ",[5208,13143,13144],{},"RTPSender",[5299,13146,13137,13147,13149,13150,13152],{},[5208,13148,13140],{}," додає локальний медіатрек до з'єднання та повертає об'єкт ",[5208,13151,13144],{},", який контролює передачу RTP-пакетів:",[5478,13154,13156],{"className":6843,"code":13155,"language":6845,"meta":5483,"style":5483},"\u002F\u002F Сигнатура методу\nfunc (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error)\n",[5208,13157,13158,13163],{"__ignoreMap":5483},[5487,13159,13160],{"class":5489,"line":5490},[5487,13161,13162],{"class":6905},"\u002F\u002F Сигнатура методу\n",[5487,13164,13165,13167,13169,13172,13174,13176,13178,13181,13183,13185,13188,13191,13193,13195,13197],{"class":5489,"line":5496},[5487,13166,6894],{"class":6237},[5487,13168,10167],{"class":6869},[5487,13170,13171],{"class":6911},"pc ",[5487,13173,10602],{"class":6869},[5487,13175,11589],{"class":6855},[5487,13177,10344],{"class":6869},[5487,13179,13180],{"class":6233},"AddTrack",[5487,13182,6926],{"class":6869},[5487,13184,12139],{"class":6911},[5487,13186,13187],{"class":6855}," TrackLocal",[5487,13189,13190],{"class":6869},") (*",[5487,13192,13144],{"class":6855},[5487,13194,5305],{"class":6869},[5487,13196,11594],{"class":6855},[5487,13198,6885],{"class":6869},[5299,13200,13201,13205],{},[5235,13202,13203],{},[5208,13204,13144],{}," надає методи для управління передачею медіа:",[5478,13207,13209],{"className":6843,"code":13208,"language":6845,"meta":5483,"style":5483},"type RTPSender struct {\n    \u002F\u002F Track - посилання на локальний трек, що передається\n    Track() TrackLocal\n    \n    \u002F\u002F Read - читання RTCP-пакетів (статистика та управління)\n    Read(b []byte) (n int, attributes interceptor.Attributes, err error)\n    \n    \u002F\u002F ReplaceTrack - заміна треку без перепогодження SDP\n    \u002F\u002F (корисно для перемикання камер або джерел)\n    ReplaceTrack(track TrackLocal) error\n    \n    \u002F\u002F GetParameters - отримання параметрів кодування (кодеки, SSRC)\n    GetParameters() RTPSendParameters\n}\n",[5208,13210,13211,13222,13227,13238,13242,13247,13292,13296,13301,13306,13322,13326,13331,13341],{"__ignoreMap":5483},[5487,13212,13213,13215,13218,13220],{"class":5489,"line":5490},[5487,13214,10383],{"class":6237},[5487,13216,13217],{"class":6855}," RTPSender",[5487,13219,10389],{"class":6237},[5487,13221,7150],{"class":6869},[5487,13223,13224],{"class":5489,"line":5496},[5487,13225,13226],{"class":6905},"    \u002F\u002F Track - посилання на локальний трек, що передається\n",[5487,13228,13229,13232,13235],{"class":5489,"line":5502},[5487,13230,13231],{"class":6911},"    Track",[5487,13233,13234],{"class":6869},"() ",[5487,13236,13237],{"class":6855},"TrackLocal\n",[5487,13239,13240],{"class":5489,"line":5509},[5487,13241,6946],{"class":6869},[5487,13243,13244],{"class":5489,"line":5515},[5487,13245,13246],{"class":6905},"    \u002F\u002F Read - читання RTCP-пакетів (статистика та управління)\n",[5487,13248,13249,13252,13254,13257,13259,13262,13264,13267,13270,13272,13275,13278,13280,13283,13285,13287,13290],{"class":5489,"line":5521},[5487,13250,13251],{"class":6911},"    Read",[5487,13253,6926],{"class":6869},[5487,13255,13256],{"class":6911},"b",[5487,13258,7639],{"class":6869},[5487,13260,13261],{"class":6855},"byte",[5487,13263,12402],{"class":6869},[5487,13265,13266],{"class":6911},"n",[5487,13268,13269],{"class":6855}," int",[5487,13271,5305],{"class":6869},[5487,13273,13274],{"class":6911},"attributes",[5487,13276,13277],{"class":6855}," interceptor",[5487,13279,5211],{"class":6869},[5487,13281,13282],{"class":6855},"Attributes",[5487,13284,5305],{"class":6869},[5487,13286,7290],{"class":6911},[5487,13288,13289],{"class":6855}," error",[5487,13291,6885],{"class":6869},[5487,13293,13294],{"class":5489,"line":5527},[5487,13295,6946],{"class":6869},[5487,13297,13298],{"class":5489,"line":5532},[5487,13299,13300],{"class":6905},"    \u002F\u002F ReplaceTrack - заміна треку без перепогодження SDP\n",[5487,13302,13303],{"class":5489,"line":5538},[5487,13304,13305],{"class":6905},"    \u002F\u002F (корисно для перемикання камер або джерел)\n",[5487,13307,13308,13311,13313,13315,13317,13319],{"class":5489,"line":5544},[5487,13309,13310],{"class":6911},"    ReplaceTrack",[5487,13312,6926],{"class":6869},[5487,13314,12139],{"class":6911},[5487,13316,13187],{"class":6855},[5487,13318,10344],{"class":6869},[5487,13320,13321],{"class":6855},"error\n",[5487,13323,13324],{"class":5489,"line":5550},[5487,13325,6946],{"class":6869},[5487,13327,13328],{"class":5489,"line":5555},[5487,13329,13330],{"class":6905},"    \u002F\u002F GetParameters - отримання параметрів кодування (кодеки, SSRC)\n",[5487,13332,13333,13336,13338],{"class":5489,"line":5561},[5487,13334,13335],{"class":6911},"    GetParameters",[5487,13337,13234],{"class":6869},[5487,13339,13340],{"class":6855},"RTPSendParameters\n",[5487,13342,13343],{"class":5489,"line":5567},[5487,13344,7336],{"class":6869},[5299,13346,13347],{},[5235,13348,13349],{},"Читання RTCP-статистики:",[5299,13351,13352],{},"RTCP (RTP Control Protocol) — супровідний протокол, що передає метрики якості з'єднання. Приймач регулярно надсилає звіти про кількість отриманих\u002Fвтрачених пакетів, джиттер (варіацію затримки) та round-trip time (RTT). Ця інформація критична для алгоритмів адаптивного контролю бітрейту:",[5478,13354,13356],{"className":6843,"code":13355,"language":6845,"meta":5483,"style":5483},"rtpSender, _ := pc.AddTrack(videoTrack)\n\ngo func() {\n    rtcpBuf := make([]byte, 1500) \u002F\u002F MTU мережі\n    for {\n        n, _, rtcpErr := rtpSender.Read(rtcpBuf)\n        if rtcpErr != nil {\n            log.Println(\"RTCP-з'єднання закрито\")\n            return\n        }\n        \n        \u002F\u002F Розбір RTCP-пакетів\n        packets, err := rtcp.Unmarshal(rtcpBuf[:n])\n        if err != nil {\n            continue\n        }\n        \n        for _, pkt := range packets {\n            switch rtcpPacket := pkt.(type) {\n            case *rtcp.ReceiverReport:\n                \u002F\u002F Звіт від приймача про якість з'єднання\n                for _, report := range rtcpPacket.Reports {\n                    log.Printf(\"Статистика SSRC %d: втрачено %d пакетів, джиттер %d\",\n                        report.SSRC, report.TotalLost, report.Jitter)\n                }\n            case *rtcp.PictureLossIndication:\n                \u002F\u002F Запит на повторну передачу ключового кадру (I-frame)\n                log.Println(\"Клієнт запитує ключовий кадр через втрату пакетів\")\n            }\n        }\n    }\n}()\n",[5208,13357,13358,13382,13386,13394,13418,13424,13454,13467,13482,13486,13490,13494,13499,13530,13542,13547,13551,13555,13575,13594,13611,13616,13641,13671,13701,13706,13721,13726,13741,13745,13749,13753],{"__ignoreMap":5483},[5487,13359,13360,13363,13365,13367,13369,13371,13373,13375,13377,13380],{"class":5489,"line":5490},[5487,13361,13362],{"class":6911},"rtpSender",[5487,13364,5305],{"class":6869},[5487,13366,12254],{"class":6911},[5487,13368,6915],{"class":6869},[5487,13370,12384],{"class":6911},[5487,13372,5211],{"class":6869},[5487,13374,13180],{"class":6233},[5487,13376,6926],{"class":6869},[5487,13378,13379],{"class":6911},"videoTrack",[5487,13381,6885],{"class":6869},[5487,13383,13384],{"class":5489,"line":5496},[5487,13385,5506],{"emptyLinePlaceholder":5505},[5487,13387,13388,13390,13392],{"class":5489,"line":5502},[5487,13389,6845],{"class":6865},[5487,13391,10899],{"class":6237},[5487,13393,6900],{"class":6869},[5487,13395,13396,13399,13401,13403,13406,13408,13410,13413,13415],{"class":5489,"line":5509},[5487,13397,13398],{"class":6911},"    rtcpBuf",[5487,13400,6915],{"class":6869},[5487,13402,10552],{"class":6233},[5487,13404,13405],{"class":6869},"([]",[5487,13407,13261],{"class":6855},[5487,13409,5305],{"class":6869},[5487,13411,13412],{"class":6405},"1500",[5487,13414,10344],{"class":6869},[5487,13416,13417],{"class":6905},"\u002F\u002F MTU мережі\n",[5487,13419,13420,13422],{"class":5489,"line":5515},[5487,13421,7647],{"class":6865},[5487,13423,7150],{"class":6869},[5487,13425,13426,13429,13431,13433,13435,13438,13440,13442,13444,13447,13449,13452],{"class":5489,"line":5521},[5487,13427,13428],{"class":6911},"        n",[5487,13430,5305],{"class":6869},[5487,13432,12254],{"class":6911},[5487,13434,5305],{"class":6869},[5487,13436,13437],{"class":6911},"rtcpErr",[5487,13439,6915],{"class":6869},[5487,13441,13362],{"class":6911},[5487,13443,5211],{"class":6869},[5487,13445,13446],{"class":6233},"Read",[5487,13448,6926],{"class":6869},[5487,13450,13451],{"class":6911},"rtcpBuf",[5487,13453,6885],{"class":6869},[5487,13455,13456,13458,13461,13463,13465],{"class":5489,"line":5527},[5487,13457,7129],{"class":6865},[5487,13459,13460],{"class":6911}," rtcpErr",[5487,13462,7293],{"class":6869},[5487,13464,7284],{"class":6237},[5487,13466,7150],{"class":6869},[5487,13468,13469,13471,13473,13475,13477,13480],{"class":5489,"line":5532},[5487,13470,11041],{"class":6911},[5487,13472,5211],{"class":6869},[5487,13474,7228],{"class":6233},[5487,13476,6926],{"class":6869},[5487,13478,13479],{"class":6241},"\"RTCP-з'єднання закрито\"",[5487,13481,6885],{"class":6869},[5487,13483,13484],{"class":5489,"line":5538},[5487,13485,7176],{"class":6865},[5487,13487,13488],{"class":5489,"line":5544},[5487,13489,7181],{"class":6869},[5487,13491,13492],{"class":5489,"line":5550},[5487,13493,7124],{"class":6869},[5487,13495,13496],{"class":5489,"line":5555},[5487,13497,13498],{"class":6905},"        \u002F\u002F Розбір RTCP-пакетів\n",[5487,13500,13501,13504,13506,13508,13510,13513,13515,13518,13520,13522,13525,13527],{"class":5489,"line":5561},[5487,13502,13503],{"class":6911},"        packets",[5487,13505,5305],{"class":6869},[5487,13507,7290],{"class":6911},[5487,13509,6915],{"class":6869},[5487,13511,13512],{"class":6911},"rtcp",[5487,13514,5211],{"class":6869},[5487,13516,13517],{"class":6233},"Unmarshal",[5487,13519,6926],{"class":6869},[5487,13521,13451],{"class":6911},[5487,13523,13524],{"class":6869},"[:",[5487,13526,13266],{"class":6911},[5487,13528,13529],{"class":6869},"])\n",[5487,13531,13532,13534,13536,13538,13540],{"class":5489,"line":5567},[5487,13533,7129],{"class":6865},[5487,13535,7265],{"class":6911},[5487,13537,7293],{"class":6869},[5487,13539,7284],{"class":6237},[5487,13541,7150],{"class":6869},[5487,13543,13544],{"class":5489,"line":5704},[5487,13545,13546],{"class":6865},"            continue\n",[5487,13548,13549],{"class":5489,"line":5710},[5487,13550,7181],{"class":6869},[5487,13552,13553],{"class":5489,"line":5716},[5487,13554,7124],{"class":6869},[5487,13556,13557,13559,13561,13563,13566,13568,13570,13573],{"class":5489,"line":5722},[5487,13558,12242],{"class":6865},[5487,13560,7650],{"class":6911},[5487,13562,5305],{"class":6869},[5487,13564,13565],{"class":6911},"pkt",[5487,13567,6915],{"class":6869},[5487,13569,7660],{"class":6865},[5487,13571,13572],{"class":6911}," packets",[5487,13574,7150],{"class":6869},[5487,13576,13577,13580,13583,13585,13587,13590,13592],{"class":5489,"line":5727},[5487,13578,13579],{"class":6865},"            switch",[5487,13581,13582],{"class":6911}," rtcpPacket",[5487,13584,6915],{"class":6869},[5487,13586,13565],{"class":6911},[5487,13588,13589],{"class":6869},".(",[5487,13591,10383],{"class":6237},[5487,13593,7043],{"class":6869},[5487,13595,13596,13599,13601,13603,13605,13608],{"class":5489,"line":6007},[5487,13597,13598],{"class":6865},"            case",[5487,13600,7033],{"class":6869},[5487,13602,13512],{"class":6855},[5487,13604,5211],{"class":6869},[5487,13606,13607],{"class":6855},"ReceiverReport",[5487,13609,13610],{"class":6869},":\n",[5487,13612,13613],{"class":5489,"line":6013},[5487,13614,13615],{"class":6905},"                \u002F\u002F Звіт від приймача про якість з'єднання\n",[5487,13617,13618,13621,13623,13625,13628,13630,13632,13634,13636,13639],{"class":5489,"line":6019},[5487,13619,13620],{"class":6865},"                for",[5487,13622,7650],{"class":6911},[5487,13624,5305],{"class":6869},[5487,13626,13627],{"class":6911},"report",[5487,13629,6915],{"class":6869},[5487,13631,7660],{"class":6865},[5487,13633,13582],{"class":6911},[5487,13635,5211],{"class":6869},[5487,13637,13638],{"class":6911},"Reports",[5487,13640,7150],{"class":6869},[5487,13642,13643,13646,13648,13650,13652,13655,13657,13660,13662,13665,13667,13669],{"class":5489,"line":6025},[5487,13644,13645],{"class":6911},"                    log",[5487,13647,5211],{"class":6869},[5487,13649,10687],{"class":6233},[5487,13651,6926],{"class":6869},[5487,13653,13654],{"class":6241},"\"Статистика SSRC ",[5487,13656,7894],{"class":6911},[5487,13658,13659],{"class":6241},": втрачено ",[5487,13661,7894],{"class":6911},[5487,13663,13664],{"class":6241}," пакетів, джиттер ",[5487,13666,7894],{"class":6911},[5487,13668,7318],{"class":6241},[5487,13670,9969],{"class":6869},[5487,13672,13673,13676,13678,13681,13683,13685,13687,13690,13692,13694,13696,13699],{"class":5489,"line":6030},[5487,13674,13675],{"class":6911},"                        report",[5487,13677,5211],{"class":6869},[5487,13679,13680],{"class":6911},"SSRC",[5487,13682,5305],{"class":6869},[5487,13684,13627],{"class":6911},[5487,13686,5211],{"class":6869},[5487,13688,13689],{"class":6911},"TotalLost",[5487,13691,5305],{"class":6869},[5487,13693,13627],{"class":6911},[5487,13695,5211],{"class":6869},[5487,13697,13698],{"class":6911},"Jitter",[5487,13700,6885],{"class":6869},[5487,13702,13703],{"class":5489,"line":6035},[5487,13704,13705],{"class":6869},"                }\n",[5487,13707,13708,13710,13712,13714,13716,13719],{"class":5489,"line":7188},[5487,13709,13598],{"class":6865},[5487,13711,7033],{"class":6869},[5487,13713,13512],{"class":6855},[5487,13715,5211],{"class":6869},[5487,13717,13718],{"class":6855},"PictureLossIndication",[5487,13720,13610],{"class":6869},[5487,13722,13723],{"class":5489,"line":7209},[5487,13724,13725],{"class":6905},"                \u002F\u002F Запит на повторну передачу ключового кадру (I-frame)\n",[5487,13727,13728,13730,13732,13734,13736,13739],{"class":5489,"line":7215},[5487,13729,11252],{"class":6911},[5487,13731,5211],{"class":6869},[5487,13733,7228],{"class":6233},[5487,13735,6926],{"class":6869},[5487,13737,13738],{"class":6241},"\"Клієнт запитує ключовий кадр через втрату пакетів\"",[5487,13740,6885],{"class":6869},[5487,13742,13743],{"class":5489,"line":7220},[5487,13744,11289],{"class":6869},[5487,13746,13747],{"class":5489,"line":7238},[5487,13748,7181],{"class":6869},[5487,13750,13751],{"class":5489,"line":7254},[5487,13752,7330],{"class":6869},[5487,13754,13755],{"class":5489,"line":7259},[5487,13756,13757],{"class":6869},"}()\n",[5470,13759,13761,13762,13141,13764],{"id":13760},"обробник-ontrack-та-структура-trackremote","Обробник ",[5208,13763,12130],{},[5208,13765,12148],{},[5299,13767,13768,13769,13771],{},"Подія ",[5208,13770,12130],{}," викликається при отриманні віддаленого медіатреку від іншого учасника:",[5478,13773,13775],{"className":6843,"code":13774,"language":6845,"meta":5483,"style":5483},"pc.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {\n    \u002F\u002F Метаінформація про трек\n    codecName := track.Codec().MimeType    \u002F\u002F Наприклад: \"video\u002FVP8\", \"audio\u002Fopus\"\n    trackID := track.ID()                   \u002F\u002F Унікальний ідентифікатор треку\n    streamID := track.StreamID()            \u002F\u002F Ідентифікатор медіапотоку\n    \n    log.Printf(\"Отримано трек: Codec=%s, TrackID=%s, StreamID=%s\",\n        codecName, trackID, streamID)\n    \n    \u002F\u002F Створення горутини для читання RTP-пакетів\n    go func() {\n        for {\n            \u002F\u002F ReadRTP блокується до отримання наступного пакету\n            rtpPacket, attributes, readErr := track.ReadRTP()\n            if readErr != nil {\n                if readErr == io.EOF {\n                    log.Println(\"Трек закрито коректно\")\n                } else {\n                    log.Printf(\"Помилка читання RTP: %v\", readErr)\n                }\n                return\n            }\n            \n            \u002F\u002F Доступ до даних пакету\n            payload := rtpPacket.Payload         \u002F\u002F Закодовані медіадані\n            timestamp := rtpPacket.Timestamp     \u002F\u002F Часова мітка (відносна)\n            sequenceNumber := rtpPacket.SequenceNumber \u002F\u002F Порядковий номер\n            \n            \u002F\u002F Обробка: запис у файл, пересилка іншим учасникам,\n            \u002F\u002F декодування для аналізу, тощо\n            _ = payload\n            _ = timestamp\n            _ = sequenceNumber\n            _ = attributes\n        }\n    }()\n})\n",[5208,13776,13777,13815,13820,13840,13859,13878,13882,13910,13927,13931,13936,13945,13951,13956,13978,13990,14009,14024,14033,14055,14059,14063,14067,14071,14076,14094,14111,14128,14132,14137,14142,14151,14160,14169,14178,14182,14186],{"__ignoreMap":5483},[5487,13778,13779,13781,13783,13785,13787,13789,13791,13793,13795,13797,13799,13801,13803,13805,13807,13809,13811,13813],{"class":5489,"line":5490},[5487,13780,12384],{"class":6911},[5487,13782,5211],{"class":6869},[5487,13784,12130],{"class":6233},[5487,13786,6926],{"class":6869},[5487,13788,6894],{"class":6237},[5487,13790,6926],{"class":6869},[5487,13792,12139],{"class":6911},[5487,13794,7033],{"class":6869},[5487,13796,11584],{"class":6855},[5487,13798,5211],{"class":6869},[5487,13800,12148],{"class":6855},[5487,13802,5305],{"class":6869},[5487,13804,12153],{"class":6911},[5487,13806,7033],{"class":6869},[5487,13808,11584],{"class":6855},[5487,13810,5211],{"class":6869},[5487,13812,12162],{"class":6855},[5487,13814,7043],{"class":6869},[5487,13816,13817],{"class":5489,"line":5496},[5487,13818,13819],{"class":6905},"    \u002F\u002F Метаінформація про трек\n",[5487,13821,13822,13825,13827,13829,13831,13833,13835,13837],{"class":5489,"line":5502},[5487,13823,13824],{"class":6911},"    codecName",[5487,13826,6915],{"class":6869},[5487,13828,12139],{"class":6911},[5487,13830,5211],{"class":6869},[5487,13832,12221],{"class":6233},[5487,13834,7056],{"class":6869},[5487,13836,12226],{"class":6911},[5487,13838,13839],{"class":6905},"    \u002F\u002F Наприклад: \"video\u002FVP8\", \"audio\u002Fopus\"\n",[5487,13841,13842,13845,13847,13849,13851,13853,13856],{"class":5489,"line":5509},[5487,13843,13844],{"class":6911},"    trackID",[5487,13846,6915],{"class":6869},[5487,13848,12139],{"class":6911},[5487,13850,5211],{"class":6869},[5487,13852,12203],{"class":6233},[5487,13854,13855],{"class":6869},"()                   ",[5487,13857,13858],{"class":6905},"\u002F\u002F Унікальний ідентифікатор треку\n",[5487,13860,13861,13864,13866,13868,13870,13872,13875],{"class":5489,"line":5515},[5487,13862,13863],{"class":6911},"    streamID",[5487,13865,6915],{"class":6869},[5487,13867,12139],{"class":6911},[5487,13869,5211],{"class":6869},[5487,13871,12212],{"class":6233},[5487,13873,13874],{"class":6869},"()            ",[5487,13876,13877],{"class":6905},"\u002F\u002F Ідентифікатор медіапотоку\n",[5487,13879,13880],{"class":5489,"line":5521},[5487,13881,6946],{"class":6869},[5487,13883,13884,13886,13888,13890,13892,13895,13897,13900,13902,13904,13906,13908],{"class":5489,"line":5527},[5487,13885,7223],{"class":6911},[5487,13887,5211],{"class":6869},[5487,13889,10687],{"class":6233},[5487,13891,6926],{"class":6869},[5487,13893,13894],{"class":6241},"\"Отримано трек: Codec=",[5487,13896,8020],{"class":6911},[5487,13898,13899],{"class":6241},", TrackID=",[5487,13901,8020],{"class":6911},[5487,13903,12182],{"class":6241},[5487,13905,8020],{"class":6911},[5487,13907,7318],{"class":6241},[5487,13909,9969],{"class":6869},[5487,13911,13912,13915,13917,13920,13922,13925],{"class":5489,"line":5532},[5487,13913,13914],{"class":6911},"        codecName",[5487,13916,5305],{"class":6869},[5487,13918,13919],{"class":6911},"trackID",[5487,13921,5305],{"class":6869},[5487,13923,13924],{"class":6911},"streamID",[5487,13926,6885],{"class":6869},[5487,13928,13929],{"class":5489,"line":5538},[5487,13930,6946],{"class":6869},[5487,13932,13933],{"class":5489,"line":5544},[5487,13934,13935],{"class":6905},"    \u002F\u002F Створення горутини для читання RTP-пакетів\n",[5487,13937,13938,13941,13943],{"class":5489,"line":5550},[5487,13939,13940],{"class":6865},"    go",[5487,13942,10899],{"class":6237},[5487,13944,6900],{"class":6869},[5487,13946,13947,13949],{"class":5489,"line":5555},[5487,13948,12242],{"class":6865},[5487,13950,7150],{"class":6869},[5487,13952,13953],{"class":5489,"line":5561},[5487,13954,13955],{"class":6905},"            \u002F\u002F ReadRTP блокується до отримання наступного пакету\n",[5487,13957,13958,13960,13962,13964,13966,13968,13970,13972,13974,13976],{"class":5489,"line":5567},[5487,13959,12249],{"class":6911},[5487,13961,5305],{"class":6869},[5487,13963,13274],{"class":6911},[5487,13965,5305],{"class":6869},[5487,13967,12259],{"class":6911},[5487,13969,6915],{"class":6869},[5487,13971,12139],{"class":6911},[5487,13973,5211],{"class":6869},[5487,13975,12268],{"class":6233},[5487,13977,10726],{"class":6869},[5487,13979,13980,13982,13984,13986,13988],{"class":5489,"line":5704},[5487,13981,11220],{"class":6865},[5487,13983,12277],{"class":6911},[5487,13985,7293],{"class":6869},[5487,13987,7284],{"class":6237},[5487,13989,7150],{"class":6869},[5487,13991,13992,13995,13997,13999,14002,14004,14007],{"class":5489,"line":5710},[5487,13993,13994],{"class":6865},"                if",[5487,13996,12277],{"class":6911},[5487,13998,7140],{"class":6869},[5487,14000,14001],{"class":6911},"io",[5487,14003,5211],{"class":6869},[5487,14005,14006],{"class":6911},"EOF",[5487,14008,7150],{"class":6869},[5487,14010,14011,14013,14015,14017,14019,14022],{"class":5489,"line":5716},[5487,14012,13645],{"class":6911},[5487,14014,5211],{"class":6869},[5487,14016,7228],{"class":6233},[5487,14018,6926],{"class":6869},[5487,14020,14021],{"class":6241},"\"Трек закрито коректно\"",[5487,14023,6885],{"class":6869},[5487,14025,14026,14029,14031],{"class":5489,"line":5722},[5487,14027,14028],{"class":6869},"                } ",[5487,14030,11298],{"class":6865},[5487,14032,7150],{"class":6869},[5487,14034,14035,14037,14039,14041,14043,14045,14047,14049,14051,14053],{"class":5489,"line":5727},[5487,14036,13645],{"class":6911},[5487,14038,5211],{"class":6869},[5487,14040,10687],{"class":6233},[5487,14042,6926],{"class":6869},[5487,14044,12296],{"class":6241},[5487,14046,6767],{"class":6911},[5487,14048,7318],{"class":6241},[5487,14050,5305],{"class":6869},[5487,14052,12259],{"class":6911},[5487,14054,6885],{"class":6869},[5487,14056,14057],{"class":5489,"line":6007},[5487,14058,13705],{"class":6869},[5487,14060,14061],{"class":5489,"line":6013},[5487,14062,12311],{"class":6865},[5487,14064,14065],{"class":5489,"line":6019},[5487,14066,11289],{"class":6869},[5487,14068,14069],{"class":5489,"line":6025},[5487,14070,12320],{"class":6869},[5487,14072,14073],{"class":5489,"line":6030},[5487,14074,14075],{"class":6905},"            \u002F\u002F Доступ до даних пакету\n",[5487,14077,14078,14081,14083,14086,14088,14091],{"class":5489,"line":6035},[5487,14079,14080],{"class":6911},"            payload",[5487,14082,6915],{"class":6869},[5487,14084,14085],{"class":6911},"rtpPacket",[5487,14087,5211],{"class":6869},[5487,14089,14090],{"class":6911},"Payload",[5487,14092,14093],{"class":6905},"         \u002F\u002F Закодовані медіадані\n",[5487,14095,14096,14099,14101,14103,14105,14108],{"class":5489,"line":7188},[5487,14097,14098],{"class":6911},"            timestamp",[5487,14100,6915],{"class":6869},[5487,14102,14085],{"class":6911},[5487,14104,5211],{"class":6869},[5487,14106,14107],{"class":6911},"Timestamp",[5487,14109,14110],{"class":6905},"     \u002F\u002F Часова мітка (відносна)\n",[5487,14112,14113,14116,14118,14120,14122,14125],{"class":5489,"line":7209},[5487,14114,14115],{"class":6911},"            sequenceNumber",[5487,14117,6915],{"class":6869},[5487,14119,14085],{"class":6911},[5487,14121,5211],{"class":6869},[5487,14123,14124],{"class":6911},"SequenceNumber",[5487,14126,14127],{"class":6905}," \u002F\u002F Порядковий номер\n",[5487,14129,14130],{"class":5489,"line":7215},[5487,14131,12320],{"class":6869},[5487,14133,14134],{"class":5489,"line":7220},[5487,14135,14136],{"class":6905},"            \u002F\u002F Обробка: запис у файл, пересилка іншим учасникам,\n",[5487,14138,14139],{"class":5489,"line":7238},[5487,14140,14141],{"class":6905},"            \u002F\u002F декодування для аналізу, тощо\n",[5487,14143,14144,14146,14148],{"class":5489,"line":7254},[5487,14145,12330],{"class":6911},[5487,14147,7451],{"class":6869},[5487,14149,14150],{"class":6911},"payload\n",[5487,14152,14153,14155,14157],{"class":5489,"line":7259},[5487,14154,12330],{"class":6911},[5487,14156,7451],{"class":6869},[5487,14158,14159],{"class":6911},"timestamp\n",[5487,14161,14162,14164,14166],{"class":5489,"line":7300},[5487,14163,12330],{"class":6911},[5487,14165,7451],{"class":6869},[5487,14167,14168],{"class":6911},"sequenceNumber\n",[5487,14170,14171,14173,14175],{"class":5489,"line":7327},[5487,14172,12330],{"class":6911},[5487,14174,7451],{"class":6869},[5487,14176,14177],{"class":6911},"attributes\n",[5487,14179,14180],{"class":5489,"line":7333},[5487,14181,7181],{"class":6869},[5487,14183,14184],{"class":5489,"line":7744},[5487,14185,10978],{"class":6869},[5487,14187,14188],{"class":5489,"line":7761},[5487,14189,14190],{"class":6869},"})\n",[5299,14192,14193,14198],{},[5235,14194,12756,14195],{},[5208,14196,14197],{},"RTPPacket"," містить такі поля:",[5478,14200,14202],{"className":6843,"code":14201,"language":6845,"meta":5483,"style":5483},"type Packet struct {\n    Header          \u002F\u002F Заголовок RTP\n    Payload []byte  \u002F\u002F Корисне навантаження (закодовані медіадані)\n}\n\ntype Header struct {\n    Version        uint8   \u002F\u002F Завжди 2 для RTP\n    Padding        bool    \u002F\u002F Чи є доповнення в кінці пакету\n    Extension      bool    \u002F\u002F Чи присутні розширення заголовка\n    Marker         bool    \u002F\u002F Маркер кінця кадру (для відео)\n    PayloadType    uint8   \u002F\u002F Тип корисного навантаження (кодек)\n    SequenceNumber uint16  \u002F\u002F Порядковий номер (збільшується на 1)\n    Timestamp      uint32  \u002F\u002F Часова мітка (у тактах годинника кодеку)\n    SSRC           uint32  \u002F\u002F Ідентифікатор джерела синхронізації\n    CSRC           []uint32 \u002F\u002F Список учасників (для мікшування аудіо)\n}\n",[5208,14203,14204,14215,14223,14235,14239,14243,14254,14265,14276,14287,14298,14309,14320,14331,14342,14356],{"__ignoreMap":5483},[5487,14205,14206,14208,14211,14213],{"class":5489,"line":5490},[5487,14207,10383],{"class":6237},[5487,14209,14210],{"class":6855}," Packet",[5487,14212,10389],{"class":6237},[5487,14214,7150],{"class":6869},[5487,14216,14217,14220],{"class":5489,"line":5496},[5487,14218,14219],{"class":6855},"    Header",[5487,14221,14222],{"class":6905},"          \u002F\u002F Заголовок RTP\n",[5487,14224,14225,14228,14230,14232],{"class":5489,"line":5502},[5487,14226,14227],{"class":6911},"    Payload",[5487,14229,7639],{"class":6869},[5487,14231,13261],{"class":6855},[5487,14233,14234],{"class":6905},"  \u002F\u002F Корисне навантаження (закодовані медіадані)\n",[5487,14236,14237],{"class":5489,"line":5509},[5487,14238,7336],{"class":6869},[5487,14240,14241],{"class":5489,"line":5515},[5487,14242,5506],{"emptyLinePlaceholder":5505},[5487,14244,14245,14247,14250,14252],{"class":5489,"line":5521},[5487,14246,10383],{"class":6237},[5487,14248,14249],{"class":6855}," Header",[5487,14251,10389],{"class":6237},[5487,14253,7150],{"class":6869},[5487,14255,14256,14259,14262],{"class":5489,"line":5527},[5487,14257,14258],{"class":6911},"    Version",[5487,14260,14261],{"class":6855},"        uint8",[5487,14263,14264],{"class":6905},"   \u002F\u002F Завжди 2 для RTP\n",[5487,14266,14267,14270,14273],{"class":5489,"line":5532},[5487,14268,14269],{"class":6911},"    Padding",[5487,14271,14272],{"class":6855},"        bool",[5487,14274,14275],{"class":6905},"    \u002F\u002F Чи є доповнення в кінці пакету\n",[5487,14277,14278,14281,14284],{"class":5489,"line":5538},[5487,14279,14280],{"class":6911},"    Extension",[5487,14282,14283],{"class":6855},"      bool",[5487,14285,14286],{"class":6905},"    \u002F\u002F Чи присутні розширення заголовка\n",[5487,14288,14289,14292,14295],{"class":5489,"line":5544},[5487,14290,14291],{"class":6911},"    Marker",[5487,14293,14294],{"class":6855},"         bool",[5487,14296,14297],{"class":6905},"    \u002F\u002F Маркер кінця кадру (для відео)\n",[5487,14299,14300,14303,14306],{"class":5489,"line":5550},[5487,14301,14302],{"class":6911},"    PayloadType",[5487,14304,14305],{"class":6855},"    uint8",[5487,14307,14308],{"class":6905},"   \u002F\u002F Тип корисного навантаження (кодек)\n",[5487,14310,14311,14314,14317],{"class":5489,"line":5555},[5487,14312,14313],{"class":6911},"    SequenceNumber",[5487,14315,14316],{"class":6855}," uint16",[5487,14318,14319],{"class":6905},"  \u002F\u002F Порядковий номер (збільшується на 1)\n",[5487,14321,14322,14325,14328],{"class":5489,"line":5561},[5487,14323,14324],{"class":6911},"    Timestamp",[5487,14326,14327],{"class":6855},"      uint32",[5487,14329,14330],{"class":6905},"  \u002F\u002F Часова мітка (у тактах годинника кодеку)\n",[5487,14332,14333,14336,14339],{"class":5489,"line":5567},[5487,14334,14335],{"class":6911},"    SSRC",[5487,14337,14338],{"class":6855},"           uint32",[5487,14340,14341],{"class":6905},"  \u002F\u002F Ідентифікатор джерела синхронізації\n",[5487,14343,14344,14347,14350,14353],{"class":5489,"line":5704},[5487,14345,14346],{"class":6911},"    CSRC",[5487,14348,14349],{"class":6869},"           []",[5487,14351,14352],{"class":6855},"uint32",[5487,14354,14355],{"class":6905}," \u002F\u002F Список учасників (для мікшування аудіо)\n",[5487,14357,14358],{"class":5489,"line":5710},[5487,14359,7336],{"class":6869},[5299,14361,14362],{},[5235,14363,14364],{},"Використання порядкових номерів та часових міток:",[5198,14366,14367,14374,14385],{},[5201,14368,14369,14373],{},[5235,14370,14371],{},[5208,14372,14124],{}," дозволяє виявляти втрачені пакети: якщо приймач отримує пакет №105 після №103, пакет №104 втрачено.",[5201,14375,14376,14380,14381,14384],{},[5235,14377,14378],{},[5208,14379,14107],{}," використовується для синхронізації відтворення. Для відео часова мітка збільшується на ",[5208,14382,14383],{},"90000 \u002F FPS"," тактів на кадр (для 30 FPS: 90000\u002F30 = 3000 тактів). Для аудіо — на кількість семплів у пакеті.",[5201,14386,14387,14393],{},[5235,14388,14389,14392],{},[5208,14390,14391],{},"Marker"," біт"," сигналізує про кінець відеокадру. Декодер не може розпочати декодування до отримання всіх пакетів кадру.",[5624,14395,14396,14397,14400,14401,14403],{},"У процесі встановлення з'єднання обидві сторони генерують ICE-кандидати асинхронно. Важливо передавати їх клієнту ",[5235,14398,14399],{},"одразу після генерації",", не чекаючи завершення збору всіх кандидатів. Це прискорює встановлення з'єднання через механізм ",[5235,14402,10068],{}," (RFC 8838), коли перевірка сумісності кандидатів починається до завершення повного збору.",[5365,14405,14407],{"id":14406},"крок-4-додавання-відеотреку-для-трансляції","Крок 4. Додавання відеотреку для трансляції",[5299,14409,14410],{},"Щоб транслювати відео клієнту, потрібно додати медіатрек до PeerConnection. Приклад створення треку із синтетичного джерела або файлу:",[5478,14412,14414],{"className":6843,"code":14413,"language":6845,"meta":5483,"style":5483},"package main\n\nimport (\n    \"fmt\"\n    \"io\"\n    \"log\"\n    \"os\"\n    \"time\"\n\n    \"github.com\u002Fpion\u002Fwebrtc\u002Fv4\"\n    \"github.com\u002Fpion\u002Fwebrtc\u002Fv4\u002Fpkg\u002Fmedia\"\n    \"github.com\u002Fpion\u002Fwebrtc\u002Fv4\u002Fpkg\u002Fmedia\u002Fivfreader\"\n)\n\n\u002F\u002F AddVideoTrack додає відеотрек до PeerConnection та транслює кадри\nfunc AddVideoTrack(pc *webrtc.PeerConnection, videoFilePath string) error {\n    \u002F\u002F Створюємо відеотрек із кодеком VP8\n    videoTrack, err := webrtc.NewTrackLocalStaticSample(\n        webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8},\n        \"video\",\n        \"pion-video-stream\",\n    )\n    if err != nil {\n        return fmt.Errorf(\"помилка створення треку: %w\", err)\n    }\n\n    \u002F\u002F Додаємо трек до PeerConnection\n    rtpSender, err := pc.AddTrack(videoTrack)\n    if err != nil {\n        return fmt.Errorf(\"помилка додавання треку: %w\", err)\n    }\n\n    \u002F\u002F Запускаємо горутину для читання RTCP-пакетів (статистика)\n    go func() {\n        rtcpBuf := make([]byte, 1500)\n        for {\n            if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {\n                return\n            }\n        }\n    }()\n\n    \u002F\u002F Запускаємо горутину для трансляції відеокадрів\n    go func() {\n        if err := streamVideoFile(videoTrack, videoFilePath); err != nil {\n            log.Printf(\"Помилка трансляції відео: %v\", err)\n        }\n    }()\n\n    return nil\n}\n\n\u002F\u002F streamVideoFile читає VP8-відео із файлу та пересилає кадри у трек\nfunc streamVideoFile(track *webrtc.TrackLocalStaticSample, filePath string) error {\n    file, err := os.Open(filePath)\n    if err != nil {\n        return fmt.Errorf(\"помилка відкриття файлу: %w\", err)\n    }\n    defer file.Close()\n\n    \u002F\u002F IVF — простий контейнер для VP8\u002FVP9 відео\n    ivf, header, err := ivfreader.NewWith(file)\n    if err != nil {\n        return fmt.Errorf(\"помилка розбору IVF: %w\", err)\n    }\n\n    \u002F\u002F Вираховуємо затримку між кадрами на основі FPS\n    frameDuration := time.Millisecond * time.Duration(\n        (float32(header.TimebaseNumerator)\u002Ffloat32(header.TimebaseDenominator))*1000,\n    )\n\n    ticker := time.NewTicker(frameDuration)\n    defer ticker.Stop()\n\n    for range ticker.C {\n        frame, _, err := ivf.ParseNextFrame()\n        if err == io.EOF {\n            log.Println(\"Відео завершилося, перезапуск...\")\n            file.Seek(0, io.SeekStart)\n            ivf, _, _ = ivfreader.NewWith(file)\n            continue\n        }\n        if err != nil {\n            return fmt.Errorf(\"помилка читання кадру: %w\", err)\n        }\n\n        \u002F\u002F Відправляємо кадр у трек\n        if err := track.WriteSample(media.Sample{Data: frame, Duration: frameDuration}); err != nil {\n            return fmt.Errorf(\"помилка запису кадру: %w\", err)\n        }\n    }\n\n    return nil\n}\n",[5208,14415,14416,14422,14426,14432,14436,14441,14445,14449,14454,14458,14462,14467,14472,14476,14480,14485,14517,14522,14543,14568,14575,14582,14587,14599,14625,14629,14633,14638,14661,14673,14698,14702,14706,14711,14719,14738,14744,14780,14784,14788,14792,14796,14800,14805,14813,14842,14865,14869,14873,14877,14884,14888,14892,14897,14930,14954,14966,14991,14995,15008,15012,15017,15047,15059,15084,15088,15092,15097,15124,15163,15167,15171,15192,15206,15210,15226,15251,15267,15282,15307,15334,15338,15342,15354,15380,15384,15388,15393,15447,15472,15476,15480,15484,15490],{"__ignoreMap":5483},[5487,14417,14418,14420],{"class":5489,"line":5490},[5487,14419,6852],{"class":6237},[5487,14421,6856],{"class":6855},[5487,14423,14424],{"class":5489,"line":5496},[5487,14425,5506],{"emptyLinePlaceholder":5505},[5487,14427,14428,14430],{"class":5489,"line":5502},[5487,14429,6866],{"class":6865},[5487,14431,6870],{"class":6869},[5487,14433,14434],{"class":5489,"line":5509},[5487,14435,7399],{"class":6241},[5487,14437,14438],{"class":5489,"line":5515},[5487,14439,14440],{"class":6241},"    \"io\"\n",[5487,14442,14443],{"class":5489,"line":5521},[5487,14444,6875],{"class":6241},[5487,14446,14447],{"class":5489,"line":5527},[5487,14448,7412],{"class":6241},[5487,14450,14451],{"class":5489,"line":5532},[5487,14452,14453],{"class":6241},"    \"time\"\n",[5487,14455,14456],{"class":5489,"line":5538},[5487,14457,5506],{"emptyLinePlaceholder":5505},[5487,14459,14460],{"class":5489,"line":5544},[5487,14461,11558],{"class":6241},[5487,14463,14464],{"class":5489,"line":5550},[5487,14465,14466],{"class":6241},"    \"github.com\u002Fpion\u002Fwebrtc\u002Fv4\u002Fpkg\u002Fmedia\"\n",[5487,14468,14469],{"class":5489,"line":5555},[5487,14470,14471],{"class":6241},"    \"github.com\u002Fpion\u002Fwebrtc\u002Fv4\u002Fpkg\u002Fmedia\u002Fivfreader\"\n",[5487,14473,14474],{"class":5489,"line":5561},[5487,14475,6885],{"class":6869},[5487,14477,14478],{"class":5489,"line":5567},[5487,14479,5506],{"emptyLinePlaceholder":5505},[5487,14481,14482],{"class":5489,"line":5704},[5487,14483,14484],{"class":6905},"\u002F\u002F AddVideoTrack додає відеотрек до PeerConnection та транслює кадри\n",[5487,14486,14487,14489,14492,14494,14496,14498,14500,14502,14504,14506,14509,14511,14513,14515],{"class":5489,"line":5710},[5487,14488,6894],{"class":6237},[5487,14490,14491],{"class":6233}," AddVideoTrack",[5487,14493,6926],{"class":6869},[5487,14495,12384],{"class":6911},[5487,14497,7033],{"class":6869},[5487,14499,11584],{"class":6855},[5487,14501,5211],{"class":6869},[5487,14503,11589],{"class":6855},[5487,14505,5305],{"class":6869},[5487,14507,14508],{"class":6911},"videoFilePath",[5487,14510,7506],{"class":6855},[5487,14512,10344],{"class":6869},[5487,14514,11594],{"class":6855},[5487,14516,7150],{"class":6869},[5487,14518,14519],{"class":5489,"line":5716},[5487,14520,14521],{"class":6905},"    \u002F\u002F Створюємо відеотрек із кодеком VP8\n",[5487,14523,14524,14527,14529,14531,14533,14535,14537,14540],{"class":5489,"line":5722},[5487,14525,14526],{"class":6911},"    videoTrack",[5487,14528,5305],{"class":6869},[5487,14530,7290],{"class":6911},[5487,14532,6915],{"class":6869},[5487,14534,11584],{"class":6911},[5487,14536,5211],{"class":6869},[5487,14538,14539],{"class":6233},"NewTrackLocalStaticSample",[5487,14541,14542],{"class":6869},"(\n",[5487,14544,14545,14548,14550,14553,14555,14557,14559,14561,14563,14566],{"class":5489,"line":5727},[5487,14546,14547],{"class":6855},"        webrtc",[5487,14549,5211],{"class":6869},[5487,14551,14552],{"class":6855},"RTPCodecCapability",[5487,14554,11651],{"class":6869},[5487,14556,12226],{"class":6911},[5487,14558,9963],{"class":6869},[5487,14560,11584],{"class":6911},[5487,14562,5211],{"class":6869},[5487,14564,14565],{"class":6911},"MimeTypeVP8",[5487,14567,11657],{"class":6869},[5487,14569,14570,14573],{"class":5489,"line":6007},[5487,14571,14572],{"class":6241},"        \"video\"",[5487,14574,9969],{"class":6869},[5487,14576,14577,14580],{"class":5489,"line":6013},[5487,14578,14579],{"class":6241},"        \"pion-video-stream\"",[5487,14581,9969],{"class":6869},[5487,14583,14584],{"class":5489,"line":6019},[5487,14585,14586],{"class":6869},"    )\n",[5487,14588,14589,14591,14593,14595,14597],{"class":5489,"line":6025},[5487,14590,7262],{"class":6865},[5487,14592,7265],{"class":6911},[5487,14594,7293],{"class":6869},[5487,14596,7284],{"class":6237},[5487,14598,7150],{"class":6869},[5487,14600,14601,14603,14606,14608,14610,14612,14615,14617,14619,14621,14623],{"class":5489,"line":6030},[5487,14602,10354],{"class":6865},[5487,14604,14605],{"class":6911}," fmt",[5487,14607,5211],{"class":6869},[5487,14609,11764],{"class":6233},[5487,14611,6926],{"class":6869},[5487,14613,14614],{"class":6241},"\"помилка створення треку: ",[5487,14616,11772],{"class":6911},[5487,14618,7318],{"class":6241},[5487,14620,5305],{"class":6869},[5487,14622,7290],{"class":6911},[5487,14624,6885],{"class":6869},[5487,14626,14627],{"class":5489,"line":6035},[5487,14628,7330],{"class":6869},[5487,14630,14631],{"class":5489,"line":7188},[5487,14632,5506],{"emptyLinePlaceholder":5505},[5487,14634,14635],{"class":5489,"line":7209},[5487,14636,14637],{"class":6905},"    \u002F\u002F Додаємо трек до PeerConnection\n",[5487,14639,14640,14643,14645,14647,14649,14651,14653,14655,14657,14659],{"class":5489,"line":7215},[5487,14641,14642],{"class":6911},"    rtpSender",[5487,14644,5305],{"class":6869},[5487,14646,7290],{"class":6911},[5487,14648,6915],{"class":6869},[5487,14650,12384],{"class":6911},[5487,14652,5211],{"class":6869},[5487,14654,13180],{"class":6233},[5487,14656,6926],{"class":6869},[5487,14658,13379],{"class":6911},[5487,14660,6885],{"class":6869},[5487,14662,14663,14665,14667,14669,14671],{"class":5489,"line":7220},[5487,14664,7262],{"class":6865},[5487,14666,7265],{"class":6911},[5487,14668,7293],{"class":6869},[5487,14670,7284],{"class":6237},[5487,14672,7150],{"class":6869},[5487,14674,14675,14677,14679,14681,14683,14685,14688,14690,14692,14694,14696],{"class":5489,"line":7238},[5487,14676,10354],{"class":6865},[5487,14678,14605],{"class":6911},[5487,14680,5211],{"class":6869},[5487,14682,11764],{"class":6233},[5487,14684,6926],{"class":6869},[5487,14686,14687],{"class":6241},"\"помилка додавання треку: ",[5487,14689,11772],{"class":6911},[5487,14691,7318],{"class":6241},[5487,14693,5305],{"class":6869},[5487,14695,7290],{"class":6911},[5487,14697,6885],{"class":6869},[5487,14699,14700],{"class":5489,"line":7254},[5487,14701,7330],{"class":6869},[5487,14703,14704],{"class":5489,"line":7259},[5487,14705,5506],{"emptyLinePlaceholder":5505},[5487,14707,14708],{"class":5489,"line":7300},[5487,14709,14710],{"class":6905},"    \u002F\u002F Запускаємо горутину для читання RTCP-пакетів (статистика)\n",[5487,14712,14713,14715,14717],{"class":5489,"line":7327},[5487,14714,13940],{"class":6865},[5487,14716,10899],{"class":6237},[5487,14718,6900],{"class":6869},[5487,14720,14721,14724,14726,14728,14730,14732,14734,14736],{"class":5489,"line":7333},[5487,14722,14723],{"class":6911},"        rtcpBuf",[5487,14725,6915],{"class":6869},[5487,14727,10552],{"class":6233},[5487,14729,13405],{"class":6869},[5487,14731,13261],{"class":6855},[5487,14733,5305],{"class":6869},[5487,14735,13412],{"class":6405},[5487,14737,6885],{"class":6869},[5487,14739,14740,14742],{"class":5489,"line":7744},[5487,14741,12242],{"class":6865},[5487,14743,7150],{"class":6869},[5487,14745,14746,14748,14750,14752,14754,14756,14758,14760,14762,14764,14766,14768,14770,14772,14774,14776,14778],{"class":5489,"line":7761},[5487,14747,11220],{"class":6865},[5487,14749,7650],{"class":6911},[5487,14751,5305],{"class":6869},[5487,14753,12254],{"class":6911},[5487,14755,5305],{"class":6869},[5487,14757,13437],{"class":6911},[5487,14759,6915],{"class":6869},[5487,14761,13362],{"class":6911},[5487,14763,5211],{"class":6869},[5487,14765,13446],{"class":6233},[5487,14767,6926],{"class":6869},[5487,14769,13451],{"class":6911},[5487,14771,7287],{"class":6869},[5487,14773,13437],{"class":6911},[5487,14775,7293],{"class":6869},[5487,14777,7284],{"class":6237},[5487,14779,7150],{"class":6869},[5487,14781,14782],{"class":5489,"line":7766},[5487,14783,12311],{"class":6865},[5487,14785,14786],{"class":5489,"line":7772},[5487,14787,11289],{"class":6869},[5487,14789,14790],{"class":5489,"line":7797},[5487,14791,7181],{"class":6869},[5487,14793,14794],{"class":5489,"line":7820},[5487,14795,10978],{"class":6869},[5487,14797,14798],{"class":5489,"line":7825},[5487,14799,5506],{"emptyLinePlaceholder":5505},[5487,14801,14802],{"class":5489,"line":7852},[5487,14803,14804],{"class":6905},"    \u002F\u002F Запускаємо горутину для трансляції відеокадрів\n",[5487,14806,14807,14809,14811],{"class":5489,"line":7876},[5487,14808,13940],{"class":6865},[5487,14810,10899],{"class":6237},[5487,14812,6900],{"class":6869},[5487,14814,14815,14817,14819,14821,14824,14826,14828,14830,14832,14834,14836,14838,14840],{"class":5489,"line":7918},[5487,14816,7129],{"class":6865},[5487,14818,7265],{"class":6911},[5487,14820,6915],{"class":6869},[5487,14822,14823],{"class":6233},"streamVideoFile",[5487,14825,6926],{"class":6869},[5487,14827,13379],{"class":6911},[5487,14829,5305],{"class":6869},[5487,14831,14508],{"class":6911},[5487,14833,7287],{"class":6869},[5487,14835,7290],{"class":6911},[5487,14837,7293],{"class":6869},[5487,14839,7284],{"class":6237},[5487,14841,7150],{"class":6869},[5487,14843,14844,14846,14848,14850,14852,14855,14857,14859,14861,14863],{"class":5489,"line":7943},[5487,14845,11041],{"class":6911},[5487,14847,5211],{"class":6869},[5487,14849,10687],{"class":6233},[5487,14851,6926],{"class":6869},[5487,14853,14854],{"class":6241},"\"Помилка трансляції відео: ",[5487,14856,6767],{"class":6911},[5487,14858,7318],{"class":6241},[5487,14860,5305],{"class":6869},[5487,14862,7290],{"class":6911},[5487,14864,6885],{"class":6869},[5487,14866,14867],{"class":5489,"line":7948},[5487,14868,7181],{"class":6869},[5487,14870,14871],{"class":5489,"line":7968},[5487,14872,10978],{"class":6869},[5487,14874,14875],{"class":5489,"line":8003},[5487,14876,5506],{"emptyLinePlaceholder":5505},[5487,14878,14879,14881],{"class":5489,"line":8042},[5487,14880,10535],{"class":6865},[5487,14882,14883],{"class":6237}," nil\n",[5487,14885,14886],{"class":5489,"line":8047},[5487,14887,7336],{"class":6869},[5487,14889,14890],{"class":5489,"line":8052},[5487,14891,5506],{"emptyLinePlaceholder":5505},[5487,14893,14894],{"class":5489,"line":8076},[5487,14895,14896],{"class":6905},"\u002F\u002F streamVideoFile читає VP8-відео із файлу та пересилає кадри у трек\n",[5487,14898,14899,14901,14904,14906,14908,14910,14912,14914,14917,14919,14922,14924,14926,14928],{"class":5489,"line":8081},[5487,14900,6894],{"class":6237},[5487,14902,14903],{"class":6233}," streamVideoFile",[5487,14905,6926],{"class":6869},[5487,14907,12139],{"class":6911},[5487,14909,7033],{"class":6869},[5487,14911,11584],{"class":6855},[5487,14913,5211],{"class":6869},[5487,14915,14916],{"class":6855},"TrackLocalStaticSample",[5487,14918,5305],{"class":6869},[5487,14920,14921],{"class":6911},"filePath",[5487,14923,7506],{"class":6855},[5487,14925,10344],{"class":6869},[5487,14927,11594],{"class":6855},[5487,14929,7150],{"class":6869},[5487,14931,14932,14935,14937,14939,14941,14943,14945,14948,14950,14952],{"class":5489,"line":8086},[5487,14933,14934],{"class":6911},"    file",[5487,14936,5305],{"class":6869},[5487,14938,7290],{"class":6911},[5487,14940,6915],{"class":6869},[5487,14942,7556],{"class":6911},[5487,14944,5211],{"class":6869},[5487,14946,14947],{"class":6233},"Open",[5487,14949,6926],{"class":6869},[5487,14951,14921],{"class":6911},[5487,14953,6885],{"class":6869},[5487,14955,14956,14958,14960,14962,14964],{"class":5489,"line":8095},[5487,14957,7262],{"class":6865},[5487,14959,7265],{"class":6911},[5487,14961,7293],{"class":6869},[5487,14963,7284],{"class":6237},[5487,14965,7150],{"class":6869},[5487,14967,14968,14970,14972,14974,14976,14978,14981,14983,14985,14987,14989],{"class":5489,"line":8101},[5487,14969,10354],{"class":6865},[5487,14971,14605],{"class":6911},[5487,14973,5211],{"class":6869},[5487,14975,11764],{"class":6233},[5487,14977,6926],{"class":6869},[5487,14979,14980],{"class":6241},"\"помилка відкриття файлу: ",[5487,14982,11772],{"class":6911},[5487,14984,7318],{"class":6241},[5487,14986,5305],{"class":6869},[5487,14988,7290],{"class":6911},[5487,14990,6885],{"class":6869},[5487,14992,14993],{"class":5489,"line":8128},[5487,14994,7330],{"class":6869},[5487,14996,14997,14999,15002,15004,15006],{"class":5489,"line":8159},[5487,14998,10715],{"class":6865},[5487,15000,15001],{"class":6911}," file",[5487,15003,5211],{"class":6869},[5487,15005,10723],{"class":6233},[5487,15007,10726],{"class":6869},[5487,15009,15010],{"class":5489,"line":8164},[5487,15011,5506],{"emptyLinePlaceholder":5505},[5487,15013,15014],{"class":5489,"line":8170},[5487,15015,15016],{"class":6905},"    \u002F\u002F IVF — простий контейнер для VP8\u002FVP9 відео\n",[5487,15018,15019,15022,15024,15027,15029,15031,15033,15036,15038,15041,15043,15045],{"class":5489,"line":8212},[5487,15020,15021],{"class":6911},"    ivf",[5487,15023,5305],{"class":6869},[5487,15025,15026],{"class":6911},"header",[5487,15028,5305],{"class":6869},[5487,15030,7290],{"class":6911},[5487,15032,6915],{"class":6869},[5487,15034,15035],{"class":6911},"ivfreader",[5487,15037,5211],{"class":6869},[5487,15039,15040],{"class":6233},"NewWith",[5487,15042,6926],{"class":6869},[5487,15044,7655],{"class":6911},[5487,15046,6885],{"class":6869},[5487,15048,15049,15051,15053,15055,15057],{"class":5489,"line":8229},[5487,15050,7262],{"class":6865},[5487,15052,7265],{"class":6911},[5487,15054,7293],{"class":6869},[5487,15056,7284],{"class":6237},[5487,15058,7150],{"class":6869},[5487,15060,15061,15063,15065,15067,15069,15071,15074,15076,15078,15080,15082],{"class":5489,"line":8234},[5487,15062,10354],{"class":6865},[5487,15064,14605],{"class":6911},[5487,15066,5211],{"class":6869},[5487,15068,11764],{"class":6233},[5487,15070,6926],{"class":6869},[5487,15072,15073],{"class":6241},"\"помилка розбору IVF: ",[5487,15075,11772],{"class":6911},[5487,15077,7318],{"class":6241},[5487,15079,5305],{"class":6869},[5487,15081,7290],{"class":6911},[5487,15083,6885],{"class":6869},[5487,15085,15086],{"class":5489,"line":8276},[5487,15087,7330],{"class":6869},[5487,15089,15090],{"class":5489,"line":8292},[5487,15091,5506],{"emptyLinePlaceholder":5505},[5487,15093,15094],{"class":5489,"line":8297},[5487,15095,15096],{"class":6905},"    \u002F\u002F Вираховуємо затримку між кадрами на основі FPS\n",[5487,15098,15099,15102,15104,15107,15109,15112,15115,15117,15119,15122],{"class":5489,"line":8339},[5487,15100,15101],{"class":6911},"    frameDuration",[5487,15103,6915],{"class":6869},[5487,15105,15106],{"class":6911},"time",[5487,15108,5211],{"class":6869},[5487,15110,15111],{"class":6911},"Millisecond",[5487,15113,15114],{"class":6869}," * ",[5487,15116,15106],{"class":6911},[5487,15118,5211],{"class":6869},[5487,15120,15121],{"class":6233},"Duration",[5487,15123,14542],{"class":6869},[5487,15125,15126,15129,15132,15134,15136,15138,15141,15144,15146,15148,15150,15152,15155,15158,15161],{"class":5489,"line":8355},[5487,15127,15128],{"class":6869},"        (",[5487,15130,15131],{"class":6855},"float32",[5487,15133,6926],{"class":6869},[5487,15135,15026],{"class":6911},[5487,15137,5211],{"class":6869},[5487,15139,15140],{"class":6911},"TimebaseNumerator",[5487,15142,15143],{"class":6869},")\u002F",[5487,15145,15131],{"class":6855},[5487,15147,6926],{"class":6869},[5487,15149,15026],{"class":6911},[5487,15151,5211],{"class":6869},[5487,15153,15154],{"class":6911},"TimebaseDenominator",[5487,15156,15157],{"class":6869},"))*",[5487,15159,15160],{"class":6405},"1000",[5487,15162,9969],{"class":6869},[5487,15164,15165],{"class":5489,"line":8360},[5487,15166,14586],{"class":6869},[5487,15168,15169],{"class":5489,"line":8365},[5487,15170,5506],{"emptyLinePlaceholder":5505},[5487,15172,15173,15176,15178,15180,15182,15185,15187,15190],{"class":5489,"line":8381},[5487,15174,15175],{"class":6911},"    ticker",[5487,15177,6915],{"class":6869},[5487,15179,15106],{"class":6911},[5487,15181,5211],{"class":6869},[5487,15183,15184],{"class":6233},"NewTicker",[5487,15186,6926],{"class":6869},[5487,15188,15189],{"class":6911},"frameDuration",[5487,15191,6885],{"class":6869},[5487,15193,15194,15196,15199,15201,15204],{"class":5489,"line":8409},[5487,15195,10715],{"class":6865},[5487,15197,15198],{"class":6911}," ticker",[5487,15200,5211],{"class":6869},[5487,15202,15203],{"class":6233},"Stop",[5487,15205,10726],{"class":6869},[5487,15207,15208],{"class":5489,"line":11072},[5487,15209,5506],{"emptyLinePlaceholder":5505},[5487,15211,15212,15214,15217,15219,15221,15224],{"class":5489,"line":11077},[5487,15213,7647],{"class":6865},[5487,15215,15216],{"class":6865}," range",[5487,15218,15198],{"class":6911},[5487,15220,5211],{"class":6869},[5487,15222,15223],{"class":6911},"C",[5487,15225,7150],{"class":6869},[5487,15227,15228,15231,15233,15235,15237,15239,15241,15244,15246,15249],{"class":5489,"line":11129},[5487,15229,15230],{"class":6911},"        frame",[5487,15232,5305],{"class":6869},[5487,15234,12254],{"class":6911},[5487,15236,5305],{"class":6869},[5487,15238,7290],{"class":6911},[5487,15240,6915],{"class":6869},[5487,15242,15243],{"class":6911},"ivf",[5487,15245,5211],{"class":6869},[5487,15247,15248],{"class":6233},"ParseNextFrame",[5487,15250,10726],{"class":6869},[5487,15252,15253,15255,15257,15259,15261,15263,15265],{"class":5489,"line":11134},[5487,15254,7129],{"class":6865},[5487,15256,7265],{"class":6911},[5487,15258,7140],{"class":6869},[5487,15260,14001],{"class":6911},[5487,15262,5211],{"class":6869},[5487,15264,14006],{"class":6911},[5487,15266,7150],{"class":6869},[5487,15268,15269,15271,15273,15275,15277,15280],{"class":5489,"line":11140},[5487,15270,11041],{"class":6911},[5487,15272,5211],{"class":6869},[5487,15274,7228],{"class":6233},[5487,15276,6926],{"class":6869},[5487,15278,15279],{"class":6241},"\"Відео завершилося, перезапуск...\"",[5487,15281,6885],{"class":6869},[5487,15283,15284,15287,15289,15292,15294,15296,15298,15300,15302,15305],{"class":5489,"line":11156},[5487,15285,15286],{"class":6911},"            file",[5487,15288,5211],{"class":6869},[5487,15290,15291],{"class":6233},"Seek",[5487,15293,6926],{"class":6869},[5487,15295,6510],{"class":6405},[5487,15297,5305],{"class":6869},[5487,15299,14001],{"class":6911},[5487,15301,5211],{"class":6869},[5487,15303,15304],{"class":6911},"SeekStart",[5487,15306,6885],{"class":6869},[5487,15308,15309,15312,15314,15316,15318,15320,15322,15324,15326,15328,15330,15332],{"class":5489,"line":11186},[5487,15310,15311],{"class":6911},"            ivf",[5487,15313,5305],{"class":6869},[5487,15315,12254],{"class":6911},[5487,15317,5305],{"class":6869},[5487,15319,12254],{"class":6911},[5487,15321,7451],{"class":6869},[5487,15323,15035],{"class":6911},[5487,15325,5211],{"class":6869},[5487,15327,15040],{"class":6233},[5487,15329,6926],{"class":6869},[5487,15331,7655],{"class":6911},[5487,15333,6885],{"class":6869},[5487,15335,15336],{"class":5489,"line":11202},[5487,15337,13546],{"class":6865},[5487,15339,15340],{"class":5489,"line":11207},[5487,15341,7181],{"class":6869},[5487,15343,15344,15346,15348,15350,15352],{"class":5489,"line":11217},[5487,15345,7129],{"class":6865},[5487,15347,7265],{"class":6911},[5487,15349,7293],{"class":6869},[5487,15351,7284],{"class":6237},[5487,15353,7150],{"class":6869},[5487,15355,15356,15359,15361,15363,15365,15367,15370,15372,15374,15376,15378],{"class":5489,"line":11249},[5487,15357,15358],{"class":6865},"            return",[5487,15360,14605],{"class":6911},[5487,15362,5211],{"class":6869},[5487,15364,11764],{"class":6233},[5487,15366,6926],{"class":6869},[5487,15368,15369],{"class":6241},"\"помилка читання кадру: ",[5487,15371,11772],{"class":6911},[5487,15373,7318],{"class":6241},[5487,15375,5305],{"class":6869},[5487,15377,7290],{"class":6911},[5487,15379,6885],{"class":6869},[5487,15381,15382],{"class":5489,"line":11286},[5487,15383,7181],{"class":6869},[5487,15385,15386],{"class":5489,"line":11292},[5487,15387,5506],{"emptyLinePlaceholder":5505},[5487,15389,15390],{"class":5489,"line":11303},[5487,15391,15392],{"class":6905},"        \u002F\u002F Відправляємо кадр у трек\n",[5487,15394,15395,15397,15399,15401,15403,15405,15408,15410,15413,15415,15418,15420,15423,15425,15428,15430,15432,15434,15436,15439,15441,15443,15445],{"class":5489,"line":11332},[5487,15396,7129],{"class":6865},[5487,15398,7265],{"class":6911},[5487,15400,6915],{"class":6869},[5487,15402,12139],{"class":6911},[5487,15404,5211],{"class":6869},[5487,15406,15407],{"class":6233},"WriteSample",[5487,15409,6926],{"class":6869},[5487,15411,15412],{"class":6855},"media",[5487,15414,5211],{"class":6869},[5487,15416,15417],{"class":6855},"Sample",[5487,15419,11651],{"class":6869},[5487,15421,15422],{"class":6911},"Data",[5487,15424,9963],{"class":6869},[5487,15426,15427],{"class":6911},"frame",[5487,15429,5305],{"class":6869},[5487,15431,15121],{"class":6911},[5487,15433,9963],{"class":6869},[5487,15435,15189],{"class":6911},[5487,15437,15438],{"class":6869},"}); ",[5487,15440,7290],{"class":6911},[5487,15442,7293],{"class":6869},[5487,15444,7284],{"class":6237},[5487,15446,7150],{"class":6869},[5487,15448,15449,15451,15453,15455,15457,15459,15462,15464,15466,15468,15470],{"class":5489,"line":11337},[5487,15450,15358],{"class":6865},[5487,15452,14605],{"class":6911},[5487,15454,5211],{"class":6869},[5487,15456,11764],{"class":6233},[5487,15458,6926],{"class":6869},[5487,15460,15461],{"class":6241},"\"помилка запису кадру: ",[5487,15463,11772],{"class":6911},[5487,15465,7318],{"class":6241},[5487,15467,5305],{"class":6869},[5487,15469,7290],{"class":6911},[5487,15471,6885],{"class":6869},[5487,15473,15474],{"class":5489,"line":11342},[5487,15475,7181],{"class":6869},[5487,15477,15478],{"class":5489,"line":11347},[5487,15479,7330],{"class":6869},[5487,15481,15482],{"class":5489,"line":11352},[5487,15483,5506],{"emptyLinePlaceholder":5505},[5487,15485,15486,15488],{"class":5489,"line":11361},[5487,15487,10535],{"class":6865},[5487,15489,14883],{"class":6237},[5487,15491,15492],{"class":5489,"line":11374},[5487,15493,7336],{"class":6869},[5299,15495,15496],{},[5235,15497,15498],{},"Пояснення роботи трансляції:",[5198,15500,15501,15507,15513,15516,15522],{},[5201,15502,15503,15504,15506],{},"Створюється локальний статичний трек (",[5208,15505,14916],{},") з підтримкою кодеку VP8.",[5201,15508,15509,15510,15512],{},"Трек додається до PeerConnection через ",[5208,15511,13140],{},", що автоматично включає його у SDP-опис.",[5201,15514,15515],{},"У фоновій горутині відкривається відеофайл у форматі IVF (контейнер для чистого VP8\u002FVP9 без аудіо).",[5201,15517,15518,15519,5211],{},"Кадри з файлу читаються із частотою, що відповідає FPS відео, та записуються у трек методом ",[5208,15520,15521],{},"WriteSample()",[5201,15523,15524],{},"Бібліотека Pion автоматично інкапсулює кадри у RTP-пакети, шифрує їх через SRTP та відправляє клієнту.",[10063,15526,15527,15528,5305,15531,15534],{},"Формат IVF є простим, але обмеженим (лише VP8\u002FVP9, без аудіо). Для підтримки різних кодеків (H.264, H.265) та синхронізованого аудіо у реальних системах використовуються інструменти транскодування (FFmpeg) або бібліотеки роботи з медіаконтейнерами (MP4, Matroska). Бібліотека Pion надає пакети ",[5208,15529,15530],{},"github.com\u002Fpion\u002Fwebrtc\u002Fv4\u002Fpkg\u002Fmedia\u002Fh264reader",[5208,15532,15533],{},"github.com\u002Fpion\u002Fwebrtc\u002Fv4\u002Fpkg\u002Fmedia\u002Foggreader"," для читання популярних форматів.",[5291,15536],{},[5294,15538,15540],{"id":15539},"інтеграція-webrtc-із-клієнтськими-платформами","Інтеграція WebRTC із клієнтськими платформами",[5299,15542,15543],{},"Після реалізації серверних компонентів необхідно створити клієнтську частину, що взаємодіє з сервером сигналізації та встановлює WebRTC-з'єднання. Розглянемо приклади для різних платформ.",[5365,15545,15547],{"id":15546},"вебклієнт-javascript-у-браузері","Вебклієнт (JavaScript у браузері)",[5299,15549,15550,15551,15554],{},"Сучасні браузери мають нативну підтримку WebRTC через JavaScript API ",[5208,15552,15553],{},"RTCPeerConnection",". Приклад клієнта для отримання відеопотоку від сервера:",[5478,15556,15560],{"className":15557,"code":15558,"language":15559,"meta":5483,"style":5483},"language-html shiki shiki-themes light-plus dark-plus dark-plus","\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Ctitle>WebRTC Відеопрогравач\u003C\u002Ftitle>\n    \u003Cstyle>\n        body {\n            font-family: -apple-system, system-ui, sans-serif;\n            max-width: 1200px;\n            margin: 40px auto;\n            padding: 20px;\n        }\n        video {\n            width: 100%;\n            max-width: 800px;\n            background: #000;\n            border-radius: 8px;\n        }\n        button {\n            padding: 12px 24px;\n            font-size: 16px;\n            cursor: pointer;\n            background: #3b82f6;\n            color: white;\n            border: none;\n            border-radius: 6px;\n            margin-top: 20px;\n        }\n        button:hover {\n            background: #2563eb;\n        }\n        #status {\n            margin-top: 16px;\n            padding: 12px;\n            background: #f3f4f6;\n            border-radius: 6px;\n            font-family: monospace;\n            font-size: 14px;\n        }\n    \u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n    \u003Ch1>WebRTC Відеопрогравач\u003C\u002Fh1>\n    \u003Cvideo id=\"remoteVideo\" autoplay playsinline controls>\u003C\u002Fvideo>\n    \u003Cbutton id=\"connectBtn\">Підключитися до трансляції\u003C\u002Fbutton>\n    \u003Cdiv id=\"status\">Статус: Очікування підключення...\u003C\u002Fdiv>\n\n    \u003Cscript>\n        const remoteVideo = document.getElementById('remoteVideo');\n        const connectBtn = document.getElementById('connectBtn');\n        const statusDiv = document.getElementById('status');\n\n        let peerConnection = null;\n        let websocket = null;\n\n        \u002F\u002F Конфігурація ICE серверів\n        const iceServers = {\n            iceServers: [\n                { urls: 'stun:stun.l.google.com:19302' },\n                \u002F\u002F Додайте TURN-сервер для надійності:\n                \u002F\u002F { urls: 'turn:turn.example.com:3478', username: 'user', credential: 'pass' }\n            ]\n        };\n\n        function updateStatus(message) {\n            console.log(message);\n            statusDiv.textContent = `Статус: ${message}`;\n        }\n\n        async function startConnection() {\n            try {\n                updateStatus('Підключення до сервера сигналізації...');\n                \n                \u002F\u002F Підключення до WebSocket-сервера\n                websocket = new WebSocket('ws:\u002F\u002Flocalhost:8080\u002Fsignal?id=client-' + Date.now());\n                \n                websocket.onopen = () => {\n                    updateStatus('З\\'єднання з сервером встановлено. Ініціалізація WebRTC...');\n                    initializeWebRTC();\n                };\n\n                websocket.onmessage = async (event) => {\n                    const message = JSON.parse(event.data);\n                    await handleSignalingMessage(message);\n                };\n\n                websocket.onerror = (error) => {\n                    updateStatus('Помилка WebSocket: ' + error);\n                };\n\n                websocket.onclose = () => {\n                    updateStatus('З\\'єднання з сервером закрито');\n                };\n\n            } catch (error) {\n                updateStatus('Помилка: ' + error.message);\n            }\n        }\n\n        async function initializeWebRTC() {\n            \u002F\u002F Створення PeerConnection\n            peerConnection = new RTCPeerConnection(iceServers);\n\n            \u002F\u002F Обробник отримання віддалених медіапотоків\n            peerConnection.ontrack = (event) => {\n                updateStatus('Отримано відеопотік! Відтворення...');\n                remoteVideo.srcObject = event.streams[0];\n            };\n\n            \u002F\u002F Обробник генерації ICE-кандидатів\n            peerConnection.onicecandidate = (event) => {\n                if (event.candidate) {\n                    console.log('Відправка ICE-кандидата на сервер');\n                    websocket.send(JSON.stringify({\n                        type: 'ice-candidate',\n                        candidate: event.candidate,\n                        target: 'server'\n                    }));\n                }\n            };\n\n            \u002F\u002F Обробник зміни стану з'єднання\n            peerConnection.onconnectionstatechange = () => {\n                updateStatus('Стан WebRTC: ' + peerConnection.connectionState);\n                \n                if (peerConnection.connectionState === 'connected') {\n                    updateStatus('WebRTC з\\'єднання встановлено! Трансляція активна.');\n                }\n            };\n\n            \u002F\u002F Створення та відправка SDP Offer\n            const offer = await peerConnection.createOffer({\n                offerToReceiveVideo: true,\n                offerToReceiveAudio: true\n            });\n\n            await peerConnection.setLocalDescription(offer);\n\n            updateStatus('Відправка SDP Offer на сервер...');\n            websocket.send(JSON.stringify({\n                type: 'offer',\n                sdp: offer.sdp,\n                target: 'server'\n            }));\n        }\n\n        async function handleSignalingMessage(message) {\n            switch (message.type) {\n                case 'answer':\n                    \u002F\u002F Отримали SDP Answer від сервера\n                    updateStatus('Отримано SDP Answer. Встановлення з\\'єднання...');\n                    const answer = new RTCSessionDescription({\n                        type: 'answer',\n                        sdp: message.sdp\n                    });\n                    await peerConnection.setRemoteDescription(answer);\n                    break;\n\n                case 'ice-candidate':\n                    \u002F\u002F Отримали ICE-кандидат від сервера\n                    if (message.candidate) {\n                        console.log('Додавання ICE-кандидата від сервера');\n                        await peerConnection.addIceCandidate(\n                            new RTCIceCandidate(message.candidate)\n                        );\n                    }\n                    break;\n\n                default:\n                    console.warn('Невідомий тип повідомлення:', message.type);\n            }\n        }\n\n        \u002F\u002F Обробник кнопки підключення\n        connectBtn.addEventListener('click', () => {\n            connectBtn.disabled = true;\n            connectBtn.textContent = 'Підключення...';\n            startConnection();\n        });\n    \u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n","html",[5208,15561,15562,15575,15583,15592,15608,15627,15636,15645,15665,15677,15692,15704,15708,15715,15727,15738,15750,15762,15766,15773,15787,15799,15811,15822,15834,15846,15857,15868,15872,15879,15890,15894,15901,15911,15921,15932,15942,15953,15964,15968,15976,15984,15993,16009,16039,16064,16089,16093,16102,16129,16151,16173,16177,16191,16204,16208,16213,16224,16232,16246,16251,16256,16261,16266,16270,16285,16301,16329,16333,16337,16350,16357,16369,16374,16379,16411,16415,16434,16452,16460,16465,16469,16493,16522,16536,16540,16544,16565,16580,16584,16588,16605,16620,16624,16628,16642,16661,16665,16669,16673,16684,16689,16708,16712,16717,16738,16749,16776,16781,16786,16792,16814,16829,16846,16870,16881,16895,16904,16910,16915,16920,16925,16931,16949,16970,16975,16997,17014,17019,17024,17029,17035,17058,17068,17077,17083,17088,17107,17112,17125,17145,17156,17171,17179,17185,17190,17195,17210,17225,17236,17242,17259,17275,17284,17297,17303,17321,17329,17334,17343,17349,17365,17382,17397,17416,17422,17428,17435,17440,17448,17473,17478,17483,17488,17494,17517,17534,17550,17558,17564,17573,17582],{"__ignoreMap":5483},[5487,15563,15564,15567,15570,15573],{"class":5489,"line":5490},[5487,15565,15566],{"class":8610},"\u003C!",[5487,15568,15569],{"class":8614},"DOCTYPE",[5487,15571,15572],{"class":8617}," html",[5487,15574,8690],{"class":8610},[5487,15576,15577,15579,15581],{"class":5489,"line":5496},[5487,15578,8641],{"class":8610},[5487,15580,15559],{"class":8614},[5487,15582,8690],{"class":8610},[5487,15584,15585,15587,15590],{"class":5489,"line":5502},[5487,15586,8641],{"class":8610},[5487,15588,15589],{"class":8614},"head",[5487,15591,8690],{"class":8610},[5487,15593,15594,15596,15599,15602,15604,15606],{"class":5489,"line":5509},[5487,15595,8731],{"class":8610},[5487,15597,15598],{"class":8614},"meta",[5487,15600,15601],{"class":8617}," charset",[5487,15603,8621],{"class":6869},[5487,15605,8633],{"class":8624},[5487,15607,8690],{"class":8610},[5487,15609,15610,15612,15615,15618,15621,15623,15625],{"class":5489,"line":5515},[5487,15611,8731],{"class":8610},[5487,15613,15614],{"class":8614},"title",[5487,15616,15617],{"class":8610},">",[5487,15619,15620],{"class":6869},"WebRTC Відеопрогравач",[5487,15622,9157],{"class":8610},[5487,15624,15614],{"class":8614},[5487,15626,8690],{"class":8610},[5487,15628,15629,15631,15634],{"class":5489,"line":5521},[5487,15630,8731],{"class":8610},[5487,15632,15633],{"class":8614},"style",[5487,15635,8690],{"class":8610},[5487,15637,15638,15642],{"class":5489,"line":5527},[5487,15639,15641],{"class":15640},"sqdDX","        body",[5487,15643,7150],{"class":15644},"sD7JJ",[5487,15646,15647,15650,15653,15657,15659,15662],{"class":5489,"line":5532},[5487,15648,15649],{"class":8617},"            font-family",[5487,15651,15652],{"class":15644},": -apple-system, ",[5487,15654,15656],{"class":15655},"sDUd3","system-ui",[5487,15658,5305],{"class":15644},[5487,15660,15661],{"class":15655},"sans-serif",[5487,15663,15664],{"class":15644},";\n",[5487,15666,15667,15670,15672,15675],{"class":5489,"line":5538},[5487,15668,15669],{"class":8617},"            max-width",[5487,15671,9963],{"class":15644},[5487,15673,15674],{"class":6405},"1200px",[5487,15676,15664],{"class":15644},[5487,15678,15679,15682,15684,15687,15690],{"class":5489,"line":5544},[5487,15680,15681],{"class":8617},"            margin",[5487,15683,9963],{"class":15644},[5487,15685,15686],{"class":6405},"40px",[5487,15688,15689],{"class":15655}," auto",[5487,15691,15664],{"class":15644},[5487,15693,15694,15697,15699,15702],{"class":5489,"line":5550},[5487,15695,15696],{"class":8617},"            padding",[5487,15698,9963],{"class":15644},[5487,15700,15701],{"class":6405},"20px",[5487,15703,15664],{"class":15644},[5487,15705,15706],{"class":5489,"line":5555},[5487,15707,7181],{"class":15644},[5487,15709,15710,15713],{"class":5489,"line":5561},[5487,15711,15712],{"class":15640},"        video",[5487,15714,7150],{"class":15644},[5487,15716,15717,15720,15722,15725],{"class":5489,"line":5567},[5487,15718,15719],{"class":8617},"            width",[5487,15721,9963],{"class":15644},[5487,15723,15724],{"class":6405},"100%",[5487,15726,15664],{"class":15644},[5487,15728,15729,15731,15733,15736],{"class":5489,"line":5704},[5487,15730,15669],{"class":8617},[5487,15732,9963],{"class":15644},[5487,15734,15735],{"class":6405},"800px",[5487,15737,15664],{"class":15644},[5487,15739,15740,15743,15745,15748],{"class":5489,"line":5710},[5487,15741,15742],{"class":8617},"            background",[5487,15744,9963],{"class":15644},[5487,15746,15747],{"class":15655},"#000",[5487,15749,15664],{"class":15644},[5487,15751,15752,15755,15757,15760],{"class":5489,"line":5716},[5487,15753,15754],{"class":8617},"            border-radius",[5487,15756,9963],{"class":15644},[5487,15758,15759],{"class":6405},"8px",[5487,15761,15664],{"class":15644},[5487,15763,15764],{"class":5489,"line":5722},[5487,15765,7181],{"class":15644},[5487,15767,15768,15771],{"class":5489,"line":5727},[5487,15769,15770],{"class":15640},"        button",[5487,15772,7150],{"class":15644},[5487,15774,15775,15777,15779,15782,15785],{"class":5489,"line":6007},[5487,15776,15696],{"class":8617},[5487,15778,9963],{"class":15644},[5487,15780,15781],{"class":6405},"12px",[5487,15783,15784],{"class":6405}," 24px",[5487,15786,15664],{"class":15644},[5487,15788,15789,15792,15794,15797],{"class":5489,"line":6013},[5487,15790,15791],{"class":8617},"            font-size",[5487,15793,9963],{"class":15644},[5487,15795,15796],{"class":6405},"16px",[5487,15798,15664],{"class":15644},[5487,15800,15801,15804,15806,15809],{"class":5489,"line":6019},[5487,15802,15803],{"class":8617},"            cursor",[5487,15805,9963],{"class":15644},[5487,15807,15808],{"class":15655},"pointer",[5487,15810,15664],{"class":15644},[5487,15812,15813,15815,15817,15820],{"class":5489,"line":6025},[5487,15814,15742],{"class":8617},[5487,15816,9963],{"class":15644},[5487,15818,15819],{"class":15655},"#3b82f6",[5487,15821,15664],{"class":15644},[5487,15823,15824,15827,15829,15832],{"class":5489,"line":6030},[5487,15825,15826],{"class":8617},"            color",[5487,15828,9963],{"class":15644},[5487,15830,15831],{"class":15655},"white",[5487,15833,15664],{"class":15644},[5487,15835,15836,15839,15841,15844],{"class":5489,"line":6035},[5487,15837,15838],{"class":8617},"            border",[5487,15840,9963],{"class":15644},[5487,15842,15843],{"class":15655},"none",[5487,15845,15664],{"class":15644},[5487,15847,15848,15850,15852,15855],{"class":5489,"line":7188},[5487,15849,15754],{"class":8617},[5487,15851,9963],{"class":15644},[5487,15853,15854],{"class":6405},"6px",[5487,15856,15664],{"class":15644},[5487,15858,15859,15862,15864,15866],{"class":5489,"line":7209},[5487,15860,15861],{"class":8617},"            margin-top",[5487,15863,9963],{"class":15644},[5487,15865,15701],{"class":6405},[5487,15867,15664],{"class":15644},[5487,15869,15870],{"class":5489,"line":7215},[5487,15871,7181],{"class":15644},[5487,15873,15874,15877],{"class":5489,"line":7220},[5487,15875,15876],{"class":15640},"        button:hover",[5487,15878,7150],{"class":15644},[5487,15880,15881,15883,15885,15888],{"class":5489,"line":7238},[5487,15882,15742],{"class":8617},[5487,15884,9963],{"class":15644},[5487,15886,15887],{"class":15655},"#2563eb",[5487,15889,15664],{"class":15644},[5487,15891,15892],{"class":5489,"line":7254},[5487,15893,7181],{"class":15644},[5487,15895,15896,15899],{"class":5489,"line":7259},[5487,15897,15898],{"class":15640},"        #status",[5487,15900,7150],{"class":15644},[5487,15902,15903,15905,15907,15909],{"class":5489,"line":7300},[5487,15904,15861],{"class":8617},[5487,15906,9963],{"class":15644},[5487,15908,15796],{"class":6405},[5487,15910,15664],{"class":15644},[5487,15912,15913,15915,15917,15919],{"class":5489,"line":7327},[5487,15914,15696],{"class":8617},[5487,15916,9963],{"class":15644},[5487,15918,15781],{"class":6405},[5487,15920,15664],{"class":15644},[5487,15922,15923,15925,15927,15930],{"class":5489,"line":7333},[5487,15924,15742],{"class":8617},[5487,15926,9963],{"class":15644},[5487,15928,15929],{"class":15655},"#f3f4f6",[5487,15931,15664],{"class":15644},[5487,15933,15934,15936,15938,15940],{"class":5489,"line":7744},[5487,15935,15754],{"class":8617},[5487,15937,9963],{"class":15644},[5487,15939,15854],{"class":6405},[5487,15941,15664],{"class":15644},[5487,15943,15944,15946,15948,15951],{"class":5489,"line":7761},[5487,15945,15649],{"class":8617},[5487,15947,9963],{"class":15644},[5487,15949,15950],{"class":15655},"monospace",[5487,15952,15664],{"class":15644},[5487,15954,15955,15957,15959,15962],{"class":5489,"line":7766},[5487,15956,15791],{"class":8617},[5487,15958,9963],{"class":15644},[5487,15960,15961],{"class":6405},"14px",[5487,15963,15664],{"class":15644},[5487,15965,15966],{"class":5489,"line":7772},[5487,15967,7181],{"class":15644},[5487,15969,15970,15972,15974],{"class":5489,"line":7797},[5487,15971,9006],{"class":8610},[5487,15973,15633],{"class":8614},[5487,15975,8690],{"class":8610},[5487,15977,15978,15980,15982],{"class":5489,"line":7820},[5487,15979,9157],{"class":8610},[5487,15981,15589],{"class":8614},[5487,15983,8690],{"class":8610},[5487,15985,15986,15988,15991],{"class":5489,"line":7825},[5487,15987,8641],{"class":8610},[5487,15989,15990],{"class":8614},"body",[5487,15992,8690],{"class":8610},[5487,15994,15995,15997,15999,16001,16003,16005,16007],{"class":5489,"line":7852},[5487,15996,8731],{"class":8610},[5487,15998,5186],{"class":8614},[5487,16000,15617],{"class":8610},[5487,16002,15620],{"class":6869},[5487,16004,9157],{"class":8610},[5487,16006,5186],{"class":8614},[5487,16008,8690],{"class":8610},[5487,16010,16011,16013,16016,16018,16020,16023,16026,16029,16032,16035,16037],{"class":5489,"line":7876},[5487,16012,8731],{"class":8610},[5487,16014,16015],{"class":8614},"video",[5487,16017,8706],{"class":8617},[5487,16019,8621],{"class":6869},[5487,16021,16022],{"class":8624},"\"remoteVideo\"",[5487,16024,16025],{"class":8617}," autoplay",[5487,16027,16028],{"class":8617}," playsinline",[5487,16030,16031],{"class":8617}," controls",[5487,16033,16034],{"class":8610},">\u003C\u002F",[5487,16036,16015],{"class":8614},[5487,16038,8690],{"class":8610},[5487,16040,16041,16043,16046,16048,16050,16053,16055,16058,16060,16062],{"class":5489,"line":7918},[5487,16042,8731],{"class":8610},[5487,16044,16045],{"class":8614},"button",[5487,16047,8706],{"class":8617},[5487,16049,8621],{"class":6869},[5487,16051,16052],{"class":8624},"\"connectBtn\"",[5487,16054,15617],{"class":8610},[5487,16056,16057],{"class":6869},"Підключитися до трансляції",[5487,16059,9157],{"class":8610},[5487,16061,16045],{"class":8614},[5487,16063,8690],{"class":8610},[5487,16065,16066,16068,16071,16073,16075,16078,16080,16083,16085,16087],{"class":5489,"line":7943},[5487,16067,8731],{"class":8610},[5487,16069,16070],{"class":8614},"div",[5487,16072,8706],{"class":8617},[5487,16074,8621],{"class":6869},[5487,16076,16077],{"class":8624},"\"status\"",[5487,16079,15617],{"class":8610},[5487,16081,16082],{"class":6869},"Статус: Очікування підключення...",[5487,16084,9157],{"class":8610},[5487,16086,16070],{"class":8614},[5487,16088,8690],{"class":8610},[5487,16090,16091],{"class":5489,"line":7948},[5487,16092,5506],{"emptyLinePlaceholder":5505},[5487,16094,16095,16097,16100],{"class":5489,"line":7968},[5487,16096,8731],{"class":8610},[5487,16098,16099],{"class":8614},"script",[5487,16101,8690],{"class":8610},[5487,16103,16104,16107,16110,16113,16116,16118,16121,16123,16126],{"class":5489,"line":8003},[5487,16105,16106],{"class":6237},"        const",[5487,16108,16109],{"class":7447}," remoteVideo",[5487,16111,16112],{"class":6869}," =",[5487,16114,16115],{"class":6911}," document",[5487,16117,5211],{"class":15644},[5487,16119,16120],{"class":6233},"getElementById",[5487,16122,6926],{"class":15644},[5487,16124,16125],{"class":6241},"'remoteVideo'",[5487,16127,16128],{"class":15644},");\n",[5487,16130,16131,16133,16136,16138,16140,16142,16144,16146,16149],{"class":5489,"line":8042},[5487,16132,16106],{"class":6237},[5487,16134,16135],{"class":7447}," connectBtn",[5487,16137,16112],{"class":6869},[5487,16139,16115],{"class":6911},[5487,16141,5211],{"class":15644},[5487,16143,16120],{"class":6233},[5487,16145,6926],{"class":15644},[5487,16147,16148],{"class":6241},"'connectBtn'",[5487,16150,16128],{"class":15644},[5487,16152,16153,16155,16158,16160,16162,16164,16166,16168,16171],{"class":5489,"line":8047},[5487,16154,16106],{"class":6237},[5487,16156,16157],{"class":7447}," statusDiv",[5487,16159,16112],{"class":6869},[5487,16161,16115],{"class":6911},[5487,16163,5211],{"class":15644},[5487,16165,16120],{"class":6233},[5487,16167,6926],{"class":15644},[5487,16169,16170],{"class":6241},"'status'",[5487,16172,16128],{"class":15644},[5487,16174,16175],{"class":5489,"line":8052},[5487,16176,5506],{"emptyLinePlaceholder":5505},[5487,16178,16179,16182,16184,16186,16189],{"class":5489,"line":8076},[5487,16180,16181],{"class":6237},"        let",[5487,16183,12354],{"class":6911},[5487,16185,16112],{"class":6869},[5487,16187,16188],{"class":6237}," null",[5487,16190,15664],{"class":15644},[5487,16192,16193,16195,16198,16200,16202],{"class":5489,"line":8081},[5487,16194,16181],{"class":6237},[5487,16196,16197],{"class":6911}," websocket",[5487,16199,16112],{"class":6869},[5487,16201,16188],{"class":6237},[5487,16203,15664],{"class":15644},[5487,16205,16206],{"class":5489,"line":8086},[5487,16207,5506],{"emptyLinePlaceholder":5505},[5487,16209,16210],{"class":5489,"line":8095},[5487,16211,16212],{"class":6905},"        \u002F\u002F Конфігурація ICE серверів\n",[5487,16214,16215,16217,16220,16222],{"class":5489,"line":8101},[5487,16216,16106],{"class":6237},[5487,16218,16219],{"class":7447}," iceServers",[5487,16221,16112],{"class":6869},[5487,16223,7150],{"class":15644},[5487,16225,16226,16229],{"class":5489,"line":8128},[5487,16227,16228],{"class":6911},"            iceServers:",[5487,16230,16231],{"class":15644}," [\n",[5487,16233,16234,16237,16240,16243],{"class":5489,"line":8159},[5487,16235,16236],{"class":15644},"                { ",[5487,16238,16239],{"class":6911},"urls:",[5487,16241,16242],{"class":6241}," 'stun:stun.l.google.com:19302'",[5487,16244,16245],{"class":15644}," },\n",[5487,16247,16248],{"class":5489,"line":8164},[5487,16249,16250],{"class":6905},"                \u002F\u002F Додайте TURN-сервер для надійності:\n",[5487,16252,16253],{"class":5489,"line":8170},[5487,16254,16255],{"class":6905},"                \u002F\u002F { urls: 'turn:turn.example.com:3478', username: 'user', credential: 'pass' }\n",[5487,16257,16258],{"class":5489,"line":8212},[5487,16259,16260],{"class":15644},"            ]\n",[5487,16262,16263],{"class":5489,"line":8229},[5487,16264,16265],{"class":15644},"        };\n",[5487,16267,16268],{"class":5489,"line":8234},[5487,16269,5506],{"emptyLinePlaceholder":5505},[5487,16271,16272,16275,16278,16280,16283],{"class":5489,"line":8276},[5487,16273,16274],{"class":6237},"        function",[5487,16276,16277],{"class":6233}," updateStatus",[5487,16279,6926],{"class":15644},[5487,16281,16282],{"class":6911},"message",[5487,16284,7043],{"class":15644},[5487,16286,16287,16290,16292,16295,16297,16299],{"class":5489,"line":8292},[5487,16288,16289],{"class":6911},"            console",[5487,16291,5211],{"class":15644},[5487,16293,16294],{"class":6233},"log",[5487,16296,6926],{"class":15644},[5487,16298,16282],{"class":6911},[5487,16300,16128],{"class":15644},[5487,16302,16303,16306,16308,16311,16313,16316,16319,16321,16324,16327],{"class":5489,"line":8297},[5487,16304,16305],{"class":6911},"            statusDiv",[5487,16307,5211],{"class":15644},[5487,16309,16310],{"class":6911},"textContent",[5487,16312,16112],{"class":6869},[5487,16314,16315],{"class":6241}," `Статус: ",[5487,16317,16318],{"class":6237},"${",[5487,16320,16282],{"class":6911},[5487,16322,16323],{"class":6237},"}",[5487,16325,16326],{"class":6241},"`",[5487,16328,15664],{"class":15644},[5487,16330,16331],{"class":5489,"line":8339},[5487,16332,7181],{"class":15644},[5487,16334,16335],{"class":5489,"line":8355},[5487,16336,5506],{"emptyLinePlaceholder":5505},[5487,16338,16339,16342,16345,16348],{"class":5489,"line":8360},[5487,16340,16341],{"class":6237},"        async",[5487,16343,16344],{"class":6237}," function",[5487,16346,16347],{"class":6233}," startConnection",[5487,16349,6900],{"class":15644},[5487,16351,16352,16355],{"class":5489,"line":8365},[5487,16353,16354],{"class":6865},"            try",[5487,16356,7150],{"class":15644},[5487,16358,16359,16362,16364,16367],{"class":5489,"line":8381},[5487,16360,16361],{"class":6233},"                updateStatus",[5487,16363,6926],{"class":15644},[5487,16365,16366],{"class":6241},"'Підключення до сервера сигналізації...'",[5487,16368,16128],{"class":15644},[5487,16370,16371],{"class":5489,"line":8409},[5487,16372,16373],{"class":15644},"                \n",[5487,16375,16376],{"class":5489,"line":11072},[5487,16377,16378],{"class":6905},"                \u002F\u002F Підключення до WebSocket-сервера\n",[5487,16380,16381,16384,16386,16389,16392,16394,16397,16400,16403,16405,16408],{"class":5489,"line":11077},[5487,16382,16383],{"class":6911},"                websocket",[5487,16385,16112],{"class":6869},[5487,16387,16388],{"class":6237}," new",[5487,16390,16391],{"class":6233}," WebSocket",[5487,16393,6926],{"class":15644},[5487,16395,16396],{"class":6241},"'ws:\u002F\u002Flocalhost:8080\u002Fsignal?id=client-'",[5487,16398,16399],{"class":6869}," +",[5487,16401,16402],{"class":6911}," Date",[5487,16404,5211],{"class":15644},[5487,16406,16407],{"class":6233},"now",[5487,16409,16410],{"class":15644},"());\n",[5487,16412,16413],{"class":5489,"line":11129},[5487,16414,16373],{"class":15644},[5487,16416,16417,16419,16421,16424,16426,16429,16432],{"class":5489,"line":11134},[5487,16418,16383],{"class":6911},[5487,16420,5211],{"class":15644},[5487,16422,16423],{"class":6233},"onopen",[5487,16425,16112],{"class":6869},[5487,16427,16428],{"class":15644}," () ",[5487,16430,16431],{"class":6237},"=>",[5487,16433,7150],{"class":15644},[5487,16435,16436,16439,16441,16444,16447,16450],{"class":5489,"line":11140},[5487,16437,16438],{"class":6233},"                    updateStatus",[5487,16440,6926],{"class":15644},[5487,16442,16443],{"class":6241},"'З",[5487,16445,16446],{"class":6245},"\\'",[5487,16448,16449],{"class":6241},"єднання з сервером встановлено. Ініціалізація WebRTC...'",[5487,16451,16128],{"class":15644},[5487,16453,16454,16457],{"class":5489,"line":11156},[5487,16455,16456],{"class":6233},"                    initializeWebRTC",[5487,16458,16459],{"class":15644},"();\n",[5487,16461,16462],{"class":5489,"line":11186},[5487,16463,16464],{"class":15644},"                };\n",[5487,16466,16467],{"class":5489,"line":11202},[5487,16468,5506],{"emptyLinePlaceholder":5505},[5487,16470,16471,16473,16475,16478,16480,16483,16485,16487,16489,16491],{"class":5489,"line":11207},[5487,16472,16383],{"class":6911},[5487,16474,5211],{"class":15644},[5487,16476,16477],{"class":6233},"onmessage",[5487,16479,16112],{"class":6869},[5487,16481,16482],{"class":6237}," async",[5487,16484,10167],{"class":15644},[5487,16486,6753],{"class":6911},[5487,16488,10344],{"class":15644},[5487,16490,16431],{"class":6237},[5487,16492,7150],{"class":15644},[5487,16494,16495,16498,16501,16503,16506,16508,16511,16513,16515,16517,16520],{"class":5489,"line":11217},[5487,16496,16497],{"class":6237},"                    const",[5487,16499,16500],{"class":7447}," message",[5487,16502,16112],{"class":6869},[5487,16504,16505],{"class":7447}," JSON",[5487,16507,5211],{"class":15644},[5487,16509,16510],{"class":6233},"parse",[5487,16512,6926],{"class":15644},[5487,16514,6753],{"class":6911},[5487,16516,5211],{"class":15644},[5487,16518,16519],{"class":6911},"data",[5487,16521,16128],{"class":15644},[5487,16523,16524,16527,16530,16532,16534],{"class":5489,"line":11249},[5487,16525,16526],{"class":6865},"                    await",[5487,16528,16529],{"class":6233}," handleSignalingMessage",[5487,16531,6926],{"class":15644},[5487,16533,16282],{"class":6911},[5487,16535,16128],{"class":15644},[5487,16537,16538],{"class":5489,"line":11286},[5487,16539,16464],{"class":15644},[5487,16541,16542],{"class":5489,"line":11292},[5487,16543,5506],{"emptyLinePlaceholder":5505},[5487,16545,16546,16548,16550,16553,16555,16557,16559,16561,16563],{"class":5489,"line":11303},[5487,16547,16383],{"class":6911},[5487,16549,5211],{"class":15644},[5487,16551,16552],{"class":6233},"onerror",[5487,16554,16112],{"class":6869},[5487,16556,10167],{"class":15644},[5487,16558,11594],{"class":6911},[5487,16560,10344],{"class":15644},[5487,16562,16431],{"class":6237},[5487,16564,7150],{"class":15644},[5487,16566,16567,16569,16571,16574,16576,16578],{"class":5489,"line":11332},[5487,16568,16438],{"class":6233},[5487,16570,6926],{"class":15644},[5487,16572,16573],{"class":6241},"'Помилка WebSocket: '",[5487,16575,16399],{"class":6869},[5487,16577,13289],{"class":6911},[5487,16579,16128],{"class":15644},[5487,16581,16582],{"class":5489,"line":11337},[5487,16583,16464],{"class":15644},[5487,16585,16586],{"class":5489,"line":11342},[5487,16587,5506],{"emptyLinePlaceholder":5505},[5487,16589,16590,16592,16594,16597,16599,16601,16603],{"class":5489,"line":11347},[5487,16591,16383],{"class":6911},[5487,16593,5211],{"class":15644},[5487,16595,16596],{"class":6233},"onclose",[5487,16598,16112],{"class":6869},[5487,16600,16428],{"class":15644},[5487,16602,16431],{"class":6237},[5487,16604,7150],{"class":15644},[5487,16606,16607,16609,16611,16613,16615,16618],{"class":5489,"line":11352},[5487,16608,16438],{"class":6233},[5487,16610,6926],{"class":15644},[5487,16612,16443],{"class":6241},[5487,16614,16446],{"class":6245},[5487,16616,16617],{"class":6241},"єднання з сервером закрито'",[5487,16619,16128],{"class":15644},[5487,16621,16622],{"class":5489,"line":11361},[5487,16623,16464],{"class":15644},[5487,16625,16626],{"class":5489,"line":11374},[5487,16627,5506],{"emptyLinePlaceholder":5505},[5487,16629,16630,16633,16636,16638,16640],{"class":5489,"line":11379},[5487,16631,16632],{"class":15644},"            } ",[5487,16634,16635],{"class":6865},"catch",[5487,16637,10167],{"class":15644},[5487,16639,11594],{"class":6911},[5487,16641,7043],{"class":15644},[5487,16643,16644,16646,16648,16651,16653,16655,16657,16659],{"class":5489,"line":11404},[5487,16645,16361],{"class":6233},[5487,16647,6926],{"class":15644},[5487,16649,16650],{"class":6241},"'Помилка: '",[5487,16652,16399],{"class":6869},[5487,16654,13289],{"class":6911},[5487,16656,5211],{"class":15644},[5487,16658,16282],{"class":6911},[5487,16660,16128],{"class":15644},[5487,16662,16663],{"class":5489,"line":11441},[5487,16664,11289],{"class":15644},[5487,16666,16667],{"class":5489,"line":11446},[5487,16668,7181],{"class":15644},[5487,16670,16671],{"class":5489,"line":11462},[5487,16672,5506],{"emptyLinePlaceholder":5505},[5487,16674,16675,16677,16679,16682],{"class":5489,"line":11489},[5487,16676,16341],{"class":6237},[5487,16678,16344],{"class":6237},[5487,16680,16681],{"class":6233}," initializeWebRTC",[5487,16683,6900],{"class":15644},[5487,16685,16686],{"class":5489,"line":12617},[5487,16687,16688],{"class":6905},"            \u002F\u002F Створення PeerConnection\n",[5487,16690,16691,16694,16696,16698,16701,16703,16706],{"class":5489,"line":12622},[5487,16692,16693],{"class":6911},"            peerConnection",[5487,16695,16112],{"class":6869},[5487,16697,16388],{"class":6237},[5487,16699,16700],{"class":6233}," RTCPeerConnection",[5487,16702,6926],{"class":15644},[5487,16704,16705],{"class":6911},"iceServers",[5487,16707,16128],{"class":15644},[5487,16709,16710],{"class":5489,"line":12628},[5487,16711,5506],{"emptyLinePlaceholder":5505},[5487,16713,16714],{"class":5489,"line":12659},[5487,16715,16716],{"class":6905},"            \u002F\u002F Обробник отримання віддалених медіапотоків\n",[5487,16718,16719,16721,16723,16726,16728,16730,16732,16734,16736],{"class":5489,"line":12689},[5487,16720,16693],{"class":6911},[5487,16722,5211],{"class":15644},[5487,16724,16725],{"class":6233},"ontrack",[5487,16727,16112],{"class":6869},[5487,16729,10167],{"class":15644},[5487,16731,6753],{"class":6911},[5487,16733,10344],{"class":15644},[5487,16735,16431],{"class":6237},[5487,16737,7150],{"class":15644},[5487,16739,16740,16742,16744,16747],{"class":5489,"line":12694},[5487,16741,16361],{"class":6233},[5487,16743,6926],{"class":15644},[5487,16745,16746],{"class":6241},"'Отримано відеопотік! Відтворення...'",[5487,16748,16128],{"class":15644},[5487,16750,16751,16754,16756,16759,16761,16764,16766,16769,16771,16773],{"class":5489,"line":12699},[5487,16752,16753],{"class":6911},"                remoteVideo",[5487,16755,5211],{"class":15644},[5487,16757,16758],{"class":6911},"srcObject",[5487,16760,16112],{"class":6869},[5487,16762,16763],{"class":6911}," event",[5487,16765,5211],{"class":15644},[5487,16767,16768],{"class":6911},"streams",[5487,16770,10481],{"class":15644},[5487,16772,6510],{"class":6405},[5487,16774,16775],{"class":15644},"];\n",[5487,16777,16778],{"class":5489,"line":12715},[5487,16779,16780],{"class":15644},"            };\n",[5487,16782,16784],{"class":5489,"line":16783},109,[5487,16785,5506],{"emptyLinePlaceholder":5505},[5487,16787,16789],{"class":5489,"line":16788},110,[5487,16790,16791],{"class":6905},"            \u002F\u002F Обробник генерації ICE-кандидатів\n",[5487,16793,16795,16797,16799,16802,16804,16806,16808,16810,16812],{"class":5489,"line":16794},111,[5487,16796,16693],{"class":6911},[5487,16798,5211],{"class":15644},[5487,16800,16801],{"class":6233},"onicecandidate",[5487,16803,16112],{"class":6869},[5487,16805,10167],{"class":15644},[5487,16807,6753],{"class":6911},[5487,16809,10344],{"class":15644},[5487,16811,16431],{"class":6237},[5487,16813,7150],{"class":15644},[5487,16815,16817,16819,16821,16823,16825,16827],{"class":5489,"line":16816},112,[5487,16818,13994],{"class":6865},[5487,16820,10167],{"class":15644},[5487,16822,6753],{"class":6911},[5487,16824,5211],{"class":15644},[5487,16826,11958],{"class":6911},[5487,16828,7043],{"class":15644},[5487,16830,16832,16835,16837,16839,16841,16844],{"class":5489,"line":16831},113,[5487,16833,16834],{"class":6911},"                    console",[5487,16836,5211],{"class":15644},[5487,16838,16294],{"class":6233},[5487,16840,6926],{"class":15644},[5487,16842,16843],{"class":6241},"'Відправка ICE-кандидата на сервер'",[5487,16845,16128],{"class":15644},[5487,16847,16849,16852,16854,16857,16859,16862,16864,16867],{"class":5489,"line":16848},114,[5487,16850,16851],{"class":6911},"                    websocket",[5487,16853,5211],{"class":15644},[5487,16855,16856],{"class":6233},"send",[5487,16858,6926],{"class":15644},[5487,16860,16861],{"class":7447},"JSON",[5487,16863,5211],{"class":15644},[5487,16865,16866],{"class":6233},"stringify",[5487,16868,16869],{"class":15644},"({\n",[5487,16871,16873,16876,16879],{"class":5489,"line":16872},115,[5487,16874,16875],{"class":6911},"                        type:",[5487,16877,16878],{"class":6241}," 'ice-candidate'",[5487,16880,9969],{"class":15644},[5487,16882,16884,16887,16889,16891,16893],{"class":5489,"line":16883},116,[5487,16885,16886],{"class":6911},"                        candidate:",[5487,16888,16763],{"class":6911},[5487,16890,5211],{"class":15644},[5487,16892,11958],{"class":6911},[5487,16894,9969],{"class":15644},[5487,16896,16898,16901],{"class":5489,"line":16897},117,[5487,16899,16900],{"class":6911},"                        target:",[5487,16902,16903],{"class":6241}," 'server'\n",[5487,16905,16907],{"class":5489,"line":16906},118,[5487,16908,16909],{"class":15644},"                    }));\n",[5487,16911,16913],{"class":5489,"line":16912},119,[5487,16914,13705],{"class":15644},[5487,16916,16918],{"class":5489,"line":16917},120,[5487,16919,16780],{"class":15644},[5487,16921,16923],{"class":5489,"line":16922},121,[5487,16924,5506],{"emptyLinePlaceholder":5505},[5487,16926,16928],{"class":5489,"line":16927},122,[5487,16929,16930],{"class":6905},"            \u002F\u002F Обробник зміни стану з'єднання\n",[5487,16932,16934,16936,16938,16941,16943,16945,16947],{"class":5489,"line":16933},123,[5487,16935,16693],{"class":6911},[5487,16937,5211],{"class":15644},[5487,16939,16940],{"class":6233},"onconnectionstatechange",[5487,16942,16112],{"class":6869},[5487,16944,16428],{"class":15644},[5487,16946,16431],{"class":6237},[5487,16948,7150],{"class":15644},[5487,16950,16952,16954,16956,16959,16961,16963,16965,16968],{"class":5489,"line":16951},124,[5487,16953,16361],{"class":6233},[5487,16955,6926],{"class":15644},[5487,16957,16958],{"class":6241},"'Стан WebRTC: '",[5487,16960,16399],{"class":6869},[5487,16962,12354],{"class":6911},[5487,16964,5211],{"class":15644},[5487,16966,16967],{"class":6911},"connectionState",[5487,16969,16128],{"class":15644},[5487,16971,16973],{"class":5489,"line":16972},125,[5487,16974,16373],{"class":15644},[5487,16976,16978,16980,16982,16985,16987,16989,16992,16995],{"class":5489,"line":16977},126,[5487,16979,13994],{"class":6865},[5487,16981,10167],{"class":15644},[5487,16983,16984],{"class":6911},"peerConnection",[5487,16986,5211],{"class":15644},[5487,16988,16967],{"class":6911},[5487,16990,16991],{"class":6869}," ===",[5487,16993,16994],{"class":6241}," 'connected'",[5487,16996,7043],{"class":15644},[5487,16998,17000,17002,17004,17007,17009,17012],{"class":5489,"line":16999},127,[5487,17001,16438],{"class":6233},[5487,17003,6926],{"class":15644},[5487,17005,17006],{"class":6241},"'WebRTC з",[5487,17008,16446],{"class":6245},[5487,17010,17011],{"class":6241},"єднання встановлено! Трансляція активна.'",[5487,17013,16128],{"class":15644},[5487,17015,17017],{"class":5489,"line":17016},128,[5487,17018,13705],{"class":15644},[5487,17020,17022],{"class":5489,"line":17021},129,[5487,17023,16780],{"class":15644},[5487,17025,17027],{"class":5489,"line":17026},130,[5487,17028,5506],{"emptyLinePlaceholder":5505},[5487,17030,17032],{"class":5489,"line":17031},131,[5487,17033,17034],{"class":6905},"            \u002F\u002F Створення та відправка SDP Offer\n",[5487,17036,17038,17041,17044,17046,17049,17051,17053,17056],{"class":5489,"line":17037},132,[5487,17039,17040],{"class":6237},"            const",[5487,17042,17043],{"class":7447}," offer",[5487,17045,16112],{"class":6869},[5487,17047,17048],{"class":6865}," await",[5487,17050,12354],{"class":6911},[5487,17052,5211],{"class":15644},[5487,17054,17055],{"class":6233},"createOffer",[5487,17057,16869],{"class":15644},[5487,17059,17061,17064,17066],{"class":5489,"line":17060},133,[5487,17062,17063],{"class":6911},"                offerToReceiveVideo:",[5487,17065,10357],{"class":6237},[5487,17067,9969],{"class":15644},[5487,17069,17071,17074],{"class":5489,"line":17070},134,[5487,17072,17073],{"class":6911},"                offerToReceiveAudio:",[5487,17075,17076],{"class":6237}," true\n",[5487,17078,17080],{"class":5489,"line":17079},135,[5487,17081,17082],{"class":15644},"            });\n",[5487,17084,17086],{"class":5489,"line":17085},136,[5487,17087,5506],{"emptyLinePlaceholder":5505},[5487,17089,17091,17094,17096,17098,17101,17103,17105],{"class":5489,"line":17090},137,[5487,17092,17093],{"class":6865},"            await",[5487,17095,12354],{"class":6911},[5487,17097,5211],{"class":15644},[5487,17099,17100],{"class":6233},"setLocalDescription",[5487,17102,6926],{"class":15644},[5487,17104,12492],{"class":6911},[5487,17106,16128],{"class":15644},[5487,17108,17110],{"class":5489,"line":17109},138,[5487,17111,5506],{"emptyLinePlaceholder":5505},[5487,17113,17115,17118,17120,17123],{"class":5489,"line":17114},139,[5487,17116,17117],{"class":6233},"            updateStatus",[5487,17119,6926],{"class":15644},[5487,17121,17122],{"class":6241},"'Відправка SDP Offer на сервер...'",[5487,17124,16128],{"class":15644},[5487,17126,17128,17131,17133,17135,17137,17139,17141,17143],{"class":5489,"line":17127},140,[5487,17129,17130],{"class":6911},"            websocket",[5487,17132,5211],{"class":15644},[5487,17134,16856],{"class":6233},[5487,17136,6926],{"class":15644},[5487,17138,16861],{"class":7447},[5487,17140,5211],{"class":15644},[5487,17142,16866],{"class":6233},[5487,17144,16869],{"class":15644},[5487,17146,17148,17151,17154],{"class":5489,"line":17147},141,[5487,17149,17150],{"class":6911},"                type:",[5487,17152,17153],{"class":6241}," 'offer'",[5487,17155,9969],{"class":15644},[5487,17157,17159,17162,17164,17166,17169],{"class":5489,"line":17158},142,[5487,17160,17161],{"class":6911},"                sdp:",[5487,17163,17043],{"class":6911},[5487,17165,5211],{"class":15644},[5487,17167,17168],{"class":6911},"sdp",[5487,17170,9969],{"class":15644},[5487,17172,17174,17177],{"class":5489,"line":17173},143,[5487,17175,17176],{"class":6911},"                target:",[5487,17178,16903],{"class":6241},[5487,17180,17182],{"class":5489,"line":17181},144,[5487,17183,17184],{"class":15644},"            }));\n",[5487,17186,17188],{"class":5489,"line":17187},145,[5487,17189,7181],{"class":15644},[5487,17191,17193],{"class":5489,"line":17192},146,[5487,17194,5506],{"emptyLinePlaceholder":5505},[5487,17196,17198,17200,17202,17204,17206,17208],{"class":5489,"line":17197},147,[5487,17199,16341],{"class":6237},[5487,17201,16344],{"class":6237},[5487,17203,16529],{"class":6233},[5487,17205,6926],{"class":15644},[5487,17207,16282],{"class":6911},[5487,17209,7043],{"class":15644},[5487,17211,17213,17215,17217,17219,17221,17223],{"class":5489,"line":17212},148,[5487,17214,13579],{"class":6865},[5487,17216,10167],{"class":15644},[5487,17218,16282],{"class":6911},[5487,17220,5211],{"class":15644},[5487,17222,10383],{"class":6911},[5487,17224,7043],{"class":15644},[5487,17226,17228,17231,17234],{"class":5489,"line":17227},149,[5487,17229,17230],{"class":6865},"                case",[5487,17232,17233],{"class":6241}," 'answer'",[5487,17235,13610],{"class":15644},[5487,17237,17239],{"class":5489,"line":17238},150,[5487,17240,17241],{"class":6905},"                    \u002F\u002F Отримали SDP Answer від сервера\n",[5487,17243,17245,17247,17249,17252,17254,17257],{"class":5489,"line":17244},151,[5487,17246,16438],{"class":6233},[5487,17248,6926],{"class":15644},[5487,17250,17251],{"class":6241},"'Отримано SDP Answer. Встановлення з",[5487,17253,16446],{"class":6245},[5487,17255,17256],{"class":6241},"єднання...'",[5487,17258,16128],{"class":15644},[5487,17260,17262,17264,17266,17268,17270,17273],{"class":5489,"line":17261},152,[5487,17263,16497],{"class":6237},[5487,17265,12704],{"class":7447},[5487,17267,16112],{"class":6869},[5487,17269,16388],{"class":6237},[5487,17271,17272],{"class":6233}," RTCSessionDescription",[5487,17274,16869],{"class":15644},[5487,17276,17278,17280,17282],{"class":5489,"line":17277},153,[5487,17279,16875],{"class":6911},[5487,17281,17233],{"class":6241},[5487,17283,9969],{"class":15644},[5487,17285,17287,17290,17292,17294],{"class":5489,"line":17286},154,[5487,17288,17289],{"class":6911},"                        sdp:",[5487,17291,16500],{"class":6911},[5487,17293,5211],{"class":15644},[5487,17295,17296],{"class":6911},"sdp\n",[5487,17298,17300],{"class":5489,"line":17299},155,[5487,17301,17302],{"class":15644},"                    });\n",[5487,17304,17306,17308,17310,17312,17315,17317,17319],{"class":5489,"line":17305},156,[5487,17307,16526],{"class":6865},[5487,17309,12354],{"class":6911},[5487,17311,5211],{"class":15644},[5487,17313,17314],{"class":6233},"setRemoteDescription",[5487,17316,6926],{"class":15644},[5487,17318,12646],{"class":6911},[5487,17320,16128],{"class":15644},[5487,17322,17324,17327],{"class":5489,"line":17323},157,[5487,17325,17326],{"class":6865},"                    break",[5487,17328,15664],{"class":15644},[5487,17330,17332],{"class":5489,"line":17331},158,[5487,17333,5506],{"emptyLinePlaceholder":5505},[5487,17335,17337,17339,17341],{"class":5489,"line":17336},159,[5487,17338,17230],{"class":6865},[5487,17340,16878],{"class":6241},[5487,17342,13610],{"class":15644},[5487,17344,17346],{"class":5489,"line":17345},160,[5487,17347,17348],{"class":6905},"                    \u002F\u002F Отримали ICE-кандидат від сервера\n",[5487,17350,17352,17355,17357,17359,17361,17363],{"class":5489,"line":17351},161,[5487,17353,17354],{"class":6865},"                    if",[5487,17356,10167],{"class":15644},[5487,17358,16282],{"class":6911},[5487,17360,5211],{"class":15644},[5487,17362,11958],{"class":6911},[5487,17364,7043],{"class":15644},[5487,17366,17368,17371,17373,17375,17377,17380],{"class":5489,"line":17367},162,[5487,17369,17370],{"class":6911},"                        console",[5487,17372,5211],{"class":15644},[5487,17374,16294],{"class":6233},[5487,17376,6926],{"class":15644},[5487,17378,17379],{"class":6241},"'Додавання ICE-кандидата від сервера'",[5487,17381,16128],{"class":15644},[5487,17383,17385,17388,17390,17392,17395],{"class":5489,"line":17384},163,[5487,17386,17387],{"class":6865},"                        await",[5487,17389,12354],{"class":6911},[5487,17391,5211],{"class":15644},[5487,17393,17394],{"class":6233},"addIceCandidate",[5487,17396,14542],{"class":15644},[5487,17398,17400,17403,17406,17408,17410,17412,17414],{"class":5489,"line":17399},164,[5487,17401,17402],{"class":6237},"                            new",[5487,17404,17405],{"class":6233}," RTCIceCandidate",[5487,17407,6926],{"class":15644},[5487,17409,16282],{"class":6911},[5487,17411,5211],{"class":15644},[5487,17413,11958],{"class":6911},[5487,17415,6885],{"class":15644},[5487,17417,17419],{"class":5489,"line":17418},165,[5487,17420,17421],{"class":15644},"                        );\n",[5487,17423,17425],{"class":5489,"line":17424},166,[5487,17426,17427],{"class":15644},"                    }\n",[5487,17429,17431,17433],{"class":5489,"line":17430},167,[5487,17432,17326],{"class":6865},[5487,17434,15664],{"class":15644},[5487,17436,17438],{"class":5489,"line":17437},168,[5487,17439,5506],{"emptyLinePlaceholder":5505},[5487,17441,17443,17446],{"class":5489,"line":17442},169,[5487,17444,17445],{"class":6865},"                default",[5487,17447,13610],{"class":15644},[5487,17449,17451,17453,17455,17458,17460,17463,17465,17467,17469,17471],{"class":5489,"line":17450},170,[5487,17452,16834],{"class":6911},[5487,17454,5211],{"class":15644},[5487,17456,17457],{"class":6233},"warn",[5487,17459,6926],{"class":15644},[5487,17461,17462],{"class":6241},"'Невідомий тип повідомлення:'",[5487,17464,5305],{"class":15644},[5487,17466,16282],{"class":6911},[5487,17468,5211],{"class":15644},[5487,17470,10383],{"class":6911},[5487,17472,16128],{"class":15644},[5487,17474,17476],{"class":5489,"line":17475},171,[5487,17477,11289],{"class":15644},[5487,17479,17481],{"class":5489,"line":17480},172,[5487,17482,7181],{"class":15644},[5487,17484,17486],{"class":5489,"line":17485},173,[5487,17487,5506],{"emptyLinePlaceholder":5505},[5487,17489,17491],{"class":5489,"line":17490},174,[5487,17492,17493],{"class":6905},"        \u002F\u002F Обробник кнопки підключення\n",[5487,17495,17497,17500,17502,17505,17507,17510,17513,17515],{"class":5489,"line":17496},175,[5487,17498,17499],{"class":6911},"        connectBtn",[5487,17501,5211],{"class":15644},[5487,17503,17504],{"class":6233},"addEventListener",[5487,17506,6926],{"class":15644},[5487,17508,17509],{"class":6241},"'click'",[5487,17511,17512],{"class":15644},", () ",[5487,17514,16431],{"class":6237},[5487,17516,7150],{"class":15644},[5487,17518,17520,17523,17525,17528,17530,17532],{"class":5489,"line":17519},176,[5487,17521,17522],{"class":6911},"            connectBtn",[5487,17524,5211],{"class":15644},[5487,17526,17527],{"class":6911},"disabled",[5487,17529,16112],{"class":6869},[5487,17531,10357],{"class":6237},[5487,17533,15664],{"class":15644},[5487,17535,17537,17539,17541,17543,17545,17548],{"class":5489,"line":17536},177,[5487,17538,17522],{"class":6911},[5487,17540,5211],{"class":15644},[5487,17542,16310],{"class":6911},[5487,17544,16112],{"class":6869},[5487,17546,17547],{"class":6241}," 'Підключення...'",[5487,17549,15664],{"class":15644},[5487,17551,17553,17556],{"class":5489,"line":17552},178,[5487,17554,17555],{"class":6233},"            startConnection",[5487,17557,16459],{"class":15644},[5487,17559,17561],{"class":5489,"line":17560},179,[5487,17562,17563],{"class":15644},"        });\n",[5487,17565,17567,17569,17571],{"class":5489,"line":17566},180,[5487,17568,9006],{"class":8610},[5487,17570,16099],{"class":8614},[5487,17572,8690],{"class":8610},[5487,17574,17576,17578,17580],{"class":5489,"line":17575},181,[5487,17577,9157],{"class":8610},[5487,17579,15990],{"class":8614},[5487,17581,8690],{"class":8610},[5487,17583,17585,17587,17589],{"class":5489,"line":17584},182,[5487,17586,9157],{"class":8610},[5487,17588,15559],{"class":8614},[5487,17590,8690],{"class":8610},[5299,17592,17593],{},[5235,17594,17595],{},"Ключові моменти клієнтської реалізації:",[6045,17597,17598,17604,17615,17625,17631],{},[5201,17599,17600,17603],{},[5235,17601,17602],{},"Створення RTCPeerConnection"," із конфігурацією ICE-серверів.",[5201,17605,17606,17611,17612,5211],{},[5235,17607,17608,17609],{},"Реєстрація обробника ",[5208,17610,16725],{}," для отримання віддалених медіапотоків та їх прив'язки до елементу ",[5208,17613,17614],{},"\u003Cvideo>",[5201,17616,17617,17620,17621,17624],{},[5235,17618,17619],{},"Створення SDP Offer"," із параметрами ",[5208,17622,17623],{},"offerToReceiveVideo: true"," (клієнт лише приймає, не відправляє медіа).",[5201,17626,17627,17630],{},[5235,17628,17629],{},"Обмін SDP та ICE-кандидатами"," через WebSocket із сервером сигналізації.",[5201,17632,17633,17636],{},[5235,17634,17635],{},"Автоматичне відтворення"," отриманого потоку після встановлення з'єднання.",[5365,17638,17640],{"id":17639},"мобільний-клієнт-react-native","Мобільний клієнт (React Native)",[5299,17642,17643,17644,17647],{},"Для мобільних застосунків на React Native використовується бібліотека ",[5208,17645,17646],{},"react-native-webrtc",", що надає JavaScript API, аналогічний браузерному:",[10208,17649,17650,17668],{},[10211,17651,17653],{"label":17652},"npm",[5478,17654,17656],{"className":6224,"code":17655,"language":6226,"meta":5483,"style":5483},"npm install react-native-webrtc\n",[5208,17657,17658],{"__ignoreMap":5483},[5487,17659,17660,17662,17665],{"class":5489,"line":5490},[5487,17661,17652],{"class":6233},[5487,17663,17664],{"class":6241}," install",[5487,17666,17667],{"class":6241}," react-native-webrtc\n",[10211,17669,17671],{"label":17670},"yarn",[5478,17672,17674],{"className":6224,"code":17673,"language":6226,"meta":5483,"style":5483},"yarn add react-native-webrtc\n",[5208,17675,17676],{"__ignoreMap":5483},[5487,17677,17678,17680,17683],{"class":5489,"line":5490},[5487,17679,17670],{"class":6233},[5487,17681,17682],{"class":6241}," add",[5487,17684,17667],{"class":6241},[5299,17686,17687],{},"Приклад компоненту для відеочату:",[5478,17689,17693],{"className":17690,"code":17691,"language":17692,"meta":5483,"style":5483},"language-javascript shiki shiki-themes light-plus dark-plus dark-plus","import React, { useEffect, useRef, useState } from 'react';\nimport { View, Button, StyleSheet } from 'react-native';\nimport { RTCView, RTCPeerConnection, mediaDevices } from 'react-native-webrtc';\n\nconst WebRTCVideo = () => {\n  const [remoteStream, setRemoteStream] = useState(null);\n  const peerConnectionRef = useRef(null);\n\n  useEffect(() => {\n    return () => {\n      \u002F\u002F Очищення ресурсів при розмонтуванні\n      if (peerConnectionRef.current) {\n        peerConnectionRef.current.close();\n      }\n    };\n  }, []);\n\n  const startCall = async () => {\n    const configuration = {\n      iceServers: [\n        { urls: 'stun:stun.l.google.com:19302' }\n      ]\n    };\n\n    const pc = new RTCPeerConnection(configuration);\n    peerConnectionRef.current = pc;\n\n    pc.ontrack = (event) => {\n      if (event.streams && event.streams[0]) {\n        setRemoteStream(event.streams[0]);\n      }\n    };\n\n    \u002F\u002F Створення offer та сигналізація (аналогічно веб-версії)\n    const offer = await pc.createOffer({ offerToReceiveVideo: true });\n    await pc.setLocalDescription(offer);\n\n    \u002F\u002F Відправка offer через WebSocket сервер сигналізації\n    \u002F\u002F ... (код WebSocket підключення)\n  };\n\n  return (\n    \u003CView style={styles.container}>\n      {remoteStream && (\n        \u003CRTCView\n          streamURL={remoteStream.toURL()}\n          style={styles.video}\n          objectFit=\"cover\"\n        \u002F>\n      )}\n      \u003CButton title=\"Розпочати трансляцію\" onPress={startCall} \u002F>\n    \u003C\u002FView>\n  );\n};\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    backgroundColor: '#000',\n    justifyContent: 'center',\n    alignItems: 'center',\n  },\n  video: {\n    width: '100%',\n    height: '70%',\n  },\n});\n\nexport default WebRTCVideo;\n","javascript",[5208,17694,17695,17729,17758,17785,17789,17803,17830,17847,17851,17863,17873,17878,17895,17911,17916,17921,17926,17930,17948,17959,17966,17978,17983,17987,17991,18012,18027,18031,18051,18079,18099,18103,18107,18111,18116,18144,18161,18165,18170,18175,18180,18184,18191,18216,18228,18235,18256,18273,18283,18288,18295,18324,18332,18337,18342,18346,18364,18371,18381,18391,18401,18410,18415,18422,18432,18442,18446,18451,18455],{"__ignoreMap":5483},[5487,17696,17697,17699,17702,17705,17708,17710,17713,17715,17718,17721,17724,17727],{"class":5489,"line":5490},[5487,17698,6866],{"class":6865},[5487,17700,17701],{"class":6911}," React",[5487,17703,17704],{"class":6869},", { ",[5487,17706,17707],{"class":6911},"useEffect",[5487,17709,5305],{"class":6869},[5487,17711,17712],{"class":6911},"useRef",[5487,17714,5305],{"class":6869},[5487,17716,17717],{"class":6911},"useState",[5487,17719,17720],{"class":6869}," } ",[5487,17722,17723],{"class":6865},"from",[5487,17725,17726],{"class":6241}," 'react'",[5487,17728,15664],{"class":6869},[5487,17730,17731,17733,17736,17739,17741,17744,17746,17749,17751,17753,17756],{"class":5489,"line":5496},[5487,17732,6866],{"class":6865},[5487,17734,17735],{"class":6869}," { ",[5487,17737,17738],{"class":6911},"View",[5487,17740,5305],{"class":6869},[5487,17742,17743],{"class":6911},"Button",[5487,17745,5305],{"class":6869},[5487,17747,17748],{"class":6911},"StyleSheet",[5487,17750,17720],{"class":6869},[5487,17752,17723],{"class":6865},[5487,17754,17755],{"class":6241}," 'react-native'",[5487,17757,15664],{"class":6869},[5487,17759,17760,17762,17764,17767,17769,17771,17773,17776,17778,17780,17783],{"class":5489,"line":5502},[5487,17761,6866],{"class":6865},[5487,17763,17735],{"class":6869},[5487,17765,17766],{"class":6911},"RTCView",[5487,17768,5305],{"class":6869},[5487,17770,15553],{"class":6911},[5487,17772,5305],{"class":6869},[5487,17774,17775],{"class":6911},"mediaDevices",[5487,17777,17720],{"class":6869},[5487,17779,17723],{"class":6865},[5487,17781,17782],{"class":6241}," 'react-native-webrtc'",[5487,17784,15664],{"class":6869},[5487,17786,17787],{"class":5489,"line":5509},[5487,17788,5506],{"emptyLinePlaceholder":5505},[5487,17790,17791,17793,17796,17799,17801],{"class":5489,"line":5515},[5487,17792,7440],{"class":6237},[5487,17794,17795],{"class":6233}," WebRTCVideo",[5487,17797,17798],{"class":6869}," = () ",[5487,17800,16431],{"class":6237},[5487,17802,7150],{"class":6869},[5487,17804,17805,17808,17811,17814,17816,17819,17821,17823,17825,17828],{"class":5489,"line":5521},[5487,17806,17807],{"class":6237},"  const",[5487,17809,17810],{"class":6869}," [",[5487,17812,17813],{"class":7447},"remoteStream",[5487,17815,5305],{"class":6869},[5487,17817,17818],{"class":7447},"setRemoteStream",[5487,17820,10846],{"class":6869},[5487,17822,17717],{"class":6233},[5487,17824,6926],{"class":6869},[5487,17826,17827],{"class":6237},"null",[5487,17829,16128],{"class":6869},[5487,17831,17832,17834,17837,17839,17841,17843,17845],{"class":5489,"line":5527},[5487,17833,17807],{"class":6237},[5487,17835,17836],{"class":7447}," peerConnectionRef",[5487,17838,7451],{"class":6869},[5487,17840,17712],{"class":6233},[5487,17842,6926],{"class":6869},[5487,17844,17827],{"class":6237},[5487,17846,16128],{"class":6869},[5487,17848,17849],{"class":5489,"line":5532},[5487,17850,5506],{"emptyLinePlaceholder":5505},[5487,17852,17853,17856,17859,17861],{"class":5489,"line":5538},[5487,17854,17855],{"class":6233},"  useEffect",[5487,17857,17858],{"class":6869},"(() ",[5487,17860,16431],{"class":6237},[5487,17862,7150],{"class":6869},[5487,17864,17865,17867,17869,17871],{"class":5489,"line":5544},[5487,17866,10535],{"class":6865},[5487,17868,16428],{"class":6869},[5487,17870,16431],{"class":6237},[5487,17872,7150],{"class":6869},[5487,17874,17875],{"class":5489,"line":5550},[5487,17876,17877],{"class":6905},"      \u002F\u002F Очищення ресурсів при розмонтуванні\n",[5487,17879,17880,17883,17885,17888,17890,17893],{"class":5489,"line":5555},[5487,17881,17882],{"class":6865},"      if",[5487,17884,10167],{"class":6869},[5487,17886,17887],{"class":6911},"peerConnectionRef",[5487,17889,5211],{"class":6869},[5487,17891,17892],{"class":6911},"current",[5487,17894,7043],{"class":6869},[5487,17896,17897,17900,17902,17904,17906,17909],{"class":5489,"line":5561},[5487,17898,17899],{"class":6911},"        peerConnectionRef",[5487,17901,5211],{"class":6869},[5487,17903,17892],{"class":6911},[5487,17905,5211],{"class":6869},[5487,17907,17908],{"class":6233},"close",[5487,17910,16459],{"class":6869},[5487,17912,17913],{"class":5489,"line":5567},[5487,17914,17915],{"class":6869},"      }\n",[5487,17917,17918],{"class":5489,"line":5704},[5487,17919,17920],{"class":6869},"    };\n",[5487,17922,17923],{"class":5489,"line":5710},[5487,17924,17925],{"class":6869},"  }, []);\n",[5487,17927,17928],{"class":5489,"line":5716},[5487,17929,5506],{"emptyLinePlaceholder":5505},[5487,17931,17932,17934,17937,17939,17942,17944,17946],{"class":5489,"line":5722},[5487,17933,17807],{"class":6237},[5487,17935,17936],{"class":6233}," startCall",[5487,17938,7451],{"class":6869},[5487,17940,17941],{"class":6237},"async",[5487,17943,16428],{"class":6869},[5487,17945,16431],{"class":6237},[5487,17947,7150],{"class":6869},[5487,17949,17950,17953,17956],{"class":5489,"line":5727},[5487,17951,17952],{"class":6237},"    const",[5487,17954,17955],{"class":7447}," configuration",[5487,17957,17958],{"class":6869}," = {\n",[5487,17960,17961,17964],{"class":5489,"line":6007},[5487,17962,17963],{"class":6911},"      iceServers:",[5487,17965,16231],{"class":6869},[5487,17967,17968,17971,17973,17975],{"class":5489,"line":6013},[5487,17969,17970],{"class":6869},"        { ",[5487,17972,16239],{"class":6911},[5487,17974,16242],{"class":6241},[5487,17976,17977],{"class":6869}," }\n",[5487,17979,17980],{"class":5489,"line":6019},[5487,17981,17982],{"class":6869},"      ]\n",[5487,17984,17985],{"class":5489,"line":6025},[5487,17986,17920],{"class":6869},[5487,17988,17989],{"class":5489,"line":6030},[5487,17990,5506],{"emptyLinePlaceholder":5505},[5487,17992,17993,17995,17998,18000,18003,18005,18007,18010],{"class":5489,"line":6035},[5487,17994,17952],{"class":6237},[5487,17996,17997],{"class":7447}," pc",[5487,17999,7451],{"class":6869},[5487,18001,18002],{"class":6237},"new",[5487,18004,16700],{"class":6233},[5487,18006,6926],{"class":6869},[5487,18008,18009],{"class":6911},"configuration",[5487,18011,16128],{"class":6869},[5487,18013,18014,18017,18019,18021,18023,18025],{"class":5489,"line":7188},[5487,18015,18016],{"class":6911},"    peerConnectionRef",[5487,18018,5211],{"class":6869},[5487,18020,17892],{"class":6911},[5487,18022,7451],{"class":6869},[5487,18024,12384],{"class":6911},[5487,18026,15664],{"class":6869},[5487,18028,18029],{"class":5489,"line":7209},[5487,18030,5506],{"emptyLinePlaceholder":5505},[5487,18032,18033,18036,18038,18040,18043,18045,18047,18049],{"class":5489,"line":7215},[5487,18034,18035],{"class":6911},"    pc",[5487,18037,5211],{"class":6869},[5487,18039,16725],{"class":6233},[5487,18041,18042],{"class":6869}," = (",[5487,18044,6753],{"class":6911},[5487,18046,10344],{"class":6869},[5487,18048,16431],{"class":6237},[5487,18050,7150],{"class":6869},[5487,18052,18053,18055,18057,18059,18061,18063,18066,18068,18070,18072,18074,18076],{"class":5489,"line":7220},[5487,18054,17882],{"class":6865},[5487,18056,10167],{"class":6869},[5487,18058,6753],{"class":6911},[5487,18060,5211],{"class":6869},[5487,18062,16768],{"class":6911},[5487,18064,18065],{"class":6869}," && ",[5487,18067,6753],{"class":6911},[5487,18069,5211],{"class":6869},[5487,18071,16768],{"class":6911},[5487,18073,10481],{"class":6869},[5487,18075,6510],{"class":6405},[5487,18077,18078],{"class":6869},"]) {\n",[5487,18080,18081,18084,18086,18088,18090,18092,18094,18096],{"class":5489,"line":7238},[5487,18082,18083],{"class":6233},"        setRemoteStream",[5487,18085,6926],{"class":6869},[5487,18087,6753],{"class":6911},[5487,18089,5211],{"class":6869},[5487,18091,16768],{"class":6911},[5487,18093,10481],{"class":6869},[5487,18095,6510],{"class":6405},[5487,18097,18098],{"class":6869},"]);\n",[5487,18100,18101],{"class":5489,"line":7254},[5487,18102,17915],{"class":6869},[5487,18104,18105],{"class":5489,"line":7259},[5487,18106,17920],{"class":6869},[5487,18108,18109],{"class":5489,"line":7300},[5487,18110,5506],{"emptyLinePlaceholder":5505},[5487,18112,18113],{"class":5489,"line":7327},[5487,18114,18115],{"class":6905},"    \u002F\u002F Створення offer та сигналізація (аналогічно веб-версії)\n",[5487,18117,18118,18120,18122,18124,18127,18129,18131,18133,18136,18139,18141],{"class":5489,"line":7333},[5487,18119,17952],{"class":6237},[5487,18121,17043],{"class":7447},[5487,18123,7451],{"class":6869},[5487,18125,18126],{"class":6865},"await",[5487,18128,17997],{"class":6911},[5487,18130,5211],{"class":6869},[5487,18132,17055],{"class":6233},[5487,18134,18135],{"class":6869},"({ ",[5487,18137,18138],{"class":6911},"offerToReceiveVideo:",[5487,18140,10357],{"class":6237},[5487,18142,18143],{"class":6869}," });\n",[5487,18145,18146,18149,18151,18153,18155,18157,18159],{"class":5489,"line":7744},[5487,18147,18148],{"class":6865},"    await",[5487,18150,17997],{"class":6911},[5487,18152,5211],{"class":6869},[5487,18154,17100],{"class":6233},[5487,18156,6926],{"class":6869},[5487,18158,12492],{"class":6911},[5487,18160,16128],{"class":6869},[5487,18162,18163],{"class":5489,"line":7761},[5487,18164,5506],{"emptyLinePlaceholder":5505},[5487,18166,18167],{"class":5489,"line":7766},[5487,18168,18169],{"class":6905},"    \u002F\u002F Відправка offer через WebSocket сервер сигналізації\n",[5487,18171,18172],{"class":5489,"line":7772},[5487,18173,18174],{"class":6905},"    \u002F\u002F ... (код WebSocket підключення)\n",[5487,18176,18177],{"class":5489,"line":7797},[5487,18178,18179],{"class":6869},"  };\n",[5487,18181,18182],{"class":5489,"line":7820},[5487,18183,5506],{"emptyLinePlaceholder":5505},[5487,18185,18186,18189],{"class":5489,"line":7825},[5487,18187,18188],{"class":6865},"  return",[5487,18190,6870],{"class":6869},[5487,18192,18193,18195,18197,18200,18202,18204,18207,18209,18212,18214],{"class":5489,"line":7852},[5487,18194,8731],{"class":8610},[5487,18196,17738],{"class":6855},[5487,18198,18199],{"class":8617}," style",[5487,18201,8621],{"class":6869},[5487,18203,11651],{"class":6237},[5487,18205,18206],{"class":6911},"styles",[5487,18208,5211],{"class":15644},[5487,18210,18211],{"class":6911},"container",[5487,18213,16323],{"class":6237},[5487,18215,8690],{"class":8610},[5487,18217,18218,18221,18223,18226],{"class":5489,"line":7876},[5487,18219,18220],{"class":6237},"      {",[5487,18222,17813],{"class":6911},[5487,18224,18225],{"class":6869}," &&",[5487,18227,6870],{"class":15644},[5487,18229,18230,18232],{"class":5489,"line":7918},[5487,18231,8820],{"class":8610},[5487,18233,18234],{"class":6855},"RTCView\n",[5487,18236,18237,18240,18242,18244,18246,18248,18251,18254],{"class":5489,"line":7943},[5487,18238,18239],{"class":8617},"          streamURL",[5487,18241,8621],{"class":6869},[5487,18243,11651],{"class":6237},[5487,18245,17813],{"class":6911},[5487,18247,5211],{"class":15644},[5487,18249,18250],{"class":6233},"toURL",[5487,18252,18253],{"class":15644},"()",[5487,18255,7336],{"class":6237},[5487,18257,18258,18261,18263,18265,18267,18269,18271],{"class":5489,"line":7948},[5487,18259,18260],{"class":8617},"          style",[5487,18262,8621],{"class":6869},[5487,18264,11651],{"class":6237},[5487,18266,18206],{"class":6911},[5487,18268,5211],{"class":15644},[5487,18270,16015],{"class":6911},[5487,18272,7336],{"class":6237},[5487,18274,18275,18278,18280],{"class":5489,"line":7968},[5487,18276,18277],{"class":8617},"          objectFit",[5487,18279,8621],{"class":6869},[5487,18281,18282],{"class":6241},"\"cover\"\n",[5487,18284,18285],{"class":5489,"line":8003},[5487,18286,18287],{"class":8610},"        \u002F>\n",[5487,18289,18290,18293],{"class":5489,"line":8042},[5487,18291,18292],{"class":15644},"      )",[5487,18294,7336],{"class":6237},[5487,18296,18297,18299,18301,18304,18306,18309,18312,18314,18316,18319,18321],{"class":5489,"line":8047},[5487,18298,8795],{"class":8610},[5487,18300,17743],{"class":6855},[5487,18302,18303],{"class":8617}," title",[5487,18305,8621],{"class":6869},[5487,18307,18308],{"class":6241},"\"Розпочати трансляцію\"",[5487,18310,18311],{"class":8617}," onPress",[5487,18313,8621],{"class":6869},[5487,18315,11651],{"class":6237},[5487,18317,18318],{"class":6911},"startCall",[5487,18320,16323],{"class":6237},[5487,18322,18323],{"class":8610}," \u002F>\n",[5487,18325,18326,18328,18330],{"class":5489,"line":8052},[5487,18327,9006],{"class":8610},[5487,18329,17738],{"class":6855},[5487,18331,8690],{"class":8610},[5487,18333,18334],{"class":5489,"line":8076},[5487,18335,18336],{"class":6869},"  );\n",[5487,18338,18339],{"class":5489,"line":8081},[5487,18340,18341],{"class":6869},"};\n",[5487,18343,18344],{"class":5489,"line":8086},[5487,18345,5506],{"emptyLinePlaceholder":5505},[5487,18347,18348,18350,18353,18355,18357,18359,18362],{"class":5489,"line":8095},[5487,18349,7440],{"class":6237},[5487,18351,18352],{"class":7447}," styles",[5487,18354,7451],{"class":6869},[5487,18356,17748],{"class":6911},[5487,18358,5211],{"class":6869},[5487,18360,18361],{"class":6233},"create",[5487,18363,16869],{"class":6869},[5487,18365,18366,18369],{"class":5489,"line":8101},[5487,18367,18368],{"class":6911},"  container:",[5487,18370,7150],{"class":6869},[5487,18372,18373,18376,18379],{"class":5489,"line":8128},[5487,18374,18375],{"class":6911},"    flex:",[5487,18377,18378],{"class":6405}," 1",[5487,18380,9969],{"class":6869},[5487,18382,18383,18386,18389],{"class":5489,"line":8159},[5487,18384,18385],{"class":6911},"    backgroundColor:",[5487,18387,18388],{"class":6241}," '#000'",[5487,18390,9969],{"class":6869},[5487,18392,18393,18396,18399],{"class":5489,"line":8164},[5487,18394,18395],{"class":6911},"    justifyContent:",[5487,18397,18398],{"class":6241}," 'center'",[5487,18400,9969],{"class":6869},[5487,18402,18403,18406,18408],{"class":5489,"line":8170},[5487,18404,18405],{"class":6911},"    alignItems:",[5487,18407,18398],{"class":6241},[5487,18409,9969],{"class":6869},[5487,18411,18412],{"class":5489,"line":8212},[5487,18413,18414],{"class":6869},"  },\n",[5487,18416,18417,18420],{"class":5489,"line":8229},[5487,18418,18419],{"class":6911},"  video:",[5487,18421,7150],{"class":6869},[5487,18423,18424,18427,18430],{"class":5489,"line":8234},[5487,18425,18426],{"class":6911},"    width:",[5487,18428,18429],{"class":6241}," '100%'",[5487,18431,9969],{"class":6869},[5487,18433,18434,18437,18440],{"class":5489,"line":8276},[5487,18435,18436],{"class":6911},"    height:",[5487,18438,18439],{"class":6241}," '70%'",[5487,18441,9969],{"class":6869},[5487,18443,18444],{"class":5489,"line":8292},[5487,18445,18414],{"class":6869},[5487,18447,18448],{"class":5489,"line":8297},[5487,18449,18450],{"class":6869},"});\n",[5487,18452,18453],{"class":5489,"line":8339},[5487,18454,5506],{"emptyLinePlaceholder":5505},[5487,18456,18457,18460,18463,18465],{"class":5489,"line":8355},[5487,18458,18459],{"class":6865},"export",[5487,18461,18462],{"class":6865}," default",[5487,18464,17795],{"class":6911},[5487,18466,15664],{"class":6869},[5365,18468,18470],{"id":18469},"десктопний-клієнт-c-avalonia","Десктопний клієнт (C# \u002F Avalonia)",[5299,18472,18473,18474,18477],{},"Для десктопних застосунків на C# існує бібліотека ",[5235,18475,18476],{},"SIPSorcery.WebRTC",", що надає повноцінну реалізацію WebRTC:",[5478,18479,18481],{"className":6224,"code":18480,"language":6226,"meta":5483,"style":5483},"dotnet add package SIPSorcery.WebRTC\n",[5208,18482,18483],{"__ignoreMap":5483},[5487,18484,18485,18488,18490,18493],{"class":5489,"line":5490},[5487,18486,18487],{"class":6233},"dotnet",[5487,18489,17682],{"class":6241},[5487,18491,18492],{"class":6241}," package",[5487,18494,18495],{"class":6241}," SIPSorcery.WebRTC\n",[5299,18497,18498],{},"Приклад встановлення з'єднання у C#:",[5478,18500,18504],{"className":18501,"code":18502,"language":18503,"meta":5483,"style":5483},"language-csharp shiki shiki-themes light-plus dark-plus dark-plus","using System;\nusing System.Threading.Tasks;\nusing SIPSorcery.Net;\nusing SIPSorceryMedia.Abstractions;\n\npublic class WebRTCClient\n{\n    private RTCPeerConnection _peerConnection;\n\n    public async Task StartConnection()\n    {\n        var config = new RTCConfiguration\n        {\n            iceServers = new List\u003CRTCIceServer>\n            {\n                new RTCIceServer { urls = \"stun:stun.l.google.com:19302\" }\n            }\n        };\n\n        _peerConnection = new RTCPeerConnection(config);\n\n        \u002F\u002F Обробник віддалених треків\n        _peerConnection.ontrack += (track, type, ssrc) =>\n        {\n            Console.WriteLine($\"Отримано {type} трек\");\n            \u002F\u002F Прив'язка до UI-елементу відображення відео\n        };\n\n        \u002F\u002F Обробник ICE-кандидатів\n        _peerConnection.onicecandidate += (candidate) =>\n        {\n            \u002F\u002F Відправка кандидата через WebSocket\n        };\n\n        \u002F\u002F Створення offer\n        var offer = _peerConnection.createOffer();\n        await _peerConnection.setLocalDescription(offer);\n\n        \u002F\u002F Відправка offer на сервер через WebSocket\n    }\n}\n","csharp",[5208,18505,18506,18516,18534,18548,18562,18566,18577,18581,18593,18597,18612,18617,18631,18635,18654,18658,18677,18681,18685,18689,18706,18710,18715,18740,18744,18770,18775,18779,18783,18788,18802,18806,18811,18815,18819,18824,18841,18858,18862,18867,18871],{"__ignoreMap":5483},[5487,18507,18508,18511,18514],{"class":5489,"line":5490},[5487,18509,18510],{"class":6865},"using",[5487,18512,18513],{"class":6855}," System",[5487,18515,15664],{"class":6869},[5487,18517,18518,18520,18522,18524,18527,18529,18532],{"class":5489,"line":5496},[5487,18519,18510],{"class":6865},[5487,18521,18513],{"class":6855},[5487,18523,5211],{"class":6869},[5487,18525,18526],{"class":6855},"Threading",[5487,18528,5211],{"class":6869},[5487,18530,18531],{"class":6855},"Tasks",[5487,18533,15664],{"class":6869},[5487,18535,18536,18538,18541,18543,18546],{"class":5489,"line":5502},[5487,18537,18510],{"class":6865},[5487,18539,18540],{"class":6855}," SIPSorcery",[5487,18542,5211],{"class":6869},[5487,18544,18545],{"class":6855},"Net",[5487,18547,15664],{"class":6869},[5487,18549,18550,18552,18555,18557,18560],{"class":5489,"line":5509},[5487,18551,18510],{"class":6865},[5487,18553,18554],{"class":6855}," SIPSorceryMedia",[5487,18556,5211],{"class":6869},[5487,18558,18559],{"class":6855},"Abstractions",[5487,18561,15664],{"class":6869},[5487,18563,18564],{"class":5489,"line":5515},[5487,18565,5506],{"emptyLinePlaceholder":5505},[5487,18567,18568,18571,18574],{"class":5489,"line":5521},[5487,18569,18570],{"class":6237},"public",[5487,18572,18573],{"class":6237}," class",[5487,18575,18576],{"class":6855}," WebRTCClient\n",[5487,18578,18579],{"class":5489,"line":5527},[5487,18580,9954],{"class":6869},[5487,18582,18583,18586,18588,18591],{"class":5489,"line":5532},[5487,18584,18585],{"class":6237},"    private",[5487,18587,16700],{"class":6855},[5487,18589,18590],{"class":6911}," _peerConnection",[5487,18592,15664],{"class":6869},[5487,18594,18595],{"class":5489,"line":5538},[5487,18596,5506],{"emptyLinePlaceholder":5505},[5487,18598,18599,18602,18604,18607,18610],{"class":5489,"line":5544},[5487,18600,18601],{"class":6237},"    public",[5487,18603,16482],{"class":6237},[5487,18605,18606],{"class":6855}," Task",[5487,18608,18609],{"class":6233}," StartConnection",[5487,18611,10726],{"class":6869},[5487,18613,18614],{"class":5489,"line":5550},[5487,18615,18616],{"class":6869},"    {\n",[5487,18618,18619,18621,18624,18626,18628],{"class":5489,"line":5555},[5487,18620,10998],{"class":6237},[5487,18622,18623],{"class":6911}," config",[5487,18625,7451],{"class":6869},[5487,18627,18002],{"class":6237},[5487,18629,18630],{"class":6855}," RTCConfiguration\n",[5487,18632,18633],{"class":5489,"line":5561},[5487,18634,13006],{"class":6869},[5487,18636,18637,18640,18642,18644,18647,18649,18652],{"class":5489,"line":5567},[5487,18638,18639],{"class":6911},"            iceServers",[5487,18641,7451],{"class":6869},[5487,18643,18002],{"class":6237},[5487,18645,18646],{"class":6855}," List",[5487,18648,8641],{"class":6869},[5487,18650,18651],{"class":6855},"RTCIceServer",[5487,18653,8690],{"class":6869},[5487,18655,18656],{"class":5489,"line":5704},[5487,18657,11639],{"class":6869},[5487,18659,18660,18663,18666,18668,18671,18673,18675],{"class":5489,"line":5710},[5487,18661,18662],{"class":6237},"                new",[5487,18664,18665],{"class":6855}," RTCIceServer",[5487,18667,17735],{"class":6869},[5487,18669,18670],{"class":6911},"urls",[5487,18672,7451],{"class":6869},[5487,18674,11654],{"class":6241},[5487,18676,17977],{"class":6869},[5487,18678,18679],{"class":5489,"line":5716},[5487,18680,11289],{"class":6869},[5487,18682,18683],{"class":5489,"line":5722},[5487,18684,16265],{"class":6869},[5487,18686,18687],{"class":5489,"line":5727},[5487,18688,5506],{"emptyLinePlaceholder":5505},[5487,18690,18691,18694,18696,18698,18700,18702,18704],{"class":5489,"line":6007},[5487,18692,18693],{"class":6911},"        _peerConnection",[5487,18695,7451],{"class":6869},[5487,18697,18002],{"class":6237},[5487,18699,16700],{"class":6855},[5487,18701,6926],{"class":6869},[5487,18703,11733],{"class":6911},[5487,18705,16128],{"class":6869},[5487,18707,18708],{"class":5489,"line":6013},[5487,18709,5506],{"emptyLinePlaceholder":5505},[5487,18711,18712],{"class":5489,"line":6019},[5487,18713,18714],{"class":6905},"        \u002F\u002F Обробник віддалених треків\n",[5487,18716,18717,18719,18721,18723,18726,18728,18730,18732,18734,18737],{"class":5489,"line":6025},[5487,18718,18693],{"class":6911},[5487,18720,5211],{"class":6869},[5487,18722,16725],{"class":6911},[5487,18724,18725],{"class":6869}," += (",[5487,18727,12139],{"class":6911},[5487,18729,5305],{"class":6869},[5487,18731,10383],{"class":6911},[5487,18733,5305],{"class":6869},[5487,18735,18736],{"class":6911},"ssrc",[5487,18738,18739],{"class":6869},") =>\n",[5487,18741,18742],{"class":5489,"line":6030},[5487,18743,13006],{"class":6869},[5487,18745,18746,18749,18751,18754,18756,18759,18761,18763,18765,18768],{"class":5489,"line":6035},[5487,18747,18748],{"class":6911},"            Console",[5487,18750,5211],{"class":6869},[5487,18752,18753],{"class":6233},"WriteLine",[5487,18755,6926],{"class":6869},[5487,18757,18758],{"class":6241},"$\"Отримано ",[5487,18760,11651],{"class":15644},[5487,18762,10383],{"class":6911},[5487,18764,16323],{"class":15644},[5487,18766,18767],{"class":6241}," трек\"",[5487,18769,16128],{"class":6869},[5487,18771,18772],{"class":5489,"line":7188},[5487,18773,18774],{"class":6905},"            \u002F\u002F Прив'язка до UI-елементу відображення відео\n",[5487,18776,18777],{"class":5489,"line":7209},[5487,18778,16265],{"class":6869},[5487,18780,18781],{"class":5489,"line":7215},[5487,18782,5506],{"emptyLinePlaceholder":5505},[5487,18784,18785],{"class":5489,"line":7220},[5487,18786,18787],{"class":6905},"        \u002F\u002F Обробник ICE-кандидатів\n",[5487,18789,18790,18792,18794,18796,18798,18800],{"class":5489,"line":7238},[5487,18791,18693],{"class":6911},[5487,18793,5211],{"class":6869},[5487,18795,16801],{"class":6911},[5487,18797,18725],{"class":6869},[5487,18799,11958],{"class":6911},[5487,18801,18739],{"class":6869},[5487,18803,18804],{"class":5489,"line":7254},[5487,18805,13006],{"class":6869},[5487,18807,18808],{"class":5489,"line":7259},[5487,18809,18810],{"class":6905},"            \u002F\u002F Відправка кандидата через WebSocket\n",[5487,18812,18813],{"class":5489,"line":7300},[5487,18814,16265],{"class":6869},[5487,18816,18817],{"class":5489,"line":7327},[5487,18818,5506],{"emptyLinePlaceholder":5505},[5487,18820,18821],{"class":5489,"line":7333},[5487,18822,18823],{"class":6905},"        \u002F\u002F Створення offer\n",[5487,18825,18826,18828,18830,18832,18835,18837,18839],{"class":5489,"line":7744},[5487,18827,10998],{"class":6237},[5487,18829,17043],{"class":6911},[5487,18831,7451],{"class":6869},[5487,18833,18834],{"class":6911},"_peerConnection",[5487,18836,5211],{"class":6869},[5487,18838,17055],{"class":6233},[5487,18840,16459],{"class":6869},[5487,18842,18843,18846,18848,18850,18852,18854,18856],{"class":5489,"line":7761},[5487,18844,18845],{"class":6237},"        await",[5487,18847,18590],{"class":6911},[5487,18849,5211],{"class":6869},[5487,18851,17100],{"class":6233},[5487,18853,6926],{"class":6869},[5487,18855,12492],{"class":6911},[5487,18857,16128],{"class":6869},[5487,18859,18860],{"class":5489,"line":7766},[5487,18861,5506],{"emptyLinePlaceholder":5505},[5487,18863,18864],{"class":5489,"line":7772},[5487,18865,18866],{"class":6905},"        \u002F\u002F Відправка offer на сервер через WebSocket\n",[5487,18868,18869],{"class":5489,"line":7797},[5487,18870,7330],{"class":6869},[5487,18872,18873],{"class":5489,"line":7820},[5487,18874,7336],{"class":6869},[5333,18876,18877],{},"Десктопні клієнти часто використовуються для професійних застосувань (телемедицина, відеоспостереження, онлайн-навчання), де потрібні розширені функції: запис трансляцій, віртуальні фони, обробка відео у реальному часі. Бібліотека SIPSorcery дозволяє інтегрувати власні аудіо\u002Fвідео кодеки та джерела медіа.",[5291,18879],{},[5294,18881,18883],{"id":18882},"практичні-сценарії-використання","Практичні сценарії використання",[5365,18885,18887],{"id":18886},"сценарій-1-відеоконференція-один-на-один-p2p","Сценарій 1: Відеоконференція один-на-один (P2P)",[5299,18889,18890],{},"Найпростіший випадок — пряме з'єднання між двома учасниками без участі сервера у передачі медіа:",[18892,18893,18894,18898,18901,18905,18908,18912,18915,18919,18922,18926],"steps",{},[5365,18895,18897],{"id":18896},"крок-1-реєстрація-учасників","Крок 1. Реєстрація учасників",[5299,18899,18900],{},"Обидва користувачі підключаються до сервера сигналізації через WebSocket, повідомляючи свої унікальні ідентифікатори.",[5365,18902,18904],{"id":18903},"крок-2-ініціація-виклику","Крок 2. Ініціація виклику",[5299,18906,18907],{},"Користувач A натискає кнопку \"Викликати\", створюється RTCPeerConnection, генерується SDP Offer та відправляється користувачу B через сервер сигналізації.",[5365,18909,18911],{"id":18910},"крок-3-прийняття-виклику","Крок 3. Прийняття виклику",[5299,18913,18914],{},"Користувач B отримує Offer, створює власний PeerConnection, встановлює Remote Description, генерує Answer та відправляє його користувачу A.",[5365,18916,18918],{"id":18917},"крок-4-обмін-ice-кандидатами","Крок 4. Обмін ICE-кандидатами",[5299,18920,18921],{},"Обидві сторони генерують ICE-кандидати та обмінюються ними через сигналізацію. Розпочинається процес встановлення прямого з'єднання (connectivity checks).",[5365,18923,18925],{"id":18924},"крок-5-активна-комунікація","Крок 5. Активна комунікація",[5299,18927,18928],{},"Після успішного встановлення з'єднання медіапотоки (аудіо з мікрофонів, відео з камер) передаються напряму між учасниками без участі сервера.",[5365,18930,18932],{"id":18931},"сценарій-2-груповий-відеочат-sfu-архітектура","Сценарій 2: Груповий відеочат (SFU архітектура)",[5299,18934,18935,18936,18939],{},"Для конференцій із більше ніж двома учасниками пряме P2P з'єднання між усіма парами стає неефективним (при N учасниках потрібно N-1 вихідних потоків на кожного). Застосовується архітектура ",[5235,18937,18938],{},"SFU (Selective Forwarding Unit)"," — спеціалізований сервер, що приймає медіапотоки від усіх учасників та вибірково пересилає їх іншим без транскодування:",[5904,18941,18943],{"alt":18942},"Архітектура SFU для групових викликів",[5478,18944,18946],{"className":5909,"code":18945,"language":5911,"meta":5483,"style":5483},"@startuml\nskinparam style plain\nskinparam backgroundColor #FFFFFF\n\nactor \"Учасник A\" as UserA #DBEAFE\nactor \"Учасник B\" as UserB #DCFCE7\nactor \"Учасник C\" as UserC #FEF3C7\ncomponent \"SFU Server\\n(Media Router)\" as SFU #F1F5F9\n\nUserA -up-> SFU : Відправка\\nвідео\u002Fаудіо потоку\nUserB -up-> SFU : Відправка\\nвідео\u002Fаудіо потоку\nUserC -up-> SFU : Відправка\\nвідео\u002Fаудіо потоку\n\nSFU -down-> UserA : Отримання потоків\\nвід B та C\nSFU -down-> UserB : Отримання потоків\\nвід A та C\nSFU -down-> UserC : Отримання потоків\\nвід A та B\n\nnote right of SFU\n  SFU пересилає медіапакети\n  без декодування\u002Fкодування.\n  Кожен учасник відправляє\n  лише один вихідний потік.\nend note\n\n@enduml\n",[5208,18947,18948,18952,18956,18960,18964,18969,18974,18979,18984,18988,18993,18998,19003,19007,19012,19017,19022,19026,19031,19036,19041,19046,19051,19055,19059],{"__ignoreMap":5483},[5487,18949,18950],{"class":5489,"line":5490},[5487,18951,5918],{},[5487,18953,18954],{"class":5489,"line":5496},[5487,18955,5923],{},[5487,18957,18958],{"class":5489,"line":5502},[5487,18959,5928],{},[5487,18961,18962],{"class":5489,"line":5509},[5487,18963,5506],{"emptyLinePlaceholder":5505},[5487,18965,18966],{"class":5489,"line":5515},[5487,18967,18968],{},"actor \"Учасник A\" as UserA #DBEAFE\n",[5487,18970,18971],{"class":5489,"line":5521},[5487,18972,18973],{},"actor \"Учасник B\" as UserB #DCFCE7\n",[5487,18975,18976],{"class":5489,"line":5527},[5487,18977,18978],{},"actor \"Учасник C\" as UserC #FEF3C7\n",[5487,18980,18981],{"class":5489,"line":5532},[5487,18982,18983],{},"component \"SFU Server\\n(Media Router)\" as SFU #F1F5F9\n",[5487,18985,18986],{"class":5489,"line":5538},[5487,18987,5506],{"emptyLinePlaceholder":5505},[5487,18989,18990],{"class":5489,"line":5544},[5487,18991,18992],{},"UserA -up-> SFU : Відправка\\nвідео\u002Fаудіо потоку\n",[5487,18994,18995],{"class":5489,"line":5550},[5487,18996,18997],{},"UserB -up-> SFU : Відправка\\nвідео\u002Fаудіо потоку\n",[5487,18999,19000],{"class":5489,"line":5555},[5487,19001,19002],{},"UserC -up-> SFU : Відправка\\nвідео\u002Fаудіо потоку\n",[5487,19004,19005],{"class":5489,"line":5561},[5487,19006,5506],{"emptyLinePlaceholder":5505},[5487,19008,19009],{"class":5489,"line":5567},[5487,19010,19011],{},"SFU -down-> UserA : Отримання потоків\\nвід B та C\n",[5487,19013,19014],{"class":5489,"line":5704},[5487,19015,19016],{},"SFU -down-> UserB : Отримання потоків\\nвід A та C\n",[5487,19018,19019],{"class":5489,"line":5710},[5487,19020,19021],{},"SFU -down-> UserC : Отримання потоків\\nвід A та B\n",[5487,19023,19024],{"class":5489,"line":5716},[5487,19025,5506],{"emptyLinePlaceholder":5505},[5487,19027,19028],{"class":5489,"line":5722},[5487,19029,19030],{},"note right of SFU\n",[5487,19032,19033],{"class":5489,"line":5727},[5487,19034,19035],{},"  SFU пересилає медіапакети\n",[5487,19037,19038],{"class":5489,"line":6007},[5487,19039,19040],{},"  без декодування\u002Fкодування.\n",[5487,19042,19043],{"class":5489,"line":6013},[5487,19044,19045],{},"  Кожен учасник відправляє\n",[5487,19047,19048],{"class":5489,"line":6019},[5487,19049,19050],{},"  лише один вихідний потік.\n",[5487,19052,19053],{"class":5489,"line":6025},[5487,19054,6000],{},[5487,19056,19057],{"class":5489,"line":6030},[5487,19058,5506],{"emptyLinePlaceholder":5505},[5487,19060,19061],{"class":5489,"line":6035},[5487,19062,6038],{},[5299,19064,19065,19066,5305,19069,5305,19072,19075],{},"Популярні open-source реалізації SFU: ",[5235,19067,19068],{},"Janus Gateway",[5235,19070,19071],{},"Mediasoup",[5235,19073,19074],{},"Pion SFU"," (на базі бібліотеки Pion для Go).",[5365,19077,19079],{"id":19078},"сценарій-3-live-трансляція-для-широкої-аудиторії-webrtc-hls","Сценарій 3: Live-трансляція для широкої аудиторії (WebRTC + HLS)",[5299,19081,19082],{},"Для трансляцій із великою кількістю глядачів (тисячі одночасних переглядів) чисте WebRTC стає неефективним через навантаження на SFU. Застосовується гібридний підхід:",[6045,19084,19085,19091,19097],{},[5201,19086,19087,19090],{},[5235,19088,19089],{},"Стрімер передає відео на сервер через WebRTC"," (низька затримка, двосторонній зв'язок для можливості інтерактивності).",[5201,19092,19093,19096],{},[5235,19094,19095],{},"Сервер транскодує потік у HLS"," та роздає через CDN для масової аудиторії (затримка 10–30 секунд, але необмежена масштабованість).",[5201,19098,19099,19102],{},[5235,19100,19101],{},"VIP-глядачі або модератори"," можуть підключатися через WebRTC для інтерактивної участі.",[5299,19104,19105],{},"Цей підхід використовують платформи типу Twitch, YouTube Live, Facebook Live для балансу між інтерактивністю та масштабованістю.",[5624,19107,19108,19118],{},[5299,19109,19110,19111,19113,19114,19117],{},"Бібліотека ",[5235,19112,5383],{}," підтримує прийом WebRTC-потоку через протокол ",[5235,19115,19116],{},"WHIP (WebRTC-HTTP Ingestion Protocol)"," та його перекодування у HLS одночасно:",[5478,19119,19121],{"className":6224,"code":19120,"language":6226,"meta":5483,"style":5483},"ffmpeg -protocol_whitelist file,udp,rtp \\\n  -i webrtc_stream.sdp \\\n  -c:v libx264 -preset veryfast -b:v 2000k \\\n  -c:a aac -b:a 128k \\\n  -f hls -hls_time 6 -hls_list_size 10 \\\n  output.m3u8\n",[5208,19122,19123,19135,19145,19165,19178,19197],{"__ignoreMap":5483},[5487,19124,19125,19127,19130,19133],{"class":5489,"line":5490},[5487,19126,6234],{"class":6233},[5487,19128,19129],{"class":6237}," -protocol_whitelist",[5487,19131,19132],{"class":6241}," file,udp,rtp",[5487,19134,6246],{"class":6245},[5487,19136,19137,19140,19143],{"class":5489,"line":5496},[5487,19138,19139],{"class":6237},"  -i",[5487,19141,19142],{"class":6241}," webrtc_stream.sdp",[5487,19144,6246],{"class":6245},[5487,19146,19147,19150,19152,19155,19158,19161,19163],{"class":5489,"line":5502},[5487,19148,19149],{"class":6237},"  -c:v",[5487,19151,6296],{"class":6241},[5487,19153,19154],{"class":6237}," -preset",[5487,19156,19157],{"class":6241}," veryfast",[5487,19159,19160],{"class":6237}," -b:v",[5487,19162,6334],{"class":6241},[5487,19164,6246],{"class":6245},[5487,19166,19167,19170,19172,19174,19176],{"class":5489,"line":5509},[5487,19168,19169],{"class":6237},"  -c:a",[5487,19171,6393],{"class":6241},[5487,19173,6396],{"class":6237},[5487,19175,6399],{"class":6241},[5487,19177,6246],{"class":6245},[5487,19179,19180,19182,19184,19187,19190,19193,19195],{"class":5489,"line":5515},[5487,19181,6413],{"class":6237},[5487,19183,6416],{"class":6241},[5487,19185,19186],{"class":6237}," -hls_time",[5487,19188,19189],{"class":6405}," 6",[5487,19191,19192],{"class":6237}," -hls_list_size",[5487,19194,6426],{"class":6405},[5487,19196,6246],{"class":6245},[5487,19198,19199],{"class":5489,"line":5521},[5487,19200,19201],{"class":6241},"  output.m3u8\n",[5291,19203],{},[5294,19205,19207],{"id":19206},"порівняння-технологій-потокової-передачі","Порівняння технологій потокової передачі",[8450,19209,19210,19223],{},[8453,19211,19212],{},[8456,19213,19214,19217,19220],{},[8459,19215,19216],{},"Критерій",[8459,19218,19219],{},"HLS \u002F DASH",[8459,19221,19222],{},"WebRTC",[8469,19224,19225,19238,19251,19264,19277,19289,19302,19315],{},[8456,19226,19227,19232,19235],{},[8474,19228,19229],{},[5235,19230,19231],{},"Затримка (Latency)",[8474,19233,19234],{},"10–30 секунд",[8474,19236,19237],{},"100–500 мс",[8456,19239,19240,19245,19248],{},[8474,19241,19242],{},[5235,19243,19244],{},"Масштабованість",[8474,19246,19247],{},"Необмежена (CDN)",[8474,19249,19250],{},"Обмежена (до ~100 учасників на SFU)",[8456,19252,19253,19258,19261],{},[8474,19254,19255],{},[5235,19256,19257],{},"Якість адаптації",[8474,19259,19260],{},"ABR за сегментами (6–10 сек)",[8474,19262,19263],{},"Адаптація у реальному часі (кожні 100 мс)",[8456,19265,19266,19271,19274],{},[8474,19267,19268],{},[5235,19269,19270],{},"Двостороння комунікація",[8474,19272,19273],{},"Немає (лише доставка)",[8474,19275,19276],{},"Так (аудіо, відео, дані)",[8456,19278,19279,19283,19286],{},[8474,19280,19281],{},[5235,19282,8532],{},[8474,19284,19285],{},"Універсальна (через HTML5 або hls.js)",[8474,19287,19288],{},"Нативна у всіх сучасних браузерах",[8456,19290,19291,19296,19299],{},[8474,19292,19293],{},[5235,19294,19295],{},"Складність реалізації",[8474,19297,19298],{},"Низька (статичні файли + HTTP)",[8474,19300,19301],{},"Висока (сигналізація, ICE, STUN\u002FTURN)",[8456,19303,19304,19309,19312],{},[8474,19305,19306],{},[5235,19307,19308],{},"Використання серверних ресурсів",[8474,19310,19311],{},"Мінімальне (лише роздача файлів)",[8474,19313,19314],{},"Високе (обробка RTP, пересилка потоків)",[8456,19316,19317,19322,19325],{},[8474,19318,19319],{},[5235,19320,19321],{},"Типові сценарії",[8474,19323,19324],{},"VOD, live-трансляції, освітні курси",[8474,19326,19327],{},"Відеоконференції, ігри, телемедицина",[19329,19330,19331,19337,19349],"accordion",{},[19332,19333,19336],"accordion-item",{"icon":19334,"label":19335},"i-lucide-help-circle","❓ Чому не можна використовувати лише WebRTC для всіх сценаріїв стрімінгу?","WebRTC оптимізований для інтерактивних сценаріїв із низькою затримкою та невеликою кількістю учасників. При масових трансляціях (10 000+ глядачів) навантаження на SFU-сервер стає критичним, оскільки кожен глядач потребує окремого з'єднання та обробки RTP-пакетів. Крім того, WebRTC потребує TURN-серверів для гарантованого з'єднання через NAT, що додає витрат на інфраструктуру. HLS\u002FDASH у таких ситуаціях ефективніші, оскільки використовують стандартні HTTP-сервери та можуть повністю кешуватися на CDN.",[19332,19338,19340,19341,19344,19345,19348],{"icon":19334,"label":19339},"❓ Як вибрати між HLS та DASH для нового проєкту?","Якщо ваша цільова аудиторія переважно використовує Apple-пристрої (iPhone, iPad, Mac), або потрібна нативна підтримка у Safari без додаткових бібліотек — обирайте ",[5235,19342,19343],{},"HLS",". Якщо важлива відкритість стандарту, підтримка найновіших кодеків (VP9, AV1) та інтеграція з DRM-системами різних виробників — обирайте ",[5235,19346,19347],{},"DASH",". Багато сучасних платформ генерують обидва формати одночасно та динамічно обирають підходящий залежно від клієнта.",[19332,19350,19352],{"icon":19334,"label":19351},"❓ Чи можна організувати WebRTC-трансляцію без TURN-сервера?","Так, але з обмеженнями. Якщо обидва учасники знаходяться у простих NAT-мережах (наприклад, домашні роутери), STUN-сервера зазвичай достатньо для визначення публічних адрес та встановлення прямого з'єднання. Проте у корпоративних мережах із симетричним NAT або строгими firewall правилами пряме з'єднання може бути неможливим. Статистика показує, що без TURN успішність встановлення з'єднання падає до 85–92% замість гарантованих 100% з TURN.",[5291,19354],{},[5294,19356,19358],{"id":19357},"практичні-рекомендації-та-найкращі-практики","Практичні рекомендації та найкращі практики",[5365,19360,19362],{"id":19361},"оптимізація-hlsdash-трансляцій","Оптимізація HLS\u002FDASH трансляцій",[5299,19364,19365,19368,19370],{},[5235,19366,19367],{},"1. Вибір тривалості сегменту.",[5378,19369],{},"\nКороткі сегменти (2–4 секунди) забезпечують швидший старт та зменшують затримку, але збільшують кількість HTTP-запитів та погіршують ефективність кешування. Довгі сегменти (10+ секунд) знижують накладні витрати, але уповільнюють реакцію ABR-алгоритму. Оптимальний баланс для VOD — 6–10 секунд, для live — 4–6 секунд.",[5299,19372,19373,19376,19378],{},[5235,19374,19375],{},"2. Організація структури каталогів для CDN.",[5378,19377],{},"\nРозміщуйте маніфести та сегменти у передбачуваній структурі каталогів, що полегшує налаштування правил кешування на CDN:",[5478,19380,19383],{"className":19381,"code":19382,"language":10005},[10003],"\u002Fcontent\u002Fvideo_id\u002F\n    master.m3u8\n    360p\u002Fplaylist.m3u8, segment_000.ts, segment_001.ts, ...\n    720p\u002Fplaylist.m3u8, segment_000.ts, segment_001.ts, ...\n    1080p\u002Fplaylist.m3u8, segment_000.ts, segment_001.ts, ...\n",[5208,19384,19382],{"__ignoreMap":5483},[5299,19386,19387,19390,19392],{},[5235,19388,19389],{},"3. Налаштування HTTP-заголовків кешування.",[5378,19391],{},"\nМедіасегменти є іммутабельними (після створення ніколи не змінюються), тому можуть кешуватися агресивно:",[5478,19394,19397],{"className":19395,"code":19396,"language":10005},[10003],"Cache-Control: public, max-age=31536000, immutable\n",[5208,19398,19396],{"__ignoreMap":5483},[5299,19400,19401],{},"Маніфести для VOD також статичні:",[5478,19403,19406],{"className":19404,"code":19405,"language":10005},[10003],"Cache-Control: public, max-age=3600\n",[5208,19407,19405],{"__ignoreMap":5483},[5299,19409,19410],{},"Для live-трансляцій маніфести оновлюються постійно:",[5478,19412,19415],{"className":19413,"code":19414,"language":10005},[10003],"Cache-Control: public, max-age=2\n",[5208,19416,19414],{"__ignoreMap":5483},[5299,19418,19419,19422,19424,19425,19428],{},[5235,19420,19421],{},"4. Використання Byte-Range Requests для зменшення затримки.",[5378,19423],{},"\nЗамість чекання завершення запису всього сегменту можна використовувати часткові HTTP-запити (",[5328,19426,19427],{},"HTTP Range Requests",") для завантаження початку сегменту та розпочати декодування до завершення передачі. Це знижує затримку старту на 1–2 секунди.",[5860,19430,19431,19432,19435],{},"При live-трансляціях переконайтеся, що ваш вебсервер правильно обробляє ",[5235,19433,19434],{},"HTTP 206 Partial Content"," для сегментів, що ще записуються. Неправильна конфігурація може призвести до пошкоджених фрагментів та зупинок відтворення.",[5365,19437,19439],{"id":19438},"оптимізація-webrtc-зєднань","Оптимізація WebRTC з'єднань",[5299,19441,19442,19445,19447],{},[5235,19443,19444],{},"1. Мінімізація переданих даних.",[5378,19446],{},"\nWebRTC автоматично адаптує бітрейт, але початкові параметри кодування критично важливі:",[5198,19449,19450,19457],{},[5201,19451,19452,19453,19456],{},"Встановлюйте ",[5208,19454,19455],{},"maxBitrate"," відповідно до очікуваних умов мережі (наприклад, 2500 kbps для мобільних мереж).",[5201,19458,19459,19460,19463],{},"Використовуйте ",[5235,19461,19462],{},"Simulcast"," — одночасну передачу кількох версій потоку (наприклад, 360p, 720p, 1080p). SFU-сервер вибирає підходящу версію для кожного отримувача залежно від його пропускної здатності.",[5299,19465,19466,19469,19471],{},[5235,19467,19468],{},"2. Налаштування параметрів кодеків.",[5378,19470],{},"\nУ SDP-описі можна явно вказати пріоритетні кодеки та їх параметри:",[5478,19473,19475],{"className":6843,"code":19474,"language":6845,"meta":5483,"style":5483},"\u002F\u002F Приклад обмеження роздільної здатності у Pion WebRTC\nmediaEngine := webrtc.MediaEngine{}\nmediaEngine.RegisterCodec(webrtc.RTPCodecParameters{\n    RTPCodecCapability: webrtc.RTPCodecCapability{\n        MimeType:  webrtc.MimeTypeVP8,\n        ClockRate: 90000,\n    },\n    PayloadType: 96,\n}, webrtc.RTPCodecTypeVideo)\n",[5208,19476,19477,19482,19499,19519,19534,19549,19561,19565,19576],{"__ignoreMap":5483},[5487,19478,19479],{"class":5489,"line":5490},[5487,19480,19481],{"class":6905},"\u002F\u002F Приклад обмеження роздільної здатності у Pion WebRTC\n",[5487,19483,19484,19487,19489,19491,19493,19496],{"class":5489,"line":5496},[5487,19485,19486],{"class":6911},"mediaEngine",[5487,19488,6915],{"class":6869},[5487,19490,11584],{"class":6855},[5487,19492,5211],{"class":6869},[5487,19494,19495],{"class":6855},"MediaEngine",[5487,19497,19498],{"class":6869},"{}\n",[5487,19500,19501,19503,19505,19508,19510,19512,19514,19517],{"class":5489,"line":5502},[5487,19502,19486],{"class":6911},[5487,19504,5211],{"class":6869},[5487,19506,19507],{"class":6233},"RegisterCodec",[5487,19509,6926],{"class":6869},[5487,19511,11584],{"class":6855},[5487,19513,5211],{"class":6869},[5487,19515,19516],{"class":6855},"RTPCodecParameters",[5487,19518,9954],{"class":6869},[5487,19520,19521,19524,19526,19528,19530,19532],{"class":5489,"line":5509},[5487,19522,19523],{"class":6911},"    RTPCodecCapability",[5487,19525,9963],{"class":6869},[5487,19527,11584],{"class":6855},[5487,19529,5211],{"class":6869},[5487,19531,14552],{"class":6855},[5487,19533,9954],{"class":6869},[5487,19535,19536,19539,19541,19543,19545,19547],{"class":5489,"line":5515},[5487,19537,19538],{"class":6911},"        MimeType",[5487,19540,12455],{"class":6869},[5487,19542,11584],{"class":6911},[5487,19544,5211],{"class":6869},[5487,19546,14565],{"class":6911},[5487,19548,9969],{"class":6869},[5487,19550,19551,19554,19556,19559],{"class":5489,"line":5521},[5487,19552,19553],{"class":6911},"        ClockRate",[5487,19555,9963],{"class":6869},[5487,19557,19558],{"class":6405},"90000",[5487,19560,9969],{"class":6869},[5487,19562,19563],{"class":5489,"line":5527},[5487,19564,10365],{"class":6869},[5487,19566,19567,19569,19571,19574],{"class":5489,"line":5532},[5487,19568,14302],{"class":6911},[5487,19570,9963],{"class":6869},[5487,19572,19573],{"class":6405},"96",[5487,19575,9969],{"class":6869},[5487,19577,19578,19581,19583,19585,19588],{"class":5489,"line":5538},[5487,19579,19580],{"class":6869},"}, ",[5487,19582,11584],{"class":6911},[5487,19584,5211],{"class":6869},[5487,19586,19587],{"class":6911},"RTPCodecTypeVideo",[5487,19589,6885],{"class":6869},[5299,19591,19592,19595,19597],{},[5235,19593,19594],{},"3. Моніторинг якості з'єднання.",[5378,19596],{},"\nРегулярно аналізуйте RTCP-статистику для виявлення проблем:",[5198,19599,19600,19606,19611],{},[5201,19601,19602,19605],{},[5235,19603,19604],{},"Packet Loss"," > 3% — погіршення якості, потрібно зниження бітрейту.",[5201,19607,19608,19610],{},[5235,19609,13698],{}," > 30 мс — нестабільне з'єднання, можливі артефакти аудіо.",[5201,19612,19613,19616],{},[5235,19614,19615],{},"RTT (Round-Trip Time)"," > 200 мс — висока затримка, погіршення інтерактивності.",[5299,19618,19619,19622,19624,19625,19628],{},[5235,19620,19621],{},"4. Обов'язкове використання TURN для продакшн-систем.",[5378,19623],{},"\nНавіть якщо 90% користувачів можуть встановити пряме з'єднання через STUN, наявність резервного TURN-сервера гарантує 100% успішність підключення. Розгорніть власний TURN-сервер на базі ",[5235,19626,19627],{},"coturn"," із аутентифікацією для захисту від зловживань:",[10208,19630,19631,19687],{},[10211,19632,19633],{"label":3077},[5478,19634,19636],{"className":6224,"code":19635,"language":6226,"meta":5483,"style":5483},"docker run -d --network=host \\\n  -e TURN_USERNAME=myuser \\\n  -e TURN_PASSWORD=mypassword \\\n  -e TURN_REALM=turn.example.com \\\n  coturn\u002Fcoturn\n",[5208,19637,19638,19654,19664,19673,19682],{"__ignoreMap":5483},[5487,19639,19640,19643,19646,19649,19652],{"class":5489,"line":5490},[5487,19641,19642],{"class":6233},"docker",[5487,19644,19645],{"class":6241}," run",[5487,19647,19648],{"class":6237}," -d",[5487,19650,19651],{"class":6237}," --network=host",[5487,19653,6246],{"class":6245},[5487,19655,19656,19659,19662],{"class":5489,"line":5496},[5487,19657,19658],{"class":6237},"  -e",[5487,19660,19661],{"class":6241}," TURN_USERNAME=myuser",[5487,19663,6246],{"class":6245},[5487,19665,19666,19668,19671],{"class":5489,"line":5502},[5487,19667,19658],{"class":6237},[5487,19669,19670],{"class":6241}," TURN_PASSWORD=mypassword",[5487,19672,6246],{"class":6245},[5487,19674,19675,19677,19680],{"class":5489,"line":5509},[5487,19676,19658],{"class":6237},[5487,19678,19679],{"class":6241}," TURN_REALM=turn.example.com",[5487,19681,6246],{"class":6245},[5487,19683,19684],{"class":5489,"line":5515},[5487,19685,19686],{"class":6241},"  coturn\u002Fcoturn\n",[10211,19688,19690],{"label":19689},"Systemd Service",[5478,19691,19693],{"className":6224,"code":19692,"language":6226,"meta":5483,"style":5483},"# Встановлення coturn\nsudo apt install coturn\n\n# Налаштування \u002Fetc\u002Fturnserver.conf\nlistening-port=3478\nrealm=turn.example.com\nuser=myuser:mypassword\nlt-cred-mech\n",[5208,19694,19695,19700,19713,19717,19722,19732,19742,19752],{"__ignoreMap":5483},[5487,19696,19697],{"class":5489,"line":5490},[5487,19698,19699],{"class":6905},"# Встановлення coturn\n",[5487,19701,19702,19705,19708,19710],{"class":5489,"line":5496},[5487,19703,19704],{"class":6233},"sudo",[5487,19706,19707],{"class":6241}," apt",[5487,19709,17664],{"class":6241},[5487,19711,19712],{"class":6241}," coturn\n",[5487,19714,19715],{"class":5489,"line":5502},[5487,19716,5506],{"emptyLinePlaceholder":5505},[5487,19718,19719],{"class":5489,"line":5509},[5487,19720,19721],{"class":6905},"# Налаштування \u002Fetc\u002Fturnserver.conf\n",[5487,19723,19724,19727,19729],{"class":5489,"line":5515},[5487,19725,19726],{"class":6911},"listening-port",[5487,19728,8621],{"class":6869},[5487,19730,19731],{"class":6241},"3478\n",[5487,19733,19734,19737,19739],{"class":5489,"line":5521},[5487,19735,19736],{"class":6911},"realm",[5487,19738,8621],{"class":6869},[5487,19740,19741],{"class":6241},"turn.example.com\n",[5487,19743,19744,19747,19749],{"class":5489,"line":5527},[5487,19745,19746],{"class":6911},"user",[5487,19748,8621],{"class":6869},[5487,19750,19751],{"class":6241},"myuser:mypassword\n",[5487,19753,19754],{"class":5489,"line":5532},[5487,19755,19756],{"class":6233},"lt-cred-mech\n",[5365,19758,19760],{"id":19759},"безпека-медіатрансляцій","Безпека медіатрансляцій",[5299,19762,19763,19766,19768,19769,19772],{},[5235,19764,19765],{},"1. Захист контенту від несанкціонованого доступу.",[5378,19767],{},"\nДля HLS\u002FDASH використовуйте ",[5235,19770,19771],{},"підписані URL (Signed URLs)"," із обмеженим терміном дії:",[5478,19774,19776],{"className":6843,"code":19775,"language":6845,"meta":5483,"style":5483},"\u002F\u002F Генерація підписаного URL із терміном дії 1 година\nfunc generateSignedURL(baseURL string, secretKey []byte, expiry time.Duration) string {\n    expiresAt := time.Now().Add(expiry).Unix()\n    message := fmt.Sprintf(\"%s?expires=%d\", baseURL, expiresAt)\n    \n    h := hmac.New(sha256.New, secretKey)\n    h.Write([]byte(message))\n    signature := hex.EncodeToString(h.Sum(nil))\n    \n    return fmt.Sprintf(\"%s&signature=%s\", message, signature)\n}\n",[5208,19777,19778,19783,19824,19854,19892,19896,19926,19945,19975,19979,20013],{"__ignoreMap":5483},[5487,19779,19780],{"class":5489,"line":5490},[5487,19781,19782],{"class":6905},"\u002F\u002F Генерація підписаного URL із терміном дії 1 година\n",[5487,19784,19785,19787,19790,19792,19795,19797,19799,19802,19804,19806,19808,19811,19814,19816,19818,19820,19822],{"class":5489,"line":5496},[5487,19786,6894],{"class":6237},[5487,19788,19789],{"class":6233}," generateSignedURL",[5487,19791,6926],{"class":6869},[5487,19793,19794],{"class":6911},"baseURL",[5487,19796,7506],{"class":6855},[5487,19798,5305],{"class":6869},[5487,19800,19801],{"class":6911},"secretKey",[5487,19803,7639],{"class":6869},[5487,19805,13261],{"class":6855},[5487,19807,5305],{"class":6869},[5487,19809,19810],{"class":6911},"expiry",[5487,19812,19813],{"class":6855}," time",[5487,19815,5211],{"class":6869},[5487,19817,15121],{"class":6855},[5487,19819,10344],{"class":6869},[5487,19821,10484],{"class":6855},[5487,19823,7150],{"class":6869},[5487,19825,19826,19829,19831,19833,19835,19838,19840,19843,19845,19847,19849,19852],{"class":5489,"line":5502},[5487,19827,19828],{"class":6911},"    expiresAt",[5487,19830,6915],{"class":6869},[5487,19832,15106],{"class":6911},[5487,19834,5211],{"class":6869},[5487,19836,19837],{"class":6233},"Now",[5487,19839,7056],{"class":6869},[5487,19841,19842],{"class":6233},"Add",[5487,19844,6926],{"class":6869},[5487,19846,19810],{"class":6911},[5487,19848,5622],{"class":6869},[5487,19850,19851],{"class":6233},"Unix",[5487,19853,10726],{"class":6869},[5487,19855,19856,19859,19861,19863,19865,19868,19870,19872,19874,19877,19879,19881,19883,19885,19887,19890],{"class":5489,"line":5509},[5487,19857,19858],{"class":6911},"    message",[5487,19860,6915],{"class":6869},[5487,19862,11759],{"class":6911},[5487,19864,5211],{"class":6869},[5487,19866,19867],{"class":6233},"Sprintf",[5487,19869,6926],{"class":6869},[5487,19871,7318],{"class":6241},[5487,19873,8020],{"class":6911},[5487,19875,19876],{"class":6241},"?expires=",[5487,19878,7894],{"class":6911},[5487,19880,7318],{"class":6241},[5487,19882,5305],{"class":6869},[5487,19884,19794],{"class":6911},[5487,19886,5305],{"class":6869},[5487,19888,19889],{"class":6911},"expiresAt",[5487,19891,6885],{"class":6869},[5487,19893,19894],{"class":5489,"line":5515},[5487,19895,6946],{"class":6869},[5487,19897,19898,19901,19903,19906,19908,19911,19913,19916,19918,19920,19922,19924],{"class":5489,"line":5521},[5487,19899,19900],{"class":6911},"    h",[5487,19902,6915],{"class":6869},[5487,19904,19905],{"class":6911},"hmac",[5487,19907,5211],{"class":6869},[5487,19909,19910],{"class":6233},"New",[5487,19912,6926],{"class":6869},[5487,19914,19915],{"class":6911},"sha256",[5487,19917,5211],{"class":6869},[5487,19919,19910],{"class":6911},[5487,19921,5305],{"class":6869},[5487,19923,19801],{"class":6911},[5487,19925,6885],{"class":6869},[5487,19927,19928,19930,19932,19935,19937,19939,19941,19943],{"class":5489,"line":5527},[5487,19929,19900],{"class":6911},[5487,19931,5211],{"class":6869},[5487,19933,19934],{"class":6233},"Write",[5487,19936,13405],{"class":6869},[5487,19938,13261],{"class":6855},[5487,19940,6926],{"class":6869},[5487,19942,16282],{"class":6911},[5487,19944,6941],{"class":6869},[5487,19946,19947,19950,19952,19955,19957,19960,19962,19964,19966,19969,19971,19973],{"class":5489,"line":5532},[5487,19948,19949],{"class":6911},"    signature",[5487,19951,6915],{"class":6869},[5487,19953,19954],{"class":6911},"hex",[5487,19956,5211],{"class":6869},[5487,19958,19959],{"class":6233},"EncodeToString",[5487,19961,6926],{"class":6869},[5487,19963,6540],{"class":6911},[5487,19965,5211],{"class":6869},[5487,19967,19968],{"class":6233},"Sum",[5487,19970,6926],{"class":6869},[5487,19972,7284],{"class":6237},[5487,19974,6941],{"class":6869},[5487,19976,19977],{"class":5489,"line":5538},[5487,19978,6946],{"class":6869},[5487,19980,19981,19983,19985,19987,19989,19991,19993,19995,19998,20000,20002,20004,20006,20008,20011],{"class":5489,"line":5544},[5487,19982,10535],{"class":6865},[5487,19984,14605],{"class":6911},[5487,19986,5211],{"class":6869},[5487,19988,19867],{"class":6233},[5487,19990,6926],{"class":6869},[5487,19992,7318],{"class":6241},[5487,19994,8020],{"class":6911},[5487,19996,19997],{"class":6241},"&signature=",[5487,19999,8020],{"class":6911},[5487,20001,7318],{"class":6241},[5487,20003,5305],{"class":6869},[5487,20005,16282],{"class":6911},[5487,20007,5305],{"class":6869},[5487,20009,20010],{"class":6911},"signature",[5487,20012,6885],{"class":6869},[5487,20014,20015],{"class":5489,"line":5550},[5487,20016,7336],{"class":6869},[5299,20018,20019,20022,20024],{},[5235,20020,20021],{},"2. Шифрування сегментів HLS за допомогою AES-128.",[5378,20023],{},"\nFFmpeg підтримує генерацію зашифрованих сегментів:",[5478,20026,20028],{"className":6224,"code":20027,"language":6226,"meta":5483,"style":5483},"# Генерація ключа шифрування\nopenssl rand 16 > encryption.key\n\n# Створення файлу keyinfo\necho \"http:\u002F\u002Fexample.com\u002Fkeys\u002Fencryption.key\" > keyinfo\necho \"encryption.key\" >> keyinfo\necho $(openssl rand -hex 16) >> keyinfo\n\n# Кодування із шифруванням\nffmpeg -i input.mp4 \\\n  -c:v libx264 -c:a aac \\\n  -hls_key_info_file keyinfo \\\n  -f hls output.m3u8\n",[5208,20029,20030,20035,20052,20056,20061,20074,20086,20107,20111,20116,20127,20139,20149],{"__ignoreMap":5483},[5487,20031,20032],{"class":5489,"line":5490},[5487,20033,20034],{"class":6905},"# Генерація ключа шифрування\n",[5487,20036,20037,20040,20043,20046,20049],{"class":5489,"line":5496},[5487,20038,20039],{"class":6233},"openssl",[5487,20041,20042],{"class":6241}," rand",[5487,20044,20045],{"class":6405}," 16",[5487,20047,20048],{"class":6869}," > ",[5487,20050,20051],{"class":6241},"encryption.key\n",[5487,20053,20054],{"class":5489,"line":5502},[5487,20055,5506],{"emptyLinePlaceholder":5505},[5487,20057,20058],{"class":5489,"line":5509},[5487,20059,20060],{"class":6905},"# Створення файлу keyinfo\n",[5487,20062,20063,20066,20069,20071],{"class":5489,"line":5515},[5487,20064,20065],{"class":6233},"echo",[5487,20067,20068],{"class":6241}," \"http:\u002F\u002Fexample.com\u002Fkeys\u002Fencryption.key\"",[5487,20070,20048],{"class":6869},[5487,20072,20073],{"class":6241},"keyinfo\n",[5487,20075,20076,20078,20081,20084],{"class":5489,"line":5521},[5487,20077,20065],{"class":6233},[5487,20079,20080],{"class":6241}," \"encryption.key\"",[5487,20082,20083],{"class":6869}," >> ",[5487,20085,20073],{"class":6241},[5487,20087,20088,20090,20093,20095,20097,20100,20102,20105],{"class":5489,"line":5527},[5487,20089,20065],{"class":6233},[5487,20091,20092],{"class":6869}," $(",[5487,20094,20039],{"class":6233},[5487,20096,20042],{"class":6241},[5487,20098,20099],{"class":6237}," -hex",[5487,20101,20045],{"class":6405},[5487,20103,20104],{"class":6869},") >> ",[5487,20106,20073],{"class":6241},[5487,20108,20109],{"class":5489,"line":5532},[5487,20110,5506],{"emptyLinePlaceholder":5505},[5487,20112,20113],{"class":5489,"line":5538},[5487,20114,20115],{"class":6905},"# Кодування із шифруванням\n",[5487,20117,20118,20120,20122,20125],{"class":5489,"line":5544},[5487,20119,6234],{"class":6233},[5487,20121,6238],{"class":6237},[5487,20123,20124],{"class":6241}," input.mp4",[5487,20126,6246],{"class":6245},[5487,20128,20129,20131,20133,20135,20137],{"class":5489,"line":5550},[5487,20130,19149],{"class":6237},[5487,20132,6296],{"class":6241},[5487,20134,6390],{"class":6237},[5487,20136,6393],{"class":6241},[5487,20138,6246],{"class":6245},[5487,20140,20141,20144,20147],{"class":5489,"line":5555},[5487,20142,20143],{"class":6237},"  -hls_key_info_file",[5487,20145,20146],{"class":6241}," keyinfo",[5487,20148,6246],{"class":6245},[5487,20150,20151,20153,20155],{"class":5489,"line":5561},[5487,20152,6413],{"class":6237},[5487,20154,6416],{"class":6241},[5487,20156,20157],{"class":6241}," output.m3u8\n",[5299,20159,20160,20163,20165],{},[5235,20161,20162],{},"3. Обмеження доступу до серверів сигналізації.",[5378,20164],{},"\nЗахистіть WebSocket-ендпоінт сигналізації автентифікацією (JWT токени у параметрах з'єднання) та rate limiting для запобігання DoS-атак:",[5478,20167,20169],{"className":6843,"code":20168,"language":6845,"meta":5483,"style":5483},"func authenticateWebSocket(r *http.Request) bool {\n    token := r.URL.Query().Get(\"token\")\n    \u002F\u002F Перевірка JWT токену\n    claims, err := validateJWT(token)\n    return err == nil && claims.Valid()\n}\n",[5208,20170,20171,20196,20224,20229,20250,20272],{"__ignoreMap":5483},[5487,20172,20173,20175,20178,20180,20182,20184,20186,20188,20190,20192,20194],{"class":5489,"line":5490},[5487,20174,6894],{"class":6237},[5487,20176,20177],{"class":6233}," authenticateWebSocket",[5487,20179,6926],{"class":6869},[5487,20181,7030],{"class":6911},[5487,20183,7033],{"class":6869},[5487,20185,6918],{"class":6855},[5487,20187,5211],{"class":6869},[5487,20189,7040],{"class":6855},[5487,20191,10344],{"class":6869},[5487,20193,10347],{"class":6855},[5487,20195,7150],{"class":6869},[5487,20197,20198,20201,20203,20205,20207,20209,20211,20213,20215,20217,20219,20222],{"class":5489,"line":5496},[5487,20199,20200],{"class":6911},"    token",[5487,20202,6915],{"class":6869},[5487,20204,7030],{"class":6911},[5487,20206,5211],{"class":6869},[5487,20208,10749],{"class":6911},[5487,20210,5211],{"class":6869},[5487,20212,10754],{"class":6233},[5487,20214,7056],{"class":6869},[5487,20216,10759],{"class":6233},[5487,20218,6926],{"class":6869},[5487,20220,20221],{"class":6241},"\"token\"",[5487,20223,6885],{"class":6869},[5487,20225,20226],{"class":5489,"line":5502},[5487,20227,20228],{"class":6905},"    \u002F\u002F Перевірка JWT токену\n",[5487,20230,20231,20234,20236,20238,20240,20243,20245,20248],{"class":5489,"line":5509},[5487,20232,20233],{"class":6911},"    claims",[5487,20235,5305],{"class":6869},[5487,20237,7290],{"class":6911},[5487,20239,6915],{"class":6869},[5487,20241,20242],{"class":6233},"validateJWT",[5487,20244,6926],{"class":6869},[5487,20246,20247],{"class":6911},"token",[5487,20249,6885],{"class":6869},[5487,20251,20252,20254,20256,20258,20260,20262,20265,20267,20270],{"class":5489,"line":5515},[5487,20253,10535],{"class":6865},[5487,20255,7265],{"class":6911},[5487,20257,7140],{"class":6869},[5487,20259,7284],{"class":6237},[5487,20261,18065],{"class":6869},[5487,20263,20264],{"class":6911},"claims",[5487,20266,5211],{"class":6869},[5487,20268,20269],{"class":6233},"Valid",[5487,20271,10726],{"class":6869},[5487,20273,20274],{"class":5489,"line":5521},[5487,20275,7336],{"class":6869},[5299,20277,20278,20281,20283,20284,20286],{},[5235,20279,20280],{},"4. Ізоляція учасників у SFU-системах.",[5378,20282],{},"\nУ груповому відеочаті створюйте окремі ізольовані кімнати (",[5328,20285,11497],{},"), куди учасники можуть приєднатися лише за запрошенням або після автентифікації. Ніколи не транслюйте медіапотоки між різними кімнатами.",[5291,20288],{},[5294,20290,20292],{"id":20291},"інструменти-розробки-та-налагодження","Інструменти розробки та налагодження",[5365,20294,20296],{"id":20295},"тестування-hlsdash-трансляцій","Тестування HLS\u002FDASH трансляцій",[5299,20298,20299,20302],{},[5235,20300,20301],{},"1. VLC Media Player"," — універсальний програвач для тестування маніфестів:",[5478,20304,20306],{"className":6224,"code":20305,"language":6226,"meta":5483,"style":5483},"vlc http:\u002F\u002Flocalhost:8080\u002Fstream\u002Fmaster.m3u8\n",[5208,20307,20308],{"__ignoreMap":5483},[5487,20309,20310,20313],{"class":5489,"line":5490},[5487,20311,20312],{"class":6233},"vlc",[5487,20314,20315],{"class":6241}," http:\u002F\u002Flocalhost:8080\u002Fstream\u002Fmaster.m3u8\n",[5299,20317,20318,20321],{},[5235,20319,20320],{},"2. FFplay"," — легковаговий програвач із FFmpeg для швидкого тестування:",[5478,20323,20325],{"className":6224,"code":20324,"language":6226,"meta":5483,"style":5483},"ffplay http:\u002F\u002Flocalhost:8080\u002Fstream\u002Fmaster.m3u8\n",[5208,20326,20327],{"__ignoreMap":5483},[5487,20328,20329,20332],{"class":5489,"line":5490},[5487,20330,20331],{"class":6233},"ffplay",[5487,20333,20315],{"class":6241},[5299,20335,20336],{},[5235,20337,20338],{},"3. Онлайн-тестери:",[5198,20340,20341,20352],{},[5201,20342,20343,6099,20346],{},[5235,20344,20345],{},"HLS.js Demo:",[20347,20348,20349],"a",{"href":20349,"rel":20350},"https:\u002F\u002Fhls-js.netlify.app\u002Fdemo\u002F",[20351],"nofollow",[5201,20353,20354,6099,20357],{},[5235,20355,20356],{},"DASH.js Reference Player:",[20347,20358,20359],{"href":20359,"rel":20360},"https:\u002F\u002Freference.dashif.org\u002Fdash.js\u002F",[20351],[5299,20362,20363,20366],{},[5235,20364,20365],{},"4. Chrome DevTools Network Inspector"," — аналіз запитів до сегментів та заголовків кешування у вкладці Network браузера.",[5365,20368,20370],{"id":20369},"тестування-webrtc-зєднань","Тестування WebRTC з'єднань",[5299,20372,20373,20376,20378],{},[5235,20374,20375],{},"1. about:webrtc у Firefox \u002F chrome:\u002F\u002Fwebrtc-internals у Chrome.",[5378,20377],{},"\nВбудовані інструменти браузерів для детального аналізу WebRTC-сесій: статистика пакетів, RTT, кодеки, ICE-кандидати, графіки бітрейту.",[5299,20380,20381,20384,20386,20387,20389],{},[5235,20382,20383],{},"2. Trickle ICE Testing Tool:",[5378,20385],{},"\nОнлайн-інструмент для перевірки доступності STUN\u002FTURN серверів:",[5378,20388],{},[20347,20390,20391],{"href":20391,"rel":20392},"https:\u002F\u002Fwebrtc.github.io\u002Fsamples\u002Fsrc\u002Fcontent\u002Fpeerconnection\u002Ftrickle-ice\u002F",[20351],[5299,20394,20395],{},[5235,20396,20397],{},"3. Логування у Pion WebRTC:",[5478,20399,20401],{"className":6843,"code":20400,"language":6845,"meta":5483,"style":5483},"import \"github.com\u002Fpion\u002Flogging\"\n\n\u002F\u002F Увімкнення детального логування\nlogger := logging.NewDefaultLoggerFactory()\nlogger.DefaultLogLevel = logging.LogLevelTrace\n\napi := webrtc.NewAPI(webrtc.WithSettingEngine(settingEngine))\n",[5208,20402,20403,20410,20414,20419,20436,20454,20458],{"__ignoreMap":5483},[5487,20404,20405,20407],{"class":5489,"line":5490},[5487,20406,6866],{"class":6865},[5487,20408,20409],{"class":6241}," \"github.com\u002Fpion\u002Flogging\"\n",[5487,20411,20412],{"class":5489,"line":5496},[5487,20413,5506],{"emptyLinePlaceholder":5505},[5487,20415,20416],{"class":5489,"line":5502},[5487,20417,20418],{"class":6905},"\u002F\u002F Увімкнення детального логування\n",[5487,20420,20421,20424,20426,20429,20431,20434],{"class":5489,"line":5509},[5487,20422,20423],{"class":6911},"logger",[5487,20425,6915],{"class":6869},[5487,20427,20428],{"class":6911},"logging",[5487,20430,5211],{"class":6869},[5487,20432,20433],{"class":6233},"NewDefaultLoggerFactory",[5487,20435,10726],{"class":6869},[5487,20437,20438,20440,20442,20445,20447,20449,20451],{"class":5489,"line":5515},[5487,20439,20423],{"class":6911},[5487,20441,5211],{"class":6869},[5487,20443,20444],{"class":6911},"DefaultLogLevel",[5487,20446,7451],{"class":6869},[5487,20448,20428],{"class":6911},[5487,20450,5211],{"class":6869},[5487,20452,20453],{"class":6911},"LogLevelTrace\n",[5487,20455,20456],{"class":5489,"line":5521},[5487,20457,5506],{"emptyLinePlaceholder":5505},[5487,20459,20460,20463,20465,20467,20469,20472,20474,20476,20478,20481,20483,20486],{"class":5489,"line":5527},[5487,20461,20462],{"class":6911},"api",[5487,20464,6915],{"class":6869},[5487,20466,11584],{"class":6911},[5487,20468,5211],{"class":6869},[5487,20470,20471],{"class":6233},"NewAPI",[5487,20473,6926],{"class":6869},[5487,20475,11584],{"class":6911},[5487,20477,5211],{"class":6869},[5487,20479,20480],{"class":6233},"WithSettingEngine",[5487,20482,6926],{"class":6869},[5487,20484,20485],{"class":6911},"settingEngine",[5487,20487,6941],{"class":6869},[5299,20489,20490,20493,20495,20496,5309,20499,20502],{},[5235,20491,20492],{},"4. Wireshark для аналізу RTP\u002FSRTP пакетів.",[5378,20494],{},"\nЗахопіть мережевий трафік та застосуйте фільтр ",[5208,20497,20498],{},"rtp",[5208,20500,20501],{},"stun"," для аналізу на рівні пакетів.",[5291,20504],{},[5294,20506,20508],{"id":20507},"висновки-та-подальше-вивчення","Висновки та подальше вивчення",[5299,20510,20511],{},"У цій лекції ми здійснили глибоке занурення у світ потокової передачі медіаконтенту, розглянувши дві принципово різні парадигми: HTTP-based стрімінг (HLS, DASH) та прямий P2P зв'язок у реальному часі (WebRTC).",[5299,20513,20514],{},[5235,20515,20516],{},"Ключові висновки:",[5190,20518,20519,20524,20528],{},[5193,20520,20523],{"icon":20521,"title":20522},"i-lucide-film","🌐 HTTP Live Streaming","Протокол HLS забезпечує надійну доставку відео через звичайні HTTP-сервери та CDN, використовуючи сегментацію контенту та адаптивний бітрейт. Ідеальний вибір для VOD-платформ, освітніх курсів та масових live-трансляцій, де критична масштабованість, а затримка 10–30 секунд є прийнятною.",[5193,20525,20527],{"icon":943,"title":20526},"📡 Dynamic Adaptive Streaming","DASH є відкритим міжнародним стандартом, що пропонує більшу гнучкість у виборі кодеків та контейнерів. Підтримка MPEG-CENC дозволяє уніфікувати захист контенту для різних DRM-систем. Рекомендується для платформ, що цінують відкритість стандартів та незалежність від виробників.",[5193,20529,20531],{"icon":3301,"title":20530},"🎥 WebRTC Communications","Технологія WebRTC революціонізувала інтерактивну комунікацію, забезпечуючи затримку менше секунди та прямий обмін медіа між учасниками. Незамінна для відеоконференцій, телемедицини, онлайн-ігор та будь-яких сценаріїв, де критична двостороння інтерактивність у реальному часі.",[5365,20533,20535],{"id":20534},"рекомендації-щодо-вибору-технології","Рекомендації щодо вибору технології",[8450,20537,20538,20551],{},[8453,20539,20540],{},[8456,20541,20542,20545,20548],{},[8459,20543,20544],{},"Сценарій використання",[8459,20546,20547],{},"Рекомендована технологія",[8459,20549,20550],{},"Обґрунтування",[8469,20552,20553,20564,20575,20586,20597,20608,20619],{},[8456,20554,20555,20558,20561],{},[8474,20556,20557],{},"VOD-платформа (Netflix-style)",[8474,20559,20560],{},"HLS + CDN",[8474,20562,20563],{},"Необмежена масштабованість, мінімальні серверні витрати",[8456,20565,20566,20569,20572],{},[8474,20567,20568],{},"Освітні онлайн-курси",[8474,20570,20571],{},"HLS або DASH",[8474,20573,20574],{},"Підтримка широкого діапазону пристроїв, можливість DRM",[8456,20576,20577,20580,20583],{},[8474,20578,20579],{},"Live-трансляція спортивних подій",[8474,20581,20582],{},"HLS із низькою затримкою (LL-HLS)",[8474,20584,20585],{},"Масова аудиторія + прийнятна затримка 3–5 секунд",[8456,20587,20588,20591,20594],{},[8474,20589,20590],{},"Відеоконференції 1-на-1",[8474,20592,20593],{},"WebRTC P2P",[8474,20595,20596],{},"Мінімальна затримка, прямий зв'язок без серверів",[8456,20598,20599,20602,20605],{},[8474,20600,20601],{},"Груповий відеочат до 50 учасників",[8474,20603,20604],{},"WebRTC + SFU",[8474,20606,20607],{},"Ефективна маршрутизація медіа без транскодування",[8456,20609,20610,20613,20616],{},[8474,20611,20612],{},"Масова інтерактивна трансляція",[8474,20614,20615],{},"WebRTC (стрімер) → HLS (глядачі)",[8474,20617,20618],{},"Інтерактивність для ведучого + масштабованість для аудиторії",[8456,20620,20621,20624,20627],{},[8474,20622,20623],{},"Віддалений робочий стіл",[8474,20625,20626],{},"WebRTC DataChannels + Video",[8474,20628,20629],{},"Низька затримка, двостороння передача даних та екрану",[5365,20631,20633],{"id":20632},"ресурси-для-подальшого-вивчення","Ресурси для подальшого вивчення",[5299,20635,20636],{},[5235,20637,20638],{},"Специфікації та стандарти:",[5198,20640,20641,20648,20651,20654,20657],{},[5201,20642,20643,20644],{},"RFC 8216: HTTP Live Streaming (HLS) — ",[20347,20645,20646],{"href":20646,"rel":20647},"https:\u002F\u002Fdatatracker.ietf.org\u002Fdoc\u002Fhtml\u002Frfc8216",[20351],[5201,20649,20650],{},"ISO\u002FIEC 23009-1: MPEG-DASH Specification",[5201,20652,20653],{},"RFC 8445: Interactive Connectivity Establishment (ICE)",[5201,20655,20656],{},"RFC 8866: SDP: Session Description Protocol",[5201,20658,20659],{},"RFC 3550: RTP: A Transport Protocol for Real-Time Applications",[5299,20661,20662],{},[5235,20663,20664],{},"Документація бібліотек:",[5198,20666,20667,20674,20681,20688],{},[5201,20668,20669,20670],{},"Pion WebRTC Documentation — ",[20347,20671,20672],{"href":20672,"rel":20673},"https:\u002F\u002Fgithub.com\u002Fpion\u002Fwebrtc",[20351],[5201,20675,20676,20677],{},"FFmpeg HLS Muxer — ",[20347,20678,20679],{"href":20679,"rel":20680},"https:\u002F\u002Fffmpeg.org\u002Fffmpeg-formats.html#hls-2",[20351],[5201,20682,20683,20684],{},"Video.js Player — ",[20347,20685,20686],{"href":20686,"rel":20687},"https:\u002F\u002Fvideojs.com\u002F",[20351],[5201,20689,20690,20691],{},"hls.js Library — ",[20347,20692,20693],{"href":20693,"rel":20694},"https:\u002F\u002Fgithub.com\u002Fvideo-dev\u002Fhls.js",[20351],[5299,20696,20697],{},[5235,20698,20699],{},"Онлайн-ресурси та спільноти:",[5198,20701,20702,20709,20716],{},[5201,20703,20704,20705],{},"WebRTC для розробників (Google) — ",[20347,20706,20707],{"href":20707,"rel":20708},"https:\u002F\u002Fwebrtc.org\u002F",[20351],[5201,20710,20711,20712],{},"Streaming Media Magazine — ",[20347,20713,20714],{"href":20714,"rel":20715},"https:\u002F\u002Fwww.streamingmedia.com\u002F",[20351],[5201,20717,20718,20719],{},"WebRTC Weekly Newsletter — ",[20347,20720,20721],{"href":20721,"rel":20722},"https:\u002F\u002Fwebrtcweekly.com\u002F",[20351],[5333,20724,20725,20726,5305,20729,20732,20733,5305,20736,20739],{},"Технології потокового медіа постійно еволюціонують. Слідкуйте за новими стандартами, такими як ",[5235,20727,20728],{},"Low-Latency HLS (LL-HLS)",[5235,20730,20731],{},"WebRTC HTTP Ingestion Protocol (WHIP\u002FWHEP)"," та кодеками наступного покоління (",[5235,20734,20735],{},"AV1",[5235,20737,20738],{},"VVC\u002FH.266","), що обіцяють кращу якість при нижчій пропускній здатності.",[5291,20741],{},[5294,20743,20745],{"id":20744},"контрольні-питання-для-самоперевірки","Контрольні питання для самоперевірки",[19329,20747,20748,20759,20774,20797,20804,20816],{},[19332,20749,20751,20754,20755,20758],{"icon":19334,"label":20750},"1. Поясніть принципову різницю між прогресивним завантаженням та потоковою передачею медіа.",[5235,20752,20753],{},"Прогресивне завантаження"," передбачає повне або часткове завантаження медіафайлу через HTTP із можливістю розпочати відтворення до завершення завантаження. Проте весь файл є монолітним, адаптація якості неможлива, і клієнт змушений завантажувати дані лінійно. ",[5235,20756,20757],{},"Потокова передача (Streaming)"," розділяє контент на короткі самодостатні сегменти, які завантажуються послідовно. Це дозволяє динамічно змінювати якість (ABR), швидко перемотувати без завантаження проміжних даних та ефективно кешувати сегменти на CDN.",[19332,20760,20762,20763,20765,20766,20769,20770,20773],{"icon":19334,"label":20761},"2. Чому у HLS використовується формат .m3u8, а не бінарний формат маніфесту?","Текстовий формат ",[5208,20764,5210],{}," (UTF-8 варіант M3U) обраний для ",[5235,20767,20768],{},"простоти налагодження, читабельності людиною та легкості інтеграції",". Розробники можуть відкрити маніфест у текстовому редакторі для швидкої діагностики проблем, а HTTP-сервери без додаткової конфігурації правильно визначають MIME-тип ",[5208,20771,20772],{},"application\u002Fvnd.apple.mpegurl",". Накладні витрати на розмір текстового файлу мінімальні (маніфести зазвичай \u003C10 KB) та компенсуються gzip-стисненням при передачі.",[19332,20775,20777,20780,20781,20784,20785,20788,20789,20792,20793,20796],{"icon":19334,"label":20776},"3. Що таке ICE-кандидат у контексті WebRTC, і чому їх потрібно обмінювати через сигналізацію?",[5235,20778,20779],{},"ICE-кандидат"," — це можливий мережевий шлях (IP-адреса + порт + протокол), через який може бути встановлено з'єднання. Типи кандидатів: ",[5235,20782,20783],{},"host"," (локальна адреса пристрою), ",[5235,20786,20787],{},"srflx"," (публічна адреса через STUN), ",[5235,20790,20791],{},"relay"," (адреса TURN-сервера). Обмін кандидатами потрібен, оскільки браузери та застосунки за NAT не знають своїх публічних адрес та мережевої топології один одного. Лише після отримання всіх кандидатів обох сторін відбуваються ",[5235,20794,20795],{},"connectivity checks"," — серія тестових пакетів STUN для визначення найкращого шляху з'єднання.",[19332,20798,20800,20803],{"icon":19334,"label":20799},"4. Яка роль протоколу SDP у WebRTC, і що міститься у SDP Offer та Answer?",[5235,20801,20802],{},"SDP (Session Description Protocol)"," — це текстовий формат опису параметрів медіасесії. SDP Offer створюється ініціатором виклику і містить: список підтримуваних кодеків (H.264, VP8, Opus тощо), типи медіапотоків (аудіо, відео, дані), ICE-параметри, DTLS-відбитки сертифікатів для шифрування. SDP Answer — це відповідь другої сторони, що підтверджує узгоджені параметри (наприклад, \"я підтримую лише VP8 із твого списку кодеків\"). Після обміну SDP обидві сторони мають єдине розуміння формату та параметрів майбутнього медіапотоку.",[19332,20805,20807,20808,20811,20812,20815],{"icon":19334,"label":20806},"5. Чому для live-трансляцій із затримкою \u003C1 секунди краще WebRTC, а не HLS із короткими сегментами?","Навіть при зменшенні тривалості HLS-сегментів до 1 секунди загальна затримка залишається високою через ",[5235,20809,20810],{},"послідовний характер обробки",": кодувальник повинен завершити запис сегменту, закрити файл, оновити маніфест, програвач — завантажити маніфест, завантажити новий сегмент, розпочати декодування. Це додає мінімум 3–4 тривалості сегменту затримки (3–4 секунди). WebRTC передає RTP-пакети ",[5235,20813,20814],{},"безперервним потоком без чекання завершення фрагментів",", що знижує затримку до одного RTT + час кодування\u002Fдекодування кадру (зазвичай 100–300 мс). Додатково, WebRTC використовує UDP, що дозволяє пропускати втрачені пакети замість повторної передачі.",[19332,20817,20819,20820,20823],{"icon":19334,"label":20818},"6. Опишіть сценарій, коли гібридний підхід WebRTC + HLS є оптимальним рішенням.","Типовий приклад — ",[5235,20821,20822],{},"інтерактивний стрімінг із великою аудиторією"," (аукціони, спортивні події з коментаторами, освітні вебінари). Ведучий\u002Fстрімер передає відео через WebRTC на SFU-сервер (затримка \u003C500 мс), що дозволяє йому бачити реакцію аудиторії та взаємодіяти у реальному часі. Одночасно SFU транскодує потік у HLS та роздає через CDN тисячам глядачів (затримка 10–20 секунд), забезпечуючи масштабованість без перевантаження SFU. Обрані VIP-учасники або модератори можуть приєднатися через WebRTC для активної участі, тоді як основна маса глядачів споживає контент через HLS.",[5291,20825],{},[5190,20827,20828,20843],{},[5193,20829,20832,20835,20836,20838,20839,20842],{"icon":20830,"title":20831},"i-lucide-arrow-right","📚 Наступна тема",[5235,20833,20834],{},"Тема 18:"," Моніторинг, безпека та контейнеризація застосунків",[5378,20837],{},"\nДізнаємось про структуроване логування (",[5208,20840,20841],{},"log\u002Fslog","), збір метрик для Prometheus, захист серверних інтерфейсів та створення мінімальних Docker-образів для Go-застосунків.",[5193,20844,20847],{"icon":20845,"title":20846},"i-lucide-link","🔗 Пов'язані теми",[5198,20848,20849,20855,20861],{},[5201,20850,20851,20854],{},[5235,20852,20853],{},"Тема 8:"," Протокол HTTP та стандартний вебсервер Go",[5201,20856,20857,20860],{},[5235,20858,20859],{},"Тема 14:"," Потокова передача даних та протокол WebSocket",[5201,20862,20863,20866],{},[5235,20864,20865],{},"Тема 15:"," Міжсервісна взаємодія на основі протоколу gRPC",[15633,20868,20869],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sjcCO, html code.shiki .sjcCO{--shiki-light:#EE0000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html pre.shiki code .s8xlr, html code.shiki .s8xlr{--shiki-light:#AF00DB;--shiki-default:#C586C0;--shiki-dark:#C586C0}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .siwwj, html code.shiki .siwwj{--shiki-light:#001080;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .s-QsJ, html code.shiki .s-QsJ{--shiki-light:#0070C1;--shiki-default:#4FC1FF;--shiki-dark:#4FC1FF}html pre.shiki code .s0P7L, html code.shiki .s0P7L{--shiki-light:#800000;--shiki-default:#808080;--shiki-dark:#808080}html pre.shiki code .sKtos, html code.shiki .sKtos{--shiki-light:#800000;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sa4r_, html code.shiki .sa4r_{--shiki-light:#E50000;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .su9tN, html code.shiki .su9tN{--shiki-light:#0000FF;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sLwNe, html code.shiki .sLwNe{--shiki-light:#0451A5;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .sqdDX, html code.shiki .sqdDX{--shiki-light:#800000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}html pre.shiki code .sD7JJ, html code.shiki .sD7JJ{--shiki-light:#000000FF;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .sDUd3, html code.shiki .sDUd3{--shiki-light:#0451A5;--shiki-default:#CE9178;--shiki-dark:#CE9178}",{"title":5483,"searchDepth":5496,"depth":5496,"links":20871},[20872,20873,20878,20883,20888,20893,20899,20907,20912,20922,20923,20928,20932,20936],{"id":5296,"depth":5496,"text":5297},{"id":5351,"depth":5496,"text":5352,"children":20874},[20875,20876],{"id":5367,"depth":5502,"text":5368},{"id":5433,"depth":5502,"text":20877},"Формат маніфесту .m3u8",{"id":5791,"depth":5496,"text":5792,"children":20879},[20880,20881,20882],{"id":5798,"depth":5502,"text":5799},{"id":5869,"depth":5502,"text":5870},{"id":5887,"depth":5502,"text":5888},{"id":6201,"depth":5496,"text":6202,"children":20884},[20885,20886,20887],{"id":6208,"depth":5502,"text":6209},{"id":6836,"depth":5502,"text":6837},{"id":7366,"depth":5502,"text":7367},{"id":8433,"depth":5496,"text":8434,"children":20889},[20890,20891,20892],{"id":8447,"depth":5502,"text":8448},{"id":8554,"depth":5502,"text":8555},{"id":9221,"depth":5502,"text":9222},{"id":9262,"depth":5496,"text":9263,"children":20894},[20895,20896,20897,20898],{"id":9276,"depth":5502,"text":9277},{"id":9335,"depth":5502,"text":9336},{"id":9678,"depth":5502,"text":9679},{"id":10074,"depth":5502,"text":10075},{"id":10159,"depth":5496,"text":10160,"children":20900},[20901,20902,20903,20904,20905,20906],{"id":10174,"depth":5502,"text":10175},{"id":10205,"depth":5502,"text":10206},{"id":10240,"depth":5502,"text":10241},{"id":11512,"depth":5502,"text":11513},{"id":12751,"depth":5502,"text":12752},{"id":14406,"depth":5502,"text":14407},{"id":15539,"depth":5496,"text":15540,"children":20908},[20909,20910,20911],{"id":15546,"depth":5502,"text":15547},{"id":17639,"depth":5502,"text":17640},{"id":18469,"depth":5502,"text":18470},{"id":18882,"depth":5496,"text":18883,"children":20913},[20914,20915,20916,20917,20918,20919,20920,20921],{"id":18886,"depth":5502,"text":18887},{"id":18896,"depth":5502,"text":18897},{"id":18903,"depth":5502,"text":18904},{"id":18910,"depth":5502,"text":18911},{"id":18917,"depth":5502,"text":18918},{"id":18924,"depth":5502,"text":18925},{"id":18931,"depth":5502,"text":18932},{"id":19078,"depth":5502,"text":19079},{"id":19206,"depth":5496,"text":19207},{"id":19357,"depth":5496,"text":19358,"children":20924},[20925,20926,20927],{"id":19361,"depth":5502,"text":19362},{"id":19438,"depth":5502,"text":19439},{"id":19759,"depth":5502,"text":19760},{"id":20291,"depth":5496,"text":20292,"children":20929},[20930,20931],{"id":20295,"depth":5502,"text":20296},{"id":20369,"depth":5502,"text":20370},{"id":20507,"depth":5496,"text":20508,"children":20933},[20934,20935],{"id":20534,"depth":5502,"text":20535},{"id":20632,"depth":5502,"text":20633},{"id":20744,"depth":5496,"text":20745},"Глибоке вивчення принципів потокової передачі медіаконтенту через HTTP (HLS, DASH), адаптивного бітрейту та технології прямого P2P зв","md",null,{},{"title":4053,"description":20937},"eFSBbEf8EgBOGM2E_3CdZjMsUeNi1gQTMjR39S0NK1o",[20944,20946],{"title":4049,"path":4050,"stem":4051,"description":20945,"children":-1},"Парадигма асинхронного обміну повідомленнями. Протокол AMQP 0-9-1 та брокер RabbitMQ. Producers, Exchanges, Queues, Bindings. Протокол MQTT для IoT та мобільних пристроїв. Рівні якості обслуговування QoS, топіки та клієнтська інтеграція у Go.",{"title":4057,"path":4058,"stem":4059,"description":20947,"children":-1},"Комплексне вивчення спостережуваності Go-застосунків через структуроване логування (log\u002Fslog), збір метрик для Prometheus, захист серверних інтерфейсів від типових вразливостей та створення мінімальних безпечних Docker-образів із багатоетапним складанням",1791986750184]