[

вторник, 23 октября 2012 г.

Замена табов на пробелы в VIM

Способов много, но больше всего понравился из комментария вот тут: http://stackoverflow.com/questions/426963/replace-tab-with-spaces-in-vim

:retab

Табы будут заменены на пробелы в соответствии с текущими настройками.

PS: после этого, возможно, захочется поправить отступы. это можно сделать так: gg=G. В командном режиме. Дословно это значит "перейти в начало файла, сделать реформатирование текста до конца". = сработает и в режиме выделения.

воскресенье, 7 октября 2012 г.

Стриминг IP камеры с сайта

Как делал

Есть IP камера производства AXIS. Она отдает RTSP и MJPEG. Надо live стримить ее поток с сайта через какой-нибудь flash плеер.

Пошел по привычному пути - ffmpeg. У них есть сервер для стриминга - ffserver.

Скажу сразу, что с RTSP намучился вусмерть! С MJPEG все оказалось проще.

Покопавшись с форматами и контейнерами уяснил, что далеко не все контейнеры можно стримить. Можно, например, FLV или ASF. Так как наша цель flash, взял FLV.

Далее надо было конвертировать. Как оказалось параметры надо указывать в настройках ffserver, а ему поток можно скармливать через ffmpeg, например.

Конфиг ffserver:
Port 8888
BindAddress 0.0.0.0

# другие опции...

# Конфигурация feed - входящий видеопоток
<Feed cam1.ffm>
    File /tmp/cam1.ffm
    FileMaxSize 10M
    # скармливать видео можно только с данной машины
    ACL allow 127.0.0.1
</Feed>

# Конфигурация выходного потока
<Stream cam1.flv>
    Format flv
    Feed cam1.ffm
    VideoCodec flv
    # без +global_header live stream не получится
    AVOptionVideo flags +global_header
    # для простоты в выходном потоке должно быть столько же кадров в секунду
    # сколько в входном, иначе картинка _может_ рассыпаться
    VideoFrameRate 25
    VideoBitRate 1000k
    VideoSize 512x384
    # нас интересует только видео
    NoAudio
</Stream>

Запуск ffserver (в конфиге по умолчанию стоит опция NoDaemon а лог выдается в stdout):
/path/to/ffserver -f /path/to/ffserver.conf

Все сервер работает... но ничего не стримит, так как на вход ничего не подается. Команда для забора нашего MJPEG с камеры и перенаправление его в feed ffserver:
/path/to/ffmpeg -f mjpeg -i http://login:pass@cap_ip/mjpg/video.mjpg http://localhost:8888/cam1.ffm

Перед указанием выходного файла никакие параметры указывать не надо - они берутся из конфига фида в ffserver.conf и будут проигнорированы.

Для проверки сделал тестовую страницу с плеером:
<html>
<head>
<title>Test IP Cam</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="/flowplayer/flowplayer-3.2.11.min.js"></script>
<script>
$(document).ready(function(){
    flowplayer("player", "/flowplayer/flowplayer-3.2.15.swf");
});
</script>
</head>

<body>
<a id="player" style="display:block;width:512px;height:384px;" href="http://ffserver_ip:8888/cam1.flv"></a>
</body>
</html>

Что дальше

А дальше это бы гнать в h264 (контейнер F4V). Беда в том, что конфиг файл ffserver не понимает presets и все параметры надо указывать через AVOptionVideo/AVOptionAudio.

воскресенье, 15 июля 2012 г.

Конвертирование видео для iPad new с ffmpeg

ffmpeg компилировал сам (если не забуду - выложу команды из хистори).


/opt/ffmpeg/bin/ffmpeg -y -i <in_file> -vcodec libx264 -acodec libfaac -f mp4 <out_file>.mp4

PS: поддерживаемые форматы нашел тут: http://www.isoftcoupon.com/tutorial/the-new-ipad-3-video-formats.html

вторник, 13 марта 2012 г.

Пометка элемента как просмотренного с jquery

Скрипт-игра. не скажу где используется и на что похоже;) Но это чудо эффективно может определить виден ли элемент клиенту (в текущем viewport'е) и как-то его отметить.

Собственно, листинг тестовой версии:

<html>
<head>
<title>TEST :: is element visible in viewport</title>

<script language="javascript" type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

<script>
/*
 * Получение текущего вьюпорта:  координаты верхнего левого угла и ширина с высотой
 */
function current_viewport()
{
 var w = $(window);
 
 return {
  "left": w.scrollLeft(),
  "top": w.scrollTop(),
  "width": w.width(),
  "height": w.height()
 };
}


/*
 * Проверка находится ли объект во вьюпорте
 *
 * paddings = {
 *     "left": ...,
 *     "top": ..., 
 *     "right": ...,
 *     "bottom": ...
 * }
 * эффективно увеличивают размеры объекта. по умолчанию 0
 */
function in_viewport(selector, paddings)
{
 var el = $(selector);
 var off = el.offset();
 var dim = {
  "width": el.width(),
  "height": el.height()
 };
 
 if(!paddings)
 {
  paddings = {
   "left": 0,
   "top": 0,
      "right": 0,
      "bottom": 0
  };
 }
 
 var viewport = current_viewport();
 
 return (
  (off.top - paddings.top) >= viewport.top &&
  (off.left - paddings.left) >= viewport.left &&
  (off.top + dim.height + paddings.bottom) <= (viewport.top + viewport.height) &&
        (off.left + dim.width + paddings.right) <= (viewport.left + viewport.width)
 );
}

/*
 * Помечает объект-ответ, как прочтенный
 */
function mark_as_read(obj)
{
    $(obj).css({
        "border": "1px solid #eeeeee"
    });
    $(obj).data('is_read', true);
}


$(document).ready(function() {
 
 // паддинги для проверки попадает ли объект во вьюпорт
 var paddings = {
        "left": 0,
        "top": 0,
        "right": 0,
        "bottom": 0
    };
 
 for(var i = 0; i < 40; i++)
 {
  $('<div class="ticket_reply"></div>').css({
   "height": "100px",
   "margin-bottom": "20px",
   "border": "1px solid blue"
  }).appendTo('body');
 }
 
 var replies = $('.ticket_reply');
 
 replies.each(function(i) {
  // отметим видимые при загрузке
  if( in_viewport(this, paddings) )
     mark_as_read(this);
 });
 
 // зарегистрируем проверку прочтения сообщений по скроллингу
 $(window).scroll(function() {
  replies.each(function(i) {
   if( !$(this).data('is_read') && in_viewport(this, paddings) )
       mark_as_read(this);
  });
 });
 
});
</script>

</head>
<body>

</body>
</html>

За основу взят вот этот пост на stackoverflow: http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport

суббота, 24 декабря 2011 г.

Django 1.4 alpha release

На днях писал о кастомных фильтрах в админке: http://readability-counts.blogspot.com/2011/12/django-admin-listfilter.html. Все оказалось не совсем так - я смотрел trunk репозитория. И вот вышла Django 1.4 alpha с новой фичей, описанной в моем предыдущем посте. Там еще много всего интересного - изучайте!

четверг, 22 декабря 2011 г.

Django Admin и кастомный фильтр для list_filter

Это только набросок - нужно все опробовать на живом коде.

"Вдруг" родилась задача выводить кастомный список значений для фильтра в админке. Поясню, имеем следующую модель:
class NewsPost(models.Model):
    """Новость"""

    authors = models.ManyToManyField(User, blank = True, null = True)

В админке она представлена так:
class NewsPostAdmin(admin.ModelAdmin):
    """Представление Новости в админке"""

    # фильтрация новостей по автору
    list_filter = ('authors',)

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        """Специальная фильтрация для списка допустимых авторов

        Данная фильтрация распространяется только на поле authors в
        форме редактирования новости в админке
        """

        if db_field.name == 'authors':
            kwargs['queryset'] = User.objects.filter(is_staff=True)
        return super(PostAdmin, self).formfield_for_manytomany(
            db_field, request, **kwargs)

В итоге в форме редактирования новости в админке в качестве автора можно будет указать только пользователя со статусом Персонал (Staff), но для фильтра списка новостей будут выведены все пользователи системы.

Из документации по админке Django сказано, что в list_filter можно указывать только название поля:
Set list_filter to activate filters in the right sidebar of the change list page of the admin. This should be a list of field names, and each specified field should be either a BooleanFieldCharFieldDateFieldDateTimeFieldIntegerField or ForeignKey.
Fields in list_filter can also span relations using the __ lookup:
Но если посмотреть код в django.contrib.admin.views.main.ChangeList, метод get_filters() то увидим следующее:
...
filter_specs = []
if self.list_filter:
    for list_filter in self.list_filter:
        if callable(list_filter):
            # This is simply a custom list filter class.
            spec = list_filter(request, lookup_params,
                self.model, self.model_admin)
        else:
            ...

Оказывается в list_filter можно указать "какой-то" класс, экземпляр, которого будет создан для фильтрации. Классы эти можно увидеть вот тут:  django.contrib.admin.filters.

To be continued...

среда, 7 декабря 2011 г.

Скрипт-лоттерея

Для чего его использовать - ваша воля. Мы же его использовали для распределения смен технической поддержки на НГ.


# coding: utf-8
import random
import readline


def get_random_workers():
    """Start lottery"""

    # Список людей
    people = []

    # Заполним список участников
    while 1 == 1:
        name = raw_input("Введите имя сотрудника (пустая строка для завершения): ")
        name = name.strip()
        if name == '':
            break
        people.append(name)

    # проверим, что участники введены
    if not people:
        print("Нет участников для нашей лоттереи :( жаль.")
        sys.exit()

    # Случайный выбор из списка
    print("\nА сейчас... победители!")
    print("Нажмите Enter для получения следующего \"счастливчика\" или q для завершения")
    while 1 == 1:
        s = raw_input()
        if s == 'q':
            break
        random.seed()
        print(random.choice(people))

    # конец
    print("\nДо встречи перед следующими праздниками!")

if __name__ == '__main__':
    get_random_workers()

Баги:

  1. raw_input() отказывается читать кирилицу, хотя везде все UTF-8. можно переопределить sys.stdin - экземпляр StreamReader.