Category Archives: Mac

Mac workflow: Markdown to MS Word .docx

Ma szembejött két állásajánlat is, amiben a fejvadász kikötötte, hogy az önéletrajzot és a cover lettert is csak Microsoft Word .docx formátumban hajlandóak elfogadni. Én az ilyesmit Markdownban tartom, a Word dokumentumoktól meg az Isten óvjon, úgyhogy a pandoc becenevű fegyverrel tüzeltem az ajánlatokra:

brew install pandoc
pandoc -o cv.docx -f markdown -t docx cv.md

A pandoc az a minden OS-re megírt svájci bicska, amit először kell körbenézni akkor, ha valaha arra lenne szükséged, hogy két különböző markup formáttum között konvertálj.

Update: ha ezen fellelkesültél volna és mondjuk innentől minden Markdown doksit is CLI-ből szeretnél PDF formátumúvá konvertálni, az sem lehetetlen. A pandoc ehhez a pdflatex binárist használja, ami a basictex csomagban lakik eltemetve, de mi kibányásszuk:

brew cask install basictex
ln -s /Library/TeX/texbin/pdflatex /usr/local/bin/pdflatex

Innentől látszik a pdflatex a pandocnak, már onthatjuk a README.pdf-eket:

pandoc -o README.pdf -f markdown README.md

Mac workflow: hátha SONOSt hoz a Jézuska

Régóta szerettem volna rendes multiroom audio rendszert, valami olyat, amit én is könnyen bővítgetek és persze ami nem zárt, hanem beszélgethetek vele elektromosan. Long story short: az eddig használt Apple AirPlay nem ilyen, sőt, több sebből vérzik:

  • Apple === zártság, ezt tudtuk eddig is. Persze AirPlay servert azért csinálhatunk egy Raspberry Pi-ből, használhatunk hozzá egy kis Airport Expresst vagy egy Apple TV-t ha elég annak az egy darab digitális audiokimenete, de ezekhez még mind speakert kell illesztenünk ugye.
  • Az AirPlay lagos. Isten látja lelkemet, amióta az AirPlay megjelent, azóta várom, hogy majd a szuper mérnökök megoldják azt, hogy az egymástól 10 méterre levő két hangszóróból egyszerre szóló zene ne legyen a késés miatt visszhangos, de a 2010 szeptembere óta létező AirPlay a mai napig késik, úgyhogy épp 6. éve már, hogy képtelenek orvosolni a bosszantó problémát.
  • Ahhoz, hogy AirPlayen játszak le, a mai napig futnia kell egy AirPlay targetre zenét küldő szoftvernek valami dedikált frontend vason.

Sokat olvastam már a SONOS-ról, csábított is, de az Apple Music támogatás hiánya mindig vissza tartott. Pár hónapja azonban megjött a SONOSokba ez is, úgyhogy az idei karácsonyi hajrában bevásároltunk egy kazal speakert.

A SONOS speakerek saját maguk cibálják el a zenét a forrásból, a controller szoftvernek csak addig kell futnia, amíg te kiválasztod, hogy mit akarsz hallgatni, melyik hangszórókon és milyen hangerőn. Hab a tortán, hogy brit szoftveresek rég meg tudták oldani a lag problémát: az összes hangszóró teljes szinkronban tolja ugyanazt a kontentot.

Ez így közel tökéletes lenne, ám nekem sokszor jött elő munka közben, hogy egy playlistben olyan track került sorra, ami zavart. Ilyenkor taskot kell váltani meg egerezni ahelyett, hogy a billentyűzet elengedése nélkül foshatnám a kódot. Na ezért született a SONOS Controller workflow.

A SONOS Controller egy Alfred 3-ba begyógyítható workflow. Az agyát nem én írtam, hanem 2012-ben itt Sydneyben a Music Hack Day nevű eseményen kezdte Rahim Sonawalla, majd egy csodálatos Python fejlesztői közösség gyűlt köré és tartja karban a projektet a mai napig.
Ahhoz, hogy az általam hackolt Alfred workflow bármit is csináljon a SONOS speakereiddel, előbb a SoCo python classt kell installálnod mondjuk a Python csomagkezelőjével:

pip install soco

Eszembe nem jutott zeneválasztó funkcionalitást gyártani, hiszen egyfelől a SONOS kismillió adatforrásból képes audiot lejátszani, másfelől erre tökéletesek a desktop és mobil appok, amiket a gyártó ingyen kínál, végül pedig nem ez volt a cél, hanem csak annyi, hogy meló közben gyorsan tracket léptessek, halkítsam/hangosítsam a cuccot, vagy megállítsam a streamet, mindezt úgy, hogy a kezemnek ne kelljen elhagynia a billentyűzetet.

A workflow két keywordöt ismer:

  • sc - ha ezzel a kulcsszóval indítasz, akkor az összes zónát vezérled egyszerre:
    soco_alfred-commands_on_selected_zone
  • scs - ez a kulcsszó pedig előbb összeszedi a LAN-odon látható SONOS speakereket, amikből választasz egyet, majd ezután kapod a parancsmenüt, ami így értelemszerűen a már az előbb kiválasztott speakerre fog vonatkozni:
    soco_alfred-zone_selector

A parancslista sincsen széjjel bonyolítva:

  • play - elindítja a lejátszást
  • pause - megállítja a lejátszást
  • next - a következő trackre ugrik
  • inc - emel a hangerőn 5%-ot
  • dec - csökkent a hangerőn 5%-ot
  • vol XX - beállítja a hangerőt XX% értékre, ha XX-nek 0-100 közötti egész számot definiáltál:
    soco_alfred-set_volume

Az ikonokon nem megbotránkozni, én Mórickáztam össze őket éjjel amíg ti alukáltatok (a 10 óra timezone offset legrosszabb tulajdonsága, hogy az azonnali agymenéses petprojecthez nem tudok leakasztani egy ingyendesignert :))

A workflowt szokás szerint szedheted a packal.org-ról vagy tőlem. Boldog Karácsonyt!

Az Apple dongle hisztiről

Preface: Soha nem szoktam ilyen "vallási" témáról írni, a jelenlegi poszt csak azért születik, mert még sehol nem olvastam a sajátommal egyező véleményt.

A múlt héten az Apple frissítette a MacBook Pro palettát, melyen a legkisebb, 13"-os gépük 2 db, míg a többi 4 db USB C csatlakozót kapott (a 3.5"-os Jack lyuk mellett). Az USB C új szabvány, kevés még az USB C portra direktben rádugható eszköz - a szabvány váltás miatt a korábbi kütyükhöz mind adapterre van szükség, ha azokat az új gépünkkel használnák. Lassan annyi dongle kapható Apple termékekhez, hogy azokkal fontot lehet rajzolni:

apple-dongle-font
image source: @andy23 Twitter

Én jelenleg egy mid-2012-es, első generációs minimal config MacBook Pro-t használok, amin a MagSafe tápcsatlakozón és a 3.5"-es jack lyukon túl 2 db Thunderbolt, 2 db USB 3, 1 db HDMI és egy normál SD kártya bemenet található. Ennek ellenére minden nap az alábbi szettel megyek terepre:

my-edc-apple-dongles

A fenti kép kicsit gonosz, mert a két jobb oldali dongle nélkül is túlélek melóban, azok csak azért vannak velem, mert egy tasakban tartom az összes donglét plusz minden drótot és ezek egyszerre jönnek velem, akár melóba megyek, akár víz alá.

A fotón balról jobbra az alábbi játékszerek láthatóak:

  • analog VGA - DisplayPort: ez akkor kell, ha olyan meeting roomban akarsz prezentálni, ahol a projektoron csak analog VGA van, illetve a mennyezetről csak azt vezeti ki drót a gépedig (amerre én járok, ott az esetek >70%-a ilyen)
  • DVI - DisplayPort: arra az esetre, ha (csak) digitális videojelet fogadó DVI port lenne a projektoron, vagy esetleg ilyet tudó monitor van a pool asztalon, mert ugye ha van digitális video in, akkor mégsem vagyunk állatok, hogy analógot használjunk
  • USB - Ethernet: ha nincs WiFi, vagy van, de az enterprise IT nem ad hozzáférést (bizony, ilyen simán létezik). Ez csak 10/100 mbps, van belőle gigabites Thunderbolt verzió is, de az a containerben hajózik éppen.
  • USB + Lightning - Lightning: iPadhez, ha tápot is adnál neki meg fotót/videót is töltenél fel rá egyszerre (búvározós szituációban nagyon hasznos tud lenni).
  • USB - Lightning: ez ugyanarra jó, mint az előző - USB connectiont ad iOS eszközhöz. A végén lógó kék infrás DiveMate Fusion IR nevű bigyó egy gateway az iOS-es DiveMate app és az IR protokollt beszélő búvárcomputerek közé.

Szóval nálam ezekből 3 mindenképpen ott van. Ha most gépet váltanék, mind a három dongle helyett újra lenne szükség, plusz nagy eséllyel beszereznék:

  • egy HDMI - USB C donglét, hogy ha csak HDMI van terepen, akkor azt tudjam használni
  • egy USB - USB C donglét, hogy amíg ki nem kopik nálam minden USB, addig tudjam őket használni az új géppel
  • egy Thunderbolt 1/2 - USB C donglét vagy jobb esetben egy rövid Thunderbolt 1/2 - USB C kábelt, hogy a külső Seagate GoFlex dokkomat se kelljen kidobni

Ha 50 USD / dongle átlagárral számolunk, akkor ez 300 USD plusz költséget jelent.

Nekem nem azzal van problémám, hogy egy szabvány kikopik és az Apple veszi a bátorságot, hogy kidobja. Sokkal inkább az zavar, hogy a leglojálisabb, Pro gépeket használó vásárlóival sem kegyes annyira, hogy az új szabványra való első teljes átálláskor mellékelne egy váltást megkönnyítő dongle packot.

Számoljunk egy kicsit. A cég egy év alatt cirka 2.5 millió notebookot ad el és mondjuk vegyünk másfél éves gépfrissítési ciklust alapul. Tegyük fel, hogy a Pro usernek egyféle régi szabványú video out és egy obsolete USB port hiányzik veszettül.
Ha úgy állnának oda a pódiumra, hogy az új szabvány bevezetésekor ott van az új 1st gen gépek dobozában a 2 extra dongle (ami jelen kalkulációban 2.5 * 1.5 * 2 = 7.5 millió drótot jelent), akkor akár az is lehet, hogy kevésbé lenne keserű a szoftver fejlesztésből élő Apple notebook userek szájíze. Akár akkor is, ha 2 dongle árával megemelnék a termékek árát, annak ellenére, hogy az évi 200+ milliárd USD bevétel lehet, hogy elbírná a ~4 millió Pro user felé gyakorolt gesztust.

PS.: ez a poszt pusztán arról szólt, hogy nem szeretem, hogy a világ legtöbb tech pénzét termelő vállalata nem empatikus a lojális Pro vevőivel szemben. Ezért aztán arról commentben is kár beszélnünk, hogy:

  • túl kevés az új gépeken a port, úgyhogy még USB C elosztó / docking station / bármi más is kéne
  • nem tudom hozzádugni az új iPhone 7-et sem az új MacBook Pro-khoz
  • miért nem fért el legalább egy microSD slot a házban (holott ezért én is ölnék)
  • miért megy a vékonyítás hiszti ahelyett, hogy még akkut kapnánk
  • et cetera

Mac workflow: Alfred set color label

A probléma

Egér nélkül akarjuk a Finderben kiválasztott elemek color label tag-jét beállítani.

A megoldás

Csinálunk hozzá Alfred workflow-t.

A workflow kétféleképpen használható:

  1. Van benne 8, paraméter nélkül használható keyword: lred, lorange, lyellow, lpurple, lblue, lgreen, lgray, lnone - ezek értelemszerűen a nevekben szereplő színűre állítják a kiválasztott Finder elemeket.
    set-color-label_workflow2
  2. Az "scl" kulcsszóval és az utána írt color paraméterrel ugyanígy beállítja a kívánt színt a Finder által használt 7 féle variációból:
    set-color-label_workflow1

A workflowt szedheted a packal.org-ról, vagy tőlem innen.

Mac workflow: textfile to Reminders items

Rövid, de annál szárazabb, scriptelős post lesz. Mivel bármilyen unalmas contentet jótékonyan meg tud támogatni akár egy irreleváns kép is, ezért itt egy panoráma a Tátrából, mielőtt scriptet kéne olvasnod:

Tatra panorama

Erától kaptam szép hosszú szerelmetes levelet to-do listát emailben, amit nekem kényelmesebb lenne kipipálgatni egy Reminders listában. Szerencsére van egy kis eszünk, AppleScriptünk, meg a Remindersnek AppleScript supportja, úgyhogy csak bemásoljuk az emailből a hosszú listát a clipboardba, aztán ráeresztjük ezt a scriptet és voila:

tell application "System Events"
	set myText to paragraphs of (the clipboard as text)
end tell
repeat with myLine in myText
	if length of myLine is greater than 0 then
		tell application "Reminders"
			tell list q
				make new reminder with properties {name:myLine}
			end tell
		end tell
	end if
end repeat

Mac workflow: Alfred translate-shell CLI translation

Régóta küzdök a különböző dictionary megoldásokkal - legutóbb pl. a dict.cc szótáraihoz csináltam Alfred workflowt.
Nemrég szembejött a csodás shell-ben való fordításhoz írt translate-shell, amely használja a Google Translate, Bing Translator és a Yandex online service-eket, ráadásul a fejlesztők naprakészen tartják az alkalmazást, így ha a Google teker egyet az API-n, csak a translate-shellt kell frissíteni és már működik is újra minden. Ez kell nekem, ebből csináltam is gyorsan egy Alfred workflow-t (a workflowban szerepel egy, az Alfred 3 által bevezetett var/arg manipulator widget, így Alfred 2-vel nem lesz kompatibilis, de mivel az Alfred 3 már kijött a bétából, így ez nem lehet gond).

A trans workflow "trans" keywordjét a translate-shell szintaxissal kell használni:

trans-keyword

trans-output

trans-multilang

trans-multilang-output

A nyelv-specifikus keywordöknél a target language előre definiált és a kifejezéseket nem kell idézőjelbe tenned:

thu-keyword

trans-output

A trans workflow-t viheted innen, vagy a packal.org-ról.

Mac workflow: Alfred dict.cc translation

Azonnali fordításhoz (=nem akarom levenni a billentyűzetről a kezem ahhoz, hogy lefordítsak egy szót) jó ideig nagy örömmel használtam a Thomas Hempel féle Alfred Google Translate workflow-t, ám a Google egy ideje DDOS attacknak veszi a workflow ismétlődő lekérdezéseit és ez azt eredményezi, hogy viszonylag gyorsan kizár a szolgáltatásból egy teljes napra. Nem volt mese, más fordító megoldás után kellett nézni - így akadtam rá a dict.cc-re.

Innen már csak pár lépés volt Raphael Baron dict.cc-hez írt Python kódja, onnan meg Thomas Hirsch Alfredre szabott workflow-ja.
Thomas megoldása sajna bedrótozta a workflowba, hogy az angol->német irányban fordítson, viszont a dict.cc angolról és németről számos más nyelvre képes fordítani, így csak egy picit kellett megpiszkálni a workflowt ahhoz, hogy angol<->magyar és angol<->lengyel irányokban is fordítson:

dict_cc-en-hu

dict_cc-hu-en

dict_cc-en-pl

dict_cc-pl-en

Újabb forrás->cél nyelv hozzáadásához csak újabb script filtert kell létrehozni:

dict_cc-definitions

amiben csak ezeket kell cserélgetned:

dict_cc-script-filter-details

A workflow-t szedheted tőlem, vagy a github forkból.

JSON adatbányászat shellben

TL;DR: ha stock OS X alatt shell scriptben JSON-t akarsz parse-olni, jsc-vel csináld.

A probléma

Van egy Alfred workflow-m, amivel villámgyorsan át tudom állítani a Transmission torrent kliens speed limitjét - ez a script a bele.

Ehhez akartam olyat, hogy amint az Alfred querybe beírom a script filer kulcsszavát, azonnal jelenjen meg, hogy mennyi az aktuális speed limit a Transmission kliensben.

A megoldás: 1/3 (eFi)

Először abban reménykedtem, hogy egy Google search majd azonnal ontja magából a találatokat a "parse JSON in bash"-re, de amiket talált, abból egyik sem tetszett igazán, így aztán benga favágó módjára kikapáltam az engem érdeklő infót, duplán regexpelve (mint az állatok):

#!/bin/sh
## guess Transmission's speed limit
 
USER=yourUserName
PASS=yourPassword
server=http://yourTransmissionHostIP
SPEED=$1
 
curlout=$(curl -u $USER:$PASS ${server}:9091/transmission/rpc 2>/dev/null)
regex='(X-Transmission-Session-Id\: )([^<]*)'
 
if [[ $curlout =~ $regex ]]; then
    sessionid=${BASH_REMATCH[2]}
else
    exit 1
fi
 
data='{"method": "session-get"}'
R=$(curl -H $USER:$PASS ${server}:9091/transmission/rpc -d "$data" -H "X-Transmission-Session-Id: $sessionid")
 
ASD_ENABLED=`echo "$R" | grep -o -e '\"alt\-speed\-enabled\":[a-z]*'`
ASD_ENABLED=`echo "$ASD_ENABLED" | grep -o -e '[a-z]*$'`
 
ASD=`echo "$R" | grep -o -e '\"alt\-speed\-down\":[0-9]*'`
ASD=`echo "$ASD" | grep -o -e '[0-9]*$'`
 
SLD_ENABLED=`echo "$R" | grep -o -e '\"speed\-limit\-down\-enabled\":[a-z]*'`
SLD_ENABLED=`echo "$SLD_ENABLED" | grep -o -e '[a-z]*$'`
 
SLD=`echo "$R" | grep -o -e '\"speed\-limit\-down\":[0-9]*'`
SLD=`echo "$SLD" | grep -o -e '[0-9]*$'`
 
echo '<?xml version="1.0"?>'
echo '<items>'
echo " <item uid='tspd' arg='$SPEED' valid='yes'>"
echo " <title>Set Transmission speed limit</title>"
if [ "$ASD_ENABLED" = "true" ]
then
	echo " <subtitle>Current speed limit: $ASD kbps</subtitle>" 
else
	if [ "$SLD_ENABLED" = "true" ]
	then
		echo " <subtitle>Current speed limit: $SLD kbps</subtitle>" 
	else
		echo " <subtitle>Currently no speed limit is set</subtitle>" 
	fi
fi
echo ' <icon>speed-limit.png</icon>'
echo '</item>'
echo '</items>'

A megoldás: 2/3 (fds)

Mennyivel szebb lenne ez egy JSON parserrel!
Eltelt pár óra és Fazekas "fds" Dani barátom megjelent, majd kettő perc múlva kirázta a kisujjából ezt:

JSON='{"efi":24,"csokijat":42,"mar":242,"elvittek":666,"fene":[1,2,3,4]}'
/System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Resources/jsc -e "debug(JSON.parse(readline())['fene'][2])" <<< "$JSON"

Script az script, ez meg parsol, úgyhogy ezért INSTANT CSOKI jár.

A megoldás: 3/3 (fds)

fds a posztot látva azt mondta, hogy szerinte nem dolgozott meg a megacsokiért, de ha adnék egy Transmission kimenetet amit a curl leszippant, akkor faragna egy csinosabb verziót. A maximalizmusát már jól ismerve meg nem fosztottam volna a világot az új változattól, úgyhogy azonnal ment az output.
Nem is lenne fds, ha nem csinálta volna meg azonnal jsc, php, python és ruby használatával is az egészet (azért "csak" ennyi interpreterrel, mert ezek érhetők el a gyári OS X alól 2016 januárjában). A hozzám hasonló bolondok biztosan fülig érő szájjal olvassák a scriptet:

#!/bin/bash
 
## guess Transmission's speed limit
 
USER="yourUserName"
PASS="yourPassword"
server="http://yourTransmissionHostIP"
SPEED="$1"
JSONPARSER="jsc"
 
curlout=$(curl -u $USER:$PASS ${server}:9091/transmission/rpc 2>/dev/null)
regex='(X-Transmission-Session-Id\: )([^<]*)'
 
if [[ $curlout =~ $regex ]]; then
    sessionid=${BASH_REMATCH[2]}
else
    exit 1
fi
 
data='{"method": "session-get"}'
R=$(curl -H $USER:$PASS ${server}:9091/transmission/rpc -d "$data" -H "X-Transmission-Session-Id: $sessionid")
 
getargument() {
	local ret
 
	case "$JSONPARSER" in
		jsc)
			ret=$(2>&1 /System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Resources/jsc -e "debug(JSON.parse(readline())['arguments']['$2'])" <<< "$1")
			ret="${ret#-->* }"
			;;
		php)
			ret=$(php -nr "echo json_encode(json_decode(file_get_contents('php://stdin'), true)['arguments']['$2']);" <<< "$1")
			;;
		python)
			ret=$(python -c "import json,sys
print(json.dumps(json.load(sys.stdin)['arguments']['$2']))" <<< "$1")
			;;
		ruby)
			ret=$(ruby -e "require 'json'
puts JSON.parse(STDIN.read)['arguments']['$2']" <<< "$1")
			;;
	esac
 
	echo "$ret"
}
 
ASD_ENABLED=$(getargument "$R" "alt-speed-enabled")
ASD=$(getargument "$R" "alt-speed-down")
SLD_ENABLED=$(getargument "$R" "speed-limit-down-enabled")
SLD=$(getargument "$R" "speed-limit-down")
 
echo '<?xml version="1.0"?>'
echo '<items>'
echo $'\t''<item uid="tspd" arg="'"$SPEED"'" valid="yes">'
echo $'\t'$'\t'"<title>Set Transmission speed limit</title>"
if [ "$ASD_ENABLED" = "true" ]; then
	echo $'\t'$'\t'"<subtitle>Current speed limit: $ASD kbps</subtitle>" 
else
	if [ "$SLD_ENABLED" = "true" ]; then
		echo $'\t'$'\t'"<subtitle>Current speed limit: $SLD kbps</subtitle>" 
	else
		echo $'\t'$'\t'"<subtitle>Currently no speed limit is set</subtitle>" 
	fi
fi
echo $'\t'$'\t''<icon>speed-limit.png</icon>'
echo $'\t''</item>'
echo '</items>'

Persze ennyivel nem érte be, megmérte, hogy melyik verzió mennyi időt zabál el az életemből:

for p in jsc php python ruby; do
	echo -n "$p: "
	JSONPARSER="$p"
	OUT=$(( time -p (
		ASD_ENABLED=$(getargument "$R" "alt-speed-enabled")
		ASD=$(getargument "$R" "alt-speed-down")
		SLD_ENABLED=$(getargument "$R" "speed-limit-down-enabled")
		SLD=$(getargument "$R" "speed-limit-down")
		echo "${ASD_ENABLED}|${ASD}|${SLD_ENABLED}|${SLD}"
	) ) 2>&1)
	echo -n $(tail -n 3 <<< "$OUT" | head -n 1 | cut -d ' ' -f 2)s
	echo ' '$(head -n 1 <<< "$OUT")
done
exit

Az eredmény az, hogy a jsc mindent lever:

jsc: 0.04s false|10|true|500
php: 0.25s false|10|true|500
python: 0.11s false|10|true|500
ruby: 0.23s false|10|true|500

Maximalista grafikusnak megismert Rack barátom szokta azt mondani az árajánlatot sokalló ügyfélnek, hogy "de minden pixel a helyén van". Nos, nekem Dani ugyanez a szerethető fanatikus coderben. Le vagyok nyűgözve, mint mindig :)