1
# PiTiVi , Non-linear video editor
3
# ui/sourcefactories.py
5
# Copyright (c) 2005, Edward Hervey <bilboed@bilboed.com>
7
# This program is free software; you can redistribute it and/or
8
# modify it under the terms of the GNU Lesser General Public
9
# License as published by the Free Software Foundation; either
10
# version 2.1 of the License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# Lesser General Public License for more details.
17
# You should have received a copy of the GNU Lesser General Public
18
# License along with this program; if not, write to the
19
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
# Boston, MA 02111-1307, USA.
23
Source and effects list widgets
34
from urllib import unquote
36
import pitivi.instance as instance
37
import pitivi.dnd as dnd
38
from pitivi.configure import get_pixmap_dir
39
from pitivi.signalgroup import SignalGroup
40
from pitivi.threads import Thread
42
from filelisterrordialog import FileListErrorDialog
44
from gettext import gettext as _
46
def beautify_length(length):
47
sec = length / gst.SECOND
51
return "%02dm%02ds" % (mins, sec)
54
return "%02dh%02dm%02ds" % (hours, mins, sec)
56
class SourceFactoriesWidget(gtk.Notebook):
58
Widget for the various source factories (files, effects, live,...)
63
gtk.Notebook.__init__(self)
67
""" set up the gui """
68
self.set_tab_pos(gtk.POS_TOP)
69
self.sourcelist = SourceListWidget()
70
self.append_page(self.sourcelist, gtk.Label(_("Clips")))
72
## FIXME: The following are deactivated until they do more than just
75
## self.audiofxlist = AudioFxListWidget()
76
## #self.audiofxlist.set_sensitive(False)
77
## self.append_page(self.audiofxlist, gtk.Label("Audio FX"))
78
## self.videofxlist = VideoFxListWidget()
79
## #self.videofxlist.set_sensitive(False)
80
## self.append_page(self.videofxlist, gtk.Label("Video FX"))
81
## self.transitionlist = TransitionListWidget()
82
## self.transitionlist.set_sensitive(False)
83
## self.append_page(self.transitionlist, gtk.Label("Transitions"))
90
COL_LENGTH) = range(5)
92
class SourceListWidget(gtk.VBox):
93
""" Widget for listing sources """
96
gtk.VBox.__init__(self)
99
# icon, infotext, objectfactory, uri, length
100
self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, str, object, str, str)
102
self.set_border_width(5)
106
self.scrollwin = gtk.ScrolledWindow()
107
self.scrollwin.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC)
108
self.scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)
111
self.popup = gtk.Menu()
112
additem = gtk.ImageMenuItem(_("Add Clips..."))
114
image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
115
additem.set_image(image)
117
remitem = gtk.ImageMenuItem(_("Remove Clip"))
119
image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
120
remitem.set_image(image)
121
playmenuitem = gtk.MenuItem(_("Play Clip"))
122
playmenuitem.connect("activate", self._playButtonClickedCb)
123
additem.connect("activate", self._addButtonClickedCb)
124
remitem.connect("activate", self._removeButtonClickedCb)
128
self.popup.append(additem)
129
self.popup.append(remitem)
130
self.popup.append(playmenuitem)
132
# import sources dialogbox
133
self._importDialog = None
134
self._lastFolder = None
137
# Displays icon, name, type, length
138
self.treeview = gtk.TreeView(self.storemodel)
139
self.treeview.connect("button-press-event", self._treeViewButtonPressEventCb)
140
self.treeview.connect("row-activated", self._rowActivatedCb)
141
self.treeview.set_property("rules_hint", True)
142
self.treeview.set_headers_visible(False)
143
tsel = self.treeview.get_selection()
144
tsel.set_mode(gtk.SELECTION_MULTIPLE)
146
pixbufcol = gtk.TreeViewColumn(_("Icon"))
147
pixbufcol.set_expand(False)
148
pixbufcol.set_spacing(5)
149
self.treeview.append_column(pixbufcol)
150
pixcell = gtk.CellRendererPixbuf()
151
pixcell.props.xpad = 6
152
pixbufcol.pack_start(pixcell)
153
pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)
155
namecol = gtk.TreeViewColumn(_("Information"))
156
self.treeview.append_column(namecol)
157
namecol.set_expand(True)
158
namecol.set_spacing(5)
159
namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
160
namecol.set_min_width(150)
161
txtcell = gtk.CellRendererText()
162
txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
163
namecol.pack_start(txtcell)
164
namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)
166
namecol = gtk.TreeViewColumn(_("Duration"))
167
namecol.set_expand(False)
168
self.treeview.append_column(namecol)
169
txtcell = gtk.CellRendererText()
170
txtcell.set_property("yalign", 0.0)
171
namecol.pack_start(txtcell)
172
namecol.add_attribute(txtcell, "markup", COL_LENGTH)
174
# Start up with tree view
175
self.scrollwin.add(self.treeview)
177
# Explanatory message label
178
textbox = gtk.EventBox()
179
textbox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('white'))
182
txtlabel = gtk.Label()
183
txtlabel.set_padding(10, 10)
184
txtlabel.set_line_wrap(True)
185
txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
186
txtlabel.set_justify(gtk.JUSTIFY_CENTER)
188
_("<span size='x-large'>Import your clips by dragging them here or "
189
"by using the buttons above.</span>"))
190
textbox.add(txtlabel)
191
self.txtlabel = txtlabel
193
self.textbox = textbox
195
self.pack_start(self.textbox, expand=True, fill=True)
196
self.reorder_child(self.textbox, 0)
197
self.showingTreeView = False
199
self.dragMotionSigId = self.txtlabel.connect("drag-motion",
202
self.infostub = InfoStub()
203
self.infostub.connect("remove-me", self._removeInfoStub)
205
# Connect to project. We must remove and reset the callbacks when
207
self.project_signals = SignalGroup()
208
self._connectToProject(instance.PiTiVi.current)
209
instance.PiTiVi.connect("new-project", self._newProjectCb)
212
icontheme = gtk.icon_theme_get_default()
213
self.filepixbuf = icontheme.load_icon("misc", 32, 0)
214
self.audiofilepixbuf = icontheme.load_icon("audio-x-generic", 32, 0)
215
self.videofilepixbuf = icontheme.load_icon("video-x-generic", 32, 0)
218
self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
219
[dnd.URI_TUPLE, dnd.FILE_TUPLE],
221
self.connect("drag_data_received", self._dndDataReceivedCb)
223
self.treeview.drag_source_set(gtk.gdk.BUTTON1_MASK,
224
[dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE],
226
self.treeview.connect("drag_begin", self._dndTreeBeginCb)
227
self.treeview.connect("drag_data_get", self._dndDataGetCb)
229
# Hack so that the views have the same method as self
230
self.treeview.getSelectedItems = self.getSelectedItems
233
self.errorDialogBox = None
235
def _connectToProject(self, project):
236
"""Connect signal handlers to a project.
238
This first disconnects any handlers connected to an old project.
239
If project is None, this just disconnects any connected handlers.
242
self.project_signals.connect(project.sources, "file_added", None, self._fileAddedCb)
243
self.project_signals.connect(project.sources, "file_removed", None, self._fileRemovedCb)
244
self.project_signals.connect(project.sources, "not_media_file", None, self._notMediaFileCb)
245
self.project_signals.connect(project.sources, "ready", None, self._sourcesStoppedImportingCb)
246
self.project_signals.connect(project.sources, "starting", None, self._sourcesStartedImportingCb)
249
## Explanatory message methods
251
def _displayTreeView(self, displayed=True, usesignals=True):
252
""" Display the tree view in the scrolled window.
253
If displayed is False, then the default explanation message will be
255
If usesignals is True, then signals on the mesagewindow will be
259
if self.showingTreeView:
261
gst.debug("displaying tree view")
262
self.remove(self.textbox)
265
if self.dragMotionSigId:
266
self.txtlabel.disconnect(self.dragMotionSigId)
267
self.dragMotionSigId = 0
268
self.pack_start(self.scrollwin)
269
self.reorder_child(self.scrollwin, 0)
270
self.scrollwin.show_all()
271
self.showingTreeView = True
273
if not self.showingTreeView:
275
gst.debug("hiding tree view")
276
self.remove(self.scrollwin)
277
self.scrollwin.hide()
278
self.pack_start(self.textbox)
279
self.reorder_child(self.textbox, 0)
281
self.showingTreeView = False
283
def _dragMotionCb(self, unused_layout, unused_context, x, unused_y,
286
gobject.idle_add(self._displayTreeView, True, False)
288
def showImportSourcesDialog(self, select_folders=False):
289
if self._importDialog:
293
chooser_action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
294
dialogtitle = _("Import a folder")
296
chooser_action = gtk.FILE_CHOOSER_ACTION_OPEN
297
dialogtitle = _("Import a clip")
299
self._importDialog = gtk.FileChooserDialog(dialogtitle, None,
301
(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE,
302
gtk.STOCK_ADD, gtk.RESPONSE_OK))
304
self._importDialog.set_default_response(gtk.RESPONSE_OK)
305
self._importDialog.set_select_multiple(True)
306
self._importDialog.set_modal(False)
308
self._importDialog.set_current_folder(self._lastFolder)
309
self._importDialog.connect('response', self._dialogBoxResponseCb, select_folders)
310
self._importDialog.connect('close', self._dialogBoxCloseCb)
311
self._importDialog.show()
313
def addFiles(self, list):
314
""" Add files to the list """
315
instance.PiTiVi.current.sources.addUris(list)
317
def addFolders(self, list):
318
""" walks the trees of the folders in the list and adds the files it finds """
319
instance.PiTiVi.threads.addThread(PathWalker, list, instance.PiTiVi.current.sources.addUris)
321
# sourcelist callbacks
323
def _fileAddedCb(self, unused_sourcelist, factory):
324
""" a file was added to the sourcelist """
326
pixbuf = gtk.gdk.pixbuf_new_from_file(factory.thumbnail)
329
thumbnail = self.videofilepixbuf
330
elif factory.is_audio:
331
thumbnail = self.audiofilepixbuf
333
if not factory.video_info_stream:
334
desiredheight = 64 * pixbuf.get_height() / pixbuf.get_width()
336
vi = factory.video_info_stream
337
desiredheight = int(64 / float(vi.dar))
338
thumbnail = pixbuf.scale_simple(64, desiredheight, gtk.gdk.INTERP_BILINEAR)
339
self.storemodel.append([thumbnail,
340
factory.getPrettyInfo(),
343
"<b>%s</b>" % beautify_length(factory.length)])
344
self._displayTreeView()
346
def _fileRemovedCb(self, unused_sourcelist, uri):
347
""" the given uri was removed from the sourcelist """
348
# find the good line in the storemodel and remove it
349
model = self.storemodel
351
if uri == row[COL_URI]:
352
model.remove(row.iter)
355
self._displayTreeView(False)
357
def _notMediaFileCb(self, unused_sourcelist, uri, reason, extra):
358
""" The given uri isn't a media file """
359
self.infostub.addErrors(uri, reason, extra)
361
def _sourcesStartedImportingCb(self, unused_sourcelist):
362
if not self.infostub.showing:
363
self.pack_start(self.infostub, expand=False)
364
self.infostub.startingImport()
366
def _sourcesStoppedImportingCb(self, unused_sourcelist):
367
self.infostub.stoppingImport()
369
def _removeInfoStub(self, infostub):
370
self.remove(self.infostub)
372
## Error Dialog Box callbacks
374
def _errorDialogBoxCloseCb(self, unused_dialog):
375
self.errorDialogBox.destroy()
376
self.errorDialogBox = None
378
def _errorDialogBoxResponseCb(self, unused_dialog, unused_response):
379
self.errorDialogBox.destroy()
380
self.errorDialogBox = None
383
## Import Sources Dialog Box callbacks
385
def _dialogBoxResponseCb(self, dialogbox, response, select_folders):
386
gst.debug("response:%r" % response)
387
if response == gtk.RESPONSE_OK:
388
self._lastFolder = dialogbox.get_current_folder()
389
filenames = dialogbox.get_uris()
391
self.addFolders(filenames)
393
self.addFiles(filenames)
396
self._importDialog = None
398
def _dialogBoxCloseCb(self, unused_dialogbox):
400
self._importDialog = None
403
## UI Button callbacks
405
def _addButtonClickedCb(self, unused_widget=None):
406
""" called when a user clicks on the add button """
407
self.showImportSourcesDialog()
409
def _removeButtonClickedCb(self, unused_widget=None):
410
""" Called when a user clicks on the remove button """
411
tsel = self.treeview.get_selection()
412
if tsel.count_selected_rows() < 1:
414
model, selected = tsel.get_selected_rows()
415
# Sort the list in reverse order so we remove from
416
# the end and make sure that the paths are always valid
417
selected.sort(reverse=True)
418
for path in selected:
419
uri = model[path][COL_URI]
420
del instance.PiTiVi.current.sources[uri]
422
def _errorDialogButtonClickedCb(self, unused_widget=None):
423
""" called when the user click on the import errors button """
424
if self.errorDialogBox:
425
self.errorDialogBox.show()
426
if self.errorDialogButton.parent:
427
self.bothbox.remove(self.errorDialogButton)
429
def _playButtonClickedCb(self, unused_widget):
430
""" Called when a user clicks on the play button """
431
# get the selected filesourcefactory
432
model, paths = self.treeview.get_selection().get_selected_rows()
436
factory = model[path][COL_FACTORY]
437
gst.debug("Let's play %s" % factory.name)
438
instance.PiTiVi.playground.playTemporaryFilesourcefactory(factory)
440
def _treeViewButtonPressEventCb(self, unused_treeview, event):
441
if event.button == 3:
442
self.popup.popup(None, None, None, event.button, event.time)
444
def _rowActivatedCb(self, unused_treeview, path, unused_column):
445
factory = self.storemodel[path][COL_FACTORY]
446
instance.PiTiVi.playground.playTemporaryFilesourcefactory(factory)
448
def _newProjectCb(self, unused_pitivi, project):
449
# clear the storemodel
450
self.storemodel.clear()
452
# synchronize the storemodel with the new project's sourcelist
453
for uri, factory in project.sources:
455
if factory.thumbnail:
456
thumbnail = gtk.gdk.pixbuf_new_from_file(factory.thumbnail)
457
desiredheight = 64 * thumbnail.get_height() / thumbnail.get_width()
458
thumbnail = thumbnail.scale_simple(64, desiredheight, gtk.gdk.INTERP_BILINEAR)
459
name = os.path.basename(unquote(factory.name))
461
if not factory.thumbnail:
462
thumbnail = self.videofilepixbuf
464
if not factory.thumbnail:
465
thumbnail = self.audiofilepixbuf
466
# FIXME : update with new table structure (icon, infotext, objectfactory, uri
467
self.storemodel.append([thumbnail, name, factory, factory.name,
468
"<b>%s</b>" % beautify_length(factory.length)])
470
self._connectToProject(project)
475
def _dndDataReceivedCb(self, unused_widget, unused_context, unused_x,
476
unused_y, selection, targetType, unused_time):
478
if path[:7] == "file://":
479
# either it's on local system and we know if it's a directory
480
return os.path.isfile(path[7:])
482
# or it's not, in which case we assume it's a file
484
# or it's on local system with "file://"
485
return os.path.isfile(path)
487
gst.debug("targetType:%d, selection.data:%r" % (targetType, selection.data))
489
if targetType == dnd.TYPE_URI_LIST:
490
incoming = [x.strip('\x00') for x in selection.data.strip().split("\r\n") if x.strip('\x00')]
491
filenames = [x for x in incoming if isfile(x)]
492
directories = [x for x in incoming if not isfile(x)]
493
elif targetType == dnd.TYPE_TEXT_PLAIN:
494
incoming = selection.data.strip()
496
filenames = [incoming]
498
directories = [incoming]
500
self.addFolders(directories)
501
self.addFiles(filenames)
503
def _dndTreeBeginCb(self, unused_widget, context):
504
gst.info("tree drag_begin")
505
model, paths = self.treeview.get_selection().get_selected_rows()
507
context.drag_abort(int(time.time()))
509
row = model[paths[0]]
510
self.treeview.drag_source_set_icon_pixbuf(row[COL_ICON])
512
def getSelectedItems(self):
513
""" returns a list of selected items uri """
514
model, rows = self.treeview.get_selection().get_selected_rows()
515
return [model[path][COL_URI] for path in rows]
517
def _dndDataGetCb(self, unused_widget, unused_context, selection,
518
targetType, unused_eventTime):
519
gst.info("data get, type:%d" % targetType)
520
uris = self.getSelectedItems()
523
if targetType == dnd.TYPE_PITIVI_FILESOURCE:
524
selection.set(selection.target, 8,
526
elif targetType == dnd.TYPE_URI_LIST:
527
selection.set(selection.target, 8,
528
string.join(uris, "\n"))
530
class AudioFxListWidget(gtk.VBox):
531
""" Widget for listing video effects """
534
gtk.VBox.__init__(self)
535
self.set_border_width(5)
538
self.storemodel = gtk.ListStore(str, str, object)
540
self.scrollwin = gtk.ScrolledWindow()
541
self.scrollwin.set_policy(gtk.POLICY_NEVER,
542
gtk.POLICY_AUTOMATIC)
543
self.pack_start(self.scrollwin)
545
self.iconview = gtk.IconView(self.storemodel)
546
self.treeview = gtk.TreeView(self.storemodel)
548
namecol = gtk.TreeViewColumn(_("Name"))
549
self.treeview.append_column(namecol)
550
namecell = gtk.CellRendererText()
551
namecol.pack_start(namecell)
552
namecol.add_attribute(namecell, "text", 0)
554
namecol = gtk.TreeViewColumn(_("Description"))
555
self.treeview.append_column(namecol)
556
namecell = gtk.CellRendererText()
557
namecell.set_property("ellipsize", pango.ELLIPSIZE_END)
558
namecol.pack_start(namecell)
559
namecol.add_attribute(namecell, "text", 1)
561
self.scrollwin.add(self.treeview)
565
def _fillUpModel(self):
566
for factory in instance.PiTiVi.effects.simple_audio:
567
self.storemodel.append([factory.get_longname(),
568
factory.get_description(),
573
COL_FACTORY) = range(3)
575
class VideoFxListWidget(gtk.VBox):
576
""" Widget for listing video effects """
579
gtk.VBox.__init__(self)
580
self.set_border_width(5)
583
self.storemodel = gtk.ListStore(str, str, object)
585
self.scrollwin = gtk.ScrolledWindow()
586
self.scrollwin.set_policy(gtk.POLICY_NEVER,
587
gtk.POLICY_AUTOMATIC)
588
self.pack_start(self.scrollwin)
590
self.iconview = gtk.IconView(self.storemodel)
591
self.treeview = gtk.TreeView(self.storemodel)
593
namecol = gtk.TreeViewColumn(_("Name"))
594
self.treeview.append_column(namecol)
595
namecell = gtk.CellRendererText()
596
namecol.pack_start(namecell)
597
namecol.add_attribute(namecell, "text", COL_NAME)
599
namecol = gtk.TreeViewColumn(_("Description"))
600
self.treeview.append_column(namecol)
601
namecell = gtk.CellRendererText()
602
namecell.set_property("ellipsize", pango.ELLIPSIZE_END)
603
namecol.pack_start(namecell)
604
namecol.add_attribute(namecell, "text", COL_DESCRIPTION)
606
self.scrollwin.add(self.treeview)
610
def _fillUpModel(self):
611
for factory in instance.PiTiVi.effects.simple_video:
612
self.storemodel.append([factory.get_longname(),
613
factory.get_description(),
617
class TransitionListWidget(gtk.VBox):
618
""" Widget for listing transitions """
621
gtk.VBox.__init__(self)
622
self.iconview = gtk.IconView()
623
self.treeview = gtk.TreeView()
624
self.pack_start(self.iconview)
626
class InfoStub(gtk.HBox):
628
Box used to display information on the current state of the lists
632
"remove-me" : (gobject.SIGNAL_RUN_LAST,
638
gtk.HBox.__init__(self)
641
self._importingmessage = _("Importing clips...")
642
self._errorsmessage = _("Error(s) occured while importing")
643
self._errormessage = _("An error occured while importing")
648
anim = gtk.gdk.PixbufAnimation(get_pixmap_dir() + "/busy.gif")
649
self.busyanim = gtk.image_new_from_animation(anim)
651
self.erroricon = gtk.image_new_from_stock(gtk.STOCK_DIALOG_WARNING,
652
gtk.ICON_SIZE_SMALL_TOOLBAR)
654
self.infolabel = gtk.Label(self._importingmessage)
655
self.infolabel.set_alignment(0, 0.5)
657
self.questionbutton = gtk.Button()
658
self.questionbutton.set_image(gtk.image_new_from_stock(gtk.STOCK_INFO,
659
gtk.ICON_SIZE_SMALL_TOOLBAR))
660
self.questionbutton.connect("clicked", self._questionButtonClickedCb)
661
self._questionshowing = False
663
self.pack_start(self.busyanim, expand=False)
664
self._busyshowing = True
665
self.pack_start(self.infolabel, expand=True, fill=True)
667
def startingImport(self):
670
# if we're already showing and we have errors, show spinner
674
self.infolabel.set_text(self._importingmessage)
675
self._showQuestionButton(False)
678
def stoppingImport(self):
680
self._showErrorIcon()
681
if len(self.errors) > 1:
682
self.infolabel.set_text(self._errorsmessage)
684
self.infolabel.set_text(self._errormessage)
685
self._showQuestionButton()
688
self.emit("remove-me")
690
def addErrors(self, *args):
691
self.errors.append(args)
693
def _showBusyAnim(self):
694
if self._busyshowing:
696
self.remove(self.erroricon)
697
self.pack_start(self.busyanim, expand=False)
698
self.reorder_child(self.busyanim, 0)
700
self._busyshowing = True
702
def _showErrorIcon(self):
703
if not self._busyshowing:
705
self.remove(self.busyanim)
706
self.pack_start(self.erroricon, expand=False)
707
self.reorder_child(self.erroricon, 0)
708
self.erroricon.show()
709
self._busyshowing = False
711
def _showQuestionButton(self, visible=True):
712
if visible and not self._questionshowing:
713
self.pack_start(self.questionbutton, expand=False)
714
self.questionbutton.show()
715
self._questionshowing = True
716
elif not visible and self._questionshowing:
717
self.remove(self.questionbutton)
718
self._questionshowing = False
720
def _errorDialogBoxCloseCb(self, dialog):
723
def _errorDialogBoxResponseCb(self, dialog, unused_response):
726
def _questionButtonClickedCb(self, unused_button):
727
if len(self.errors) > 1:
728
msgs = (_("Error while analyzing files"),
729
_("The following files can not be used with PiTiVi."))
731
msgs = (_("Error while analyzing a file"),
732
_("The following file can not be used with PiTiVi."))
734
dbox = FileListErrorDialog(*msgs)
735
dbox.connect("close", self._errorDialogBoxCloseCb)
736
dbox.connect("response", self._errorDialogBoxResponseCb)
737
for uri,reason,extra in self.errors:
738
dbox.addFailedFile(uri, reason, extra)
743
self.emit("remove-me")
756
class PathWalker(Thread):
758
Thread for recursively searching in a list of directories
761
def __init__(self, paths, callback):
762
Thread.__init__(self)
763
gst.log("New PathWalker for %s" % paths)
765
self.callback = callback
766
self.stopme = threading.Event()
769
for folder in self.paths:
770
gst.log("folder %s" % folder)
771
if folder.startswith("file://"):
772
folder = folder[len("file://"):]
773
for path, dirs, files in os.walk(folder):
774
if self.stopme.isSet():
778
uriList.append("file://%s" % os.path.join(path, afile))
780
self.callback(uriList)