1
# PiTiVi , Non-linear video editor
5
# Copyright (c) 2009, Alessandro Decina <alessandrod.@gmail.com>
6
# Copyright (c) 2009, Edward Hervey <bilboed@bilboed.com>
8
# This program is free software; you can redistribute it and/or
9
# modify it under the terms of the GNU Lesser General Public
10
# License as published by the Free Software Foundation; either
11
# version 2.1 of the License, or (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
# Lesser General Public License for more details.
18
# You should have received a copy of the GNU Lesser General Public
19
# License along with this program; if not, write to the
20
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21
# Boston, MA 02110-1301, USA.
23
from gettext import gettext as _
27
from xml.etree.ElementTree import Element, SubElement, tostring, parse
29
from pitivi.reflect import qual, namedAny
30
from pitivi.factories.base import SourceFactory
31
from pitivi.factories.file import FileSourceFactory
32
from pitivi.factories.operation import EffectFactory
33
from pitivi.timeline.track import Track, TrackEffect
34
from pitivi.timeline.timeline import TimelineObject
35
from pitivi.formatters.base import Formatter, FormatterError
36
from pitivi.utils import get_filesystem_encoding
37
from pitivi.settings import ExportSettings
38
from pitivi.stream import match_stream_groups_map
43
def indent(elem, level=0):
44
i = "\n" + level * " "
46
if not elem.text or not elem.text.strip():
48
if not elem.tail or not elem.tail.strip():
51
indent(elem, level + 1)
52
if not elem.tail or not elem.tail.strip():
55
if level and (not elem.tail or not elem.tail.strip()):
59
class ElementTreeFormatterContext(object):
63
self.track_objects = {}
64
self.rootelement = None
67
class ElementTreeFormatterSaveContext(ElementTreeFormatterContext):
71
class ElementTreeFormatterLoadContext(ElementTreeFormatterContext):
75
class ElementTreeFormatter(Formatter):
77
_our_properties = ["id", "type"]
79
def __init__(self, avalaible_effects, *args, **kwargs):
80
Formatter.__init__(self, avalaible_effects, *args, **kwargs)
81
# An Element representing the <factories> element.
82
self.factoriesnode = None
83
# An Element representing the <timeline> element.
84
self.timelinenode = None
85
# An Element representing the <export-settings> element.
86
self._settingsnode = None
87
# An Element representing the <metadata> element.
88
self._metadatanode = None
90
# A list of SourceFactory objects.
92
self._context = ElementTreeFormatterContext()
94
def _new_element_id(self):
95
element_id = self._element_id
98
return str(element_id)
100
def _filterElementProperties(self, element):
101
for name, value in element.attrib.iteritems():
102
if name in self._our_properties:
107
def _parsePropertyValue(self, value):
110
# We treat the GEnum values differently because they are actually ints.
111
if "(GEnum)" in value:
112
return int(value.split("(GEnum)")[1])
113
# We treat the guint64 values differently because when we serialize
114
# a long integer, the serialization is, for example, "(guint64) 4L",
115
# and gst.Caps() fails to parse it, because it has "L" at the end.
116
if "(guint64)" in value:
117
value = value.rstrip('lL')
119
# TODO: Use what gst.Caps() uses to parse a property value.
120
caps = gst.Caps("structure1, property1=%s;" % value)
121
except TypeError, exception:
122
# Report the value which caused the exception.
123
raise Exception(exception, value)
125
return structure["property1"]
127
def _saveStream(self, stream):
128
element = Element("stream")
129
element.attrib["id"] = self._new_element_id()
130
element.attrib["type"] = qual(stream.__class__)
131
element.attrib["caps"] = str(stream.caps)
132
element.attrib["name"] = str(stream.pad_name)
134
self._context.streams[stream] = element
138
def _loadStream(self, element):
139
id_ = element.attrib["id"]
140
klass = namedAny(element.attrib["type"])
141
caps = gst.Caps(element.attrib["caps"])
143
stream = klass(caps, element.attrib.get("name", None))
145
self._context.streams[id_] = stream
149
def _saveStreamRef(self, stream):
150
stream_element = self._context.streams[stream]
151
element = Element("stream-ref")
152
element.attrib["id"] = stream_element.attrib["id"]
156
def _loadStreamRef(self, element):
157
return self._context.streams[element.attrib["id"]]
159
def _saveSource(self, source):
160
element = self._saveObjectFactory(source)
161
if isinstance(source, FileSourceFactory):
162
return self._saveFileSourceFactory(element, source)
166
def _loadFactory(self, element):
167
klass = namedAny(element.attrib["type"])
168
assert issubclass(klass, SourceFactory)
169
factory = self._loadObjectFactory(klass, element)
170
self._context.factories[element.attrib["id"]] = factory
173
def _saveObjectFactory(self, factory):
174
element = Element("source")
175
element.attrib["id"] = self._new_element_id()
176
element.attrib["type"] = qual(factory.__class__)
177
element.attrib["default_duration"] = str(factory.default_duration)
178
element.attrib["duration"] = str(factory.duration)
180
input_streams_element = SubElement(element, "input-streams")
181
input_streams = factory.getInputStreams()
182
for stream in input_streams:
183
stream_element = self._saveStream(stream)
184
input_streams_element.append(stream_element)
186
output_streams_element = SubElement(element, "output-streams")
187
output_streams = factory.getOutputStreams()
188
for stream in output_streams:
189
stream_element = self._saveStream(stream)
190
output_streams_element.append(stream_element)
192
self._context.factories[factory] = element
196
def _loadObjectFactory(self, klass, element):
197
"""Instantiate the specified class and set its attributes.
199
@param klass: An ObjectFactory subclass.
200
@param element: The Element representing the object to be created.
201
@return: An instance of the specified klass.
203
self.debug("klass:%r, element:%r", klass, element)
204
# Instantiate the class.
206
if issubclass(klass, FileSourceFactory):
207
filename = element.attrib.get("filename")
208
if isinstance(filename, unicode):
209
filename = filename.encode("utf-8")
210
args.append(filename)
211
factory = klass(*args)
213
# Set the attributes of the instance.
214
factory.duration = long(element.attrib["duration"])
215
factory.default_duration = long(element.attrib["default_duration"])
217
input_streams = element.find("input-streams")
218
if input_streams is not None:
219
for stream_element in input_streams:
220
stream = self._loadStream(stream_element)
221
factory.addInputStream(stream)
223
output_streams = element.find("output-streams")
224
if output_streams is not None:
225
for stream_element in output_streams:
226
stream = self._loadStream(stream_element)
227
factory.addOutputStream(stream)
229
if issubclass(klass, FileSourceFactory):
230
filename1 = self.validateSourceURI(filename, factory)
231
if filename != filename1:
233
factory.uri = filename1
234
factory.filename = filename1
238
def _saveFileSourceFactory(self, element, source):
239
# FIXME: we should probably have a rule that we only deal with unicode
241
if not isinstance(source.filename, unicode):
242
fs_encoding = get_filesystem_encoding()
243
filename = source.filename.decode(fs_encoding)
245
filename = source.filename
246
element.attrib["filename"] = filename
250
def _saveFactoryRef(self, factory):
251
element = Element("factory-ref")
252
element.attrib["id"] = self._context.factories[factory].attrib["id"]
256
def _loadFactoryRef(self, element):
257
return self._context.factories[element.attrib["id"]]
259
def _saveFactories(self, factories):
260
element = Element("factories")
261
sources = SubElement(element, "sources")
262
for factory in factories:
263
if isinstance(factory, SourceFactory):
264
source_element = self._saveSource(factory)
265
sources.append(source_element)
269
def _loadSources(self):
270
"""Deserialize the sources.
272
@return: A list of SourceFactory objects.
274
sources = self.factoriesnode.find("sources")
276
# For each <source> element in the <sources> element, create the
277
# object represented by it.
279
res.append(self._loadFactory(src))
282
def _serializeDict(self, element, values_dict):
283
"""Serialize the specified dict into the specified Element instance."""
284
for key, value in values_dict.iteritems():
285
if isinstance(value, str):
286
# TODO: If this starts with "(<type>)", or if it contains ";",
287
# the deserialization might fail.
288
serialized_value = value
289
elif isinstance(value, bool):
290
serialized_value = "(boolean) %r" % value
291
elif isinstance(value, float):
292
serialized_value = "(float) %r" % value
294
serialized_value = "(guint64) %r" % value
295
element.attrib[key] = serialized_value
297
def _deserializeDict(self, element):
298
"""Get the specified Element as a deserialized dict."""
300
for name, value_string in element.attrib.iteritems():
301
values_dict[name] = self._parsePropertyValue(value_string)
304
def _saveProjectSettings(self, settings):
305
element = Element('export-settings')
306
element.attrib["videowidth"] = str(int(settings.videowidth))
307
element.attrib["videoheight"] = str(int(settings.videoheight))
308
element.attrib["render-scale"] = str(int(settings.render_scale))
309
element.attrib["videorate-num"] = str(int(settings.videorate.num))
310
element.attrib["videorate-denom"] = str(int(settings.videorate.denom))
311
element.attrib["videopar-num"] = str(int(settings.videopar.num))
312
element.attrib["videopar-denom"] = str(int(settings.videopar.denom))
313
element.attrib["audiochannels"] = str(int(settings.audiochannels))
314
element.attrib["audiorate"] = str(int(settings.audiorate))
315
element.attrib["audiodepth"] = str(int(settings.audiodepth))
316
element.attrib["vencoder"] = settings.vencoder or ""
317
element.attrib["aencoder"] = settings.aencoder or ""
318
element.attrib["muxer"] = settings.muxer
320
# container/encoder settings
321
if settings.containersettings != {}:
322
ss = SubElement(element, "container-settings")
323
self._serializeDict(ss, settings.containersettings)
324
if settings.vcodecsettings != {}:
325
ss = SubElement(element, "vcodec-settings")
326
self._serializeDict(ss, settings.vcodecsettings)
327
if settings.acodecsettings != {}:
328
ss = SubElement(element, "acodec-settings")
329
self._serializeDict(ss, settings.acodecsettings)
332
def _saveProjectMetadata(self, project):
333
element = Element('metadata')
334
element.attrib["author"] = project.author
335
element.attrib["name"] = project.name
336
element.attrib["year"] = project.year
339
def _loadProjectSettings(self, element):
340
self.debug("element:%r", element)
341
settings = ExportSettings()
342
settings.videowidth = int(element.attrib["videowidth"])
343
settings.videoheight = int(element.attrib["videoheight"])
344
if "render-scale" in element.attrib:
345
settings.render_scale = int(element.attrib["render-scale"])
346
settings.videorate = gst.Fraction(int(element.attrib["videorate-num"]),
347
int(element.attrib["videorate-denom"]))
348
settings.videopar = gst.Fraction(int(element.attrib["videopar-num"]),
349
int(element.attrib["videopar-denom"]))
350
settings.audiochannels = int(element.attrib["audiochannels"])
351
settings.audiorate = int(element.attrib["audiorate"])
352
settings.audiodepth = int(element.attrib["audiodepth"])
353
settings.aencoder = element.attrib["aencoder"]
354
settings.vencoder = element.attrib["vencoder"]
355
settings.muxer = element.attrib["muxer"]
357
sett = element.find("container-settings")
359
settings.containersettings = self._deserializeDict(sett)
360
sett = element.find("vcodec-settings")
362
settings.vcodecsettings = self._deserializeDict(sett)
363
sett = element.find("acodec-settings")
365
settings.acodecsettings = self._deserializeDict(sett)
369
def _loadProjectMetadata(self, element, project):
370
project.name = element.attrib["name"]
371
project.author = element.attrib["author"]
372
project.year = element.attrib["year"]
374
def _saveTrackObject(self, track_object):
375
element = Element("track-object")
376
element.attrib["id"] = self._new_element_id()
377
element.attrib["type"] = qual(track_object.__class__)
378
for attribute in ("start", "duration",
379
"in_point", "media_duration"):
380
element.attrib[attribute] = \
381
str("(gint64)%s" % getattr(track_object, attribute))
383
element.attrib["priority"] = "(int)%s" % track_object.priority
384
element.attrib["active"] = "(bool)%s" % track_object.active
386
if not isinstance(track_object.factory, EffectFactory):
387
self._saveSourceTrackObject(track_object, element)
389
self._saveTrackEffect(track_object, element)
391
self._context.track_objects[track_object] = element
395
def _saveTrackEffect(self, track_object, element):
396
effect_element = Element("effect")
397
element.append(effect_element)
399
factory_element = Element("factory")
400
factory_element.attrib["name"] = track_object.factory.name
401
effect_element.append(factory_element)
403
self._saveEffectProperties(track_object, effect_element)
405
def _saveEffectProperties(self, track_object, effect_element):
406
effect_properties = Element("gst-element-properties")
407
effect = track_object.getElement()
408
properties = gobject.list_properties(effect)
409
for prop in properties:
410
if prop.flags & gobject.PARAM_READABLE:
411
type_name = str(gobject.type_name(prop.value_type.fundamental))
412
#FIXME we just take the int equivalent to the GEnum, how should it be handled?
413
if type_name == "GEnum":
414
value = str(effect.get_property(prop.name).__int__())
416
value = str(effect.get_property(prop.name))
417
effect_properties.attrib[prop.name] = '(' + type_name + ')' + value
418
effect_element.append(effect_properties)
420
def _saveSourceTrackObject(self, track_object, element):
421
factory_ref = self._saveFactoryRef(track_object.factory)
422
stream_ref = self._saveStreamRef(track_object.stream)
423
element.append(factory_ref)
424
element.append(stream_ref)
425
interpolators = track_object.getInterpolators()
426
curves = Element("curves")
427
for property, interpolator in interpolators.itervalues():
428
curves.append(self._saveInterpolator(interpolator, property))
429
element.append(curves)
431
def _loadTrackObject(self, track, element):
432
self.debug("%r", element)
433
klass = namedAny(element.attrib["type"])
434
if klass is TrackEffect:
435
track_object = self._loadEffectTrackObject(element, klass, track)
437
track_object = self._loadSourceTrackObject(element, klass, track)
440
def _loadEffectTrackObject(self, element, klass, track):
441
effect_element = element.find('effect')
442
factory_name = effect_element.find('factory').attrib['name']
443
properties_elem = effect_element.find('gst-element-properties')
445
factory = self.avalaible_effects.getFactoryFromName(factory_name)
447
# TODO: Find a way to install the missing effect.
448
raise FormatterError(_("The project contains effects which are not "
449
"available on the system."))
451
input_stream = factory.getInputStreams()
453
raise FormatterError("cant find effect factory input stream")
454
input_stream = input_stream[0]
455
track_object = klass(factory, input_stream)
456
track.addTrackObject(track_object)
458
for name, value_string in self._filterElementProperties(element):
459
value = self._parsePropertyValue(value_string)
460
setattr(track_object, name, value)
462
effect_gst_element = track_object.getElement()
463
for name, value in properties_elem.attrib.iteritems():
464
value = self._parsePropertyValue(value)
465
effect_gst_element.set_property(name, value)
467
self._context.track_objects[element.attrib["id"]] = track_object
470
def _loadSourceTrackObject(self, element, klass, track):
471
factory_ref = element.find("factory-ref")
472
factory = self._loadFactoryRef(factory_ref)
474
stream_ref = element.find("stream-ref")
475
stream = self._loadStreamRef(stream_ref)
477
track_object = klass(factory, stream)
478
for name, value_string in self._filterElementProperties(element):
479
value = self._parsePropertyValue(value_string)
480
setattr(track_object, name, value)
481
track.addTrackObject(track_object)
482
curves_element = element.find("curves")
483
if curves_element is not None:
484
for curve in curves_element.getchildren():
485
self._loadInterpolator(curve, track_object)
487
self._context.track_objects[element.attrib["id"]] = track_object
490
def _saveInterpolator(self, interpolator, prop):
491
typename = prop.value_type.name
492
element = Element("curve", property=prop.name, type=typename,
495
start = self._saveKeyframe(interpolator.start, typename, False)
497
element.append(start)
499
for kf in interpolator.getInteriorKeyframes():
500
kfel = self._saveKeyframe(kf, typename)
503
end = self._saveKeyframe(interpolator.end, typename, False)
508
def _saveKeyframe(self, keyframe, typename, time=True):
509
element = Element("keyframe")
510
element.attrib["value"] = "(%s)%r" % (typename, keyframe.value)
511
element.attrib["mode"] = str(keyframe.mode)
514
element.attrib["time"] = str(keyframe.time)
517
def _loadInterpolator(self, element, trackobject):
518
interpolator = trackobject.getInterpolator(element.attrib["property"])
519
start = element.find("start")
520
interpolator.start.value = self._parsePropertyValue(
521
start.attrib["value"])
522
interpolator.start.mode = int(start.attrib["mode"])
524
for kf in element.getiterator("keyframe"):
525
interpolator.newKeyframe(long(kf.attrib["time"]),
526
value=self._parsePropertyValue(kf.attrib["value"]),
527
mode=int(kf.attrib["mode"]))
528
end = element.find("end")
529
interpolator.end.value = self._parsePropertyValue(end.attrib["value"])
530
interpolator.end.mode = int(end.attrib["mode"])
532
# if we are using old-style keyframe curves where start, end point
533
# represent start of file, convert to the newer representation to
534
# preserve the existing shape of the curve
535
if not ("version" in element.attrib):
536
# move start, end keyframes to start of file
537
interpolator.updateMediaStart(0)
538
interpolator.updateMediaStop(trackobject.factory.duration)
540
# get the value of the curve at true start/end points
541
startval = interpolator.valueAt(trackobject.in_point)
542
endval = interpolator.valueAt(trackobject.out_point)
543
interpolator.start.value = startval
544
interpolator.end.value = endval
546
# move start, end keyframes back to proper position
547
interpolator.updateMediaStart(trackobject.in_point)
548
interpolator.updateMediaStop(trackobject.out_point)
550
def _saveTrackObjectRef(self, track_object):
551
element = Element("track-object-ref")
552
element.attrib["id"] = self._context.track_objects[track_object].attrib["id"]
556
def _loadTrackObjectRef(self, element):
557
self.debug("%r", element)
558
return self._context.track_objects[element.attrib["id"]]
560
def _saveTrackObjectRefs(self, track_objects):
561
element = Element("track-object-refs")
563
for track_object in track_objects:
564
track_object_ref = self._saveTrackObjectRef(track_object)
565
element.append(track_object_ref)
569
def _loadTrackObjectRefs(self, element):
570
self.debug("%r", element)
572
for track_object_element in element:
573
track_object = self._loadTrackObjectRef(track_object_element)
574
track_objects.append(track_object)
578
def _saveTrack(self, track):
579
element = Element("track")
580
stream_element = self._saveStream(track.stream)
581
element.append(stream_element)
582
track_objects = SubElement(element, "track-objects")
584
for track_object in track.track_objects:
586
track_object_element = self._saveTrackObject(track_object)
587
track_objects.append(track_object_element)
591
def _loadTrack(self, element):
592
self.debug("%r", element)
593
stream_element = element.find("stream")
594
stream = self._loadStream(stream_element)
596
track = Track(stream)
598
track_objects_element = element.find("track-objects")
599
for track_object_element in track_objects_element:
600
self._loadTrackObject(track, track_object_element)
604
def _saveTracks(self, tracks):
605
element = Element("tracks")
607
track_element = self._saveTrack(track)
608
element.append(track_element)
612
def _loadTracks(self, element):
613
self.debug("element:%r", element)
615
for track_element in element:
616
track = self._loadTrack(track_element)
623
def _saveTimelineObject(self, timeline_object):
624
element = Element("timeline-object")
625
factory_ref = self._saveFactoryRef(timeline_object.factory)
626
element.append(factory_ref)
627
track_object_refs = \
628
self._saveTrackObjectRefs(timeline_object.track_objects)
629
element.append(track_object_refs)
633
def _loadTimelineObject(self, element):
634
factory_ref = element.find("factory-ref")
635
factory = self._loadFactoryRef(factory_ref)
637
timeline_object = TimelineObject(factory)
638
track_object_refs_element = element.find("track-object-refs")
640
self._loadTrackObjectRefs(track_object_refs_element)
642
for track_object in track_objects:
643
timeline_object.addTrackObject(track_object)
645
return timeline_object
647
def _saveTimelineObjects(self, timeline_objects):
648
element = Element("timeline-objects")
649
for timeline_object in timeline_objects:
650
timeline_object_element = self._saveTimelineObject(timeline_object)
651
element.append(timeline_object_element)
655
def _loadTimelineObjects(self, element):
656
timeline_objects = []
657
for timeline_object_element in element:
659
self._loadTimelineObject(timeline_object_element)
660
timeline_objects.append(timeline_object)
662
return timeline_objects
666
def _saveTimeline(self, timeline):
667
element = Element("timeline")
669
tracks = self._saveTracks(timeline.tracks)
670
element.append(tracks)
673
self._saveTimelineObjects(timeline.timeline_objects)
674
element.append(timeline_objects)
678
def _loadTimeline(self, element):
679
self.debug("element:%r", element)
681
timeline = self.project.timeline
684
tracks_element = element.find("tracks")
685
tracks = self._loadTracks(tracks_element)
688
timeline_objects_element = element.find("timeline-objects")
690
self._loadTimelineObjects(timeline_objects_element)
693
timeline.addTrack(track)
695
# add the timeline objects
696
for timeline_object in timeline_objects:
697
# NOTE: this is a low-level routine that simply appends the
698
# timeline object to the timeline list. It doesn't ensure all the
699
# child track objects have been added to their respective tracks.
700
timeline.addTimelineObject(timeline_object)
706
def _saveMainTag(self):
707
element = Element("pitivi")
708
element.attrib["formatter"] = "etree"
709
element.attrib["version"] = version
713
def _serializeProject(self, project):
714
root = self._saveMainTag()
718
root.append(self._saveProjectSettings(project.settings))
721
root.append(self._saveProjectMetadata(project))
724
root.append(self._saveFactories(project.sources.getSources()))
727
root.append(self._saveTimeline(project.timeline))
730
## Formatter method implementations
732
def _saveProject(self, project, location):
733
root = self._serializeProject(project)
734
f = file(location.split('file://')[1], "w")
736
f.write(tostring(root))
741
def _loadProject(self, project_uri, project):
742
self.debug("project_uri:%s, project:%r", project_uri, project)
743
# open the given location
744
self._context.rootelement = parse(project_uri.split('://', 1)[1])
745
self.factoriesnode = self._context.rootelement.find("factories")
746
self.timelinenode = self._context.rootelement.find("timeline")
747
self._settingsnode = self._context.rootelement.find("export-settings")
748
self._metadatanode = self._context.rootelement.find("metadata")
750
if self._settingsnode != None:
751
project.setSettings(self._loadProjectSettings(self._settingsnode))
753
if self._metadatanode != None:
754
self._loadProjectMetadata(self._metadatanode, project)
755
# rediscover the factories
756
closure = {"rediscovered": 0}
758
sources = self._getSources()
759
except FormatterError, e:
760
self.emit("new-project-failed", project_uri, e)
763
uris = [source.uri for source in sources]
764
project.sources.nb_file_to_import = len(uris)
765
discoverer = project.sources.discoverer
766
discoverer.connect("discovery-done", self._discovererDiscoveryDoneCb,
767
project, sources, uris, closure)
768
discoverer.connect("discovery-error", self._discovererDiscoveryErrorCb,
769
project, sources, uris, closure, project_uri)
772
self._finishLoadingProject(project)
774
# start the rediscovering from the first source
776
discoverer.addUri(source.uri)
778
def _findFactoryContextKey(self, old_factory):
780
for k, old_factory1 in self._context.factories.iteritems():
781
if old_factory is old_factory1:
787
def _matchFactoryStreams(self, factory, old_factory):
788
old_streams = old_factory.getOutputStreams()
789
streams = factory.getOutputStreams()
790
self.debug("matching factory streams old (%s) %s new (%s) %s",
791
len(old_streams), old_streams, len(streams), streams)
792
if len(old_streams) != len(streams):
793
raise FormatterError("cant find all streams")
795
stream_map = match_stream_groups_map(old_streams, streams)
796
self.debug("stream map (%s) %s", len(stream_map), stream_map)
797
if len(stream_map) != len(old_streams):
798
raise FormatterError("streams don't match")
802
def _replaceOldFactoryStreams(self, factory, old_factory):
803
old_stream_to_new_stream = self._matchFactoryStreams(factory,
807
for stream_id, old_stream in self._context.streams.iteritems():
809
new_stream = old_stream_to_new_stream[old_stream]
811
new_stream = old_stream
812
new_streams[stream_id] = new_stream
814
self._context.streams = new_streams
816
def _replaceMatchingOldFactory(self, factory, old_factories):
818
old_factory_index = None
819
for index, old_factory1 in enumerate(old_factories):
820
if old_factory1.uri == factory.uri:
821
old_factory = old_factory1
822
old_factory_index = index
825
# this should never happen
826
assert old_factory is not None
828
# replace the old factory with the new rediscovered one
829
old_factories[old_factory_index] = factory
831
# make self._context.factories[key] point to the new factory
832
context_key = self._findFactoryContextKey(old_factory)
833
self._context.factories[context_key] = factory
835
self._replaceOldFactoryStreams(factory, old_factory)
837
def _discovererDiscoveryDoneCb(self, discoverer, uri, factory,
838
project, old_factories, uris, closure):
839
if factory.uri not in uris:
840
# someone else is using discoverer, this signal isn't for us
843
self._replaceMatchingOldFactory(factory, old_factories)
844
project.sources.addFactory(factory)
846
closure["rediscovered"] += 1
847
if closure["rediscovered"] == len(old_factories):
848
self._finishLoadingProject(project)
851
# schedule the next source
852
next = old_factories[closure["rediscovered"]]
853
discoverer.addUri(next.uri)
855
def _discovererDiscoveryErrorCb(self, discoverer, uri, error, detail,
856
project, sources, uris, closure, project_uri):
858
# someone else is using discoverer, this signal isn't for us
861
message = _("Failed loading %(uri)s.") % {"uri": uri}
862
message += "\n\n%s" % error
864
message += "\n\n%s" % detail
865
formatter_error = FormatterError(message)
866
self.emit("new-project-failed", project_uri, formatter_error)
868
def newProject(self):
869
project = Formatter.newProject(self)
871
if self._settingsnode != None:
872
project.setSettings(self._loadProjectSettings(self._settingsnode))
875
if self._metadatanode != None:
876
self._loadProjectMetadata(self._metadatanode, project)
880
def _getSources(self):
881
self.debug("%r", self)
882
if self._sources is None:
883
self._sources = self._loadSources()
886
def _fillTimeline(self):
887
# fill up self.project
888
self._loadTimeline(self.timelinenode)
891
def canHandle(cls, uri):
892
return uri.endswith(".xptv")