Сервис шаринга скриншотов и кода

    Многие из вас наверняка слышали про сервисы, позволяющие делится скриншотами одним нажатием кнопки. Общая концепция следующая: жмёшь хоткей, выделяешь область экрана, в буфер обмена получаешь ссылку на скриншот, который автоматически загружается на какой-то хостинг. Так же многие из вас наверняка пользовались другими сервисами — позволяющими опубликовать фрагмент исходного кода и получить ссылку на страничку, отображающую этот исходник с подсветкой синтаксиса.

    Мы объединили две эти идеи и сделали проект, обладающий следующими фичами:

    • Публикация скриншотов и исходников по нажатию хоткея
    • Open Source — можете форкнуть / поднять на своём сервере / добавить новые фичи
    • Прямая ссылка на изображения, отсутствие рекламы
    • Кроссплатформенность
    • Устойчивость к высоким нагрузкам (хостимся в облаке)


    Сервис состоит из следующих компонент:

    • Клиентское приложение
    • Серверный демон, принимающий скриншоты
    • Веб интерфейс


    Клиентское приложение делает скриншот и передаёт его на сервер. Серверный демон получает файл, генерирует ему уникальное имя и записывает в определённую папку. Все полученные файлы становятся доступны по http протоколу — для этих целей используется nginx. Так же nginx + php & fastcgi используется для работы библиотеки подсветки синтаксиса.

    Клиентское приложение


    Для разработки клиентского приложения был выбран фреймворк Qt. Преимущества — хорошая документация, кроссплатформенность, большое количество библиотек. И вообще он нам нравится. Кроме того, нам пришлось задействовать библиотеку qxt для кроссплатформенной работы с глобальными хоткеями.

    Приложение состоит из следующих классов:

    • Application
    • Network
    • ConfigWidget
    • ImageSelectWidget
    • LanguageSelectDialog
    • ScanHotkeyDialog


    Application — тут реализована работа с глобальными хоткеями, получение скриншотов, работа с треем и буфером обмена.

    Network — работа с сетью, т. е. подключение к серверу, отправка данных по простому http-подобному протоколу, получение от сервера ссылки.

    ConfigWidget, ImageSelectWidget, LanguageSelectDialog, ScanHokeyDialog — простые классы, отвечающие за окно конфига, окно выбора изображения, языка программирования и настройки горячих клавишь.

    Во время инициализации приложения происходит следующее. Вначале проверяем, запущенно ли уже приложение. Из кросплатформенных решений самым простым вариантом проверки оказалось запустить QLocalServer, и пытаться подключиться к нему при старте. Если подключение не удалось — значит это новый запуск и мы работаем, в противном случае выходим.

      QLocalSocket socket;
      socket.connectToServer(APP_NAME);
      if (socket.waitForConnected(500)) {
          qDebug() << "Application already launched!";
          return false;
      }
      _localServer = new QLocalServer(this);
    
      if (!_localServer->listen(APP_NAME)) {
          QLocalServer::removeServer(APP_NAME);
          _localServer->listen(APP_NAME);
      }
    


    Затем регистрируем хоткеи. Используя libqxt с хоткеями работать довольно просто. Нужно создать хоткей, указать для него комбинацию клавиш и связать со слотом, который выполнится по нажатию на данный хоткей:

    _shortcutScreenFull = new QxtGlobalShortcut;
    if (!_shortcutScreenFull->setShortcut(QKeySequence(fullHotkey)))
          qDebug() << "Error activating hotkey:" << fullHotkey;
    connect(_shortcutScreenFull, SIGNAL(activated()), this, SLOT(processScreenshotFull()))
    


    Сама процедура получения скриншота выглядит так. Получаем скриншот, сохраняем его в необходимом формате в QBuffer, получаем массив байт QByteArray и отправляем на сервер:

    QPixmap pixmap = QPixmap::grabWindow(QApplication::desktop()->winId());
    
    QByteArray imageBytes;
    QBuffer buffer(&imageBytes);
    buffer.open(QFile::WriteOnly);
    pixmap.save(&buffer, imagetype.toAscii().constData());
    buffer.close();
    _network->upload(imageBytes, imagetype);
    


    Протокол отправки файла клиентом на сервер следующий. Клиент отправляет:
    proto=pastexen
    version=1.0
    type=jpg
    size=142034
    
    binary_data
    


    Где jpg — тип файла; 142034 — размер файла в байтах; binary_data — содержимое файла

    После получения файла сервер отвечает:
    proto=pastexen
    version=1.0
    url=http://host.tst/file1.jpg
    


    url — прямая ссылка на файл или на страницу с оформленным текстом с подсветкой синтаксиса

    В методе upload класса Network подключаемся к серверу, формируем пакет согласно протоколу и передаём его:

      _socket.connectToHost(_serverAddr, 9876);
      _socket.waitForConnected();
    
      QByteArray arr;
      arr.append("proto=pastexen\n");
      arr.append("version=0.2\n");
      arr.append("type=" + type + "\n");
      arr.append(QString("size=%1\n\n").arg(data.size()));
      arr.append(data);
      _socket.write(arr);
    


    Как только получаем ответ, извлекаем из него ссылку, и кидаем сигнал linkReceived:

      const QByteArray arr = _socket.readAll();
      const QString link = getValue(arr, "url");
      emit linkReceived(link);
      _socket.disconnectFromHost();
    


    Ловим сигнал в классе Application, копируем полученную ссылку в буфер обмена, показываем сообщение в трее:

    QApplication::clipboard()->setText(link);
      _trayIcon->showMessage(tr("Done!"), tr("File shared!"), QSystemTrayIcon::Information, 6500);
    


    Похожим образом реализована отправка исходного кода.

    Серверная часть


    Серверный демон был так же сделан с использованием Qt. Мы протестировали получившееся решение под нагрузкой (1000 одновременно передающихся файлов) и решили что оно нас устроит.

    Сервер реализован на основе пула потоков. Все подключенные клиенты распределяются между имеющимися потоками, работа с клиентами происходит в асинхронном режиме. Сохранение полученных данных в файлы так же осуществляется в отельном потоке.

    В приложении используются классы:

    • Server
    • ThreadPool
    • Saver
    • Socket


    В классе Server создаётся tcp server (QTcpServer), обрабатываются новые подключения. Для подключившегося клиента создаётся Socket, который будет выполнятся в одном из потоков, предоставляемых ThreadPool_ом.

    auto socket = _server.nextPendingConnection();
    new Socket(socket, ThreadPool::getNextThread(), it.value());
    


    Кроме того, при подключении происходит проверка, не превысил ли пользователь лимит.
    В конструкторе класса Socket, он переносится в переданный ему поток выполнения (т. е. обработка всех событий, связанных с этим классом будет происходить в event-loop_e, работающем в другом потоке). Для этого используется функция moveToThread.

    Так же внутри Socket обрабатываются пришедшие данные, отправляется сигнал на сохранение (emit saveFile). Этот сигнал обработает класс Saver, который запущен в другом потоке. После того, как Saver сохранит всё в файл — он пошлёт сигнал, который обработает Socket и отправит ссылку на файл клиенту.

    Хостинг


    Проект с самого начала планировался как open-source. И когда он был практически готов, встал вопрос о хостинге. Нужен был какой-то надёжный хоситнг, который сможет выдержать хабраффект. Так же встал вопрос о том, кто будет этот хостинг оплачивать, и не надоест ли нам (в финансовом плане) поддерживать проект через какое-то время. Поэтому мы разослали письма в несколько крупных компаниям с предложением поучаствовать в нашем проекте предоставив нам хостинг. Получив несколько ответов мы остановились на облачном хостинге компании “Скалакси”.

    На сервер был установлен debian, nginx, настроены rc скрипты и автоматический рестарт с использованием monit.

    Будущее проекта


    В следующей версии программы мы планируем сделать следующие фичи:

    • Добавить функцию перевода по нажатию одной кнопки из буфера обмена с использованием сервиса translate, например от Google.
    • Превью картинок.
    • Mac сборка (если кто-то умеет собирать qt приложения под маком, помощь бы не помешала)
    • Интернационализаця
    • Возможно вы сможете предложить что-то ещё


    Ссылки


    pastexen.com — официальный сайт проекта
    github.com/bakwc/Pastexen — исходники на гитхабе
    scalaxy.ru — компания “Скалакси”, предоставившая проекту облачный хостинг
    Поделиться публикацией

    Комментарии 56

      0
      Avast не пускает на официальный сайт проекта.…
        +1
        Спасибо за информацию, написал в avast.
        +1
        Наверное, были бы ещё актуальны клиенты для мобильных платформ (Android, iOS, Windows Phone, ...). Ну и захват видео с экрана! Короткие фрагменты ;)
          0
          Мобильные платформы есть в планах. Хотя программа довольна простая, протокол простой и открытый (есть на вики на гитхабе) — если кто-то захочет сделать клиент — будем очень рады :)
          Захват видео — интересная идея, подумаем над этим.
            +1
            И в GIF результат захвата.
              0
              Тогда ограничение на размер области и продолжительности видео придется сделать. Full-HD гиф длиной даже в несколько минут будет тяжеловат.
                0
                С выбором способа кодирования.
                Просто иногда ради двух-трёх кликов не охота открывать видеофайл.
              –1
              Посмотрел спецификацию и ничего не понял. В каком виде данные отправляются на сервер? Тупо plain text? Ни XML/JSON?
                0
                Ага, plain text. До двух переносов строк — текст, разделенный одинарными переносами строк. Затем бинарные данные (аналогично http протоколу).
                  0
                  Т.е. в случае расширения функционала (например посмотреть список того что же я наскриншотил) по API также будет отдаваться plain text? Очень удобно, да, не то что всякие удобные вещи
          • НЛО прилетело и опубликовало эту надпись здесь
              +4
              Зачем свой протокол, чем HTTP POST/PUT не канали?
              можно было пользовать готовый код и на сервере и на клиенте.
                –1
                Протокол небольшой и простой, кода пару строчек. В принципе и весь проект можно было написать на php и winapi, но мы решили пойти по иному пути.
                  +8
                  ну, он ничем не отличается от http кроме своего формата.
                  а в чем его преимущество?

                  преимущество http — можно было бы chunked использовать для плохих соединений, можно было бы докачку реализовать _готовыми_ средствами, и много еще чего.
                  а так — всё накостылить и навелосипедивать надо.
                  в чем профит?
                    –7
                    На данный момент нам не нужны эти фичи. У нас была задача отправить бинарные данные на сервер. У нас не было задачи выбрать наилучший протокол. И, вообще для закачки файлов можно было бы воспользоваться ftp протоколом (который был разработан для передачи файлов), или же http расширением webdav, встречный вопрос, зачем?
                    Использовать свой протокол нам было проще чем заимплементить согласно спецификации любой другой.
                      +7
                      А, молодости простительно.
                +5
                >>А что Вы делаете, когда надо быстро показать картинку или часть кода

                Жму PrintScreen, иду на makescreen.ru, жму Ctrl + V, делюсь скриншотом.

                А ваш сервис, конечно попробую, может будет удобнее.
                  +1
                  Нельзя склонировать git, просит запустить update скрипт на сервере.
                    +1
                    Было бы здорово иметь возможность управлять загруженным. И в общем-то вопрос сразу же: сколько хранятся загруженные мною данные?
                      0
                      Постоянно. Как минимум до тех пор, пока данных не станет слишком много и мы не сможем найти хостера, готового это захостить.
                        +1
                        Ок, а что с возможностью управлять своими загрузками? Такой функционал рассматривается?
                          +1
                          Я бы посоветовал добавить возможность выбора времени хранения в клиент, почти все мои скрины мне не нужны уже к концу дня )
                            0
                            Да, полезная возможность, в следующей версии будет.
                        +2
                        Я так понимаю, под MacOS клиента нет?
                          0
                          Нет, но в ближайшее время планируем сделать сборку (проект на qt, проблем со сборкой быть не должно).
                          +1
                          Я для себя написал на баше маленький скрипт и повесил на него шорткат, может кому пригодится. Нужен dropbox и не забудьте поменять адреса.
                          #!/bin/bash
                          IMAGE_PATH=/home/shock_one/Dropbox/Public/scrn/$(date +%s).png
                          gnome-screenshot --area -f $IMAGE_PATH
                          dropbox puburl $IMAGE_PATH | xclip -selection c
                          notify-send "Screenshot has been uploaded" -i $IMAGE_PATH
                          
                            0
                            Жаль что dropbox отказался от папки Public для новых пользователей.

                            Теперь там упор на шары будет.
                              0
                              Хорошо хоть для старых оставили. Можно заливать на Ubuntu One или еще куда-то.
                              0
                              переписал под osx:

                              userid=2170730; filename=$(date +%s).jpg; screencapture -S ~/Dropbox/Public/$filename; echo https://dl.dropbox.com/u/$userid/$filename | pbcopy
                              

                              p.s. userid 2170730 взята из (copy public link) dl.dropbox.com/u/2170730/1358290980.jpg
                                0
                                Попробуйте утилиту GrabBox, я ей все время пользовался на OS X.
                            • НЛО прилетело и опубликовало эту надпись здесь
                                0
                                Личный кабинет и закрытые ссылки планируем в следующей версии.
                                0
                                Было бы неплохо делать оповещение после загрузки кода/картинки. Использую Ubuntu, сначала подумал, что хот-кеи не срабатывают, потом только дошло, что ссылки в буфер обмена сохраняются
                                  0
                                  В трее должно появляться уведомление о загрузке.
                                    0
                                    К сожалению, ничего не происходит, стоит Unity, не могу понять в чём может быть проблема
                                      0
                                      Будем фиксить. Спс!
                                • НЛО прилетело и опубликовало эту надпись здесь
                                    0
                                    Давно вот таким пользуюсь

                                    > cat ~/scripts/scrshoot
                                    #!/bin/sh
                                    mkdir -p /tmp/.private/${USER}/
                                    OUT=/tmp/.private/${USER}/$(date +"%H%M%S")scr.png
                                    
                                    import -window root ${OUT}
                                    
                                    if [ "$1" = "post" ]; then
                                    	SCRSHOT=$(ompload -u ${OUT})
                                    	 notify-send --icon gnome-stock-mail-snd "imageshack" "${SCRSHOT}"
                                    else	
                                    	 mv ${OUT} ${HOME}/tmp/scrshoots/
                                    fi
                                    
                                    


                                     > eix -I ompload
                                    [I] app-misc/ompload
                                         Available versions:  20101220
                                         Installed versions:  20101220(12:28:37 21.10.2012)
                                         Homepage:            http://ompldr.org/
                                         Description:         CLI script for uploading files to http://ompldr.org/
                                    
                                      +1
                                      А как шарить код? :) Лично для меня в сабже шаринг кода является киллер фичей. Картинки я и в скайпе передать могу по pscrn, ctr+v.
                                        –1
                                        я бы рекомендовал пользоваться системой контроля версий
                                          0
                                          Что бы показать кому то 2 строчки кода, я должен сделать коммит, затем дать права на чтение, вы серьезно?
                                            –1
                                            любой IM. 2 строчки кода вы даже с помощью твиттера зашарите, друг мой. (На их сайте говорили про pastebin)
                                              0
                                              IM? как же подсветка синтаксиса? а еще эти чертовы смайлы которые сами вставляются )).

                                              В общем хорошая попытка, но нет.
                                          0
                                          Например, быдлокод для емакса

                                          gist.github.com/4525871
                                            0
                                            Зависит от того, чем вы пользуетесь. Например:
                                            Sublime Text 2 github.com/condemil/Gist
                                            IntelliJ IDEA www.jetbrains.com/idea/webhelp/creating-git-gists.html
                                            Eclipse github.com/taichi/gist.eclipse
                                              0
                                              Phpstorm + github source, что посоветуете?
                                                0
                                                Так ведь PHPStorm тоже от JetBrains. Включите плагин GitHub и будет вам счастье.
                                                  0
                                                  Хм, ну тут еще остается вопрос с приватностью, шарить не opensource код через гитхаб не самая лучшая затея.
                                                    0
                                                    в github есть приватные репозитарии, да и git можно поднять локальный, с мордой Gitlab, например. Код обычно шарится между членами команды разработчиков, и поэтому с правами на чтение — все должно быть в порядке
                                              +1
                                              Ну и подведя итоги, все предложения это просто костыли, которые надо настраивать, трясти бубном. Лично мне проще проставить простую прогу которая все за меня сделает, и будет работать в один клик с любым приложением.
                                                0
                                                Ну да, прога то пусть будет, и со скринами функционал очень даже ничего. Просто задача частенько делиться парой строк кода, на мой взгляд — неправильная и непонятная. Может стоит тогда задуматься о каких-нибудь средствах для парного програмиирования? А куски кода покрупнее показать вебморде любой системы контроля версий не составляет труда вообще.
                                                  +1
                                                  Да большие куски кода (полезные), надо хранить на версии, но имхо не содержимое буфера обмена. Вы же не записываете все свои разговоры на диктофон?

                                                  Просто не надо усложнять то что должно быть простым.

                                            0
                                            отправилось два раза =(
                                              +2
                                              Спасибо за сервис, как раз недавно искал подобный из-за отсутствия PrintScr на клавиатуре.
                                              С картинками отлично работает, моментом загружается и открывается. А вот с кодом что-то не то. Залилось на сервер быстро, а отобразить в браузере пыталось секунд 20. К томуже отобразилось не совсем корректно: pastexen.com/i/l46irFt.png
                                              upd: Грузится долго из за социальной кнопки вконтакта, он у меня заблокирован. Но вина на вас, грузите асинхронно.
                                                0
                                                В параноидально-корпоративных компаниях запрещено использовать любые облачные сервисы, их упускаете.
                                                  +1
                                                  Вещь отличная. Пока кажется удобнее gyazo (там реклама достала), спасибо автору.

                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                  Самое читаемое