Support multi-path destinations.
[recordtv.git] / src / rtv_convert.py
blob6d77fc2215837b03866a7f6647ec3c2dff55d18d
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 dest = os.path.join(
39 *( _sanitise_filename( fn, 40 ) for fn in
40 proginfo.destination.split( "/" ) )
42 ret_dir = os.path.join( dest, progname_dir )
43 else:
44 ret_dir = progname_dir
46 ans = ( ret_dir,
47 os.path.join( ret_dir, _sanitise_filename( unsan_file, 40 ) ) )
49 return ans
52 def _get_flv_file_path( rtvinfo, fallback_filename ):
53 if not os.path.isfile( rtvinfo ):
54 print "** Error: rtvinfo file '%s' not found. **" % rtvinfo
55 sys.stdout.flush()
56 return ( "", fallback_filename )
58 proginfo = rtv_programmeinfo.ProgrammeInfo()
59 proginfo.load( rtvinfo )
61 return _get_flv_file_path_from_proginfo( proginfo )
63 def _delete_empty_dirs( config ):
64 print
65 print "Removing empty directories:"
67 for ( dirpath, dirnames, filenames ) in os.walk(
68 config.converted_progs_dir ):
69 if len( filenames ) == 0 and len( dirnames ) == 0:
71 # Don't delete the "deleted" dir
72 if ( dirpath.endswith( "deleted" ) ):
73 continue
75 empty_dir_name = os.path.join(
76 config.converted_progs_dir, dirpath )
77 print empty_dir_name
78 os.rmdir( empty_dir_name )
81 def convert( config ):
83 print
84 print "Converting:"
86 for fn in os.listdir( config.recorded_progs_dir ):
88 last_dot_loc = fn.rfind( "." )
89 fn_extn = fn[ last_dot_loc : ]
90 fn_stem = fn[ : last_dot_loc ]
92 if fn_extn not in known_filetypes:
93 continue
95 fn = os.path.join( config.recorded_progs_dir, fn )
97 rtvinfo = os.path.join( config.recorded_progs_dir,
98 fn_stem + ".rtvinfo" )
100 (flv_dir, flv_stem) = _get_flv_file_path( rtvinfo, fn_stem )
102 if fn_extn == ".audiots":
103 output_dir = config.converted_audio_dir
104 final_extn = ".ogg"
105 convert_command = config.audio_convert_command
106 else:
107 output_dir = config.converted_progs_dir
108 final_extn = ".avi"
109 convert_command = config.convert_command
111 rtv_utils.ensure_dir_exists(
112 os.path.join( output_dir, flv_dir ) )
114 flv_before = os.path.join( output_dir,
115 flv_stem + "_converting" + final_extn )
117 flv_after = os.path.join( output_dir, flv_stem + final_extn )
119 rtvinfo_new = flv_stem + ".rtvinfo"
121 old_dir = os.path.join( config.recorded_progs_dir, "old" )
123 sys.stdout.flush()
124 retval = os.system( convert_command % (
125 fn, flv_before, flv_after, old_dir ) )
127 if retval != 0:
128 print "** Conversion of '%s' returned an error. **" % fn
129 continue
131 if os.path.isfile( rtvinfo ):
132 shutil.move( rtvinfo,
133 os.path.join( output_dir, rtvinfo_new ) )
135 _delete_empty_dirs( config )
138 def rename( config ):
139 # TODO: only renames video, not audio
141 for ( dirpath, dirnames, filenames ) in os.walk(
142 config.converted_progs_dir ):
144 for fn in filenames:
145 fn_extension = fn[-4:]
147 if fn_extension not in ( ".flv", ".avi" ):
148 continue
150 fn_stem = os.path.join( dirpath, fn[ : fn.rfind( "." ) ] )
152 rtvinfo_orig = os.path.join( config.converted_progs_dir,
153 fn_stem + ".rtvinfo" )
155 flv_orig = os.path.join( config.converted_progs_dir,
156 fn_stem + fn_extension )
158 (flv_dir, flv_stem) = _get_flv_file_path( rtvinfo_orig, fn_stem )
160 rtv_utils.ensure_dir_exists(
161 os.path.join( config.converted_progs_dir, flv_dir ) )
163 flv_after = os.path.join( config.converted_progs_dir,
164 flv_stem + fn_extension )
166 rtvinfo_after = os.path.join( config.converted_progs_dir,
167 flv_stem + ".rtvinfo" )
169 if flv_orig != flv_after:
170 print "Renaming %s -> %s" % ( flv_orig, flv_after )
171 shutil.move( flv_orig, flv_after )
173 if rtvinfo_orig != rtvinfo_after:
174 print "Renaming %s -> %s" % ( rtvinfo_orig, rtvinfo_after )
175 shutil.move( rtvinfo_orig, rtvinfo_after )
177 _delete_empty_dirs( config )
179 # ------ Test code -----
181 class FakeProgInfo:
182 pass
184 def test_path_from_title_datetime_and_channel():
185 proginfo = FakeProgInfo()
186 from rtv_saxhandler import SaxHandler
187 saxhandler = SaxHandler( None )
188 xmltv_time = "20091009094500 +0100"
189 proginfo.startTime = saxhandler.parse_time( xmltv_time )
190 proginfo.sub_title = None
191 proginfo.channel_pretty = "My Channel"
192 proginfo.title = "My Programme"
193 proginfo.destination = None
195 (flv_dir, flv_stem) = _get_flv_file_path_from_proginfo( proginfo )
197 assert( flv_dir == "My_Programme" )
198 assert( flv_stem == "My_Programme/2009-10-09_09_45_My_Channel" )
200 def test_path_from_destination_title_datetime_and_channel():
201 proginfo = FakeProgInfo()
202 from rtv_saxhandler import SaxHandler
203 saxhandler = SaxHandler( None )
204 xmltv_time = "20091009094500 +0100"
205 proginfo.startTime = saxhandler.parse_time( xmltv_time )
206 proginfo.sub_title = None
207 proginfo.channel_pretty = "My Channel"
208 proginfo.title = "My Programme"
209 proginfo.destination = "grownups"
211 (flv_dir, flv_stem) = _get_flv_file_path_from_proginfo( proginfo )
213 assert( flv_dir == "grownups/My_Programme" )
214 assert( flv_stem == "grownups/My_Programme/2009-10-09_09_45_My_Channel" )
217 def test_multi_destination():
218 proginfo = FakeProgInfo()
219 from rtv_saxhandler import SaxHandler
220 saxhandler = SaxHandler( None )
221 xmltv_time = "20091009094500 +0100"
222 proginfo.startTime = saxhandler.parse_time( xmltv_time )
223 proginfo.sub_title = None
224 proginfo.channel_pretty = "My Channel"
225 proginfo.title = "My Programme"
226 proginfo.destination = "grownups/movies"
228 (flv_dir, flv_stem) = _get_flv_file_path_from_proginfo( proginfo )
230 assert( flv_dir == "grownups/movies/My_Programme" )
231 assert(
232 flv_stem == "grownups/movies/My_Programme/2009-10-09_09_45_My_Channel" )
235 def test( config ):
236 test_path_from_title_datetime_and_channel()
237 test_path_from_destination_title_datetime_and_channel()
238 test_multi_destination()