Refactor favourite implementation and add some tests.
[recordtv.git] / src / rtv_convert.py
blobc9f2c93c574e6a762b9b54a80610e60c570b8c0e
1 #!/usr/bin/python
3 import os
4 import os.path
5 import re
6 import shutil
7 import sys
8 import rtv_utils, rtv_programmeinfo
10 allowed_chars_re = re.compile( "[a-zA-z0-9_-]" )
12 known_filetypes = ( ".ts", ".audiots" )
14 def _sanitise_filename( fn, max_len ):
15 fn = str( fn )
16 san_fn = ""
17 for ch in fn:
18 if allowed_chars_re.match( ch ):
19 san_fn += ch
20 else:
21 san_fn += "_"
22 return san_fn[:max_len]
24 def _get_flv_file_path_from_proginfo( proginfo ):
26 startTime = proginfo.startTime.strftime( "%Y-%m-%d_%H_%M" )
28 if proginfo.sub_title:
29 unsan_file = startTime[:10] + "_" + str( proginfo.sub_title )
30 elif proginfo.channel_pretty:
31 unsan_file = startTime + "_" + str( proginfo.channel_pretty )
32 else:
33 unsan_file = startTime + "_" + str( proginfo.channel )
35 progname_dir = _sanitise_filename( proginfo.title, 40 )
37 if proginfo.destination and proginfo.destination != "":
38 ret_dir = os.path.join(
39 _sanitise_filename( proginfo.destination, 40 ),
40 progname_dir )
41 else:
42 ret_dir = progname_dir
44 ans = ( ret_dir,
45 os.path.join( ret_dir, _sanitise_filename( unsan_file, 40 ) ) )
47 return ans
50 def _get_flv_file_path( rtvinfo, fallback_filename ):
51 if not os.path.isfile( rtvinfo ):
52 print "** Error: rtvinfo file '%s' not found. **" % rtvinfo
53 sys.stdout.flush()
54 return ( "", fallback_filename )
56 proginfo = rtv_programmeinfo.ProgrammeInfo()
57 proginfo.load( rtvinfo )
59 return _get_flv_file_path_from_proginfo( proginfo )
61 def _delete_empty_dirs( config ):
62 print
63 print "Removing empty directories:"
65 for ( dirpath, dirnames, filenames ) in os.walk(
66 config.converted_progs_dir ):
67 if len( filenames ) == 0 and len( dirnames ) == 0:
69 # Don't delete the "deleted" dir
70 if ( dirpath.endswith( "deleted" ) ):
71 continue
73 empty_dir_name = os.path.join(
74 config.converted_progs_dir, dirpath )
75 print empty_dir_name
76 os.rmdir( empty_dir_name )
79 def convert( config ):
81 print
82 print "Converting:"
84 for fn in os.listdir( config.recorded_progs_dir ):
86 last_dot_loc = fn.rfind( "." )
87 fn_extn = fn[ last_dot_loc : ]
88 fn_stem = fn[ : last_dot_loc ]
90 if fn_extn not in known_filetypes:
91 continue
93 fn = os.path.join( config.recorded_progs_dir, fn )
95 rtvinfo = os.path.join( config.recorded_progs_dir,
96 fn_stem + ".rtvinfo" )
98 (flv_dir, flv_stem) = _get_flv_file_path( rtvinfo, fn_stem )
100 if fn_extn == ".audiots":
101 output_dir = config.converted_audio_dir
102 final_extn = ".ogg"
103 convert_command = config.audio_convert_command
104 else:
105 output_dir = config.converted_progs_dir
106 final_extn = ".avi"
107 convert_command = config.convert_command
109 rtv_utils.ensure_dir_exists(
110 os.path.join( output_dir, flv_dir ) )
112 flv_before = os.path.join( output_dir,
113 flv_stem + "_converting" + final_extn )
115 flv_after = os.path.join( output_dir, flv_stem + final_extn )
117 rtvinfo_new = flv_stem + ".rtvinfo"
119 old_dir = os.path.join( config.recorded_progs_dir, "old" )
121 sys.stdout.flush()
122 retval = os.system( convert_command % (
123 fn, flv_before, flv_after, old_dir ) )
125 if retval != 0:
126 print "** Conversion of '%s' returned an error. **" % fn
127 continue
129 if os.path.isfile( rtvinfo ):
130 shutil.move( rtvinfo,
131 os.path.join( output_dir, rtvinfo_new ) )
133 _delete_empty_dirs( config )
136 def rename( config ):
137 # TODO: only renames video, not audio
139 for ( dirpath, dirnames, filenames ) in os.walk(
140 config.converted_progs_dir ):
142 for fn in filenames:
143 fn_extension = fn[-4:]
145 if fn_extension not in ( ".flv", ".avi" ):
146 continue
148 fn_stem = os.path.join( dirpath, fn[ : fn.rfind( "." ) ] )
150 rtvinfo_orig = os.path.join( config.converted_progs_dir,
151 fn_stem + ".rtvinfo" )
153 flv_orig = os.path.join( config.converted_progs_dir,
154 fn_stem + fn_extension )
156 (flv_dir, flv_stem) = _get_flv_file_path( rtvinfo_orig, fn_stem )
158 rtv_utils.ensure_dir_exists(
159 os.path.join( config.converted_progs_dir, flv_dir ) )
161 flv_after = os.path.join( config.converted_progs_dir,
162 flv_stem + fn_extension )
164 rtvinfo_after = os.path.join( config.converted_progs_dir,
165 flv_stem + ".rtvinfo" )
167 if flv_orig != flv_after:
168 print "Renaming %s -> %s" % ( flv_orig, flv_after )
169 shutil.move( flv_orig, flv_after )
171 if rtvinfo_orig != rtvinfo_after:
172 print "Renaming %s -> %s" % ( rtvinfo_orig, rtvinfo_after )
173 shutil.move( rtvinfo_orig, rtvinfo_after )
175 _delete_empty_dirs( config )
177 # ------ Test code -----
179 class FakeProgInfo:
180 pass
182 def test( config ):
183 proginfo = FakeProgInfo()
184 from rtv_saxhandler import SaxHandler
185 saxhandler = SaxHandler( None )
186 xmltv_time = "20091009094500 +0100"
187 proginfo.startTime = saxhandler.parse_time( xmltv_time )
188 proginfo.sub_title = None
189 proginfo.channel_pretty = "My Channel"
190 proginfo.title = "My Programme"
191 proginfo.destination = None
193 (flv_dir, flv_stem) = _get_flv_file_path_from_proginfo( proginfo )
195 assert( flv_dir == "My_Programme" )
196 assert( flv_stem == "My_Programme/2009-10-09_09_45_My_Channel" )