Czasami (może nawet trochę częściej) jeśli ściąga się filmy i napisy osobno występuje problem synchronizacją. Strasznie upierdliwa sprawa. Oczywiście zwykle jest to proste przesunięcie, w całym filmie, lub na przykład po każdej przerwie na reklamy, więc wystarczy trzymać paluchy na z i x (oczywiście, o ile używa się mplayera). Jest to oczywiście półśrodek, zwłaszcza jeśli owe filmy archiwizujemy i/lub dzielimy się nimi z innymi. Dlatego też napisałem prosty plugin dla vima, który pozwala bardzo łatwo i przyjemnie (najlepiej w połączeniu z mplayerem, w którym można łatwo kontrolować numer odtwarzanej w danym momencie klatki) poradzić sobie z problemem napisów pojawiających się za wcześnie lub za późno.
" Submaker -- video subtitle editor ver 20090611 - early one
" (c) by Bartosz Pastudzki 2009
" I wrote this file. As long as you retain this notice you can do whatever you
" want with this stuff. If we meet some day, and you think this stuff is worth
" it, you can buy me a beer in return.
" Split subtitle line into list of content : [start,end,text]
function SplitLine (line)
let cline = getline (a:line)
let start = match (cline, "{")
if (start == -1)
throw "start1"
endif
let end = match (cline, "}", start)
if (end == -1)
throw "end1"
endif
let subStart = str2nr (cline[start+1 : end-1])
if (start == -1)
throw "start2"
endif
let start = match (cline, "{", end)
let end = match (cline, "}", start)
if (end == -1)
throw "end2"
endif
let subEnd = str2nr (cline[start+1 : end-1])
let content = match (cline, '\S', end+1)
return [subStart, subEnd, cline[content :] ]
endfunction
" Function opposite to SplitLine -- create valid subtitle line from content list
function JoinContent (content)
return "{". a:content[0] . "}{" . a:content[1] . "}" . a:content[2]
endfunction
" Function to find out if subtitle file is valid
function Validate ()
for lineNum in range (1,line ("$"))
try
call SplitLine (lineNum)
catch /.+/
return lineNum
endtry
endfor
return -1
endfunction
" Low level version of Delay. It takes numbers of lines too. It's not intended
" to be called directly by user.
function DelayLL (delay, start, end)
for line in range (a:start,a:end)
let content = SplitLine (line)
let content[0] += a:delay
let content[1] += a:delay
let nline = JoinContent (content)
call setline (line, nline)
endfor
endfunction
" Delay selected part of text
function DelayS (delay)
call DelayLL (a:delay, a:firstline, a:lastline)
endfunction
" Standard delay, from current line to the end
function Delay (delay)
call DelayLL (a:delay, line ("."), line ("$"))
endfunction
function GoToFrame (frame)
let linenum = 0
while 1
let linenum += 1
let last = SplitLine (linenum)
if (last[1] >= a:frame)
break
elseif (linenum == line ("$"))
break
endif
endwhile
call setpos (".", [0,linenum,1])
endfunction
Aby móc używać go w dowolnym momencie, należy zapisać go na dysku, w odzielnym pliku i do pliku .vimrc dodać linijkę "source ". Funkcjami, które służą do używania z zewnątrz są Delay, DelayS i GoToFrame.
Funkcja Delay opóźnia wszystkie napisy od linii w której znajduje się kursor do końca. Na przykład wywołanie ":call Delay (-5)" przesunie je o 5 klatek w tył.
Funkcja DelayS działa tak samo jak Delay, z tą różnicą przesuwa tylko te zaznaczone.
Funkcja GoToFrame przesuwa kursor do linijki, w której znajduje się napis, który się wyświetli się w klatce podanej w parametrze bądź następnego, jeśli w tej klatce nie ma żadnego napisu.
Należy zaznaczyć, że program działa tylko dla napisów w formacie "{start}{koniec} napis" i nie zadziała poprawnie jeśli liczba klatek przekroczy zakres 32-bitowej liczby ze znakiem (231).
Mam nadzieję, że komuś się przyda, będę wdzięczny za wszelkie uwagi i sugestie, ewentualnie łatki.
Już od dłuższego czasu używam LaTeXa i bardzo sobie go chwalę. W sumie, myślę, że chyba nikogo kto miał przyjemność z tego narzędzia korzystać, nie trzeba przekonywać o jego licznych zaletach. W tym semestrze jeszcze częściej go używam, ze względu na sprawozdania z laboratoriów z miernictwa. Nie dbam o to jak to robią inni, ale ja sobie cenię estetykę dokumentów (trzeba się naprawdę postarać, żeby LaTeX coś złożył brzydko) i bardzo wygodne składanie wzorów matematycznych, tudzież wcale niezły system referencji. Jest jednak luka w funkcjonalności (oczywiście nie uważam, że to źle, przeciwnie) LaTeXa jaką odczułem przy okazji pisania wspomnianych sprawozdań, nie daje dobrego sposobu na generowanie wykresów. W tym miejscu z pomocą przychodzi GNU R.
GNU R stanowi bardzo dobre uzupełnienie dla LaTeXa, głównie dlatego, że i ten opiera się o napisanie paru linijek kodu na podstawie którego można wygenerować żądany wykres w jednym z kilku dostępnych formatów (między innymi svg, png). Można też po prostu wyświetlić ten wykres na ekranie. Jak dla mnie to świetna sprawa.
Prosty wykres to kwestia paru linijek, na przykład:
png(file="test.png", width=400, height=400) ib <- seq(50,300, by=50) delta <- c(5.7, 5.3, 3.68, 3.24, 3.30, 1.93) plot(ib, delta, xlab="Ib[mA]", ylab="Bład względny[%]") abline(6.45, -0.015)
W sumie wszystko właściwie widać. Określamy output (plik.png). Tworzymy dwie listy (ib i delta) które wykorzystamy jako współrzędne dla naniesionych punktów. W pierwszym przypadku użyta została funkcja seq(x,y, by=z), która zwraca tablicę elementów od x do y co z. W przypadku delta użyłem funkcji c(e1, e2, e3, ..., en), która po prostu tworzy tablicę z danych argumentów. W następnej linijce jest funkcja plot, która rysuje wykres z naniesionymi punktami, których kolejne współrzędne x są wzięte z ib, a współrzędne y z delta. Dalej znajdują się jeszcze opcjonalne wartości xlab i ylab zawierające opisy osi. Pominięto też parametr, którym można ustawić, by punkty były połączone łamaną. Na sam koniec rysuję prostą przy pomocy funkcji abline, która jest nieco nieintuicyjna ze względu na to, że przyjmuje wartość y dla x = 0 i współczynnik kierunkowy, niezależne od skali i przedziałów jakie będą przyjęte dla poszczególnych osi, ale w sumie to dobrze, że tak jest.
W wyniku przetworzenia takiego kodu otrzymamy coś takiego:

Jest to oczywiście kwestia gustu, ale jak dla mnie jest to rozwiązanie szybsze, wygodniejsze i łatwiejsze w modyfikacji od konkurencji, dajmy na to Microsoft Excel.
Oczywiście jest to bardzo mała cząstka możliwości tego narzędzia (szczerze mówiąc sam ledwo je poznałem ;)), można wygenerować całą masę fajnych wykresów (tu odsyłam do polecenia demo(graphics) w interpreterze GNU R, który pokaże kilka wykresów – takich już pro, nie to co ja wam zaserwowałem ;p – wraz z kodami źródłowymi). To by było na tyle, ale jednak tekst ten powstał jedynie po to by zasygnalizować, że taka zabawka istnieje i nie jest zbyt popularna, a przecież bywa dość przydatna.
Jakiś czas temu zaprezentowałem mój pierwszy skrypt PHP, przygoda z tym językiem szybko się zakończyła. Przyszedł czas na perla – aż wstyd się przyznać, że dopiero teraz. Tak czy inaczej, po dość oględnym przestudiowaniu języka przyszedł czas na chrzest bojowy - napisanie skryptu, który generowałby menu na podstawie /usr/share/applications/. Być może jest już coś takiego, ale jak wszystkim dobrze wiadomo, jestem bardzo leniwy. Zadanie może nie wydające się być trudnym (choć pisanie w tego C to niezbyt kusząca perspektywa), aczkolwiek dobrze zweryfikowało to co zrozumiałem z kursu.
Zacznijmy od tego, jak ten skrypt działa. Mianowicie, parsuje on plik z szablonem (~/.fluxbox/menu.templ) i pliki *.desktop z /usr/share/applications/ i na podstawie informacji w nich zawartych buduje menu. Skrypt nie jest jeszcze doskonały i będzie wymagał dość radykalnego przerobienia, tak by elementy mogły być sortowane alfabetycznie i obsługiwały lokalizacje. Niemniej jednak działa i dość dobrze i szybko (w moim przypadku 0,03s) wypełnia swoje zadanie.
Pliki szablonowe mają bardzo prostą budowę – są to zwyczajne pliki menu fluxboksa, wzbogacone o wtrącenia w nawiasach ostrych (muszą być w oddzielnych linijkach), informujących program, jak interpretować /usr/share/applications/. Mój plik szablonu wygląda następująco i wyczerpuje opcje, które można póki co wtrącić:
<setterm urxvt -font -*-fixed-medium-r-*-*-20-*-*-*-*-*-*-2 -fg white -bg black>
<show-groups Network,Utility,AudioVideo,Game,Application,Office,Graphics,Science,System>
[begin] (Fluxbox)
[encoding] {UTF-8}
[exec] (uruchom) {fbrun}
<addterm terminal>
[exec] (WWW) {seamonkey}
[exec] (e-mail) {seamonkey --mail}
[exec] (vim) {gvim}
<insert-here>
[exec] (uaktualnij menu) {/home/bartek/fluxmenugen.pl}
[restart]
[exit]jak widać, nie jest tego dużo:
Na sam koniec natomiast gotowy skrypt, który udostępniam do dowlonego użytku.
#! /usr/bin/perl -w
$sdir = "/usr/share/applications";
$sext = ".desktop";
$favterm = "urxvt";
$destdir = $ENV{'HOME'}."/.fluxbox/";
open (TEMPLATE, $destdir."menu.templ") || die "Failed to open template file.";
if(open (DESTINATION, $destdir."menu"))
{
open (BACKUP, ">".$destdir."menu~");
while ( <DESTINATION> )
{
print BACKUP;
}
close (BACKUP);
}
close ( DESTINATION );
open (DESTINATION, ">".$destdir."menu");
opendir ( SHORTCUTS, $sdir ) || die ("failed to open $sdir.");
@shortcuts = grep { m/$sext$/ } readdir ( SHORTCUTS );
closedir ( SHORTCUTS );
%groups = ();
$ungrupped = "";
foreach $shortcut ( @shortcuts )
{
open ( SHORTCUT, $sdir."/".$shortcut);
while ( <SHORTCUT> )
{
if ( m/^Name=(.+)(\s*)$/ )
{
$name = $1;
}
elsif ( m/^Exec=(.+)(\s*)$/ )
{
$cmd = $1;
}
elsif ( m/^Terminal=(.+)(\s*)$/ )
{
$term = $1;
}
elsif ( m/^Categories=(.+)(\s*)$/ )
{
@cats = split ( /;/, $1 );
}
}
if ( !defined ( $name ) || !defined ( $cmd ) )
{
print "Failed to find essentional values for $shortcut\n";
}
else
{
if ( defined ( $term ) && $term eq "true" )
{
$cmd = $favterm." -e \"".$cmd."\"";
}
$stext = "[exec] ($name) {$cmd}\n";
if ( !defined ( @cats ) )
{
$ungrupped .= $stext;
}
else
{
foreach $cat ( @cats )
{
$groups{$cat} .= $stext;
}
}
}
}
while ( <TEMPLATE> )
{
if ( @tags = m/<(.+)>/g )
{
foreach $tag (@tags)
{
if ( $tag eq "insert-here" )
{
if ( defined ( @tagWhitelist ) )
{
foreach $group ( @tagWhitelist )
{
print DESTINATION "[submenu] ($group)\n";
print DESTINATION $groups{$group};
print DESTINATION "[end]\n"
}
}
else
{
foreach $group ( keys %groups )
{
print DESTINATION "[submenu] ($group)\n";
print DESTINATION $groups{$group};
print DESTINATION "[end]\n"
}
}
print DESTINATION $ungrupped;
}
elsif ( $tag =~ /^setterm (.+)/ )
{
$favterm = $1;
}
elsif ( $tag =~ /^addterm (.+)/ )
{
print DESTINATION "[exec] ($1) {$favterm}\n";
}
elsif ( $tag =~ /^show-groups (.+)$/ )
{
if ( defined ( @tagWhitelist ) )
{
push ( @tagWhitelist, split ( /,/, $1 ) );
}
else
{
@tagWhitelist = split ( /,/, $1 );
}
}
elsif ( $tag =~ /^show-all/ )
{
undef ( @tagWhitelist );
}
}
}
else
{
print DESTINATION;
}
}
Programuję już od kilku lat, ale jakoś się złożyło, że nie pisałem jeszcze aplikacji opartych o HTTP. Postanowiłem to zmienić i odejść na trochę od programowania w Rubim(zdaję sobie sprawę, że istnieje RoR, lecz chciałem zacząć od czegoś klasycznego i chyba jednak trochę łatwiejszego, jak PHP. Dlatego też postawiłem sobie serwer Apache(swoją drogą też bardzo przydatne doświadczenie) i zacząłem próbować.
Szybko też znalazł się problem, którego rozwiązanie przez prostą aplikację PHP wydawało się całkiem rozsądne. Mianowicie w domu mam dwa komputery(jeden mój i jeden brata) i tylko jedną drukarkę. W prawdzie mógłbym udostępnić serwer CUPS, ale po pierwsze brat musiałby instalować sterowniki do drukarki(tak przynajmniej mi się wydaje ;p), a ponadto zdarza mu się używać (sic!) Windows, co jeszcze bardziej komplikuje sprawę.
Oczywiście móglibyśmy użyć Samby, ale jesteśmy zbyt leniwi, żeby konfigurować Sambę na obu komputerach tylko po to, żeby od czasu do czasu coś wydrukować. Dlatego stanęło na wysyłaniu plików do druku pocztą. To upierdliwa sprawa, stąd ten skrypt(może się komuś przyda :)):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl">
<head>
<title>Stronka!</title>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<?php
$uf = $_FILES['upfile'];
if(isset($uf['name']))
{
if('php' == ($wynik = substr($uf['name'],-3,3)))
{
echo '<p>Wybacz, nie odbieram plików PHP</p>';
}
else
{
rename($uf['tmp_name'], 'udload/'.$uf['name']);
}
}
if(isset($_GET['delete']))
{
unlink('udload/'.$_GET['delete']);
echo '<p>Usunięto plik "'.$_GET['delete'].'"!</p>';
}
echo '<p>Cześć! Dzięki tej stronie możesz wysyłać mi dowolne pliki,
ściągać te, które Ci udostępniam, a także wysyłać pliki do drukowania przez
moją drukarkę.</p>';
if(isset($_GET['print']))
{
if('.ps' !== ($wynik = substr($_GET['print'],-3,3)) &&
! isset($_GET['sure']))
{
echo 'Wybrany plik ma rozszerzenie inne niż *.ps. Jeśli jesteś
pewien, że zawiera dane PostScript,
<a href="index.php?print='.$_GET['print'].'&sure=1">kliknij tutaj</a>';
}
else
{
$out = array();
$ret = 0;
exec('lp /srv/http/udload/'.$_GET['print'], $out, $ret);
if($ret == 0)
{
echo '<p>Drukowanie powiodło się.</p>';
}
else
{
echo '<p>Drukowanie nie powiodło się. Zawartość stdout:
<br/>'.$out.'</p>';
}
}
}
$cdir = dir('udload');
if($cdir !== false)
{
$n = 0;
while(false !== ($file = $cdir->read()))
{
if($file != '.' && $file != '..')
{
if($n == 0)
{
echo "<h1>Pliki na serwerze:</h1><p><ol>";
$n = 1;
}
echo '<li><a href="udload/'.$file.'">'.$file.'</a>
<a href="index.php?delete='.$file.'"><sup>[usuń plik]</sup></a>
<a href="index.php?print='.$file.'"><sup>[drukuj plik]</sup></a></li>';
}
}
if($n == 1)
{
echo "</ol></p>";
}
$cdir->close();
echo '<h1>Wyślij plik</h1><p>Możesz wysłać plik o dowolnym
rozszerzeniu, oprócz *php o wielkości nie przekraczjącej 10<sup>6</sup>
bajtów:<br/><form enctype="multipart/form-data" method="post"
action="index.php"><input type="file" name="upfile" value=""/>
<input type="hidden" name="MAX_FILE_SIZE" value="1000000"/><br/>
<input type="submit" name="send"/></form>';
}
?>
</body>
</html>
Jakie wrażenia z zabaw PHP? Zdecydowanie przechodzę do RoRa, zwłaszcza po obejrzeniu filmów, które można zobaczyć na stronie projektu.
Nagrywanie systemów plików złożonych z dużej ilości katalogów przy użyciu growisofs jest nieco kłopotliwe, bo jest dużo pisania, głównie przez ustawianie graft pointów. GNOME baker natomiast nie oferuje mi zbyt wielkiego pola do popisu, jeśli chodzi o konfigurację (jestem ślepy, czy naprawdę nie można wybrać, czy użyć Jolieta czy Rock Ridge?). Dlatego też, powodowany wrodzonym lenistwem, postanowiłem coś z tym zrobić.
Mianowicie napisałem prosty skrypt, który za nas załatwi graft pointy, a nawet sprawdzi, czy należy użyć opcji -Z czy -M. Powodowany wspomnianym lenistwem zmiana opcji odbywa się poprzez edycję skryptu (w przedstawionej wersji instaluje tylko rock-ridge). Składnia natomiast jest prosta. W argumentach wpisujemy nazwy katalogów(można spokojnie używać gwiazdki). Natomiast parametry "-f" i "-d" odpowiednio informują program, czy dane dalej parametry oznaczają pliki, czy katalogi(domyślnie katalogi). Jeśli podamy nazwę katalogu wśród plików, zostanie nagrana jego zawartość. Po wprowadzeniu polecenia program wydrukuje linię poleceń i poprosi o zatwierdzenie. Skrypt wymaga growisofsa i cdrdao, jest napisany dla archa (więc może być konieczność zmiany /dev/sr0 na odpowiednią dla twojego systemu ścieżkę). Kto chce, niech bierze:
#! /bin/bash
if [ -n "$1" ]
then
echo Burner, enter with names of wanted directories to burn on /dev/sr0 with rock-ridge, and with no joliet
else
isempty=`cdrdao disk-info --device /dev/sr0 | grep "CD-R empty" | grep "no"`
if [ "$isempty" == "" ]
then
echo -n "growisofs -Z=/dev/sr0 -R --graft-points " > .burnertmp
else
echo -n "growisofs -M=/dev/sr0 -R --graft-points " > .burnertmp
fi
isfile=0
for x in "$@"
do
case $x in
"-f") isfile=1 ;;
"-d") isfile=0 ;;
*) if [ "$isfile" == 1 ]
then
echo -n "\"`echo $x`\" " >> .burnertmp
else
echo -n "\"`echo $x`\"=\"`echo $x`\" " >> .burnertmp
fi ;;
esac
done
cat .burnertmp
echo
echo "Is that ok? If so press ENTER, CTRL+C to cancel"
read
chmod +x .burnertmp
./.burnertmp
rm .burnertmp
fi
Jakiś czas temu czytałem co nieco na temat tego języka, przeczytałem co nieco o nim, o jego cechach i tym podobnych. Skończyło się na tym, gdyż w necie było tragicznie mało materiałów owym języku. Ostatnio pod wpływem wpisu GiMa zainteresowałem się nieco bardziej tym cackiem, ze względu na liczne bajery, które przedstawił GiM, ale do rzeczy.
Na samym początku zauważyłem, że jeśli chodzi o materiały to niewiele się zmieniło. Właściwie żadnych wartościowych kursów( na miarę "C++ bez cholesterolu" na przykład) nie znalazłem. Parę mało interesujących rozdziałów wikibooks( chyba będę musiał im dopomóc jak poszerzę swoją wiedzę nieco;p ), kilka nędznych tutoriali - to wszystko. Na szczęście znalazłem bardzo dobrą metodę nauki dla tego języka. Mam na myśli przejrzenie jego dokumentacji na stornie projektu. Muszę przyznać, że jest dobrze napisana, nie przynudza zanadto - użytkownik, który używał C, C++, czy Javy; z pewnością się tam odnajdzie( jeśli wszystkich liznął to już w ogóle).
Przejrzawszy ową dokumentacji wiedziałem już w zasadzie, jak programować w tym języku. Popisałem sobie trochę, póki co nie używając jeszcze wszystkich rzeczy, których nie było w C++ stwierdzając, że w D pisze się dość znacznie wygodniej niż w innych, których próbowałem. Szczególnie spodobały mi się kontrakty - są na prawdę przydatne.
Myślę, że nie ma tu jednak co się zbytnio rozpisywać - ten język łaczy w sobie nowoczesne udogodnienia takich języków jak Java, czy Python( choć daje jeszcze więcej niż te wymienione) z wygodą i szybkością C++a. Myślę, że trzeba tego po prostu spróbować :)