Commit fb3fb6ba by Иван Кубота

bunch of texts

parent 0d9512b3
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
......@@ -37,6 +37,12 @@ var SubHeader = function(cfg, children) {
headers.push(Object.assign(item, {type: 'SubHeader'}));
return D.div({cls: 'subHeader'}, [D.h('a', {attr: {name: item.name}})].concat(children))
};
var SubSubHeader = function(cfg, children) {
var item = uniqLink(children);
if(!cfg || !cfg.hidden)
headers.push(Object.assign(item, {type: 'SubSubHeader'}));
return D.div({cls: 'subSubHeader'}, [D.h('a', {attr: {name: item.name}})].concat(children))
};
var Field = function(cfg, children) {
cfg = cfg || {};
if(children[0].name === 'radio' || children[0].name === 'checkbox'){
......@@ -55,4 +61,4 @@ var Field = function(cfg, children) {
}
};
export {Info, Content, Note, Header, SubHeader, Field, headers};
\ No newline at end of file
export {Info, Content, Note, Header, SubHeader, SubSubHeader, Field, headers};
\ No newline at end of file
......@@ -89,6 +89,11 @@
font-size: 32px;
margin: 96px 0 48px 0;
}
.subSubHeader {
font-weight: bold;
font-size: 24px;
margin: 64px 0 32px 0;
}
.thead td {
border-bottom: 2px solid #000;
}
......
......@@ -3,32 +3,59 @@ import {lal} from '/posts/post.jsx';
import {s} from '/posts/store.jsx';
import {VideoCalculator} from '/posts/VideoCalculator.jsx';
import {Interactive} from '/posts/Interactive.jsx';
import { headers, Header, SubHeader } from "cmp/Blocks.jsx";
import { headers, Header, SubHeader, SubSubHeader } from "cmp/Blocks.jsx";
import { Collapse } from "cmp/Collapse/Collapse.jsx";
import { Users } from "posts/Users.jsx";
import { Profit } from "posts/Profit.jsx";
import { Portal } from "posts/Portal.jsx";
import { Plan } from "posts/Plan.jsx";
import { Stack } from "posts/Stack.jsx";
const {IF, ELSE} = Store;
export default function() {
var krokotau = Store.Value.Boolean(false);
let graph,
let graph, klaps = false,
dom = <div renderTo={document.body} class={[ "page-content", { krokotau } ]}>
{_ => {
setTimeout( function(){
_( headers.map( i =>
<div class={D.cls( 'link-header', {
h1: i.type === 'Header',
h2: i.type === 'SubHeader'
h2: i.type === 'SubHeader',
h3: i.type === 'SubSubHeader'
} )}><a href={'#' + i.name}>{i.title}</a></div>
) )
}, 10 );
}}
<Collapse label={'Хранение и передача видео'} value={false}>
{VideoCalculator}
<Collapse label={'Пользователи'} value={klaps}>
<Users/>
</Collapse>
<Collapse label={'Портал'} value={false}>
<Portal/>
</Collapse>
<Collapse label={'Интерактив'} value={false}>
<Collapse label={'Хранение и передача видео'} value={klaps}>
<VideoCalculator/>
</Collapse>
<Collapse label={'Интерактив'} value={klaps}>
<Interactive/>
</Collapse>
<Collapse label={'Монетизация'} value={klaps}>
<Profit/>
</Collapse>
<Collapse label={'Технологический стэк'} value={klaps}>
<Stack/>
</Collapse>
<Collapse label={'План этапов работ'} value={klaps}>
<Plan/>
</Collapse>
</div>;
const w = window;
......
......@@ -256,7 +256,7 @@ var _now, vid,
Типы интерактива
</Header>
<SubHeader>
Опрос
Опрос <img className="interactive-image" src={"quiz.png"}/>
</SubHeader>
<Content>
<p>
......@@ -264,25 +264,41 @@ var _now, vid,
</p><p>
Действие доступно как из приложения smartTV с помощью пульта, так и с мобильного телефона.
</p><p>
Настраивается позиция блока, количество и текст вариантов. При включенной опции "Свой вариант" - зритель получает возможность ввести свой текст.
<Info>Можно реализовать сборку текста из заранее подготовленных кусочков. Это сильно упрощает анализ и группировку ответов пользователей, а так же на корню решает все проблемы с матом и модерацией. Пример такого ввода текста встречается в играх от From Software - Bloodborn и Dark Souls 1, 2, 3</Info>
Настраивается позиция блока, количество и текст вариантов. При включенной опции «Свой вариант» — зритель получает возможность ввести свой текст.
<Info>
<p>Можно реализовать сборку текста из заранее подготовленных кусочков. Это сильно упрощает анализ и группировку ответов пользователей, а так же на корню решает все проблемы с матом и модерацией. Пример такого ввода текста встречается в играх от From Software — Bloodborn; Dark Souls 1, 2, 3</p>
{/*<p>Кусочки выбираются из категорий\подкатегорий, например:</p>
<p>Место → Рельеф → Гора</p>
<p>Подменю зависимости слов → Вложенность</p>
<p>Место → Страна → Азия → Китай</p>
<p>Получились <i>Горы Китая</i></p>
<p>Подменю зависимости слов → Описание</p>
<p>Качество → Положительное → Красивый</p>
<p>Получилось <i>Горы Китая красивы</i></p>
<p>Время → Время года → Зима</p>
<p>Получилось <i>Горы Китая красивы зимой</i></p>*/}
</Info>
</p>
</Content>
<SubHeader>
Вопрос
Вопрос <img className="interactive-image" src={"question.png"}/>
</SubHeader>
<Content>
<p>
Предлагает зрителю <i>ответить на вопрос</i>. Без вариантов ответов. Как и опрос, можно реализовать набор фразы из кусочков.
</p>
<p>
Помимо стандартных настроек - можно задать минимальную\максимальную длину ответа.
Настраивается максимальная и минимальная длина ответа.
</p>
</Content>
<SubHeader>
Загадка
Загадка <img className="interactive-image" src={"zhsloe.png"}/>
</SubHeader>
<Content>
<p>
......@@ -295,6 +311,95 @@ var _now, vid,
Настраивается стиль клеток, их количество, расположение относительно друг друга и доступные для ввода символы\цвета или даже звуки (конкурс с подбором мелодии!)
</p>
</Content>
<SubHeader>
Переход <img className="interactive-image" src={"goto.png"}/>
</SubHeader>
<Content>
<p>
<i>Элемент навигации</i>, работает как гиперссылка. Позволяет выполнить переход в другое видео, в другой раздел или остаться в текущем видео, но попасть в другое время.
</p>
<p>
С помощью этого инструмента можно делать:
<ul>
<li><i>«Видеостатьи»</i> — сейчас я буду распаковывать этот товар, кому не интересна эта часть — переходите сразу к обзору</li>
<li><i>Квесты</i> — *перед персонажем появляется камень на котором есть три варианта действия* Терять коня, Терять жизнь, Забывать себя. При выборе любого из действий — зритель попадает или в другое видео где разворачивается соответствующая сюжетная линия, или передвигается по текущему видео по принципу <a href={'https://eksmo.ru/trends/knigi-kvesty-kak-ikh-chitat-i-zachem-ID15503007/'} target={'_blank'}>квестовых книг</a>.</li>
<li><i>Отсылки</i> — в своём прошлом видео я рассказывал о принципе устройства конденсаторов, эти знания будут важны для понимания дальнейшего материала</li>
<li><i>Рекламный баннер</i></li>
</ul>
</p>
<p>
Настраивается стиль перехода. Можно установить текст, картинку, зацикленное короткое видео или отрезок другого видео с выбором начала и конца отрезка.
</p>
</Content>
<SubHeader>
Разветвление сюжета <img className="interactive-image" src={"split.png"}/>
</SubHeader>
<Content>
<p>
<i>Обязательный выбор</i> — этот тип интерактива похож на <i>переход</i>, но является обязательным. Основной видеопоток останавливается и ждёт реакции пользователя.
</p>
<p>
Настройки аналогичны <i>переходу</i>, но добавляется настройка поведения основного видео во время выбора:
<ul>
<li>Повтор последних X секунд</li>
<li>Воспроизведение Х секунд вперёд-назад-вперёд-назад</li>
<li>Изменение скорости при повторе</li>
<li>Вставка другого зацикленного видео</li>
<li>Зацикленная звуковая дорожка</li>
</ul>
</p>
</Content>
<SubHeader>
Результат опроса <img className="interactive-image" src={"chart.png"}/>
</SubHeader>
<Content>
<p>
<img src={'tv.jpg'} className="article-image"/>
Выводит информацию о результатах опроса. Может выводить одновременно с опросом, в таком случае отображение будет аналогично телефонным голосованием.
</p>
<p>
Настраивается:
<ul>
<li>Стиль графика. Цвета, отступы</li>
<li>Тип графика. Столбцы, круговая диаграмма (пирог)</li>
<li>Фильтрация данных. Можно не показывать ответы с менее чем 5 голосами</li>
<li>Формат ответов: абсолютные числа, проценты</li>
</ul>
</p>
</Content>
<SubHeader>
Виджет <img className="interactive-image" src={"widget.png"}/>
</SubHeader>
<Content>
<p>
Отображает информацию поверх видеоряда. Погода, объявление, картинки, кусочки видео.
</p>
<p>
Виджет не подразумевает пользовательского взаимодействия
</p>
</Content>
<Info>
<SubHeader>Общие настройки</SubHeader>
<Content>
<p>Все типы интерактива имеют не только специфичные настроеки, но и стандартные:</p>
<ul>
<li>Позиция (x, y, width, height)</li>
<li>Длительность и время отображения.</li>
<li>Эффект появления и исчезновения интерактивного блока</li>
<li>Название (системное свойство для редактора, позволяет не путать блоки)</li>
</ul>
</Content>
</Info>
</div>;
......
......@@ -247,4 +247,17 @@
.inputs {
opacity: 0;
}
}
.interactive-image {
float: right;
position: relative;
margin: 0 128px 0 0;
}
.article-image {
position: absolute;
right: -64px;
}
ul {
list-style-type: bengali;
padding-inline-start: 32px;
}
\ No newline at end of file
import {Info, Content, Note, Header, SubHeader, Field} from '/cmp/Blocks.jsx';
import { PlainSelect } from "cmp/PlainSelect/PlainSelect.jsx";
import { Checkbox } from "cmp/Checkbox/Checkbox.jsx";
import { Radio } from "cmp/Radio/Radio.jsx";
import { Input } from "cmp/Input/Input.jsx";
import { TimeInput } from "cmp/TimeInput/TimeInput.jsx";
import { NumberInput } from "cmp/NumberInput/NumberInput.jsx";
import Add from "/svg/ic_add.svg";
import "./Plan.scss";
import { Collapse } from "cmp/Collapse/Collapse.jsx";
var Plan = function(){
s.set( {} );
var dom = <div class={'plan'}>
<Header>Этапы разработки</Header>
</div>;
return dom;
};
export {Plan};
\ No newline at end of file
import {Info, Content, Note, Header, SubHeader,SubSubHeader, Field} from '/cmp/Blocks.jsx';
import { PlainSelect } from "cmp/PlainSelect/PlainSelect.jsx";
import { Checkbox } from "cmp/Checkbox/Checkbox.jsx";
import { Radio } from "cmp/Radio/Radio.jsx";
import { Input } from "cmp/Input/Input.jsx";
import { TimeInput } from "cmp/TimeInput/TimeInput.jsx";
import { NumberInput } from "cmp/NumberInput/NumberInput.jsx";
import Add from "/svg/ic_add.svg";
import "./Portal.scss";
import { Collapse } from "cmp/Collapse/Collapse.jsx";
var Portal = function(){
s.set( {} );
var dom = <div class={'users'}>
<Header>Портал-Аттракцион</Header>
<Info>Этот раздел фиксирует структуру проекта. Структура описана в соответствии с макетами из cacoo.</Info>
<Content>
<p>Сложно охарактеризовать разрабатываемый проект более лаконично чем «<i>Портал-Аттракцион</i>»</p>
<p>Любой наш зритель может как круглосуточно <i>потреблять</i> имеющийся контент, так и <i>создавать</i> нечто своё-новое и выкладывать на всеобщее обозрение.</p>
<p>Пользователи будут участвовать в опросах и конкурсах. Делать это они смогут не на альтруистических началах, а зарабатывая попутно коины.</p>
</Content>
<SubHeader>Структура проекта</SubHeader>
<SubSubHeader>Раздел</SubSubHeader>
<Content>
<p>Базовой структурной единицей проекта является «Раздел».</p><p>Свойства раздела:</p>
<ul>
<li>Название — заголовок раздела</li>
<li>Описание — пояснение описывающее содержание раздела. У одного раздела может быть несколько описаний для разных форматов отображения (короткое, длинное).</li>
<li>Родительский раздел — если задан, то раздел становится <i>подразделом</i>, если нет — коренным.</li>
<li>
<a href="#Права доступа">Права доступа</a>. Определяют кто может наполнять и редактировать раздел.
</li>
<li><a href="#Предварительный просмотр">Предварительный просмотр</a> — обложка раздела. Содержит ссылки на подразделы и выборки в соответствии с настройками.</li>
<li><a href="#Тип контента">Тип контента</a> — указывает какой тип контента можно добавлять в раздел (например: текст, видео, музыка).</li>
</ul>
</Content>
<SubHeader>Права доступа</SubHeader>
<Content>
<p>Любой объект в системе имеет права доступа.</p>
<p>Права доступа похожи на систему прав *nix операционных систем.</p>
<p>У любого объекта есть <i>создатель</i>.</p>
<p>Создатель может делать с объектом что угодно, а именно — удалять, редактировать, наполнять, просматривать.</p>
<p>Создатель может дать права на удаление, редактирование, наполнение и просмотр любому другому пользователю или группе пользователей.</p>
<p>Группа пользователей — такой же объект. Создатель группы может добавлять в неё других пользователей.</p>
</Content>
</div>;
return dom;
};
export {Portal};
\ No newline at end of file
import {Info, Content, Note, Header, SubHeader, Field} from '/cmp/Blocks.jsx';
import { PlainSelect } from "cmp/PlainSelect/PlainSelect.jsx";
import { Checkbox } from "cmp/Checkbox/Checkbox.jsx";
import { Radio } from "cmp/Radio/Radio.jsx";
import { Input } from "cmp/Input/Input.jsx";
import { TimeInput } from "cmp/TimeInput/TimeInput.jsx";
import { NumberInput } from "cmp/NumberInput/NumberInput.jsx";
import Add from "/svg/ic_add.svg";
import "./Profit.scss";
import { Collapse } from "cmp/Collapse/Collapse.jsx";
var Profit = function(){
s.set( {} );
var dom = <div class={'profit'}>
<Header>Монетизация и система вознаграждения</Header>
</div>;
return dom;
};
export {Profit};
\ No newline at end of file
import {Info, Content, Note, Header, SubHeader, Field} from '/cmp/Blocks.jsx';
import { PlainSelect } from "cmp/PlainSelect/PlainSelect.jsx";
import { Checkbox } from "cmp/Checkbox/Checkbox.jsx";
import { Radio } from "cmp/Radio/Radio.jsx";
import { Input } from "cmp/Input/Input.jsx";
import { TimeInput } from "cmp/TimeInput/TimeInput.jsx";
import { NumberInput } from "cmp/NumberInput/NumberInput.jsx";
import Add from "/svg/ic_add.svg";
import "./Stack.scss";
import { Collapse } from "cmp/Collapse/Collapse.jsx";
var Stack = function(){
s.set( {} );
var dom = <div class={'stack'}>
<Header>Технологический стэк</Header>
</div>;
return dom;
};
export {Stack};
\ No newline at end of file
import {Info, Content, Note, Header, SubHeader, Field} from '/cmp/Blocks.jsx';
import { PlainSelect } from "cmp/PlainSelect/PlainSelect.jsx";
import { Checkbox } from "cmp/Checkbox/Checkbox.jsx";
import { Radio } from "cmp/Radio/Radio.jsx";
import { Input } from "cmp/Input/Input.jsx";
import { TimeInput } from "cmp/TimeInput/TimeInput.jsx";
import { NumberInput } from "cmp/NumberInput/NumberInput.jsx";
import Add from "/svg/ic_add.svg";
import "./Users.scss";
import { Collapse } from "cmp/Collapse/Collapse.jsx";
var Users = function(){
s.set( {} );
var dom = <div class={'users'}>
<Header>Пользователи</Header>
<SubHeader>
Поведенческий анализ
</SubHeader>
<Content>
<p>
Разрабатываемая система производит постоянное отслеживание поведения пользователей. Накопленные поведенческие данные могут обрабатываться с помощью Data Mining и Machine Learning алгоритмов.
</p>
<p>
Мы обеспечиваем прямую связь между зрителями и создателями контента. Создатели могут как оценивать такие статистические данные как количество просмотров видео, наиболее полпулярные видео и разделы, так и напрямую обратиться к зрителю и спросить о том что нравится в шоу, а что нет; устраивать опросы и розыгрыши призов.
</p>
</Content>
<SubHeader>
Кросс-платформенная связь
</SubHeader>
<Content>
<p>
Основной платформой для просмотра видеоконтента является телевизор, но последнее время всё больше пользователей использует мобильный телефон или персональный компьютер в качестве устройства для доступа к видео информации. Это легко объясняется тем, что интернет сервисы, такие как youtube, дают зрителю возможность выбирать контент для просмотра по своему желанию, а не основываясь на предварительно составленной сетке показов телеканала.
</p>
<p>
Наша цель — объединить эти два мира. Вернуть пользователю удобство восприятия видеоинформации с большого экрана и обеспечить выбор желаемого контента.
</p>
<p>
<i>Интерактивная часть</i>. Обратная связь, опросы, конкурсы и все другие места где от пользователя требуется ввод информации — не удобно использовать с телевизора. Пульт как правило не имеет достаточно кнопок для удобного ввода текстовой информации. По этой причине мы решили объединить SmartTV и мобильное вебприложение в единую синергитическую систему. Отвечать на вопросы и предлагать свои варианты развития сюжета можно с удобной мобильной клавиатуры. Или даже с клавиатуры своего ноутбука!
</p>
</Content>
<SubHeader>
Регистрация
</SubHeader>
<Content>
<p>
Первоначальная регистрация пользователей происходит при установке мобильного приложения или при открытии smartTV приложения.
</p>
<p>
Заполнение персональных данных не обязательно, но подкрепляется положительными мотиваторами (день просмотра без рекламы, получение мир-коинов*?).
</p>
</Content>
<SubHeader>
Слияние аккаунтов
</SubHeader>
<Content>
<p>
Система должна предусматривать возможность слияния пользователей для объединения случайно созданых разных профилей мобильного и smartTV приложений.
</p>
<p>
Для слияния пользователей и\или авторизации необходимо отсканировать мобильным телефоном QR-код с телевизора.
</p>
</Content>
<SubHeader>
Вход в существующий аккаунт
</SubHeader>
<Content>
<p>
Вход в существующий аккаунт с устройства не поддерживающего сканирование QR-кода (компьютер) возможен только при:
</p>
<ul>
<li>
прохождении двуфакторной авторизации с мобильного устройства
</li>
<li>
прозождении авторизации по смс
</li>
<li>
прозождении авторизации по email
</li>
<li>
при изначальном создании аккаунта из web версии приложения
</li>
</ul>
<p>
При изначачальном создании аккаунта из web версии - дальнейшая авторизация на телевизоре происходит с помощью сканирования мобильным приложением QR-кодов web версии и smartTV версии приложения МИР.
</p>
</Content>
<SubHeader>
Поощрение
</SubHeader>
<Content>
<p>
Нам выгодно заполнение как можно более подробное заполнение профиля пользователя. Введённые данные дают нам возможность производить точное таргетирование отображаемой рекламы и рекомендуемого контента.
</p>
<p>
После заполнения username, email и phone number — у пользователя появляется кошелёк.
</p>
<p>
По этой причине за заполнение профиля мы даём пользователю награду в виде МИР-коинов.
</p>
<p>
Распоряжение коинами происходит с мобильного приложения с использованием двуфакторной авторизации. Второй фактор может быть email, sms или сканирование QR кода с телевизора.
</p>
</Content>
</div>;
return dom;
};
export {Users};
\ No newline at end of file
......@@ -119,57 +119,88 @@ var GBFormatter = function(gb) {
};
var sizeSelector;
var VideoCalculator = <div class={'calculator'}>
<Header>
Видеосервис
</Header>
<Content>
<p>
Хранение и передача видео в реальном времени — сложная и ресурсоёмкая задача с точки зрения инфраструктуры.
</p>
<p>
Рассмотрим процесс от загрузки видеофайла создателем контента до проигрывания у конечного пользователя:
</p><p>
<ol>
<li><i>Видео</i> снято, смонтировано и <i>готово к загрузке</i> в нашу систему.</li>
<li>Выбираем видео в списке файлов и <i>начинаем загрузку</i>. Это значит что наша <i>система имеет достаточно места</i> для хранения данных. YouTube позволяет загружать файлы до 128GB.</li>
<li><i>Дисконнект</i>. До окончания загрузки осталось 6 часов, 3 часа уже прошло. YouTube в таком случае позволяет продолжить загрузку файла с контрольной точки. Это не стандартное поведение браузера и файлсервера. Такое решение требует отдельной разработки. Многие сервисы в таком случае предлагают начать процесс загрузки с начала.</li>
<li><i>Перекодирование</i>. Загрузка завершена! Теперь будем перекодировать файл в подходящие для браузеров и smartTV форматы. Перекодирование — это длительный и ресурсоёмкий процесс. Этот процесс хорошо распараллеливается, а значит его можно ускорить за счёт увеличения количества процессоров и ядер на серверах занимающихся перекодированием. Сейчас, в 2020 году, процесс перекодирования FullHD в 720p <a href="https://www.guru3d.com/articles-pages/intel-core-i9-9900k-processor-review,14.html" target="_blank">всё ещё занимает</a> время равное длительности загружаемое видео. Система должна одновременно перекодировать множество видео в большое количество форматов.
</li>
<li>
<p>
<i>Очередь задач</i> и <i>масштабирование</i>. Для общения частей системы выполняющих разные роли построим единую шину данных в которую будут добавляться новые задачи. Сервисы способные выполнить задачу будут забирать её из очереди и заниматься ей. Если очередь растёт быстрее чем задачи успевают обрабатываться — мы можем <i>моментально масштабировать инфраструктуру</i> путём добавления вычислительных узлов аналогичных загруженным.
</p><p>
Перекодирование в 144p занимает на порядки меньше времени чем перекодирование в 1080p и если издателю важно выложить контент как можно скорее — он может быть удовлетворён и таким стартом.
</p>
</li>
<li>
<p>
<i>Публикация</i> и <i>доставка</i> на устройство пользователя. Видео выложено в публичный доступ. С этого момента мы <i>отслеживаем геолокацию зрителей</i>.
var VideoCalculator = function(){
var dom = <div class={'calculator'}>
<Header>
Видеосервис
</Header>
<Content>
<p>
Хранение и передача видео в реальном времени — сложная и ресурсоёмкая задача требующая слаженной работы большое количество инфраструктурных ущлов.
</p>
<SubHeader>Жизненный цикл видеофайла</SubHeader>
<p>
Рассмотрим процесс от загрузки видеофайла создателем контента до проигрывания у конечного пользователя:
</p>
<p>
<ol>
<li><i>Видео</i> снято, смонтировано и <i>готово к загрузке</i> в нашу систему.</li>
<li>Выбираем видео в списке файлов и <i>начинаем загрузку</i>. Это значит что наша <i>система имеет достаточно
места</i> для хранения данных. YouTube позволяет загружать файлы до 128GB.
</li>
<li><i>Дисконнект</i>. До окончания загрузки осталось 6 часов, 3 часа уже прошло. YouTube в таком случае
позволяет продолжить загрузку файла с контрольной точки. Это не стандартное поведение браузера и
файлсервера. Такое решение требует отдельной разработки. Многие сервисы в таком случае предлагают начать
процесс загрузки с начала.
</li>
<li><i>Перекодирование</i>. Загрузка завершена! Теперь будем перекодировать файл в подходящие для браузеров и
smartTV форматы. Перекодирование — это длительный и ресурсоёмкий процесс. Этот процесс хорошо
распараллеливается, а значит его можно ускорить за счёт увеличения количества процессоров и ядер на серверах
занимающихся перекодированием. Сейчас, в 2020 году, процесс перекодирования FullHD в 720p <a
href="https://www.guru3d.com/articles-pages/intel-core-i9-9900k-processor-review,14.html" target="_blank">всё
ещё занимает</a> время равное длительности загружаемое видео. Система должна одновременно перекодировать
множество видео в большое количество форматов.
</li>
<li>
<p>
<i>Очередь задач</i> и <i>масштабирование</i>. Для общения частей системы выполняющих разные роли построим
единую шину данных в которую будут добавляться новые задачи. Сервисы способные выполнить задачу будут
забирать её из очереди и заниматься ей. Если очередь растёт быстрее чем задачи успевают обрабатываться —
мы можем <i>моментально масштабировать инфраструктуру</i> путём добавления вычислительных узлов
аналогичных загруженным.
</p><p>
Перекодирование в 144p занимает на порядки меньше времени чем перекодирование в 1080p и если издателю важно
выложить контент как можно скорее — он может быть удовлетворён и таким стартом.
</p>
<p>
Для экономии средств на передаче траффика и ускорения загрузки видео мы должны осуществлять доставку с серверов находящихся как можно ближе к зрителям. Можно построить свой <i>Content Delivery Network</i>, но оптимально будет воспользоваться готовыми. Как правило они дают всё необходимое для загрузки файлов и <i>балансировки нагрузки</i>.
</p>
</li>
</ol>
</p>
<p>
Каждая из описаных выше систем (перекодировка, дозагрузка, очередь задач, CDN, балансировщик) способна занять годы разработки, отладки и внедрения и не гарантирует что на выходе получится работоспособный результат. По этой причине мы настоятельно рекомендуем собрать прототип из наиболее стабильных общедоступных решений. После начального роста можно будет рассмотреть процесс замены дорогих сервисов на своими.
</p>
</Content>
<Note>
Интерактивный калькулятор покажет из каких величин складывается стоимость хранения и передачи видеоконтента. Все поля ввода можно изменять. Результаты расчётов обновляются в реальном времени.
</Note>
<Header>
Хранение и передача видео
</Header>
<SubHeader>
Размера видеофайла
</SubHeader>
<div class={'field'}><label>Разрешение видео {sizeSelector = <PlainSelect value={size} map={(val)=>({w: val.split('x')[0]-0, h: val.split('x')[1]-0})} values={`
</li>
<li>
<p>
<i>Публикация</i> и <i>доставка</i> на устройство пользователя. Видео выложено в публичный доступ. С этого
момента мы <i>отслеживаем геолокацию зрителей</i>.
</p>
<p>
Для экономии средств на передаче траффика и ускорения загрузки видео мы должны осуществлять доставку с
серверов находящихся как можно ближе к зрителям. Можно построить свой <i>Content Delivery Network</i>, но
оптимально будет воспользоваться готовыми. Как правило они дают всё необходимое для загрузки файлов и <i>балансировки
нагрузки</i>.
</p>
</li>
</ol>
</p>
<p>
Каждая из описаных выше систем (перекодировка, дозагрузка, очередь задач, CDN, балансировщик) способна занять
годы разработки, отладки и внедрения и не гарантирует что на выходе получится работоспособный результат. По этой
причине мы настоятельно рекомендуем собрать прототип из наиболее стабильных общедоступных решений. После
начального роста можно будет рассмотреть процесс замены дорогих сервисов на своими.
</p>
</Content>
<Note>
Интерактивный калькулятор покажет из каких величин складывается стоимость хранения и передачи видеоконтента. Все
поля ввода можно изменять. Результаты расчётов обновляются в реальном времени.
</Note>
<Header>
Хранение и передача видео
</Header>
<SubHeader>
Размера видеофайла
</SubHeader>
<div class={'field'}><label>Разрешение видео {sizeSelector =
<PlainSelect value={size} map={( val ) => ( { w: val.split( 'x' )[ 0 ] - 0, h: val.split( 'x' )[ 1 ] - 0 } )}
values={`
256x144
426x240
640x360
......@@ -180,18 +211,18 @@ var VideoCalculator = <div class={'calculator'}>
3840x2160
7680x4320
`}/>}</label></div>
{_=>{
sizeSelector.values.get().forEach(item=> {
s.set( 'use-' + item.id.split( 'x' )[ 1 ], true );
})
}}
<div class={'field'}><label>Используемый кодек <PlainSelect value={codec} map={Number} values={`
{_ => {
sizeSelector.values.get().forEach( item => {
s.set( 'use-' + item.id.split( 'x' )[ 1 ], true );
} )
}}
<div class={'field'}><label>Используемый кодек <PlainSelect value={codec} map={Number} values={`
180: H.264 High quality
>340: H.264 Medium quality
675: H.264 Low quality
`}/></label></div>
<div className={'field'}><label>Кадров в секунду <PlainSelect value={framerate} map={Number} values={`
<div className={'field'}><label>Кадров в секунду <PlainSelect value={framerate} map={Number} values={`
24
30
>60
......@@ -200,7 +231,7 @@ var VideoCalculator = <div class={'calculator'}>
`}/></label></div>
<div className={'field'}><label>Продолжительность серии <PlainSelect value={time} map={Number} values={`
<div className={'field'}><label>Продолжительность серии <PlainSelect value={time} map={Number} values={`
15: 15 минут
>30: 30 минут
60: 1 час
......@@ -210,96 +241,106 @@ var VideoCalculator = <div class={'calculator'}>
`}/></label></div>
{_=>s.sub([size, codec, framerate, time], (size, codec, framerate, time)=>{
var [w,h] = size.split('x');
filesize.set((w*h*3/codec*framerate*time*60 / 1024 / 1024 / 1024).toFixed(2));
})}
{_ => s.sub( [ size, codec, framerate, time ], ( size, codec, framerate, time ) => {
var [ w, h ] = size.split( 'x' );
filesize.set( ( w * h * 3 / codec * framerate * time * 60 / 1024 / 1024 / 1024 ).toFixed( 2 ) );
} )}
<div className={'field'}>Размер видеофайла: {_=>s.sub([filesize],(filesize)=>_(GBFormatter(filesize)))}</div>
<SubHeader>
Интерлейсинг
</SubHeader>
<div className={'field'}>Размер
видеофайла: {_ => s.sub( [ filesize ], ( filesize ) => _( GBFormatter( filesize ) ) )}</div>
<Content>
Технология интерлейсинга или <i>чересстрочная развёртка</i> экономит половину места при хранении и передаче видеофайлов. Вместо полного кадра, пеередаются чётные и нечётные строки поочерёдно. Технологии распространилась из-за физических ограничений накладываемых принципами работы Электронно-Лучевой трубки. Мы тоже можем извлечь выгоду из этого метода, но не рекомендуем.
</Content>
<SubHeader>
Интерлейсинг
</SubHeader>
<img src="https://blog.video.ibm.com/wp-content/uploads/2016/03/interlace-soccer2-1024x394.jpg" style="display: block;margin: auto;"/>
<Content>
Технология интерлейсинга или <i>чересстрочная развёртка</i> экономит половину места при хранении и передаче
видеофайлов. Вместо полного кадра, пеередаются чётные и нечётные строки поочерёдно. Технологии распространилась
из-за физических ограничений накладываемых принципами работы Электронно-Лучевой трубки. Мы тоже можем извлечь
выгоду из этого метода, но не рекомендуем.
</Content>
<img src="https://blog.video.ibm.com/wp-content/uploads/2016/03/interlace-soccer2-1024x394.jpg"
style="display: block;margin: auto;"/>
<div className={'field'}><label><Checkbox value={useInterlacing}/> Использовать интерлэйсинг</label></div>
<div className={'field'}><label><Checkbox value={useInterlacing}/> Использовать интерлэйсинг</label></div>
{_=>s.sub([useInterlacing, filesize], (useInterlacing, filesize)=>{
filesize2.set(useInterlacing ? filesize/2 : filesize);
})}
{_ => s.sub( [ useInterlacing, filesize ], ( useInterlacing, filesize ) => {
filesize2.set( useInterlacing ? filesize / 2 : filesize );
} )}
{_=>s.sub([size, codec, framerate, time, 'filesize3', useInterlacing,
'use-144', 'use-240', 'use-360', 'use-480', 'use-720', 'use-1080', 'use-1440', 'use-2160', 'use-4320'], (size, codec, framerate, time, filesize3, useInterlacing)=>{
var downscales = [],
{_ => s.sub( [ size, codec, framerate, time, 'filesize3', useInterlacing,
'use-144', 'use-240', 'use-360', 'use-480', 'use-720', 'use-1080', 'use-1440', 'use-2160', 'use-4320' ], ( size, codec, framerate, time, filesize3, useInterlacing ) => {
var downscales = [],
vals = sizeSelector.values.get();
for( var i = 0, _i = vals.length; i < _i; i++ ){
var [w,h] = vals[ i ].text.split('x');
for( var i = 0, _i = vals.length; i < _i; i++ ){
var [ w, h ] = vals[ i ].text.split( 'x' );
downscales.push({p: h, size: (w*h*3/codec*framerate*time*60 / 1024 / 1024 / 1024).toFixed(2)});
downscales.push( {
p: h,
size: ( w * h * 3 / codec * framerate * time * 60 / 1024 / 1024 / 1024 ).toFixed( 2 )
} );
if(vals[ i ].id === size){
break;
if( vals[ i ].id === size ){
break;
}
}
}
if(downscales.length>1){
if( downscales.length > 1 ){
s.set('filesize3', (downscales.map(item=>s.get('use-'+item.p)?item.size-0:0).reduce((a,b)=>a+b) * (useInterlacing ? 0.5 : 1)).toFixed(2));
s.set( 'filesize3', ( downscales.map( item => s.get( 'use-' + item.p ) ? item.size - 0 : 0 ).reduce( ( a, b ) => a + b ) * ( useInterlacing ? 0.5 : 1 ) ).toFixed( 2 ) );
_(<div><SubHeader hidden={true}>Объём занимаемый различными форматами</SubHeader>
<Content>Для хранения всех разрешений видео требуется в среднем в два раза больше места чем занимает видео в самом высоком разрешении. Форматы которые не нравятся можно выключить.</Content>
_( <div><SubHeader hidden={true}>Объём занимаемый различными форматами</SubHeader>
<Content>Для хранения всех разрешений видео требуется в среднем в два раза больше места чем занимает видео в
самом высоком разрешении. Форматы которые не нравятся можно выключить.</Content>
<table>
<table>
<tr class={'thead'}>
<td></td>
<td>Формат</td>
<td>Объём</td>
</tr>
{downscales.map(item=><tr>
<td><Checkbox value={s.bind('use-'+item.p)}/></td>
{downscales.map( item => <tr>
<td><Checkbox value={s.bind( 'use-' + item.p )}/></td>
<td>{item.p}p</td>
<td>{GBFormatter(item.size * (useInterlacing ? 0.5 : 1))}</td>
</tr>)}
<td>{GBFormatter( item.size * ( useInterlacing ? 0.5 : 1 ) )}</td>
</tr> )}
<tr class={'summary'}>
<td colSpan={3}>
Всего <b>{GBFormatter(s.get('filesize3'))}</b> на серию
Всего <b>{GBFormatter( s.get( 'filesize3' ) )}</b> на серию
</td>
</tr>
</table>
</div>);
console.log(s.get('filesize3'));
}else{
s.set('filesize3', (downscales[0].size * (useInterlacing ? 0.5 : 1)).toFixed(2));
_(<div>Объём необходимый для хранения одного выпуска — {GBFormatter(s.get('filesize3'))}</div>);
}
})}
<SubHeader>Выбор типа расчёта</SubHeader>
<Content>
<p>Видеоплатформы делятся на два типа — в одни контент загружают издатели (HBO, Netflix), а в другие — пользователи (YouTube, Vimeo, Pornhub). В нашем случае будет разработана смесь обоих подходов.</p>
</Content>
<Field label='Сериальный расчёт'><Radio value='tv-show' group={calculation}/></Field>
<Field label='Видеоплатформенный расчёт'><Radio value='ott' group={calculation}/></Field>
{_=>{
calculation.sub(val=>{
if(val === 'tv-show'){
_(<div>
<Header hidden={true}>Сериальный расчёт</Header>
<SubHeader hidden={true}>Количество и частота выходящих серий</SubHeader>
<Content>При сериальном расчёте занимаемый объём данных можно легко прогнозировать на полгода вперёд.</Content>
<div className={'field'}><label>Одновременно выходят <PlainSelect value={simult} map={Number} values={`
</table>
</div> );
console.log( s.get( 'filesize3' ) );
}else{
s.set( 'filesize3', ( downscales[ 0 ].size * ( useInterlacing ? 0.5 : 1 ) ).toFixed( 2 ) );
_( <div>Объём необходимый для хранения одного выпуска — {GBFormatter( s.get( 'filesize3' ) )}</div> );
}
} )}
<SubHeader>Выбор типа расчёта</SubHeader>
<Content>
<p>Видеоплатформы делятся на два типа — в одни контент загружают издатели (HBO, Netflix), а в другие —
пользователи (YouTube, Vimeo, Pornhub). В нашем случае будет разработана смесь обоих подходов.</p>
</Content>
<Field label='Сериальный расчёт'><Radio value='tv-show' group={calculation}/></Field>
<Field label='Видеоплатформенный расчёт'><Radio value='ott' group={calculation}/></Field>
{_ => {
calculation.sub( val => {
if( val === 'tv-show' ){
_( <div>
<Header hidden={true}>Сериальный расчёт</Header>
<SubHeader hidden={true}>Количество и частота выходящих серий</SubHeader>
<Content>При сериальном расчёте занимаемый объём данных можно легко прогнозировать на полгода
вперёд.</Content>
<div className={'field'}><label>Одновременно выходят <PlainSelect value={simult} map={Number} values={`
1: Одно шоу
2: Два шоу
>5: Пять шоу
......@@ -308,22 +349,25 @@ var VideoCalculator = <div class={'calculator'}>
100: Шоу, челленджи, платформа популярна, но не на весь мир
`}/></label></div>
<div class={'field'}><label>Новая серия выходит <PlainSelect value={perDay} map={Number} values={`
<div class={'field'}><label>Новая серия выходит <PlainSelect value={perDay} map={Number} values={`
1: Каждый день
>7: Каждую неделю
30: Каждый месяц
28: Сезон из 13 серий USA\Canada standard
16.5: Сезон из 22 серий USA\Canada standard
`}/></label></div>
</div>);
}else if(val === 'ott'){
_(<div>
<Header hidden={true}>Расчёт для видеоплатформы</Header>
<Info>На основе <a href="https://merchdope.com/youtube-stats/#:~:text=The%20total%20number%20of%20people,on%20Youtube%20every%20single%20day.&text=In%20an%20average%20month%2C%208,49%20year%2Dolds%20watch%20YouTube" target='_blank'>статистики по ютубу за 2020 год</a> и
доступных публичных данных <a href="https://www.similarweb.com/top-websites" target="_blank">от similarweb</a> были выстроены примерные оценки нагрузки на хранилища файлов для различных видеосервисов.
</Info>
<Field label={'Популярность'}>
<PlainSelect value={popularity} map={Number} values={`
</div> );
}else if( val === 'ott' ){
_( <div>
<Header hidden={true}>Расчёт для видеоплатформы</Header>
<Info>На основе <a
href="https://merchdope.com/youtube-stats/#:~:text=The%20total%20number%20of%20people,on%20Youtube%20every%20single%20day.&text=In%20an%20average%20month%2C%208,49%20year%2Dolds%20watch%20YouTube"
target='_blank'>статистики по ютубу за 2020 год</a> и
доступных публичных данных <a href="https://www.similarweb.com/top-websites" target="_blank">от
similarweb</a> были выстроены примерные оценки нагрузки на хранилища файлов для различных видеосервисов.
</Info>
<Field label={'Популярность'}>
<PlainSelect value={popularity} map={Number} values={`
300: YouTube. 300 hours of video uploaded every minute. Traffic: 33.39B
25: Pornhub. 25 hours of video uploaded every minute (scaled). Traffic: 2.85B
1.19: Vimeo. 1.19 hours of video uploaded every minute (scaled). Traffic: 133.7M
......@@ -331,23 +375,22 @@ var VideoCalculator = <div class={'calculator'}>
0.0125: Traffic: 1M. Good local OTT platform. 1 minute of video per minute
>0.002: Netflix 90000 minutes of original video in 2018 year
`}/>
</Field>
</div>);
}
})
}}
</Field>
</div> );
}
} )
}}
<SubHeader>Расчёт требуемого для хранения данных места</SubHeader>
<SubHeader>Расчёт требуемого для хранения данных места</SubHeader>
<p>
Для упрощения — будем считать что количество контента и зрителей нарастает линейно.
</p>
<p>
Для упрощения — будем считать что количество контента и зрителей нарастает линейно.
</p>
<Field label={'Временной промежуток'}>
<PlainSelect value={duration} map={Number} values={`
<Field label={'Временной промежуток'}>
<PlainSelect value={duration} map={Number} values={`
1:1 month
12: 1 year
>24: 2 year
......@@ -355,59 +398,66 @@ var VideoCalculator = <div class={'calculator'}>
48: 4 year
60: 5 year
`}/>
</Field>
<p>
</p>
</Field>
<Content>
<p>
На первых этапах (демонстрация, первые 6 месяцев) можно раздавать контент с одного сервера.
</p>
<p>
Далее можно взять несколько серверов в оптимальных местах (европа, северная и южная америка, азия, западная и восточная Россия).
</p>
<p>
При серьёзном масштабировании OTT платформы нужно будет обязательно выбрать подходящее CDN решение или построить своё.
</p>
<p>
CDN позволяет как экономить международный траффик между клиентом и сервером раздающим видео, так и снять нагрузку с ключевых элементов инфраструктуры.
</p>
<p>
Много пользователей из Америки одновременно смотрящих премьеру передачи с европейского сервера способны существенно забить трансокеанические кабели передачи данных, или, сделать всю затею нерентабельной.
</p>
</Content>
<Field label={'Датацентры'} width={250}>
<PlainSelect value={dc} map={Number} values={`
<Content>
<p>
На первых этапах (демонстрация, первые 6 месяцев) можно раздавать контент с одного сервера.
</p>
<p>
Далее можно взять несколько серверов в оптимальных местах (европа, северная и южная америка, азия, западная и
восточная Россия).
</p>
<p>
При серьёзном масштабировании OTT платформы нужно будет обязательно выбрать подходящее CDN решение или построить
своё.
</p>
<p>
CDN позволяет как экономить международный траффик между клиентом и сервером раздающим видео, так и снять
нагрузку с ключевых элементов инфраструктуры.
</p>
<p>
Много пользователей из Америки одновременно смотрящих премьеру передачи с европейского сервера способны
существенно забить трансокеанические кабели передачи данных, или, сделать всю затею нерентабельной.
</p>
</Content>
<Field label={'Датацентры'} width={250}>
<PlainSelect value={dc} map={Number} values={`
0: Не нужен
>2: Разворачиваем 3 своих датацентров
5: Разворачиваем 6 своих датацентров
`}/>
</Field>
</Field>
<Field label={'Хранение бэкапов'} width={250}>
<PlainSelect value={backups} map={Number} values={`
<Field label={'Хранение бэкапов'} width={250}>
<PlainSelect value={backups} map={Number} values={`
0: Мы не храним бэкапов!
1: RAID 10. Дублирование всех записанных данных
>0.3: RAID 3. Отдельный диск для битов чётности. Минимум 4 диска
2: Кворум из трёх машин. Обычно используется для финансовых БД
`}/>
</Field>
<Content>
<p>
На больших объёмах основными потребителями ежемесячного бюджета становятся сервера и оплата траффика. Траффик напрямую зависит от количества пользователей платформы.
</p>
<p>
Пользователь Netfilx в среднем проводит 2 часа в день за просмотром контента Netflix. В дальнейших вычислениях потребляемого траффика будем считать что пользователь смотрит в день один час видео в самом популярном формате 720p.
</p>
</Content>
<Field label={'Пользователей в месяц в начале'} width={350}>
<PlainSelect value={viewersFrom} map={Number} values={`
</Field>
<Content>
<p>
На больших объёмах основными потребителями ежемесячного бюджета становятся сервера и оплата траффика. Траффик
напрямую зависит от количества пользователей платформы.
</p>
<p>
Пользователь Netfilx в среднем проводит 2 часа в день за просмотром контента Netflix. В дальнейших вычислениях
потребляемого траффика будем считать что пользователь смотрит в день один час видео в самом популярном формате
720p.
</p>
</Content>
<Field label={'Пользователей в месяц в начале'} width={350}>
<PlainSelect value={viewersFrom} map={Number} values={`
0: Нет пользователей
>20: 20 — Тестирование платформы
500: 500 — Ранний запуск
......@@ -416,10 +466,10 @@ var VideoCalculator = <div class={'calculator'}>
180000000: 180 000 000 — Аудитория Netflix
`}/>
</Field>
</Field>
<Field label={'Пользователей в месяц в конце'} width={350}>
<PlainSelect value={viewersTo} map={Number} values={`
<Field label={'Пользователей в месяц в конце'} width={350}>
<PlainSelect value={viewersTo} map={Number} values={`
0: Нет пользователей
20: 20 — Тестирование платформы
500: 500 — Ранний запуск
......@@ -428,114 +478,117 @@ var VideoCalculator = <div class={'calculator'}>
180000000: 180 000 000 — Аудитория Netflix
`}/>
</Field>
<SubHeader>Результат для AWS</SubHeader>
<p>Данные в таблице отражают только объём и траффик потребляемый для хранения\передачи видео с разбивкой на месяцы. Внутреннее потребление на хранение оригиналов видеофайлов может оказаться даже выше расчётного если к проекту подключатся киностудии и начнут загружать оригинальный или Blu Ray формат</p>
<p>Цена стоимости траффика <a href="https://aws.amazon.com/mediastore/pricing/">вычисляется по данным с официального сайта AWS</a>.</p>
{_=>{
s.sub([
duration,
backups,
calculation,
simult, perDay,
popularity,
time,
filesize3, dc,
viewersFrom,
viewersTo, codec, framerate
], (
duration,
backups,
calculation,
simult, perDay,
popularity, time,
filesize3, dc,
viewersFrom,
viewersTo, codec, framerate
)=>{
var origDate = +new Date(),
</Field>
<SubHeader>Результат для AWS</SubHeader>
<p>Данные в таблице отражают только объём и траффик потребляемый для хранения\передачи видео с разбивкой на месяцы.
Внутреннее потребление на хранение оригиналов видеофайлов может оказаться даже выше расчётного если к проекту
подключатся киностудии и начнут загружать оригинальный или Blu Ray формат</p>
<p>Цена стоимости траффика <a href="https://aws.amazon.com/mediastore/pricing/">вычисляется по данным с официального
сайта AWS</a>.</p>
{_ => {
s.sub( [
duration,
backups,
calculation,
simult, perDay,
popularity,
time,
filesize3, dc,
viewersFrom,
viewersTo, codec, framerate
], (
duration,
backups,
calculation,
simult, perDay,
popularity, time,
filesize3, dc,
viewersFrom,
viewersTo, codec, framerate
) => {
var origDate = +new Date(),
d = origDate,
lastDate = d,
slices = [],
stored = 0;
viewersTo|=0;
viewersFrom|=0;
var deltaViewers = (viewersTo-viewersFrom)/(duration-1);
for(var i = 0; i < duration; i++){
d = new Date(d);
d.setMonth(d.getMonth()+1);
var slice = {date: d},
viewersTo |= 0;
viewersFrom |= 0;
var deltaViewers = ( viewersTo - viewersFrom ) / ( duration - 1 );
for( var i = 0; i < duration; i++ ){
d = new Date( d );
d.setMonth( d.getMonth() + 1 );
var slice = { date: d },
newData;
if(calculation === 'ott'){
newData =
(filesize3/time)* // minute size
(((+d)-(+lastDate))/1000/60)* // minutes in month
popularity*60;
}else{
newData = filesize3*simult*(((+d)-(+lastDate))/1000/60/60/24 / perDay)
if( calculation === 'ott' ){
newData =
( filesize3 / time ) * // minute size
( ( ( +d ) - ( +lastDate ) ) / 1000 / 60 ) * // minutes in month
popularity * 60;
}else{
newData = filesize3 * simult * ( ( ( +d ) - ( +lastDate ) ) / 1000 / 60 / 60 / 24 / perDay )
}
newData += newData * backups;
newData += newData * dc;
stored += newData;
slice.stored = stored;
slice.newData = newData;
slice.users = ( viewersFrom + deltaViewers * i ) | 0;
slice.traffic = slice.users * ( ( ( +d ) - ( +lastDate ) ) / 24 / 1000 ) * ( 1280 * 720 * 3 / codec * framerate ) / 1024 / 1024 / 1024;
lastDate = d;
slices.push( slice );
}
newData+=newData*backups;
newData+=newData*dc;
stored += newData;
slice.stored = stored;
slice.newData = newData;
slice.users = (viewersFrom + deltaViewers*i)|0;
slice.traffic = slice.users * (((+d) - (+lastDate))/24/1000)*(1280*720*3/codec*framerate)/1024/1024/1024;
lastDate = d;
slices.push(slice);
}
_(<table>
<tr className={'thead'}>
<td>Дата</td>
<td>Объём</td>
<td>AWS Storage</td>
<td>Пользователей</td>
<td>Traffic Amount</td>
<td>AWS Traffic Price</td>
</tr>
{slices.map(slice=><tr>
<td>{monthsShort[slice.date.getMonth()] +' '+ slice.date.getFullYear()}</td>
<td>{GBFormatter(slice.stored)}</td>
<td>${moneyFormatter((slice.stored*AWSStorePrice(slice.stored))|0)}/month</td>
<td>{moneyFormatter(slice.users)}</td>
<td>{GBFormatter(slice.traffic)}/month</td>
<td>${moneyFormatter((AWSTransferPrice(slice.traffic))|0)}/month</td>
</tr>)}
</table>);
});
}}
<Content>
<p>Netflix использует для перекодирования и хранения видео Amazon AWS, а для раздачи — сразу несколько CDN провайдеров</p>
<p><img src="amazon-structure.png" alt="amazon structure"/></p>
<a href="https://medium.com/refraction-tech-everything/how-netflix-works-the-hugely-simplified-complex-stuff-that-happens-every-time-you-hit-play-3a40c9be254b" target="_blank">
Здесь можно почитать о том как строится инфраструктура подобная Netflix
</a>
</Content>
USE: https://www.wowza.com/products/streaming-engine/deployment-options
<h1>TODO: Дополнить варианты реализации чтоб был выбор из трёх стульев</h1>
</div>;
_( <table>
<tr className={'thead'}>
<td>Дата</td>
<td>Объём</td>
<td>AWS Storage</td>
<td>Пользователей</td>
<td>Traffic Amount</td>
<td>AWS Traffic Price</td>
</tr>
{slices.map( slice => <tr>
<td>{monthsShort[ slice.date.getMonth() ] + ' ' + slice.date.getFullYear()}</td>
<td>{GBFormatter( slice.stored )}</td>
<td>${moneyFormatter( ( slice.stored * AWSStorePrice( slice.stored ) ) | 0 )}/month</td>
<td>{moneyFormatter( slice.users )}</td>
<td>{GBFormatter( slice.traffic )}/month</td>
<td>${moneyFormatter( ( AWSTransferPrice( slice.traffic ) ) | 0 )}/month</td>
</tr> )}
</table> );
} );
}}
<Content>
<p>Netflix использует для перекодирования и хранения видео Amazon AWS, а для раздачи — сразу несколько CDN
провайдеров</p>
<p><img src="amazon-structure.png" alt="amazon structure"/></p>
<a
href="https://medium.com/refraction-tech-everything/how-netflix-works-the-hugely-simplified-complex-stuff-that-happens-every-time-you-hit-play-3a40c9be254b"
target="_blank">
Здесь можно почитать о том как строится инфраструктура подобная Netflix
</a>
</Content>
USE: https://www.wowza.com/products/streaming-engine/deployment-options
<h1>TODO: Дополнить варианты реализации чтоб был выбор из трёх стульев</h1>
</div>;
return dom;
};
export {VideoCalculator};
\ No newline at end of file
......@@ -17,4 +17,11 @@ select, input {
.h1 {
margin: 32px 0 0 0;
font-size: 24px;
}
.h3 {
margin: 0 0 8px 16px;
font-size: 16px;
a {
text-decoration: none;
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment