TL;DR: SIR даёт имя сохраняемой картинке
на Artstation, Deviantart, Instagram, Tumblr, Twitter…
(есть для Firefox и Chrome, описание (en) можно прочесть тут)
Вступление
С незапамятных времён балуюсь сохранением картиночек из интернета. Это мания: иногда даже не рассматриваю особо, просто сохраняю. Мемы, аниме, фотожабы, арты, ну вы поняли.
Но вот незадача: начиная с какого-то количества файликов в папке на поиск нужного уходит как-то многовато времени:
«Я же помню, где-то была та вот эта вот ну вот её — с котиком, которую ещё у Артгема в инстаграмме…»
Ну или наоборот:
«Не ну я же знаю этого художника, ыыыыг, ща-ща-ща, чё-то там на 米姆局 начиналось или как-то так, прям вот здеся я это оно самое…»
Концепция идеи
Файлам надо давать имена. Но чтение глазами букв и придумывание их мозгом очень рушит медитативный-расслабляющий процесс сохранения картинок.
Обидно же! Мозг включать надо! Скажем этому нет — в идеальном мире при сохранении картинок работает исключительно спинной мозг!
Особенно, когда желанная инфа и так всегда доступна — картинку постит автор на каком-то сайте, иногда даже тэги ей прописывает.
Так и появилась идея дополнения для браузера, которое минимально инвазивно ворует картинки со всеми их раскрытыми дискриминаторами.
(Для организации уже имеющихся картиночек мой друг — который настоящий кодер — написал простой, но мощный теггер на C++ + Qt, я же взялся за браузер)
Стек
Расширение написано на чистом JS, цепляющем API Chrome (и Firefox, ибо он почти совместим)
Прототип
На этом этапе за 10% времени в коде был сооружён велосипед из дичайших костылей, а 90% времени ушло на рисование иконки и выдумывание названия похайповее.
Что может быть лучше рекурсивного акронима — поэтому Sir Image Renamer. Он ведь картинкам имя даёт. Плюс, при поиске по магазину дополнений — нуль совпадений. Уникальненько!
Морская синева — приятный цвет, стрелочка вниз вполне себе символизирует скачивание, а прерывающая её тильда взялась из изначального формата имени файла — Сайт~автор имярек.jpg
. Да только вот оказалось, что тильду никто не любит — из имени файла она ушла, а в иконке осталась.
Про код — весь прототип состоял из (здесь можно орать):
- трёх иконок,
- манифеста — на 80% слизанного из туториалов гугла,
- файла background.js — смотрит на URL, вычленяет из него ключевые подстроки, добавляет попавшуюся в имя файла
- и… ничего более.
Но это работало — значит, мои навыки JS не были совсем безнадёжны!
Стек, часть вторая
Забыл упомянуть — JavaScript к началу разработки я не знал от слова «совсем». Зато знал, что всё, что можно разломать — то можно понять.
Вооружившись курсом+кратким референсом от w3schools и документацией Chrome API (а чуть позже, Mozilla WebExtensions API), я скачал расширения, функциональность которых была похожа — и начал методично читать.
Для всех желающих — привожу чит-код для Лисы:
CTRL+Shift+A → Tab × 6 → Space → Tab × 3 → Space
(Extensions Debug
), оттуда копировать Extension ID (например, uMatrix@raymondhill.net
) и вставить в поиск у себя в %USERPROFILE%\AppData
. Найти файл XPI, открыть его в 7-zip — и сидеть изучать сколько душе угодно.
Разработка
Читал чужое, ассимилировал, переписывал. Кодил-кодил, пару раз перелопачивал уже весь проект, чтобы стало лучше. Как-то упоролся по плашкам на гитхабе (хотя друг сказал, что плашки — говно для ЧСВшников). Один раз взял себя в руки и написал огромное описание — что именно делает расширение, с картиночками.
С момента, как готов был дать миру версию 1.0.0, начал чётко трекать всё в ченжлогах.
Запуск, полёт нормальный
Пользователей не искал вообще ни разу — тестил всё сам, когда допилил более-менее адекватную версию, скинул дополнение другу. Чуть позже выложил в магазины Firefox и Chrome — там, видимо, стали по описанию находить (откуда-то аж 20 установок!).
На этом этапе посмотрел, что требуется для пассивной рекламы в магазинах и нарисовал пару баннеров-превьюшек:
Из моего субъективного опыта, примерно треть расширений выложены с минимальными усилиями: задание нормального описания и отрисовка баннеров уже поставят дополнение на ступеньку выше остальных.
Continious Integration
Ради этого я прочитал описание GitLab API, настроил зеркалирование репозитория и прогонку скрипта .gitlab-ci.yml на каждом пуше в master
. Сам скрипт безбожно скопировал у кого-то из интернета, убрав лишнее и вбив в GitLab свои токены от Addons.Mozilla.Org.
Подводные камни
Callback Hell. Колбэки. Тысячи их. Незабываемые пирамидки из вложенных функций-объектов, которые передают другим функциям функции как объекты. Как это «неизвестно, когда фукнция выполнится» и «неизвестно, в каком контексте»? Как это «когда будет время, тогда и запустим»? Уфф. Эта парадигма требует особого к ней отношения — мне после линейности микроконтроллеров сложновато.
API Хрома писали разные люди в несколько подходов — и это прям чувствуется — посмотрите, как выглядит одна и та же информация (айди вкладки) в разных местах:
- методу OnContextMenuClicked() передаётся объект
tab
, у него надо смотреть свойствоid
:tab.id
— ну, вроде норм; - методу OnActivated() — объект
tabId
— казалось бы, что это? А ВОТ И НЕТ!tabId
— это не то же, что уже знакомыеtab
илиtab.id
, здесь чтобы получить айди вкладки, надо сделатьtabId.tabId
; - аналог в методе OnUpdated() —
tabId
. А это не объект, это айди вкладки, integer как он есть!
- Оказалось, что API у FF и Cr не до конца совместимы:
- прототипе расширения использовался метод OnDeterminingFilename(), существующий в Хроме, но не реализованный в Фоксе. Он позволял получить управление в момент сохранения произвольного файла и "подсказать" браузеру, что прописать в строке имени файла. Фокс же позволяет только *инициировать новую* загрузку, с произвольным предопределённым именем файла. Поэтому я отказался от OnDetermeingFilename() и перешёл к OnContextMenuClicked() — и к лучшему, это позволило выкинуть очень много лишнего кода по определению интересуемого файла; контекстное меню знает свой контекст!
- Хром не позволяет мне выставлять реферер. Из-за этого, часть картинчоек на сайтах, что трекают рефы, не сохраняется. Почему часть? Не знаю.
- WebExtensions API от Мозиллы, которое я пользую для CI/CD, выдаёт вот такую бяку:
$ web-ext sign --source-dir=./Extension/ --api-key=$WEB_EXT_API_KEY --api-secret=$WEB_EXT_API_SECRET --channel=listed --id="{3846b265-95c4-4371-9853-86df1b8cbaaa}" ... Your add-on has been submitted for review. It passed validation but could not be automatically signed because this is a listed add-on. FAIL ... ERROR: Job failed: exit code 1
Вкратце — оно делает FAIL, хотя всё прошло ожидаемо и аддон попал на рассмотрение. Из-за этого пришлось выставить этот job как «скрипту разрешено зафейлить» — что, в общем-то, не корректно. Если оно зафейлит по другой причине, плашка по-прежнему будет «pipeline: passed».
Потрачено! И идеи по монетизации
Потратил только полгода на разработку (может, дней 20 чистого времени).
Идей по монетизации всего три:
- реклама (даже не рассматриваю);
- ссылка на донат (уже введена), можно её педалить агрессивнее;
- платные не-обязательные фичи, про-версия. ПЛОТИ
ДОЛОРсколько там минималочка в гугле?
Планы на будущее
- Потихоньку допиливать поддержку новых сайтов-галерей (как буду на них набредать)
- Неплохо было бы вникнуть в i8n и замутить-таки переключаемую локаль хотя бы для русского — приятнее, когда всё на одном языке.
- Нехорошо, что настройки пользователя хранятся только в пределах одной сессии браузера — следует разработать интерфейс настройки и хранить желания юзера между рестартами
- Совсем уж мечты — научиться подавать хэши картинок во всякие сети типа Hydrus Network — и воровать их список юзер-мэйд тегов для картинки. Предлагать их, если автор не удосужился описать картинку сам
Нужны ли какие-то советы или помощь Клуба?
Любой критике рад буду. Код-ревью — особо классно. Если кто поможет пофиксить скрипт, чтобы оно в submit_amo
обрабатывало FAIL, который происходит при штатной работе и отдавало в job SUCCESS (а другие FAIL-ы ловило) — вообще угощу кофе
Cоветы cтраждущим
Если ковыряете расширения и хотите посмотреть на них в действии — на той же странице из п.«Стек, часть вторая» на нужном плагине нажмите
INSPECT
- браузер откроет панельку с фоновой страницей, где крутится основное тело кода расширения.API FF на 99% совместимы с API Chrome — так что можно хромовские описания смотреть (объект Chrome в Mozilla просто альяс к объекту Browser).
У меня написано, к примеру, chrome.contextMenus.onClicked()
— и эта же кодовая база на Мозилле работает.
- Если будете работать со вкладками — там бывает, что однородности нет, ВНИМАТЕЛЬНО смотрите везде описание API. Бывает, что одни и те же вещи в разных методах названы разными именами — а бывает наоборот; схожими именами названы разные вещи
А не планируешь ввести выбор папок куда сохранять?
Я вот сохраняю в одну папку, потому что хром и ФФ только в одну умеют, а потом сортирую по категориям\проектам.
Хотелось бы просто иметь опцию
Save to:
Из-за необходимости переименовывать картинки я когда-то перестал их сохранять. А потом отвык.
Идея классная и завидное упорство :)
Имхо - дополнительная киллер фича - сохранить все фотки со страницы, да еще и с норм названиями
Я бы сразу начал юзать
Понадобитмя доп фильтр по размеру что бы клипарт мусор не сохранять (либо конфигурировать под каждую галерею)
Фича, о которой можно задуматься - шеринг изображения.
Условно, я могу сохранять их себе не только на папку компа, но и в saved messages в телеграм. Ну или сразу друзьям своим.
На мобилке это можно сделать прямо с браузера, а на компе еще нет.
Это немного выбивается из концепции "сохранить с названием", но зато это функционал в той же точке - правый клик по картинке.