2 import gtk
, gtk
.glade
, gobject
6 from datetime
import datetime
8 os
.environ
['DJANGO_SETTINGS_MODULE'] = 'riffle.settings'
9 from riffle
.catcher
.models
import *
10 from util
.riffle
import riffle
14 sys
.path
.append(os
.path
.expanduser("~/src/ql/trunk/mutagen"))
17 SUPPORTED_EXTENSIONS
= '.mp3'
19 def list_files(top
, ext
):
20 if isinstance(ext
,list):
21 accept
= lambda e
: e
in ext
23 accept
= lambda e
: e
== ext
24 for r
,ds
,fs
in os
.walk(top
):
26 if accept( os
.path
.splitext(f
)[1] ):
27 yield os
.path
.join(r
,f
)
29 def get_device_tracks():
30 return list_files(conf
.device_dir
, SUPPORTED_EXTENSIONS
)
32 def get_incoming_tracks():
33 return list_files(conf
.media_dir
, SUPPORTED_EXTENSIONS
)
37 self
.window
= gtk
.Window()
38 self
.window
.set_title('sync')
39 self
.window
.connect('destroy', gtk
.main_quit
)
43 scroll
= gtk
.ScrolledWindow()
44 scroll
.set_policy(gtk
.POLICY_AUTOMATIC
,gtk
.POLICY_AUTOMATIC
)
47 self
.main_list
= gtk
.TreeView()
48 self
.main_list
.set_reorderable(True)
49 self
.main_list
.set_headers_clickable(True)
50 self
.main_list
.connect("key-press-event", self
.on_key_press
)
51 self
.main_list
.get_selection().set_mode(gtk
.SELECTION_MULTIPLE
)
53 text_renderer
= gtk
.CellRendererText()
54 column
= gtk
.TreeViewColumn("date", text_renderer
)
55 text_renderer
.set_property('xalign', 1.0)
56 column
.set_cell_data_func(text_renderer
, self
.render_date
)
57 self
.main_list
.append_column( column
)
59 text_renderer
= gtk
.CellRendererText()
60 column
= gtk
.TreeViewColumn("riffle", text_renderer
)
61 column
.set_attributes(text_renderer
, text
=6)
62 column
.set_sort_column_id(6)
63 column
.set_cell_data_func(text_renderer
, self
.set_row_bg
)
64 self
.main_list
.append_column( column
)
66 text_renderer
= gtk
.CellRendererText()
67 column
= gtk
.TreeViewColumn("feed", text_renderer
)
68 column
.set_attributes(text_renderer
, text
=3)
69 column
.set_sort_column_id(3)
70 column
.set_cell_data_func(text_renderer
, self
.set_row_bg
)
71 self
.main_list
.append_column( column
)
73 text_renderer
= gtk
.CellRendererText()
74 column
= gtk
.TreeViewColumn("title", text_renderer
)
75 column
.set_cell_data_func(text_renderer
, self
.set_row_bg
)
76 column
.set_attributes(text_renderer
, text
=4)
77 self
.main_list
.append_column( column
)
79 scroll
.add(self
.main_list
)
81 go_button
= gtk
.Button('Go!')
82 go_button
.connect("clicked", self
.do_sync
)
83 vbox
.add_with_properties(go_button
,'expand',False)
87 self
.window
.resize(600,800)
88 self
.window
.show_all()
90 def set_row_bg(self
, column
, cell
, model
, iter, user_data
= None):
91 p
= model
.get_value(iter, 0)
92 cell
.set_property('background',
93 '#d0d0f0' if p
.startswith(conf
.device_dir
) else '#90ff90')
95 def render_date(self
, column
, cell
, model
, iter, user_data
= None):
96 dt
= model
.get_value(iter, 5)
97 cell
.set_property('text', dt
.strftime('%A, %d %B %Y'))
99 def add_entries(self
, lst
):
100 if self
.main_list
.get_model() is None:
101 self
.main_list
.set_model(
102 gtk
.ListStore(str,object,object,str,str,object,int) )
104 self
.main_list
.get_model().append( i
)
106 def do_sync(self
,*argv
):
107 to_copy
= [row
for row
in self
.main_list
.get_model()
108 if row
[0].startswith(conf
.media_dir
)]
109 CopyProgress(to_copy
, conf
.device_dir
)
111 def on_key_press(self
,widget
,event
,*argv
):
112 if event
.keyval
in (gtk
.keysyms
.Delete
, gtk
.keysyms
.KP_Delete
):
113 self
.delete_selected()
115 def want_file_gone(self
, title
):
116 print "Do you also want to delete track '%s' from incoming directory?" % title
119 def delete_selected(self
):
120 model
,indexes
= self
.main_list
.get_selection().get_selected_rows()
121 iters
= [model
.get_iter(i
) for i
in indexes
]
123 if model
[i
][0].startswith(conf
.media_dir
) \
124 or self
.want_file_gone(model
[i
][0]):
125 os
.remove(model
[i
][0])
129 xml
= gtk
.glade
.XML('sync.glade', root
='copy_progress_dialog')
130 dialog
= xml
.get_widget('copy_progress_dialog')
131 total_progress
= xml
.get_widget('total_progress')
132 file_progress
= xml
.get_widget('file_progress')
137 def __init__(self
, sources
, dest
):
138 if len(sources
) == 0: return
139 self
.total_progress
.set_fraction(0)
140 self
.file_progress
.set_fraction(0)
141 self
.dialog
.show_all()
143 self
.sources
= sources
144 self
.total_sources_count
= len(sources
)
146 self
.source_file
= None
147 self
.dest_file
= None
149 gobject
.idle_add(self
.copy_task
)
152 if self
.source_file
is None:
153 if len(self
.sources
) == 0:
154 self
.dialog
.hide() # done
156 self
.row
= self
.sources
[0]
157 self
.source
= self
.row
[0]
158 self
.sources
= self
.sources
[1:]
159 self
.source_file
= open(self
.source
, 'r')
160 self
.new_path
= os
.path
.join( self
.dest
,
161 os
.path
.basename( self
.source
))
162 self
.dest_file
= open(self
.new_path
, 'w')
163 self
.current_offset
= 0
164 self
.current_size
= os
.stat( self
.source
).st_size
165 self
.file_progress
.set_fraction( 0.0 )
166 chunk_size
= min( self
.buf_len
,
167 self
.current_size
- self
.current_offset
)
168 self
.dest_file
.write( self
.source_file
.read( chunk_size
))
169 self
.current_offset
+= chunk_size
170 self
.file_progress
.set_fraction(
171 float(self
.current_offset
) / self
.current_size
)
172 if self
.current_offset
== self
.current_size
:
173 self
.dest_file
.close()
174 self
.dest_file
= None
175 self
.source_file
.close()
176 self
.source_file
= None
177 os
.remove( self
.source
)
178 self
.total_progress
.set_fraction( 1.0 -
179 (float(len(self
.sources
)) / self
.total_sources_count
))
180 self
.row
[0] = self
.new_path
183 def track_columns(path
):
185 title
= os
.path
.basename(path
)
186 timestamp
= datetime
.fromtimestamp( os
.stat(path
).st_mtime
)
189 tags
= mutagen
.File(path
).tags
191 feed
= str(tags
['TALB'])
193 title
= str(tags
['TIT2'])
197 ep
= Episode
.objects
.get(local_path
= os
.path
.basename(path
))
199 timestamp
= ep
.timestamp
200 except Episode
.DoesNotExist
:
202 return [path
, tags
, ep
, feed
, title
, timestamp
]
206 from itertools
import chain
210 for x
in [track_columns(t
)
211 for t
in chain( get_device_tracks(),get_incoming_tracks()) ]:
213 if feed
not in deck_dict
:
215 deck_dict
[feed
].append(x
)
217 deck
= [deck_dict
[f
] for f
in deck_dict
]
218 map(lambda lst
: lst
.sort(key
=lambda x
: x
[5]), deck
)
219 riffled
= riffle(deck
)
220 for i
in xrange(len(riffled
)):
221 riffled
[i
].append(i
+1)
223 app
.add_entries( riffled
)