Thursday, July 31, 2008

Dada generator

Disclaimer: this program is not useful in any way.

Ok, I wrote this one when I was listening to a lecture on databases and data warehouses - I was exploring functional programming at the time, and decided that if you really thought about it you could make a very nice and pleasant function that does almost absolutely nothing, apart from being aesthetically pleasing. After I wrote one on paper, I decided that it could be expanded a bit, but it was too much hassle to do by hand... so, a generator!

What this program does is actually generate a program, which, upon running will recursively pass all its parameters to itself, and when the recursion stops it will use the first of the parameters as a function, supplying it with the other parameters as parameters... is this clear? I suppose an example is in order...

Ok, so first, I run the program in the following way:

ocaml dada_gen.ml 5 f a b c > dada_ex.ml


...and got the following code as a result (in the file dada_ex.ml):

let f a b c =
   let f a b c =
      let f a b c =
         let f a b c =
            let f a b c =
               let f a b c =
                  a b c 
               in f a b c 
            in f a b c 
10         in f a b c 
11      in f a b c 
12   in f a b c 
13;;
14



Ok, then I run the Ocaml top-level like this:

ocaml


Inside the toplevel I loaded the file:

#use "dada_ex.ml";;


...and got the response:

val f : ('a -> 'b -> 'c) -> 'a -> 'b -> 'c = <fun>


...and then the function f funtion like this:

f (+) 2 2


...and got in return:

- : int = 4


...which just means that the result is 4. I could've achieved the same by going:

(+) 2 2;;


...or even

2 + 2


That's why its dada. It's not meant to be useful!

Yeah, I know, I'm weird.

Here's the script:
(**
 * Dada generator
 * Generates a completely useless but aesthetically pleasing dada program
 * 
 * Potential issues:
 *  None
 * Parameters:
 *  number of internal functions
 *  name of the dadaistic function
10 *  list of passed parameters
11 * Requires:
12 *  Ocaml (http://caml.inria.fr/)
13 *  Sense of humor
14 *)
15
16(**
17 * Function responsible for generating dadaistic programs basing on the 
18 * supplied parameters
19 * @param function_name the name of the main function
20 * @param parameter_list a list of strings which will be inserted as the 
21 *  body of the inner-most function
22 * @param nested_function_count the number of functions generated inside 
23 *  the main function
24 * @author Konrad Siek
25 *)
26let generate_dada function_name parameter_list nested_function_count =
27   let rec list_to_string list =
28      match list with 
29        | head::tail -> head ^ " " ^ (list_to_string tail)
30        | [] -> ""
31   in
32   let rec tabs count = 
33      if count = 0 then
34         ""
35      else
36         "   " ^ (tabs (count - 1))
37   in
38   let program = ref ("let " ^ function_name  ^ " " ^ 
39        (list_to_string parameter_list) ^ "=\n") in
40   let _ = for i = 1 to nested_function_count do
41     program := !program ^ (tabs i);
42     program := !program ^ "let " ^ function_name ^ " " ^ 
43        (list_to_string parameter_list) ^ "=\n"
44   done in
45   program := !program ^ (tabs (nested_function_count + 1)) ^ 
46        (list_to_string parameter_list) ^ "\n";
47   let _ = for i = nested_function_count downto 1 do
48     program := !program ^ (tabs i);
49     program := !program ^ "in " ^  function_name ^ " " ^ 
50        (list_to_string parameter_list) ^ "\n"
51   done in
52   program := !program ^ ";;\n";
53   !program
54;;
55
56(* Main method calling the generation function. *)
57if Array.length Sys.argv > 3 then 
58   let count = int_of_string Sys.argv.(1) in
59   let function_name = Sys.argv.(2) in
60   let parameters = Array.to_list 
61    (Array.sub Sys.argv 3 (Array.length Sys.argv - 3)) in
62   let program = generate_dada function_name parameters count in
63   print_endline program
64else
65   print_endline ("Usage:\n\t" ^ Sys.argv.(0) ^ " <function_count> " ^ 
66        "<main_function_name> <parameter> [ ... <parameter> ]")
67;;


The code is also available at GitHub as ocaml/dada_generator.ml.

Tuesday, July 29, 2008

AVI to ISO/DVD

A while back I downloaded Elephants dream, and, having watched it, decided to put it on a DVD. Being a weird bloke that I am, I decided I need to do it via the command line...

I generally have no truck with all that messy part of life having to do with codecs and stuff, so I wanted to do something (more or less) simplistic, where I just put in the name of file and go.

I didn't test this extensively and can't guarantee anything. In fact, I'm not so sure about the ISO generation at the moment and will no doubt spend some more time poking at it...

But, at the moment, here it is.
1  #!/bin/bash
2  #
3  # AVI to ISO/DVD 
4  #
5  # Convert a AVI movie into a burn-ready ISO image, or even burn straight onto 
6  # DVD
7  #
8  # Potential issues:
9  #   This script will overwrite existing files,
10 #   It uses a lot of disk space.
11 # Parameters:
12 #   -a | --ac3-present - assume sound is already ac3 and skip 
13 #               audio conversion (otherwise convert audio too)
14 #   -b | --burn-dvd - output the converted data directly to dvd 
15 #               (default device: /dev/dvdrw); optionally enter 
16 #               device name (no spaces after the short switch)
17 #               (if this is not set simply generate an ISO image)
18 #   -c | --chapter-list - a string describing how the dvd should 
19 #               be divided into chapters (default: "0", 
20 #               example: "0,1:12,2:31,3:45,4:55,6:25,7:09,9:07,9:26")           
21 #   -d | --output-dir - a place to land generated files
22 #               (default: directory of the converted file)
23 #   --skip-encoding - skips encoding, if there is already an mpg file - 
24 #               used in testing.
25 #   --skip-cleanup - skips cleaning up: do not delete generated files - 
26 #               used in testing.
27 #   at least one path to an avi file to convert
28 # Examples:
29 #   (These two examples have different outcomes!)
30 #   avi_to_iso elephants_dream.avi              
31 #   avi_to_iso -a -b/dev/dvdrw -c "0,1:12,2:31,3:45,4:55,6:25,7:09,9:07,9:26"\
32 #               elephants_dream.avi
33 # Requires: 
34 #   mencoder    (http://www.mplayerhq.hu/)
35 #   dvdauthor   (http://dvdauthor.sourceforge.net/)
36 #   genisoimage and/or growisofs
37 # Author:
38 #   Konrad Siek
39 
40 #REGEX: "[0-9]{1,2}(,[0-9]{1,2}(:[0-9]{1,2}(:[0-9]{1,2})?)?)*"
41 
42 # Set default options
43 ac3_sound="false"
44 burn_dvd="false"
45 dvd_device="/dev/dvdrw"
46 chapter_list="0"
47 output_directory=""
48 skip_encoding="false"
49 skip_cleanup="false"
50 
51 # Converts a file into a DVD format
52 # @param $1: path
53 function convert_file {
54     # Strip extension from file
55     name=`basename $1 .avi`
56     basename=`basename $1`
57 
58     # Establish destination for converted file
59     if [ "$output_directory" == "" ]
60     then
61         destination=`dirname $1`
62     else
63         destination=$output_directory
64     fi
65 
66     echo "Convert file:"
67     echo -e "\t$1"
68     echo "into:"
69     echo -e "\t$destination/$name.mpg"
70 
71     # Encode video and, perhaps, audio, into mpeg2
72     # Ugh, crappy indentation...
73     if [ $skip_encoding = 'false' ] 
74     then
75         if [ $ac3_sound = 'true' ]
76         then 
77             mencoder \
78                 -oac copy -ovc lavc -of mpeg \
79                 -mpegopts format=dvd -vf scale=720:576,harddup \
80                 -lavcopts \
81                     vcodec=mpeg2video:\
82 vrc_buf_size=1835:\
83 vrc_maxrate=9800:\
84 vbitrate=5000:\
85 keyint=15:\
86 aspect=16/9 \
87                 -ofps 25 -o $destination/$name.mpg $1
88         else
89             mencoder \
90                 -oac lavc -ovc lavc -of mpeg \
91                 -mpegopts format=dvd -vf scale=720:576,harddup \
92                 -srate 48000 -af lavcresample=48000 \
93                 -lavcopts \
94                 vcodec=mpeg2video:\             
95 vrc_buf_size=1835:\
96 vrc_maxrate=9800:\
97 vbitrate=5000:\
98 keyint=15:\
99 aspect=16/9:\
100acodec=ac3:\
101abitrate=192 \
102                -ofps 25 -o $destination/$name.mpg $1 
103        fi
104
105        # Exit if anything went wrong
106        if [ $? != 0 ]
107        then
108            exit 1
109        fi            
110    fi
111
112    # Create a v. simple configuration XML file for dvdauthor
113    echo -e \
114    `echo "<dvdauthor>\n"
115     echo "\t<vmgm />\n"
116     echo "\t<titleset>\n"
117     echo "\t\t<titles>\n"
118     echo "\t\t\t<pgc>\n"
119     echo "\t\t\t<vob file=\"$destination/$name.mpg\" "
120     echo "chapters=\"$chapter_list\" />\n"
121     echo "\t\t\t</pgc>\n"
122     echo "\t\t</titles>\n"
123     echo "\t</titleset>\n"
124     echo "</dvdauthor>\n"` \
125    > $destination/$name.xml
126
127    # Create dvd structure
128    dvdauthor -o $destination/$name -x $destination/$name.xml
129
130    # Exit if anything went wrong
131    if [ $? != 0 ]
132    then
133        exit 1
134    fi
135    
136    # Create output
137    if [ $burn_dvd == "true" ] 
138    then
139        # Burn DVD
140        growisofs -dvd-compat -Z $dvd_device -dvd-video $destination/$name/
141    else
142        # Generate ISO image
143        genisoimage -dvd-video -o $destination/$name.iso $destination/$name
144    fi
145
146    if [ "$skip_cleanup" == "false" ]
147    then
148        # Clean up
149        rm $name.mpg
150        rm $name.xml
151        rm -r $name/
152    fi
153}
154
155# Main
156# Use getopt to parse command arguments.
157options=`getopt    -o ab::c:d: \
158    --long ac3-present,burn-dvd::,chapter-list:,output-dir:,skip-encoding,skip-cleanup \
159    -n $0 -- "$@"`
160
161# Proceed if everything is OK.
162if [ $? == 0 ] 
163then
164    # Set the parsed command options.
165    eval set -- "$options"
166
167    # Setup selected options
168    while true ; do
169        case "$1" in
170            -a|--ac3-present
171                # Assume AC3 sound in input file
172                ac3_sound="true"
173                shift 
174            ;;
175            -c|--chapter-list
176                # Set a chapter list
177                chapter_list=$2
178                shift 2 
179            ;;
180            -b|--burn-dvd)
181                # Burn DVD after convertion
182                burn_dvd="true"
183
184                # Optionally set DVD device
185                if [ $2 != "" ] 
186                then
187                    dvd_device=$2
188                fi
189                shift 2
190            ;;
191            -d|--output-dir)
192                # Set output directory
193                output_directory=$2
194                shift 2
195            ;;
196            --skip-encoding)
197                # Set skip encoding
198                skip_encoding="true"
199                shift
200            ;;
201            --skip-cleanup)
202                # Set skip cleanup
203                skip_cleanup="true"
204                shift
205            ;;
206            --) 
207                # Stop parsing options, continuie with the arguments
208                shift
209                break
210            ;;
211            *) 
212                # Weird error
213                echo "Something went horribly, horribly wrong in the interior."
214                exit 1 
215            ;;
216            esac
217    done
218
219    # Print settings
220    echo "Option summary:"
221    echo -e "\tac3_sound=$ac3_sound"
222    echo -e "\tburn_dvd=$burn_dvd"
223    echo -e "\tdvd_device=$dvd_device"
224    echo -e "\tchapter_list=$chapter_list"
225    echo -e "\toutput_directory=$output_directory"
226    echo -e "\tskip_encoding=$skip_encoding"
227
228    # Start converting...
229    for arg 
230    do 
231        convert_file $arg
232    done
233else
234    exit 1
235fi


The code is also available at GitHub as bash/avi_to_iso.

Sunday, July 27, 2008

Zero in front

I wrote this one when I was using the KDE default picture viewer thingie, I forget what it's called. Anyway, I had files with names starting with numbers: 1-joe-sans-pants.png, ..., 10-joe-with-undrwr-on-head.png, etc. and the program had trouble sorting them properly.

It did lexicographical sorting instread of numeric - this means that the numbers from 1 to 20 were sorted like this:
1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, ...
... you get the idea - and I just wanted them to be sorted like from lowest to highest number...

So, what this script does, is rename files, so that both types of sorting produce same results, i.e. prefixes the name with zeros. And that's it.

I actually did some work on this script before uploading it, because it didn't use to be so universal, i.e. it only handled up to three digits and worked only on the local file - now it's much more versatile, and, unfortunately much slower...

And here it is:
#!/bin/bash
#
# Zero in front
# Renames all files which start with digits, 
# padding them with 0s, to get the length of the numbers equal,
#
# Potential issues:
#   This script will overwrite existing files
# Parameters:
10#   a list of directories subject to conversion, or
11#   no parameters to convert files in current directory.
12# Requires:
13#   nothing
14# Author:
15#   Konrad Siek 
16
17# Scans directory.
18# @param $1: directory
19function scan_directory {
20    # See how long is the largest number
21    top_rank=`expr $(ls $1 | sort -nr | head -n1) : "^[0-9]*"`
22    
23    # If there's anything to do at all...
24    if [ $top_rank -gt 0 ]
25    then
26        # Cycle through the files in the directory...
27        for file in `ls $1`
28        do
29            # Check the length of the current number
30            # This is what takes so much time, it seems...
31            this_rank=`expr $file : "^[0-9]*"`;
32
33            # Act only if there is a number in front
34            if [ $top_rank -gt $this_rank ]
35            then
36                # Create a padding prefix - pad with 0s
37                padding=""
38                for i in $(seq 0 $(expr $top_rank - $this_rank - 1))
39                do
40                    padding=$padding"0"
41                done                
42
43                # Actually move the files
44                mv $1/$file $1/$padding$file
45            fi
46        done
47    fi
48}
49
50# Main
51if [ $# -gt 0 ]
52then
53    # If directories specified, scan each of them separatelly
54    for directory in $@
55    do
56        scan_directory $directory
57    done    
58else
59    # If no arguments, then use this directory
60    scan_directory `pwd`
61fi


The code is also available at GitHub as bash/zero_in_front.