HTML

Bagoj úr blogja

Kíváncsi Bagoj befigyel a Linux belsejébe, illetve különféle Linux terjesztéseket próbál ki. Ha jó napja van, scriptet ír Neked.

Friss topikok

Konfigurációk karbantartása - szkripttel

2009.02.26. 09:15 bagoj ur

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

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;
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:

perl akarmi.pl > uj_jobfile.txtHá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");
while( my $tmp = readdir(IND)){
    next unless $tmp =~ /^(.*)\.[aA][vV][iI]$/;
    push(@files,$tmp);
}
closedir(IND);
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.

open(IN,  "< $fajlnev") or die("$fajlnev nem nyithato meg: $!\n");
while( <IN> ){
    chomp;
    push(@instr,$_);
}
close(IN);
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).

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:

  1. 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ű.
  2. 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.
  3. 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.srtA $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//
//--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
Jó szórakozást... és köszi a jobfile-t, majd hasznosítom, az én telefonom is viszi az mp4-et... :-)

5 komment

Címkék: linux video perl konvertálás szkript ffmpeg gyorsítás parancssor konfigurálás avi to mp4

A bejegyzés trackback címe:

https://bagojur.blog.hu/api/trackback/id/tr8952233

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Psycho Dad 2009.02.27. 23:58:48

Köszi a írást, hasznos, de tényleg nem kellett volna. :)
Majd ki fogom próbálni, de előtte szerintem átfutom újra, mert elsőre nem értem teljesen a szkriptet, bár ez azért van, mert perl-t soha nem használtam még, pedig egyre inkább érzem, hogy kellene, mivel sok progit alapból perl szkriptekkel lehet bővíteni.

Esetleg egy könyvet tudnál ajánlani ami az alapoktól tanítja, lehetőleg nem totál száraz és magyarázatok nélküli s mindent alátámaszt konkrét példákkal?
Valami olyan részletességű és stílusú kellene mint a te írásaid. ;)

Psycho Dad 2009.03.01. 23:12:40

Köszi, ezt nézegettem is, tök jó, hogy le lehet tölteni a példa szkripteket.

Psycho Dad 2009.04.25. 12:26:27

Hello,

Most csináltam csak olyan konvertálást ahol van felirat fájl is s egy hibába botlottam.
A teljes szkriptben az egyik sorból hiányzik egy betű, méghozzá itt:
$outstr.="app.vide.addFilter$1_subname=$konyvtar$subfilename\"$3\n";
Az "app.vide.addFilter" résznél kimaradt egy "o" betű.

Üdv,
NoBe
süti beállítások módosítása