NoBe kérésére kezdtem el foglalkozni a témával. Tudom, azt mondtad hogy ne foglalkozzak vele, mert nem fontos annyira, de eszembe jutott, hogy sok helyen lehet alkalmazni azt a megoldást, hogy egy konfig fájlt szkripttel szerkesztünk - például ha webes felületet írunk.
(Ilyenkor sem érdemes például a /etc alatti dolgokat direktben írni a webroot alól futó szkripttel, de mondjuk ketté lehet választani, hoyg a webes alkalmazás egy adatbázisba ír, egy másik, esetleg magasabb jogokkal futó szkript pedig ezt az adatbázist figyeli és ha változás van, átírja a konfigot, esetleg újra is indítja a szolgáltatást ha kell.)
Persze NoBe nem akart ilyen webes felületet írni, de az elv kissé hasonló volt: végigmegyünk egy konfig fájlon, és csak egy adott részt cserélünk benne. Én az ilyeneket azért szeretem perlben leprogramozni, mert először is a perl minden általam ismert linuxon van (még beágyazottaknál is láttam!), tehát fut a legtöbb helyen, másrészről nekem az is fontos, hogy könnyen lefordíthatom ELF futtatható kóddá egyrészt a gyorsaság, másrészt pedig amiatt, mert nem szeretném ha belenyúlnának. :-) Hiszen az üzemeltető nem mindig egyezik meg a fejlesztővel.
Egy megadott sor cseréje
Legegyszerűbb eset, ha egyetlen sorra kell csak matchelnünk. Ha ez a sor nem választható el a többi konfig sortól egyértelműen, akkor viszont főhet a fejünk... Én nem szeretem a fájl leíró alapján való ellenőrzés nélküli felolvasást, viszont most mégis azt alkalmazom, az egyszerűség kedvéért. Megnyitjuk a fájlt, egyetlen while ciklussal végigmegyünk minden során. Ez a $_ változóba kerül bele, ami alapján remekül tudunk regexpelni. Keressük az app.load="akármi" sort, majd az akármit cseréljük le; egyelőre egy konstansra.
#!/usr/bin/perl
Látható, hogy lustaságom olyan mértékű hogy a fájlba mentést meg sem csináltam, hiszen az egész megoldható, ha így futtatjuk:
my $csere = "/home/bagoj/avi/0103.avi";
my $fajlnev = "jobfile.txt";
my $outstr = "";
open(IN, "< $fajlnev") or die("$fajlnev nem nyithato meg: $!\n");
while( <IN> ){
chomp;
if(/^app\.load\(\"(.*?)\".*$/){
$outstr.="app.load(\"$csere\");\n";
}
else{
$outstr.=$_."\n";
}
}
close(IN);
print $outstr;
perl akarmi.pl > uj_jobfile.txt
Három sor cseréje egy könyvtár alapján
Kicsiben kezdtük, nagyban folytatjuk: Tegyük fel van egy könyvtárunk, amelyből felolvassuk az "avi" kiterjesztésű fájlokat. Ezután felolvassuk a jobfile-t, amit szeretnénk sokszorozni. Ezek után elmentjük a job fájlokat különböző fájlokba. Szedjük ízekre az erre készült szkriptet:
opendir(IND, $konyvtar) || die("Cannot open directory");
Ez a rész, gondolom, elég egyszerű. Egy előre megadott könyvtárban keressük az .avi kiterjesztésű fájlokat (a kis- és nagybetűk akármilyen kombinációjában). Ha ilyet találunk, akkor beletesszük egy tömbbe a push() utasítással.
while( my $tmp = readdir(IND)){
next unless $tmp =~ /^(.*)\.[aA][vV][iI]$/;
push(@files,$tmp);
}
closedir(IND);
open(IN, "< $fajlnev") or die("$fajlnev nem nyithato meg: $!\n");
Ismét csak semmi nagy kaland - felolvasunk egy fájlt, és a sorait egy tömbbe tesszük bele, ezáltal a tömb egy-egy eleme a fájl egy-egy sora lesz, így könnyebb lesz majd sokszor végigmenni rajta (hiszen a job fájlt sokszoroznunk kell annyiszor, ahány avi-t találunk a könyvtárban).
while( <IN> ){
chomp;
push(@instr,$_);
}
close(IN);
A jobfájlban három helyen láttam, hogy csereberélni kell: a betöltésnél, a feliratfájlnál és végül az elmentésnél. Feltételezem, hogy a feliratfájl, illetve az elmentett .mp4 nevei meg fognak felelni egymásnak. Ezeket egy egyszerű regexpes cserével oldom meg:
$subfilename =~ s/\.avi/.srt/g;
A fájlnévben lévő ".avi" sztringet cserélem ".srt"-re. Ez nem ANNYIRA jó megoldás, mivel pl. a "ultimate.aviator.avi" nevű fáljnévből "ultimate.subator.sub" lesz. Ha valaki ezzel házi feladatozni szeretne, gondolja végig, hogyan lehet ezt regexppel kivédeni. Ha saját kútfőből nem megy, akkor biggyessze a sorvége ($) jelet a keresőmezőbe... :-) Azaz
$subfilename =~ s/\.avi$/.srt/g;
Most már csak az marad, hogy meg kell találni a cserélendő részeket, kivágni a környékéről a többi, szükséges szöveget, kicserélni a sztringet, összerakni az egészet. A legbonyolultabb példa a feliratfájl cseréje, ezért ezen mutatom be. Így néz ki a sor, amit végülis cserélni kell:
app.video.addFilter("subtitle","_fontsize=18","_subname=/file/path/valami0301.srt","_fontname=/usr/share/fonts/truetype/msttcorefonts/arial.ttf","_charset=ISO-8859-2","_baseLine=
.... stb. a sor vége nem lényeges már nekünk, csak annyiban hogy ott van. A regexpnél három dologra kell figyelni:
- Hogyan határozzuk meg a cserélendő részt? Ehhez kellenek olyan fix részek, amelyekhez tudunk igazodni. Például ilyen a sor elején az "app.video.addFilter", ami azonosítja hogy most egy filter sor jön. De ez nem elég, hiszen vannak ugyanilyen kezdetű más sorok is. Fix karakterlánc még a "_subname". Ezzel már egyértelmű.
- Találjuk el, melyek a megtartandó/cserélendő részek. Ezeket zárójelbe teszem, ezzel ún. visszahivatkozás mezőkbe másolódnak be, amiket tudok használni a továbbiakban. Ezek sorrendben a $1, $2, $3 ... $9 mezőkbe kerülnek be - ismerős a párhuzam az awk-val, remélem. Tehát a megtartandó részeket tegyük backreference-be! Ilyen minden információt tartalmazó rész, azaz a "subtitle", "_fontsize=18" illetve a sor teljes vége, ami az .srt után van.
- Nagyon kell figyelni a mohó illeszkedésre! Mindjárt látjátok, hogy a .srt fájl névcseréjéhez úgy illesztek, hogy a _subname= és a legközelebbi macskaköröm közötti sztriget veszem. De mivel a sor végén van még egy rakás macskaköröm, a regexp alapértelmezésben a legutolsó macskakörömre fog illeszteni, azaz szeretne mohón minél több sztringet "beharapni". Ezt védem ki a (.*?) illszetéssel; ezt kell használni a sima (.*), azaz nulla vagy több tetszőleges karakter helyett.
if(/^app\.video\.addFilter(.*)_subname=(.*?)\"(.*)$/){
A caret (^) a sor eleje, $ a sor vége. A (.*), (.*?) és az utolsó (.*) bekerülnek a $1, $2, $3 változókba. Ebből a $2 helyett az általunk generált fájlnevet írjuk be, a többit változatlanul visszaírjuk. Ekkor tehát a $1-ben ez lesz:
"subtitle","_fontsize=18","
A $2 tartalma:
/file/path/valami0301.srt
A $3-ban pedig a sor teljes további része, pontosabban egy macskaköröm kimarad hiszen azt felhasználtuk az illesztésre:
,"_fontname=/usr/share/fonts/truetype/msttcorefonts/arial.ttf","_charset=ISO-8859-2","_baseLine=...
Ha ezt összerakjuk, akkor ismét le kell írnunk, hogy "app.video.addFilter", hiszen azt "elhasználtuk", nem tettük bele változóba amiatt, mert azt fix részként használva illesztettünk. Ugyanez a helyzet a "_subname"-mel is.
Ha pedig olyan sor jön, amire nem kell illesztenünk, akkor egy sima "print"-tel kiírjuk.
Ezután már csak be kell zúzni a megfelelő nevű fájlba a $outstr-ben lévő eredményt:
open(OUT, "> $outfile") or die("$outfile nem nyithato meg: $!\n");
print OUT $outstr;
close(OUT);
A teljes szkript:
#!/usr/bin/perl
my $konyvtar="/home/bagoj/avi/";
my $fajlnev = "/home/bagoj/jobfile.txt";
my $outstr = "";
my @files;
my @instr;
opendir(IND, $konyvtar) || die("Cannot open directory");
while( my $tmp = readdir(IND)){
next unless $tmp =~ /^(.*)\.[aA][vV][iI]$/;
push(@files,$tmp);
}
closedir(IND);
if($#files == -1){ die("Nem találtam egy avi fájlt sem."); }
open(IN, "< $fajlnev") or die("$fajlnev nem nyithato meg: $!\n");
while( <IN> ){
chomp;
push(@instr,$_);
}
close(IN);
foreach $onefile (@files){
my $subfilename = $onefile;
my $savefile = $onefile;
my $outfile = $onefile;
$subfilename =~ s/\.avi/.sub/g;
$savefile =~ s/\.avi/.mp4/g;
$outfile =~ s/\.avi/_job.js/g;
$outstr = "";
foreach (@instr){
if(/^app\.load\(\"(.*?)\".*$/){
$outstr.="app.load(\"$konyvtar$onefile\");\n";
}
elsif(/^app\.video\.addFilter(.*)_subname=(.*?)\"(.*)$/){
$outstr.="app.video.addFilter$1_subname=$konyvtar$subfilename\"$3\n";
# app.video.addFilter("subtitle","_fontsize=18","_subname=/file/path/valami0301.srt"
}
elsif(/^setSuccess\(app.save(.*)$/){
$outstr.="setSuccess(app.save(\"$konyvtar$savefile\"));\n";
}
else{
$outstr.=$_."\n";
}
}
open(OUT, "> $outfile") or die("$outfile nem nyithato meg: $!\n");
print OUT $outstr;
close(OUT);
}
Esetleg Perl guruk szépíthetnek a kódon, ha gondolják... :-p
Ja, és a jobfile, amin dolgozunk, így néz ki (két nagyon hosszú sor volt, ezeket rövidítettem a végére írt "..."-tal. Bocs, de nem akartam ilyennel nyújtani a post hosszát):
//AD <- Needed to identify//
Jó szórakozást... és köszi a jobfile-t, majd hasznosítom, az én telefonom is viszi az mp4-et... :-)
//--automatically built--
//--Project: /home/norbi/.avidemux/jobs/valami0301.js
var app = new Avidemux();
//** Video **
// 01 videos source
app.forceUnpack();
app.load("/home/six/bin/avi/alma.avi");
//01 segments
app.clearSegments();
app.addSegment(0,0,39947);
app.markerA=0;
app.markerB=39946;
//** Postproc **
app.video.setPostProc(3,3,0);
app.video.setFps1000(23976);
//** Filters **
app.video.addFilter("mpresize","w=480","h=264","algo=0");
app.video.addFilter("addblack","left=0","right=0","top=4","bottom=4");
app.vide.addFilter("subtitle","_fontsize=18","_subname=/home/six/bin/avi/alma.sub","_fontname=/usr/share/fonts/truetype/msttcorefonts/arial.ttf","_charset=ISO-8859-2","_baseLine...
//** Video Codec conf **
app.video.codec("X264","2PASSBITRATE=384","188 00 00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 1e 00 00 00 3c 00 00 00 0a 00 00 00 33 00 00 00 04 00 00 00 02 00 00 00 28 00 00 0...
//** Audio **
app.audio.reset();
app.audio.codec("aac",192,4,"80 00 00 00 ");
app.audio.normalizeMode=0;
app.audio.normalizeValue=0;
app.audio.delay=0;
app.audio.mixer("STEREO");
app.audio.scanVBR();
app.audio.resample=48000;
app.setContainer("PSP");
setSuccess(app.save("/home/six/bin/avi/alma.mp4"));
//app.Exit();
//End of script