Vägkarta för H.VHS

De senaste månaderna har jag inte ägnat en sekund åt H.VHS. Detta beror vare sig på att jag skulle ha tappat intresset eller kommit på bättre tankar, utan på att jag varit sjuk. Därför har jag nu också en del annat att stå i, innan jag kan fortsätta utveckla programmet. Det hör förstås till saken att H.VHS är ett fritidsintresse – något jag gör enbart därför att det är roligt. Programmering är alltså inte något jag jobbar med annars, och så har det heller aldrig varit.

Därför är det oerhört roligt med all positiv respons! Antalet installationer av version 0.3.1 är nu uppe i över ett tusen installationer, vilket jag upplever som ganska mäktigt. Programmet verkar dessutom fungera någorlunda problemfritt för nio av tio. Det duger förstås inte, men annat hade jag inte väntat mig. Det är inte utan anledning jag kallar det för en alfa-version.

Många hör också av sig med felrapporter. Detta är mycket värdefullt, inte minst eftersom det visat sig att programmet är litet nyckfullt, på så sätt att problem uppenbarar sig bara under särskilda omständigheter eller enbart på vissa datorer. Jag hade aldrig haft någon chans att hitta sådana fel utan hjälp, eftersom det "fungerar för mig". Tack!

Men nu till själva saken.

H.VHS 0.3.2, någon gång i höst

Någon gång under hösten kommer jag att försöka trolla fram version 0.3.2. Den har från början varit planerad att enbart innebära buggfixar. Inga nya funktioner, inga omstruktureringar av programkoden och inte heller något annat. I allt väsentligt kommer det också att bli just så – 0.3.2 ska bli som 0.3.1, fast det ska fungera för 99 av 100, istället för bara 9 av 10.

Jag kommer även att se över möjligheterna att slänga in en provisorisk funktion för nedladdning av undertexter, eftersom jag anser att avsaknaden av den funktionen är en akut brist. Tyvärr är det dock inte särskilt sannolikt att det finns något så pass smidigt sätt att implementera detta, att det är värt tiden. Jag kommer nämligen ändå att få göra om allting till version 0.4.

Version 0.4, någon gång i vår

Till den här versionen är ganska omfattande förändringar på gång. Programmets innanmäte ska mer eller mindre rivas ut i sin helhet, för att ersättas av något bättre strukturerat. Den nuvarande koden var från början nämligen bara tänkt som ett litet experiment, och lider därför av flera designmässiga problem, som bara kommer att bli än smärtsammare ju mer programmet växer. Därför är det bäst att göra detta så tidigt som möjligt i den fortsatta utvecklingen.

Rent programmeringsmässigt kommer den nya koden att innebära flera fördelar. Några av dem förtjänar ett särskilt omnämnande.

Flera program per sida

Det kommer bli betydligt enklare att göra skrapningsmoduler som hittar flera program på samma gång. En liknande funktion finns egentligen redan i 0.3.1, men den är avstängd eftersom det till en mycket hög grad fungerade sådär. I och med den här förändringen kommer det bli rätt enkelt att implementera stöd för sajter sådana som Sveriges Radio.

Nytt skriptspråk för skrapning

Visserligen är det redan i version 0.3.1 på det viset att skrapningsmodulerna definieras i fristående textfiler. Detta innebär att man själv, åtminstone i teorin, kan lägga till stöd för nya sajter efter förmåga och behag, utan att vänta på några uppdateringar från mig.

Men tyvärr är det inte mycket mera än just en teoretisk möjlighet. Även den här delen av programmet har i dagsläget karaktären av ett experiment som urartat, och jag rekommenderar ingen att ens försöka sig på att göra egna skrapningsdefinitioner. (Om man nu inte vill få sig ett gott wtf.)

Med version 0.4 kommer det här skriptspråket att göras om fullständigt, och när version 0.5 släpps kommer det att "frysas", vilket innebär att senare versioner av H.VHS kommer kunna använda samma skrapningsdefinitioner som 0.5. Till dess ska det alltså bli förhållandevis enkelt att lägga till egna sajter.

Gemensam utveckling

I samma veva kommer jag även att ordna något slags introduktion till hur språket fungerar, samt någon typ av tjänst där man kan dela med sig av egna definitioner, liksom hämta sådana som andra gjort. Det finns en del säkerhetsmässiga aspekter av detta som kommer behöva redas ut också.

Förhoppningen med detta är givetvis att H.VHS ska fungera med långt fler sajter än de jag har tid att ordna på egen hand. Ur min synvinkel är det här helt klart den mest spännande aspekten av var programmet är på väg. :-)

Fristående back-end

Vad detta i praktiken betyder är att allt verkligt arbete som H.VHS gör – skrapning, hämtning och så vidare – kommer skötas av ett fristående, plattformsoberoende bibliotek (som går under täcknamnet Hugg.Play). Därefter kommer jag att underhålla två versioner av programmet – dels en grafisk, i stil med vad H.VHS är i dag, och dels en textbaserad i stil med vad SVTPlay.sh är. SVTPlay.sh kommer därigenom att ersättas.

Det kommer också bli förhållandevis enkelt, för den som känner sig manad, att ta fram någon annan front-end, till exempel i form av ett webbläsarplugin, eller integrera Hugg.Play i något annat program.

Som ni kanske förstår, så kan det dröja ett tag innan jag hinner med att genomföra allt detta. Om jag gissar optimistiskt så kommer 0.4 att dyka upp i januari, men det kan utan vidare komma att ta mycket längre tid. Det är därför 0.3.2 ska dyka upp så snart som möjligt, och skulle det visa sig nödvändigt kommer fler släpp i 0.3-serien i väntan på 0.4.

Buggar och nya funktioner kan rapporteras, efterfrågas och följas på Mantis-installationen på Huggpunkt.org. Inom kort ska den även uppdateras så att det som står där stämmer överens med vad jag nyss lagt fram. Givetvis går det också bra att framföra synpunkter och förslag via epost, eller genom att lämna en kommentar här nedanför.

Ha det gott!

Lisp hackers have all defun

Innan jag börjar babbla, vilket jag för en gångs skull kommer göra till en alldeles synnerlig grad i det här inlägget, så kommer här litet kuriosa.

XKCD1

"Lisp has jokingly been called 'the most intelligent way to misuse a computer'. I think that description is a great compliment because it transmits the full flavor of liberation: it has assisted a number of our most gifted fellow humans in thinking previously impossible thoughts."

Edsger Dijkstra

"SQL, Lisp, and Haskell are the only programming languages that I've seen where one spends more time thinking than typing."

Philip Greenspun

Så. Medan jag de senaste veckorna lagt alldeles för mycket tid på att lära mig alldeles för litet av C++ och Qt, särskilt i proportion till mängden kod som samtidigt blivit till, så har jag förstås funderat över hur jag ska göra med projektet (H.VHS) i framtiden. Två idéer har därmed klarnat.

  1. Att separera fram- och bakändorna är värt besväret, inte minst därför att jag förr eller senare kommer vilja ha samma funktionalitet, fast i terminalformat.
  2. Metoderna för att trolla fram information om strömmar, både sådan avsedd för människor och sådan avsedd för nedladdare, bör kunna skrivas på ett så aktivt och "dynamiskt" sätt som möjligt.

I Qt är den till synes enklaste lösningen på det andra problemet att exponera QObject för QScript kod, vilket lämpligen görs på ett sådant sätt att metoderna tycks vara skrivna i ett någorlunda lättfattligt, specialiserat skriptspråk, "dynamiskt" i den bemärkelsen att man kan ändra i metoderna utan att behöva kompilera om själva programmet, och att metoderna kan uppdateras genom att man helt enkelt ersätter metod-filer med nya versioner som hämtas från något repository.

I praktiken innebär detta att H.VHS blir inte mycket mera än en glorifierad skripttolk. Vid den punkten stöter tanken alltså samman med följande lilla regel, som tillskrivs samme Philip Greenspun som yttrat det sista bland citaten ovan.

"Greenspun's Tenth Rule of Programming: any sufficiently complicated C or Fortran program contains an ad hoc informally-specified bug-ridden slow implementation of half of Common Lisp."

Förvisso skulle jag bli omätligt imponerad av mig själv om jag lyckades implementera hälften av Common Lisp, hur illa det än fungerade, men ännu hellre slipper jag att ens komma i närheten. Beroende på hur man ser på saken så är Common Lisp ett mycket litet eller ett mycket stort programspråk – det beror på om alla funktionsbibliotek ska räknas till språket eller inte. (Parallellt kan man fråga sig om till exempel strftime() är en del av C eller inte.)

Man kan dessutom nöja sig med mycket mindre än halva Common Lisp. För flera år sedan gick jag ett par duster med språket (en av flera utmärkta böcker finns online här), men lyckades båda gångerna glömma allting eftersom andra intressen tog överhanden. Tredje gången jag försökte så gick jag en annan väg, och provade Scheme i stället. Scheme är en Lisp-dialekt, sådana finns det flera, och dessutom en relativt liten och konsekvent sådan. Nu är det mitt favoritspråk.

Scheme passar också perfekt för det jag kommit fram till att bakändan av H.VHS ska göra. Det är visserligen ett tolkat skriptspråk, men kan i regel kompileras till maskinkod. Samtidigt behåller i regel kompilerade Scheme-program möjligheten att tolka skriptfiler. Till på köpet är kärnan i vad som gör Lisp till Lisp att det är ett idealiskt språk att göra nya språk i. Det passar helt enkelt perfekt.

En annan fördel, kanske den största, är att om man använder något Lisp-språk, så sällar man sig till den kanske enda gruppen kodare som är självgoda nog att se ned på både Python och Perl.

Den stora nackdelen är förstås att det är så få som pratar Scheme, eller Lisp över huvud taget. Och jag kan väl erkänna att när jag inte lekt med språket på något år, så lyckas inte heller jag se skogen för alla parenteser.

XKCD2

Vad är det här med parenteserna då? Tja, "vanliga" programspråk, alltså alla de som (på ett ungefär) härstammar från FORTRAN (1954) i stället för LISP (1958), de körs på ett sådant sätt att man börjar med den första raden, och trallar sedan vidare tills man nått den sista.

Lisp är däremot (mer eller mindre) funktionellt, tillsammans med några andra udda fåglar, och körs inifrån-och-ut. Kanske beror skillnaden på att FORTRAN kom till som en abstraktion för hur datorer faktiskt fungerade, medan LISP egentligen bara var tänkt som ett mindre klumpigt alternativ till den helt teoretiska Turing-maskinen. Lisp står närmare matematiken än tekniken. För de flesta andra språken, bland annat just C++, gäller definitivt det motsatta.

Ett exempel. Av någon anledning vill jag ha en funktion som tar emot ett heltal x och returnerar en jeopardy-fråga, enligt följande regler.

x är positivt och jämnt: "Vad är roten ur [x*x]?"
x är negativt och jämnt: "Vad är noll minus roten ur [x*x]?"
x är udda: "Vad är roten ur [(x-1)*(x-1)] plus ett?"
x är 0: "What is mind? No matter. What is matter? Never mind."

Utan att försöka skapa särskilt elegant kod i någotdera språk, utan snarare demonstrativ, så kommer här varianter i C++ och Scheme.

Här är utmatningen.

Vad är roten ur 16 plus ett?
Vad är noll minus roten ur 1?
Vad är roten ur 40000?
What is mind? No matter. What is matter? Never mind.
Vad är roten ur 4 plus ett?

Vad hände förresten där på slutet? ;-)

Här är en Scheme-variant:

Körning resulterar i (nästan) samma output:

Vad är roten ur 16 plus ett?
Vad är noll minus roten ur 1?
Vad är roten ur 40000?
Vad är roten ur 4611686009837453316 plus ett?

Som synes så kan Scheme, till skillnad från C++, hantera tal i vilken storlek som helst, så länge datorns minne räcker.

Förutom parenteserna, och det faktum att ett funktionsanrop i Scheme blir (jeopardy x) i stället för jeopardy(x), så lägger en obekant betraktare ganska snabbt märke till prefix-notationen. Det som skrivs "5+6+7+8.5" i språk som C++ skrivs i Scheme alltså i stället "(+ 5 6 7 8.5)".

De här två sista skillnaderna är kanske allt som egentligen håller folk borta från Scheme, har jag en känsla av. Och kanske ett par saker till: rekursion och lambda.

Fundera över följande lilla söta snutt C++ (modifierad från Wikipedia), som räknar ut summan av fibonacci-seriens första 7 tal:

Det ser visserligen litet märkligt ut för att vara C++, men så vitt jag kan förstå så är det numera giltigt. Men helt säker är jag inte. Resultatet blir i vilket fall att summan av talen i termer skrivs ut. [&](int x) { total += x; } är en anonym funktion, en lambda, som skapas där den står och sedan försvinner.

På Scheme kan man säga samma sak så här:

(let loop …) är för övrigt en omskrivning av en omskrivning av ett lambda-anrop. I själva verket skapas här en anonym funktion som tar emot en parameter ("termer"), sedan tilldelas variabeln loop en pekare till funktionen, så att den till sist kan anropa sig själv med hjälp av namnet loop. (Värt att tänka på i sammanhanget är att i Scheme så är alla variabler pekare, eller rättare sagt: De är symboler.)

Tack vare att rekursionen sker på slutet av funktionen, så blir det ändå en iteration, precis som med for_each, eftersom den nya instansen av funktionen kan ersätta den föregående i minnet utan att något går förlorat.

Men nu gör jag inte Scheme någon fullständig rättvisa. En något mera exakt motsvarighet till C++-koden ovan ser ut så här:

Men eftersom vi, som första parameter till apply, vill skicka en funktion som returnerar summan av sina parametrar, så är det förstås egentligen enklare att bara använda funktionen + direkt, i stället för att stoppa den i en lambda som ovan.

Om detta är möjligt ens i den nya C++0x-standarden vet jag faktiskt inte. Och det finns förstås massor av roligare saker man kan göra än så här. Scheme-kod är strukturerad i sitt eget data-format, vilket innebär att alla Scheme-program också är nästlade listor (motsvarande arrays eller vektorer). Kombinationen av detta och att kompilerad Scheme-kod kan köra icke-kompilerad Scheme-kod, medför att man kan exponera hela programspråket för användaren på det här viset:

Och – självfallet – att alla Scheme-program kan programmera om sig själva under tiden de körs. Eftersom jag vill pressa fram något slags skriptspråk till bakändan på H.VHS, så kan något annat knappast passa bättre; här är ju en fullständig programspråksmiljö redan på plats.

Att jag sedan tänker sätta ett filter mellan eventuella skript-kodare och (read) hör väl till saken – det finns sätt att skripta man hellre ber andra lära sig än det frenetiskt parentetiserande.

Hur, till slut, kan man integrera Scheme med C++-kod? Relativt lätt: Chicken Scheme är en Scheme-implementation som inte kompilerar direkt till maskinkod, utan till C-kod som sedan kan köras genom till exempel gcc. Det är alltså enkelt att arbeta med eller åt C-funktioner, liksom det är lätt att inkludera C-stumpar i Scheme-koden.

Något annat som är kul med Scheme är att man faktiskt får något att blogga om. Nästa gång det händer lovar jag att slänga fram något mera strukturerat innehåll än det här.

H.VHS 0.3.1, Handshake 9 och Qt-knöggel

Även om jag skrivit program som gör fler och mer avancerade saker än H.VHS, så har inget av dem någonsin blivit så omfattande. Förmodligen beror det inte minst på att jag brukat använda Scheme, medan detta är skrivet i C++, och givetvis att jag än så länge kodar hellre än smart i Qt/C++/Windows-miljön.

Men det ska inlägget inte handla om. När jag hade rivit ut och ersatt mer eller mindre allt som sker bakom kulisserna i programmet, lyckats knyppla ihop hämtningsmetoder för TV3, TV4, TV6 och TV8, och därför trodde mig vara färdig att släppa nästa version, så dök två problem upp.

  1. TV4 Play vägrade plötsligt komma överens med rtmpdump (och därför även librtmp, som H.VHS använder).
  2. På en helt färsk dator syntes inga SVG-ikoner i verktygsfälten.

Lösningen på det första problemet var enklare än jag tänkt mig: De senaste, inofficiella versionerna av librtmp har inga problem att hantera "Handshake 9"-problemet, som det visst kallas, så lösningen fick bli att rulla en egen librtmp-dll. Lätt värt besväret, eftersom programmet dessutom blir av med den statiska länkningen till PolarSSL, som är GPL-licensierat, till förmån för OpenSSL, som passar bättre in i licensblandningen.

Det krångligare problemet var att lista ut hur man egentligen får Qt att snällt ladda SVG-grafik. Frågan har ställts massor av gånger på nätet, men inga svar verkar ha hjälpt de stackarna som försökt. Det hela försvåras dessutom av att på datorer där hela Qt finns installerat så fungerar allting som det ska – det är först på användarsidan som felen uppstår.

Så steg ett är att se till att själv drabbas av samma problem redan i produktionsmiljön.

  1. Infoga följande kod i main().

Det är de tre raderna i krullklasen som gör arbetet. Eftersom det blir stökigt att lagra alla stödfiler direkt i bygg-katalogen (där även alla objektfiler hamnar) så vill jag inte ha det här bekymret annat än när jag bygger en release, som jag sedan kopierar till en katalog ("dist") där QtGui4.dll och alla de andra ligger och väntar.

Med detta uppstår problemet även på min knädator. Nästa steg är förstås att också lösa det.

  1. SVG-grafik kräver QtSvg4.dll, som i sin tur vill ha QtXml4.dll, så de ska ligga direkt i dist-katalogen, tillsammans med binären.

    dist\HuggpunktVHS.exe
    dist\QtGui4.dll

    dist\QtSvg4.dll
    dist\QtXml4.dll

Man kunde tro att det skulle räcka – minns jag inte fel så gjorde det faktiskt det någon gång tidigare – men icke!

  1. Till sist måste ett plugin distribueras tillsammans med binären, och de måste placeras riktigt i förhållande till den. Så här:

    dist\imageformats\qsvg4.dll

Det ska räcka. Men beroende på vad man vill göra kan även de följande filerna vara bra att skicka med, inte minst därför att de behövs för att komponenter som QWebView ska kunna använda diverse bildformat:

dist\imageformats\qtiff4.dll
dist\imageformats\qmng4.dll
dist\imageformats\qgif4.dll
dist\imageformats\qico4.dll
dist\imageformats\qjpeg4.dll
dist\iconengines\qsvgicon4.dll

Så principen tycks vara, att när man som i steg 1 sagt åt Qt att enbart leta efter stödfiler i den katalog som binären ligger i, så begränsar sig Qt lydigt till att leta efter sina moduler där. Men när det kommer till plugins, så måste de ändå ligga i korrekta underkataloger till dist-katalogen.

Och vilka de korrekta underkatalogerna är, det ser man för övrigt här:

C:\QtSDK\Desktop\Qt\4.7.3\mingw\plugins

Så här är det inte utan att man börjar fundera på att bygga allting statiskt i stället.

Uppdatering 2 juli. Så pass tveksam kände jag mig efter att faktiskt ha tänkt över det här Qt-knögglet, att jag tänkte om. Någonstans kring version 0.4–0.5 kommer en stor del av programmet (den jag kallar Hugg.Play) att skrivas om i Scheme. C++ är helt enkelt inte tillräckligt roligt. Eventuellt kommer Qt-beroendet försvinna helt och hållet vid något senare tillfälle.

Det finns dock en del buggar i version 0.3.1 som vore bra att få ordning på så snart som möjligt, så 0.3.2 kommer släppas som planerat.

Nu kommer Huggpunkt VHS

Skärmdump

Det gick inte att hålla sig borta. Efter ett par veckors intensivkurs här hemma i C++, OO och GUI (med hjälp av Qt, fått nog än?) har jag lyckats göra om nedladdningsverktyget från grunden.

Det känns som jag knappast kunde önskat mig en bättre introduktionskurs än att återskapa funktioner från fullständigt utdöd teknik. :-)