программирование
Удаление умершей ноды в кластере «Оракла»
Небольшой видеоурок по удалению умершей ноды из кластера «Оракла». В одном месте там требуется, чтобы на машине был своп не менее 500МБ, у нас не было (на машине 96ГБ памяти, зачем ей своп), мы его просто создали на диске в файле.
Как поставить Кобол на Мак
Пусть здесь полежит, чтобы я потом не искал.
Как поставить Кобол на Мак (при помощи «брю»):
curl http://www.sim-basis.de/open-cobol-1.1.tar.gz
tar xvfz open-cobol-1.1.tar.gz
cd open-cobol-1.1
brew install gmp
brew install berkeley-db
./configure --prefix=/usr/local/Cellar/open-cobol/1.1
make && make install
brew link open-cobolБудущее формата CSS image

Мои эксперименты с картинками на чистом CSS внезапно дали следующий результат. Я взял для экспериментов логотип студии Лебедева в первом попавшемся цвете и перегнал его в CSS image при помощи своего конвертора, используя все префиксы браузеров: «webkit», «moz», «o» и «ms» («Конкерор» включать не стал, доля этого браузера исчезающе мала).
Оказалось, что если сжать получившийся файл (браузеры же понимают сжатый CSS), то получившийся файл будет размером меньше, чем та же картинка в формате PNG: 2,5КБ против 6,7. Файл с лого Студии я пропускал через онлайновый оптимизатор PNG (punypng), так что он вряд ли неоптимален.
Чем объяснить такой разврыв между форматом, предназначенном для эффективного сжатия графики, и неэффективным текстовым, с огромной избыточностью, сжатым обычным гзипом я не знаю.
Добавлено позднее: в комментариях картинку «утоптали» в 448 байт. Непонятно почему у меня такой плохой PNG получился.
Картинка на чистом CSS-2

Я тут всё это время думал как дёшево улучшить свой конвертор картинок в чистый CSS и придумал.
Выкинул 89 79. Это уже неплохо, в сжатом (gzip) виде этот код занимает 19КБ, что всего в два с половиной раза больше, чем исходное изображение в PNG. Результат можно сильно улучшить, если отказаться от повтора цвета, но изображения с мелкими деталями (с текстом, например) пострадают.
Кстати говоря, логотип студии Лебедева при конвертации из PNG (6,7КБ) уменьшился до 1,6КБ 1,3КБ. Это уже реальная экономия, если бы не префиксы браузеров.
Отказ от радиального градиента в пользу линейного позволил мне кодировать целую строку изображения одной конструкцией, что, в свою очередь, позволило сильно экономить на повторяющихся байтах (кстати, можно попробовать группировать пиксели по вертикали и горизонтали, а потом выбирать лучший вариант).
Кстати этот раз «Опера», увы, не смогла показать картинку, так как, похоже, не поддерживает позиционирование для линейных градиентов.
Добавлено позднее: сделал оптимизацию, теперь конвертор делает два прогона: один просматривает пиксели по горизонтали, второй по вертикали. Выбирается тот, который даёт наименьший результат.
Ключи запуска те же.
<?
// ffccbb → fcb
// ff0000 → red
// abcdef → abcdef
function Bo_shortcolor($color) {
static $shortnames = array(
"c0c0c0" => "silver",
"808080" => "gray",
"800000" => "maroon",
"ff0000" => "red",
"800080" => "purple",
"008000" => "green",
"808000" => "olive",
"000080" => "navy",
"008080" => "teal",
"f0ffff" => "azure",
"f5f5dc" => "beige",
"ffe4c4" => "bisque",
"a52a2a" => "brown",
"ff7f50" => "coral",
"ffd700" => "gold",
"808080" => "gray",
"008000" => "green",
"808080" => "grey",
"4b0082" => "indigo",
"fffff0" => "ivory",
"f0e68c" => "khaki",
"faf0e6" => "linen",
"800000" => "maroon",
"000080" => "navy",
"808000" => "olive",
"ffa500" => "orange",
"da70d6" => "orchid",
"cd853f" => "peru",
"ffc0cb" => "pink",
"dda0dd" => "plum",
"800080" => "purple",
"ff0000" => "red",
"fa8072" => "salmon",
"a0522d" => "sienna",
"c0c0c0" => "silver",
"fffafa" => "snow",
"d2b48c" => "tan",
"008080" => "teal",
"ff6347" => "tomato",
"ee82ee" => "violet",
"f5deb3" => "wheat",
);
$c = strtolower($color);
if (isset($shortnames[$c])) {
return $shortnames[$c];
}
if ($c[0] == $c[1] && $c[2] == $c[3] && $c[4] == $c[5]) {
return '#' . $c[1] . $c[3] . $c[5];
}
return '#' . $c;
}
class Bo {
public function __get($num) {
return $num ? $num . 'px' : 0;
}
}
function Bo_convert_helper($im, $lim1, $lim2, $getpix, $dotmask, $z) {
$end = $lim1 - 1;
$dots = array();
for ($o1 = 0; $o1<$lim2; $o1++) {
$coll = array();
$prev = null;
$start = 0;
for ($o2 = 0; $o2<$lim1; $o2++) {
$pixel = $getpix($im, $o2, $o1);
if ($prev !== null && $prev !== $pixel || $o2 == $end) {
if ($prev !== null) {
$color = Bo_shortcolor(vsprintf('%02x%02x%02x', $prev));
$coll[] = "$color {$z->$start},$color {$z->$o2}";
}
if ($o2 == $end && $prev != $pixel) {
$color = Bo_shortcolor(vsprintf('%02x%02x%02x', $pixel));
$cell[] = "$color {$z->$o2}";
}
$start = $o2;
}
$prev = $pixel;
}
$dots[] = sprintf($dotmask, implode(',', $coll), $z->$o1);
}
return $dots;
}
function Bo_convert($filename, $prefix = '') {
$z = new Bo();
if (!file_exists($filename) && !is_readable($filename)) {
throw new Exception('File not found.');
}
$types = array(
IMAGETYPE_JPG => 'jpeg',
IMAGETYPE_PNG => 'png',
IMAGETYPE_GIF => 'gif',
);
$imagedata = @getimagesize($filename);
if (!is_array($imagedata) || !isset($imagedata[2]) || !isset($types[$imagedata[2]])) {
throw new Exception('Unknown image format');
}
$im = call_user_func("imagecreatefrom" . $types[$imagedata[2]], $filename);
$sw = $w = imagesx($im);
$sh = $h = imagesy($im);
$hfunc = create_function('$im, $x, $y', 'return imagecolorsforindex($im, imagecolorat($im, $x, $y));');
$hmask = "{$prefix}linear-gradient(0,%s) 0 %s";
$hdots = join(',', Bo_convert_helper($im, $w, $h, $hfunc, $hmask, $z));
$vfunc = create_function('$im, $y, $x', 'return imagecolorsforindex($im, imagecolorat($im, $x, $y));');
$vmask = "{$prefix}linear-gradient(90,%s) %s 0";
$vdots = join(',', Bo_convert_helper($im, $h, $w, $vfunc, $vmask, $z));
if (strlen($hdots) > strlen($vdots)) {
$dots = $vdots;
$sw = 1;
} else {
$dots = $hdots;
$sh = 1;
}
return <<<HTML
<head>
<title>$filename converted in background by Evgeny Stepanischev //bolknote.ru</title>
<style text="text/css">
div {
background: $dots;
background-repeat: no-repeat;
{$prefix}background-size: {$sw}px {$sh}px;
background-size: {$sw}px {$sh}px;
width: {$w}px; height: {$h}px;
}
</style>
<body>
<div></div>
</body>
HTML;
}
if ($_SERVER['argc'] < 2) {
echo <<<HELP
Image to background CSS convertor v2 by Evgeny Stepanischev. //bolknote.ru Nov 2011
Usage: {$_SERVER['argv'][0]} filename [prefix]
filename - image file name (PNG, JPEG or GIF)
prefix - your browser name (Opera, IE, FF, Safari, Chrome)
HELP;
exit;
}
$browsers = array(
'ie' => '-ms-',
'opera' => '-o-',
'safari' => '-webkit-',
'chrome' => '-webkit-',
'chromium' => '-webkit-',
'ff' => '-moz-',
'firefox' => '-moz-',
);
if (isset($_SERVER['argv'][2])) {
$type = strtolower($_SERVER['argv'][2]);
$prefix = isset($browsers[$type]) ? $browsers[$type] : '-' . $type . '-';
} else {
$prefix = '';
}
try {
echo Bo_convert($_SERVER['argv'][1], $prefix);
} catch (Exception $e) {
echo "Error: ", $e->getMessage(), "\n";
exit(1);
};Активность аудитории
Немного статистики.
В среднем на сайта в месяц бывает примерно 90 тысяч человек. Каждый месяц это число может быть меньше или больше. Например, в июне было 108 тысяч, а в августе — 77. Я недавно перелил большинство юзерпиков пользователей на «Яндекс.Фотки» и, делая скидку на людей с одинаковыми юзерпиками и именами (у меня для двух одинаковых имён сгенерируется один юзерпик), активная аудитория сайта порядка 1700 человек.
То есть более чем за 10 лет существования сайта (правда комментарии на сайте появились сильно позже) его посетила толпень народу, но комментрировали всего несколько тысяч. Похожая ситуация, судя по всему, у Экслера на его персональном блоге, в день его сайт посещает столько же народу, как меня за месяц, а комментариев в блоге — только несколько десятков к каждой записи.
Мне бы хотелось, чтобы читатели более активно высказывались, мне сейчас интереснее обсуждать мои мысли с читателями.
Причин низкой активности я вижу две:
Надо думать в сторону почты. Проблема с почтой, что её надо
Увы и подтверждение подлинности через письмо
Идея, которую я сейчас обдумываю такова: пользователю для первого подтверждения адреса почты (что будет доступно только для пользователей, которые авторизовались через OpenID), предлагается нажать на ссылку, сформированную специально для него.
Пользователь жмёт на ссылку, отсылает пустое письмо на этот адрес, с этого момента считается, что он подтвердил свой адрес. «Гмэйл» устроен таким образом, что на часть после плюса он внимания не обращает и эти письма будут попадать на ящик
Не слишком неудобно получается?
Хочу поменять библиотеку для OpenID на сайте
Хочется поменять библиотеку, которую я использую для OpenID на сайте. Я сейчас использую Janrain OpenID, но она давно не обновлялась, генерирует варнинг и только с костылями и патчами работает с реализацией OpenID в ЖЖ
Посоветуйте
Требования — библиотека должна поддерживать OpenID 1.x и 2.x, SREG, предоставлять возможность хранить данные где я укажу (мне нужно на диске, а не в БД) и не должна использовать экзотические модули.
Юзерпики на «Яндекс.Фотках»
Вчера в Татарстане был выходной, поэтому пока мы ехали на машине за город, я сделал заготовку для хранения юзерпиков на «Яндекс.Фотках», а вечером её доделал. Сейчас все юзерпики в фоне переливаются туда в порядке запроса пользователем в закрытый альбом специального пользователя, которого я завёл.
До этого юзерпики по сложным правилам (с использование адреса почты из SRE OpenID или vCard, favatar, gravatar, pavatar, юзерпиков из «Ярушки» и ЖЖ и т. д.) получались для каждого человека в комментариях и загружались каждый раз с совершенно разношёрстных ресурсов.
Это было плохо по трём причинам.
Есть и четвёртая причина, не знаю насколько она значимая. Некоторые юзерпики (побайтно) повторяются, но так как браузер не знает, что с двух разных УРЛов грузится та же картинка, он грузит её два раза. У меня сейчас не так — я хеширую картинку и по хешу ищу совпадение. Есть вероятность совпадения хеша, но юзерпики
Сейчас юзерпики грузятся только в фоне, то есть
У̶д̶о̶б̶н̶о̶е̶ новое цитирование в блоге
Долго думал как упростить цитирование у себя в блоге, наконец придумал. Ещё остались
Сделал две вещи.
О последнем расскажу немного подробнее. Устроено всё несложно, разберём на примере выделения полужирным.

Чтобы звёздочки скопировались вместе с остальным текстом, они должны в этом тексте присутствовать. И они там есть. Вокруг слова, выделенного жирным находятся звёздочки, написанные нулевым шрифтом и прозрачным цветом. Поэтому в тексте и при выделении их не видно, но в буфер обмена они попадают. Так же устроено сохранение остальной разметки — вырезанных частей ссылок, исходных УРЛов картинок.
Цвета из CSS, названия которых короче их номеров
Кстати, если вдруг
Например, «silver» короче своего числового эквивалента «#c0c0c0» на один символ, а «tan» короче «#d2b48c» на четыре.
Картинка на чистом CSS

Сейчас CSS позволяет делать всё более интересные вещи. Раз уж сегодня короткий день, за 20 минут из «сэкономленного» часа накидал программу, которая конвертирует любую картинку (в форматах PNG, JPEG или GIF) в чистый CSS (без использования dataURI). Каждая точка кодируется через одну конструкцию
Если заморачиваться серьёзно, то можно выявлять в картинке области с градиентом (так делается в формате PNG для упаковки) и покрывать их целиком одной конструкцией, а так же оптимизировать наложением линейных градиентных областей с прозрачностью (через rgba или hsla). Достаточно интересная программисткая задача, кстати.
Самую очевидную оптимизацию я сделал в пробке по работе домой — выделил самый частый цвет и пустил его на фон, выиграл примерно 11,5КБ на картинке в примере.
За 20 минут я только успел сделать небольшой конвертор (результат работы можно посмотреть в разделе «Храню», только для «Хрома»). Так как синтаксис завязан на префиксы, то я в генераторе сделал указание браузера под который нужно сгенерировать код (поддерживаются: IE, Opera, Firefox и Chrome). Всё это весьма расточительно, картинка 75×75 занимает 164КБ.
У меня заработало для «Хрома» и «Файэрфокса», под «Оперой» не завелось, лень проверять что она не поддерживает, IE попробовать не могу — его не бывает на «Маках». Код конвертора
// Конвертор картинки в чистый CSS, / ноябрь 2011
<?
// ffccbb → fcb
// ff0000 → red
// abcdef → abcdef
function Bo_shortcolor($color) {
static $shortnames = array(
"#000080" => "navy",
"#008000" => "green",
"#008080" => "teal",
"#4b0082" => "indigo",
"#800000" => "maroon",
"#800080" => "purple",
"#808000" => "olive",
//"#808080" => "gray",
"#808080" => "grey",
"#a0522d" => "sienna",
"#a52a2a" => "brown",
"#c0c0c0" => "silver",
"#cd853f" => "peru",
"#d2b48c" => "tan",
"#da70d6" => "orchid",
"#dda0dd" => "plum",
"#ee82ee" => "violet",
"#f0e68c" => "khaki",
"#f0ffff" => "azure",
"#f5deb3" => "wheat",
"#f5f5dc" => "beige",
"#fa8072" => "salmon",
"#faf0e6" => "linen",
"#ff0000" => "red",
"#ff6347" => "tomato",
"#ff7f50" => "coral",
"#ffa500" => "orange",
"#ffc0cb" => "pink",
"#ffd700" => "gold",
"#ffe4c4" => "bisque",
"#fffafa" => "snow",
"#fffff0" => "ivory",
);
$c = strtolower($color);
if (isset($shortnames[$c])) {
return $shortnames[$c];
}
if ($c[0] == $c[1] && $c[2] == $c[3] && $c[4] == $c[5]) {
return '#' . $c[1] . $c[3] . $c[5];
}
return '#' . $c;
}
function Bo_convert($filename, $prefix = '') {
if (!file_exists($filename) && !is_readable($filename)) {
throw new Exception('File not found.');
}
$types = array(
IMAGETYPE_JPG => 'jpeg',
IMAGETYPE_PNG => 'png',
IMAGETYPE_GIF => 'gif',
);
$imagedata = @getimagesize($filename);
if (!is_array($imagedata) || !isset($imagedata[2]) || !isset($types[$imagedata[2]])) {
throw new Exception('Unknown image format');
}
$im = call_user_func("imagecreatefrom" . $types[$imagedata[2]], $filename);
$w = imagesx($im);
$h = imagesy($im);
// Самый частый цвет идёт на background-color
$colorfreq = array();
for ($x = 0; $x<$w; $x++) for ($y = 0; $y<$h; $y++) @$colorfreq[imagecolorat($im, $x, $y)]++;
$bg = array_search(max($colorfreq), $colorfreq);
for ($x = 0; $x<$w; $x++)
for ($y = 0; $y<$h; $y++) {
$pixel = imagecolorat($im, $x, $y);
if ($pixel != $bg) {
$color = Bo_shortcolor(sprintf('%06x', $pixel));
$dots[] = "{$prefix}radial-gradient($color,$color) {$x}px {$y}px";
}
}
$dots = join(',', $dots);
$bg = Bo_shortcolor(sprintf('%06x', $bg));
return <<<HTML
<head>
<title>$filename converted in background by Evgeny Stepanischev //bolknote.ru</title>
<style text="text/css">
div {
background: $dots;
background-repeat: no-repeat;
{$prefix}background-size: 1px 1px;
background-color: $bg;
background-size: 1px 1px;
width: {$w}; height: {$h};
}
</style>
<body>
<div></div>
</body>
HTML;
}
if ($_SERVER['argc'] < 2) {
echo <<<HELP
Image to background CSS convertor by Evgeny Stepanischev. //bolknote.ru Nov 2011
Usage: {$_SERVER['argv'][0]} filename [prefix]
filename - image file name (PNG, JPEG or GIF)
prefix - your browser name (Opera, IE, FF, Safari, Chrome)
HELP;
exit;
}
$browsers = array(
'ie' => '-ms-',
'opera' => '-o-',
'safari' => '-webkit-',
'chrome' => '-webkit-',
'chromium' => '-webkit-',
'ff' => '-moz-',
'firefox' => '-moz-',
);
if (isset($_SERVER['argv'][2])) {
$type = strtolower($_SERVER['argv'][2]);
$prefix = isset($browsers[$type]) ? $browsers[$type] : '-' . $type . '-';
} else {
$prefix = '';
}
try {
echo Bo_convert($_SERVER['argv'][1], $prefix);
} catch (Exception $e) {
echo "Error: ", $e->getMessage(), "\n";
};
Крестики-нолики на HTML CSS и прокрастинация

Есть такое понятие — прокрастинация, это про «никогда не откладывай на завтра то, что можно сделать послезавтра». В тяжёлых проектах есть соблазн отложить принятие трудных решений «на потом» или не делать
Я
У меня с моими «
Вчера ночью меня осенило, открыл ноут перед сном и исправил последнюю ошибку (их там оказалось две — в выводе данных из программы моделирующей игру и в самом конверторе,
Багов быть не должно, могут быть шероховатости. Я сам знаю пару неаккуратных вещей — кнопка «Restart» работает не во всех браузерах и на ничьей следующий ход уже не сделать (хотя он и не нужен
Перехода на зимнее время не будет
Все помнят, что перехода на зимнее время в ночь с этой субботы на воскресенье не будет? Переход отменён Федеральным законом Российской Федерации от 3 июня 2011 г. N
В этой связи нужно аккуратно проверить, что все ваши сервера, устройства и так далее, не перейдут на это время и не создадут путаницу. Если вы регулярно обновляете систему, то в большой вероятностью вам ничего не грозит, на «Хабре» есть довольно развёрнутая статья о том до какой версии нужно обновлять различные системы или какие обновления ставить (например, для всех актуальных версий «Виндоус», включая ЭксПи, существует обновление №2570791, проверить его установку можно, например, через «ПауэрШел», для «Мак ОС Икс» требуется обновиться до 10.7.2).
Всё плохо у «Андроидов», в большинстве устройств переход состоится даже в последних прошивках (2.3.6, 3.2.1,
Так же пострадают джейлбрейкнутые устройства на «айОС», версия 4 не имеет необходимого обновления (а пятая пока не поддаётся джейбрейку), но есть способ обновить зоны вручную, ну или выставить всё тот же «Дубай».
Смартфоны на «Симбиане», автоматические будильники, сотовые телефоны, скорее всего вас подведут, ищите где у них «Дубай» или галочку, контролирующую автоматический переход на летнее и зимнее. Подумайте какое сетевое оборудовние у вас имеет внутренние часы, нужно ли обновиться или скорректировать зоны вручную (например, у «Цисок» такая возможность есть).
Кстати, некоторые программы и языки программирования имеют собственные базы временны́х зон. Например, серверу почты «Эксченч» требуется отдельное обновление, а интерпретатору ПХП лучше выставить настройку зоны
«Крестики-нолики» на HTML и CSS
Давно хотел попробовать сделать

Играет на уровне мастера, то есть пытается либо выиграть, либо свести к ничьей. Вычисления позиций проводил через реализацию
Кстати, несмотря на всю винтажность, от браузера требуется поддержка CSS3, включая трансформации.
Я сильно не тестировал, спать уже надо ложиться. Если будут ошибки, пишите, постараюсь поправить, если только они не относятся к недостаткам алгоритма Остермиллера.
ЭЦП
На конференции «Осенний
ЭЦП (электронная цифровая подпись) сейчас, фактически, используется не для того, для чего была придумана. Её назначение — кратковременное подтверждение подлинности переданного документа. К примеру, я получил письмо и мне нужно убедиться, что оно действительно пришло именно от того адресата, которые указан в поле отправителя. Стандартный протокол обмена письмами мне этой уверенности не даёт. А ЭЦП — пожалуйста.
Я открываю письмо, по подлинности ЭЦП убеждаюсь, что всё в порядке, дальше ЭЦП мне не нужна.
В электронных документооборотах не так. Там ЭЦП используется для подтверждения подлинности документа в долгосрочном периоде — годы, десятки лет, в перспективе, возможно, сотни (если речь идёт о долгосрочных договорах между государствами, например).
Чтобы не было возможности подделать документ, изменить его содержимое, применяется ЭЦП. Как правило, доступ к хранилищу, где он размещается, имеют сотни людей, причём многие из них — на запись. С течением времени, количество людей, имевших теоритическую возможность подмены документа, увеличивается —
Но. Надёжность ЭЦП, фактически, основана на сложности математических задач, заложенных в алгоритм электронной цифровой подписи. Эти задачи выбраны таким образом, чтобы подделка подписи требовала огромных вычислительных ресурсов. Последнее с успехом может быть заменено использованием более скромных ресурсов
В чём проблема. Мы все с вами наблюдаем современные темпы развития техники, пройдёт пара лет
В долгосрочной перспективе использования ЭЦП это проблема — при хранении документов десятки и сотни лет, риск подмены документов, особенно важных, где игра стоит свеч возрастает многократно.
Конечно эту проблему следует смотреть и в разрезе обеспечения доступа к самому хранилищу, но то, что нужны
С физическими (например, бумажными) носителями несколько проще — подделку нужно изготовить заранее, если упустить шанс, то его уже больше не представится. Насколько я знаю, пока нет технологии, которая могла бы «состарить» чернила, бумагу, а так же начеркать загогулину обычной подписи, так чтобы эксперты не смогли её отличить от подлинных, оставленных на других документах. А в её отсутствие, подделка вряд ли возможна.
Можно увеличивать длину ключа, (фантазирую) применять ключи, занимающие десятки килобайт. Шифрование будет крайне медленным, но для важных документов можно и потерпеть. Но вот вопрос — если через двадцать лет появятся квантовые компьютеры, которым такая длина ключей «на один зуб», что будем делать? Как видно, простого выхода из ситуации не существует.
Sublime Text 2 (совет)
Поставил по совету коллег с прежней работы себе текстовый редактор «Sublime Text 2», всё хорошо, одна проблема — файлы с кодировкой CP1251 он открывает неправильно, приходится переоткрывать через «File → Reopen with Encoding → Cyrillic (Windows 1251)».
Покопался по настройкам, нашёл выход. Нужно зайти в настройки (на «Маке», как обычно — ⌘+,) и добавить между фигурными скобками вот такую настройку: «»fallback_encoding«: „Cyrillic (Windows 1251)“». Мой файл настроек сейчас почти пустой и выглядит вот так:
// Settings in here override those in "Default/Base File.sublime-settings", and
// are overridden in turn by file type specific settings. Place your settings
// here, to ensure they're preserved when upgrading.
{
"fallback_encoding": "Cyrillic (Windows 1251)"
}CSS hack for FireFox 6
Меня тут попросили сделать
x::-moz-progress-bar, body {
background: orange; /* FF 6.0+ */
}
@-moz-document regexp(".*") {
body {
background: orange; /* FF 6.0+ */
}
}Проверено на Firefox 6.0 и 7.0.1.
Поддержка MJPEG в «Опере»
Как я уже писал, «Опера» иначе показывает MJPEG, не как остальные браузеры. Если открыть
А мне хочется смотреть
Вдохновившись библиотекой добавляющей нативную поддержку APNG в браузер, я подумал, что я ничем не хуже и смогу добавить поддержку MJPEG в «Оперу». Получился небольшой userjs, который я, естественно, выложил на «Гитхаб».
Идея такая — находим картинку во всех тегах IMG, у которой расширение «mjpg». Через XHR получаем с сервера видеопоток, копим кадр, подменяем найденную картинку и так далее.
Работает начиная с 12 версии браузера, которая ещё не вышла (кстати, в позавчерашней сборке сломали userjs напрочь) — в предыдущих версиях XHR слабоват по возможностям для этого.
Больше всего я бился с декодированием бинарных данных. Последняя версия «Оперы» поддерживает тип ответа arraybuffer (а вот blob не поддерживается), но если его выбрать, то данные в цикле
Кстати, быстрее всего у меня получилось декодировать не циклом, а регулярными выражениями, дальше тормозит уже не мой код, а способ получения отдельного кадра — каждый раз я соединяюсь с сервером заново. Можно бы накапливать кадры пачкой и потом их сбрасывать, но у меня нет задачи смотреть трансляции футбола, у меня дом, который не бегает и полученной скорости мне вполне хватает
В общем, получился довольно небольшой код — 94 строки. Если вы не в курсе как устанавливаются скрипты userjs, то это очень просто. В интернете есть много инструкций на эту тему. Только напоминаю, что «Опера» нужна двенадцатая, причём в сборке за седьмое октября userjs сломаны.Посмотреть код.
// ==UserScript==
// @name MJPEG+Opera - Add to Opera MJPEG support
// @author Evgeny Stepanischev aka Bolk
// @version 1.00
// @namespace /files/opera-mjpeg.js
// @modified 2011-10-09
// @include *
// ==/UserScript==
opera.addEventListener('BeforeEvent.DOMContentLoaded', function() {
if (opera.version() < 12) return;
var BolkMJpeg = function (url, img) {
var req = new XMLHttpRequest();
img.src = 'about:blank'; // Сброс картинки, чтобы прекратить передачу
// Асинхронно забираем кадр при помощи XHR, нам придёт видеопоток
var start = function (url) {
req.open("GET", url, true);
// Свежепридуманный хитрый хак, позволяет получить данные в UCS-2
req.overrideMimeType('image/jpeg');
req.send(null);
}
// Хитрыми путями восстанавливаем порядок байт в reponse, там сейчас UCS-2,
// получаем просто последовательность байт
var reqbytes = function (n, l) {
return escape(req.response.substr(n, l)).
replace(/([^%]|%[^u].|%u....)/g, '<$1>').
replace(/<%u(..)(..)>/g, '%$2%$1').
replace(/<%(..)>/g, '%$1%00').
replace(/<(.)>/g, '$1%00');
}
// заменяем картинку на бинарные данные
var draw = function (bin) {
img.src = 'data:image/jpeg,' + bin;
}
// На изменение состояние входных данных собираем по частям JPEG
req.onreadystatechange = function () {
// состояние «3» — данные пошли, но ещё не кончились
if (req.readyState === 3) {
var hnd = setInterval(function () {
// по заголовку получаем размер данных
var header = unescape(reqbytes(0, 300)).toLowerCase();
var idx = header.indexOf('content-length:');
if (idx > -1) {
// 15 — длина заголовка
var len = parseInt(header.substr(idx + 15), 10);
var headend = header.indexOf("\r\n\r\n");
// дожидаемся конца заголовка
if (headend > -1) {
var resp = reqbytes(0, len);
// умножаем всё на три, так как reqbytes даёт esc-последовательность
// вида %XX%XX…
len *= 3;
headend = (headend + 4) * 3;
// если собрали весь JPEG, обрываем соединение
// и начинаем новый цикл. Мы могли бы собирать кадры и дальше, но
// это съест всю память со временем
if (resp.length >= len + headend) {
req.abort();
resp = resp.substr(headend, len);
draw(resp);
clearInterval(hnd);
start(url);
}
}
}
}, 100);
}
}
start(url);
};
// ищем картинку, у которой расширение mjpg
var imgs = document.getElementsByTagName('IMG');
for (var i = 0, len = imgs.length; i<len; i++) {
if (/\.mjpg$/.test(imgs[i].src)) {
BolkMJpeg(imgs[i].src, imgs[i]);
break;
}
}
}, true);
MJPEG, MPNG, MGIF, MSVG
Я вчера показывал вам «телевизор» про фестиваль в Самаре. Сделано очень просто — уменьшил все сделанные там снимки до 66×44 и сконвертировал их в анимированный ГИФ. Мелковато, конечно, но что делать — ГИФ
У JPEG есть свой формат анимации, если так можно выразиться, а точнее — скорее простенький формат видео, так как формат анимации, в моём представлении, помимо кадров должен содержать себе
Мне давно хотелось посмотреть что у этого формата внутри, сегодня наконец посмотрел. Всё оказалось крайне просто.
В данном случае, MJPEG — формат стримминга (потоковой подачи) видео. Собственно, это даже не формат, а простое соединение JPEG и специального заголовка
Смысл этого заголовка — содержимое которое пришло раньше заменяется тем, что пришло позже. То есть MJPEG — это просто последовательность картинок, у которых этим заголовком указано — когда приходит следующая картинка, ею надо заменить предыдущую. Внутри выглядит это вот таким образом.
Content-type: multipart/x-mixed-replace;boundary=СТРОКАРАЗДЕЛИТЕЛЬ
--СТРОКАРАЗДЕЛИТЕЛЬ
Content-type: image/jpeg
Content-length: 232311
…бинарное содержимое первой картинки…
--СТРОКАРАЗДЕЛИТЕЛЬ
Content-type: image/jpeg
Content-length: 47474
…бинарное содержимое второй картинки…СТРОКУРАЗДЕЛИТЕЛЬ надо выбирать так, чтобы она не встретилась в бинарных данных изображения.
Браузерам этот заголовок достался от фирмы Нетскейп, которая
Например для «Оперы», нужно выдавать данные с задержкой, то есть показали один кадр, ждём пару секунд, ничего с сервера не отдаём, потом следующий. Иначе, «Опера» «хитрит» — показывает следующий кадр, не дожидаясь загрузки предыдущего. Если канал хороший, то мы увидим только последний кадр.
«Файрфокс» единственный, кто хорошо справляется с тем, что кадры могут быть разных размеров — он отображает их правильно, «Опера» масштабирует по первой картинке, «Хром» и «Сафари» глючат — иногда пропускают кадр, иногда ничего не показывают, иногда показывают нормально.
Самое приятное в этом формате — что все современные браузеры считают его картинкой, то есть тег IMG его преспокойно показывает.
Любопытно, что браузеры позволяют сжимать содержимое всего файла и даже каждого кадра отдельно, то есть можно в заголовке кадра поставить
Так что, если вы задумались сделать аналог
Гоу версия 1
Язык программирования «Гугл Гоу»
We propose to issue a Go release early next year that will be called “Go version 1”, Go 1 for short, that will be the first Go release to be stable in this way. Code that compiles in Go version 1 should, with few exceptions, continue to compile throughout the lifetime of that version, as we issue updates and bug fixes such as Go version 1.1, 1.2, and so on… Go version 1 will be a stable language with stable libraries. Other than critical fixes, changes made to the library and packages for versions 1.1, 1.2 and so on may add functionality but will not break existing Go version 1 programs.
Фотки можно добавлять ещё и с picplz.com
Внёс важное изменение в движок сайта — фотографии теперь можно добавлять ещё и с сайта «Пикплз». Вообще я переделал весь механизм добавления сайтов с картинками.
Ничего не должно было сломаться, но если будут баги, пишите. В комментариях к предыдущему посту добавил картинку с сервиса «Пикплз»— вид на Казань из окна офиса «Яндекса» в Казани
Какие ещё сервисы картинок добавить? Два жёстких требования — они должны быть с регистрацией (чтобы мне не постили всякую порнуху) и имели АПИ для получения информации о фото (чтобы я мог выбрать подходящие размеры картинки).
Добавлено позже: теперь ещё и «Фликр» работает.