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>;
......
......@@ -248,3 +248,16 @@
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,47 +119,76 @@ var GBFormatter = function(gb) {
};
var sizeSelector;
var VideoCalculator = <div class={'calculator'}>
var VideoCalculator = function(){
var dom = <div class={'calculator'}>
<Header>
Видеосервис
</Header>
<Content>
<p>
Хранение и передача видео в реальном времени — сложная и ресурсоёмкая задача с точки зрения инфраструктуры.
Хранение и передача видео в реальном времени — сложная и ресурсоёмкая задача требующая слаженной работы большое количество инфраструктурных ущлов.
</p>
<SubHeader>Жизненный цикл видеофайла</SubHeader>
<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>Выбираем видео в списке файлов и <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> путём добавления вычислительных узлов аналогичных загруженным.
<i>Очередь задач</i> и <i>масштабирование</i>. Для общения частей системы выполняющих разные роли построим
единую шину данных в которую будут добавляться новые задачи. Сервисы способные выполнить задачу будут
забирать её из очереди и заниматься ей. Если очередь растёт быстрее чем задачи успевают обрабатываться —
мы можем <i>моментально масштабировать инфраструктуру</i> путём добавления вычислительных узлов
аналогичных загруженным.
</p><p>
Перекодирование в 144p занимает на порядки меньше времени чем перекодирование в 1080p и если издателю важно выложить контент как можно скорее — он может быть удовлетворён и таким стартом.
Перекодирование в 144p занимает на порядки меньше времени чем перекодирование в 1080p и если издателю важно
выложить контент как можно скорее — он может быть удовлетворён и таким стартом.
</p>
</li>
<li>
<p>
<i>Публикация</i> и <i>доставка</i> на устройство пользователя. Видео выложено в публичный доступ. С этого момента мы <i>отслеживаем геолокацию зрителей</i>.
<i>Публикация</i> и <i>доставка</i> на устройство пользователя. Видео выложено в публичный доступ. С этого
момента мы <i>отслеживаем геолокацию зрителей</i>.
</p>
<p>
Для экономии средств на передаче траффика и ускорения загрузки видео мы должны осуществлять доставку с серверов находящихся как можно ближе к зрителям. Можно построить свой <i>Content Delivery Network</i>, но оптимально будет воспользоваться готовыми. Как правило они дают всё необходимое для загрузки файлов и <i>балансировки нагрузки</i>.
Для экономии средств на передаче траффика и ускорения загрузки видео мы должны осуществлять доставку с
серверов находящихся как можно ближе к зрителям. Можно построить свой <i>Content Delivery Network</i>, но
оптимально будет воспользоваться готовыми. Как правило они дают всё необходимое для загрузки файлов и <i>балансировки
нагрузки</i>.
</p>
</li>
</ol>
</p>
<p>
Каждая из описаных выше систем (перекодировка, дозагрузка, очередь задач, CDN, балансировщик) способна занять годы разработки, отладки и внедрения и не гарантирует что на выходе получится работоспособный результат. По этой причине мы настоятельно рекомендуем собрать прототип из наиболее стабильных общедоступных решений. После начального роста можно будет рассмотреть процесс замены дорогих сервисов на своими.
Каждая из описаных выше систем (перекодировка, дозагрузка, очередь задач, CDN, балансировщик) способна занять
годы разработки, отладки и внедрения и не гарантирует что на выходе получится работоспособный результат. По этой
причине мы настоятельно рекомендуем собрать прототип из наиболее стабильных общедоступных решений. После
начального роста можно будет рассмотреть процесс замены дорогих сервисов на своими.
</p>
</Content>
<Note>
Интерактивный калькулятор покажет из каких величин складывается стоимость хранения и передачи видеоконтента. Все поля ввода можно изменять. Результаты расчётов обновляются в реальном времени.
Интерактивный калькулятор покажет из каких величин складывается стоимость хранения и передачи видеоконтента. Все
поля ввода можно изменять. Результаты расчётов обновляются в реальном времени.
</Note>
<Header>
......@@ -169,7 +198,9 @@ var VideoCalculator = <div class={'calculator'}>
Размера видеофайла
</SubHeader>
<div class={'field'}><label>Разрешение видео {sizeSelector = <PlainSelect value={size} map={(val)=>({w: val.split('x')[0]-0, h: val.split('x')[1]-0})} values={`
<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,10 +211,10 @@ var VideoCalculator = <div class={'calculator'}>
3840x2160
7680x4320
`}/>}</label></div>
{_=>{
sizeSelector.values.get().forEach(item=> {
{_ => {
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
......@@ -210,52 +241,60 @@ 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>
<div className={'field'}>Размер
видеофайла: {_ => s.sub( [ filesize ], ( filesize ) => _( GBFormatter( filesize ) ) )}</div>
<SubHeader>
Интерлейсинг
</SubHeader>
<Content>
Технология интерлейсинга или <i>чересстрочная развёртка</i> экономит половину места при хранении и передаче видеофайлов. Вместо полного кадра, пеередаются чётные и нечётные строки поочерёдно. Технологии распространилась из-за физических ограничений накладываемых принципами работы Электронно-Лучевой трубки. Мы тоже можем извлечь выгоду из этого метода, но не рекомендуем.
Технология интерлейсинга или <i>чересстрочная развёртка</i> экономит половину места при хранении и передаче
видеофайлов. Вместо полного кадра, пеередаются чётные и нечётные строки поочерёдно. Технологии распространилась
из-за физических ограничений накладываемых принципами работы Электронно-Лучевой трубки. Мы тоже можем извлечь
выгоду из этого метода, но не рекомендуем.
</Content>
<img src="https://blog.video.ibm.com/wp-content/uploads/2016/03/interlace-soccer2-1024x394.jpg" style="display: block;margin: auto;"/>
<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>
{_=>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)=>{
{_ => 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');
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){
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>
<tr class={'thead'}>
......@@ -263,42 +302,44 @@ var VideoCalculator = <div class={'calculator'}>
<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>);
</div> );
console.log(s.get('filesize3'));
console.log( s.get( 'filesize3' ) );
}else{
s.set('filesize3', (downscales[0].size * (useInterlacing ? 0.5 : 1)).toFixed(2));
_(<div>Объём необходимый для хранения одного выпуска — {GBFormatter(s.get('filesize3'))}</div>);
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>
<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>
{_ => {
calculation.sub( val => {
if( val === 'tv-show' ){
_( <div>
<Header hidden={true}>Сериальный расчёт</Header>
<SubHeader hidden={true}>Количество и частота выходящих серий</SubHeader>
<Content>При сериальном расчёте занимаемый объём данных можно легко прогнозировать на полгода вперёд.</Content>
<Content>При сериальном расчёте занимаемый объём данных можно легко прогнозировать на полгода
вперёд.</Content>
<div className={'field'}><label>Одновременно выходят <PlainSelect value={simult} map={Number} values={`
1: Одно шоу
2: Два шоу
......@@ -315,12 +356,15 @@ var VideoCalculator = <div class={'calculator'}>
28: Сезон из 13 серий USA\Canada standard
16.5: Сезон из 22 серий USA\Canada standard
`}/></label></div>
</div>);
}else if(val === 'ott'){
_(<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>На основе <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={`
......@@ -333,13 +377,12 @@ var VideoCalculator = <div class={'calculator'}>
`}/>
</Field>
</div>);
</div> );
}
})
} )
}}
<SubHeader>Расчёт требуемого для хранения данных места</SubHeader>
<p>
......@@ -366,16 +409,20 @@ var VideoCalculator = <div class={'calculator'}>
На первых этапах (демонстрация, первые 6 месяцев) можно раздавать контент с одного сервера.
</p>
<p>
Далее можно взять несколько серверов в оптимальных местах (европа, северная и южная америка, азия, западная и восточная Россия).
Далее можно взять несколько серверов в оптимальных местах (европа, северная и южная америка, азия, западная и
восточная Россия).
</p>
<p>
При серьёзном масштабировании OTT платформы нужно будет обязательно выбрать подходящее CDN решение или построить своё.
При серьёзном масштабировании OTT платформы нужно будет обязательно выбрать подходящее CDN решение или построить
своё.
</p>
<p>
CDN позволяет как экономить международный траффик между клиентом и сервером раздающим видео, так и снять нагрузку с ключевых элементов инфраструктуры.
CDN позволяет как экономить международный траффик между клиентом и сервером раздающим видео, так и снять
нагрузку с ключевых элементов инфраструктуры.
</p>
<p>
Много пользователей из Америки одновременно смотрящих премьеру передачи с европейского сервера способны существенно забить трансокеанические кабели передачи данных, или, сделать всю затею нерентабельной.
Много пользователей из Америки одновременно смотрящих премьеру передачи с европейского сервера способны
существенно забить трансокеанические кабели передачи данных, или, сделать всю затею нерентабельной.
</p>
</Content>
......@@ -399,10 +446,13 @@ var VideoCalculator = <div class={'calculator'}>
<Content>
<p>
На больших объёмах основными потребителями ежемесячного бюджета становятся сервера и оплата траффика. Траффик напрямую зависит от количества пользователей платформы.
На больших объёмах основными потребителями ежемесячного бюджета становятся сервера и оплата траффика. Траффик
напрямую зависит от количества пользователей платформы.
</p>
<p>
Пользователь Netfilx в среднем проводит 2 часа в день за просмотром контента Netflix. В дальнейших вычислениях потребляемого траффика будем считать что пользователь смотрит в день один час видео в самом популярном формате 720p.
Пользователь Netfilx в среднем проводит 2 часа в день за просмотром контента Netflix. В дальнейших вычислениях
потребляемого траффика будем считать что пользователь смотрит в день один час видео в самом популярном формате
720p.
</p>
</Content>
......@@ -431,12 +481,14 @@ var VideoCalculator = <div class={'calculator'}>
</Field>
<SubHeader>Результат для AWS</SubHeader>
<p>Данные в таблице отражают только объём и траффик потребляемый для хранения\передачи видео с разбивкой на месяцы. Внутреннее потребление на хранение оригиналов видеофайлов может оказаться даже выше расчётного если к проекту подключатся киностудии и начнут загружать оригинальный или Blu Ray формат</p>
<p>Цена стоимости траффика <a href="https://aws.amazon.com/mediastore/pricing/">вычисляется по данным с официального сайта AWS</a>.</p>
{_=>{
s.sub([
<p>Данные в таблице отражают только объём и траффик потребляемый для хранения\передачи видео с разбивкой на месяцы.
Внутреннее потребление на хранение оригиналов видеофайлов может оказаться даже выше расчётного если к проекту
подключатся киностудии и начнут загружать оригинальный или Blu Ray формат</p>
<p>Цена стоимости траффика <a href="https://aws.amazon.com/mediastore/pricing/">вычисляется по данным с официального
сайта AWS</a>.</p>
{_ => {
s.sub( [
duration,
......@@ -450,54 +502,52 @@ var VideoCalculator = <div class={'calculator'}>
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'){
if( calculation === 'ott' ){
newData =
(filesize3/time)* // minute size
(((+d)-(+lastDate))/1000/60)* // minutes in month
popularity*60;
( 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 = filesize3 * simult * ( ( ( +d ) - ( +lastDate ) ) / 1000 / 60 / 60 / 24 / perDay )
}
newData+=newData*backups;
newData+=newData*dc;
newData += newData * backups;
newData += newData * dc;
stored += newData;
slice.stored = stored;
slice.newData = newData;
slice.users = (viewersFrom + deltaViewers*i)|0;
slice.users = ( viewersFrom + deltaViewers * i ) | 0;
slice.traffic = slice.users * (((+d) - (+lastDate))/24/1000)*(1280*720*3/codec*framerate)/1024/1024/1024;
slice.traffic = slice.users * ( ( ( +d ) - ( +lastDate ) ) / 24 / 1000 ) * ( 1280 * 720 * 3 / codec * framerate ) / 1024 / 1024 / 1024;
lastDate = d;
slices.push(slice);
slices.push( slice );
}
_(<table>
_( <table>
<tr className={'thead'}>
<td>Дата</td>
<td>Объём</td>
......@@ -507,26 +557,28 @@ var VideoCalculator = <div class={'calculator'}>
<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>);
});
{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>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">
<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>
......@@ -536,6 +588,7 @@ var VideoCalculator = <div class={'calculator'}>
<h1>TODO: Дополнить варианты реализации чтоб был выбор из трёх стульев</h1>
</div>;
</div>;
return dom;
};
export {VideoCalculator};
\ No newline at end of file
......@@ -18,3 +18,10 @@ select, input {
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