Example #1
0
    def run(self, path_, subtitle_loader, cuts=None):
        """Loads a SubtitleList from the subtitle file path_ using the
        given subtitle_loader function and applies the loaded Submod-
        script.
        
        subtitle_loader must be a function that accept a path as
        parameter and returns a SubtitleList.
        
        If cuts is set then the timings are adapted for these cuts so
        that the created subtitle and the cut video are in sync.
        
        The changes are always made in the following order: move,
        update, remove, add.

        NOTE: All ids specified in the Submod-script refer to the
              subtitles in the original/unchanged SubtitleList. If for
              example you specify multiple items in the "remove"-
              section you don't need to take into account that removing
              a subtitle will change the id of following subtitles.
        
        A ValueError/IndexError may be raised, for example if the
        subtitle file has a wrong checksum or if a subtitle was not
        found.
        
        Returns a SubtitleList.
        """
        # NOTE: We do not use SubtitleList's methods to modify the
        #       timestamps of subtitles. For example move_subtitle()
        #       could change the order of the subtitles so that the id's
        #       from the submod may not refer to the correct subtitle
        #       anymore. Instead we modify the subtitle's start/end
        #       values directly which will result in a SubtitleList that
        #       is not properly ordered. After moving, updating and
        #       removing subtitles we add all subtitles of the current
        #       SubtitleList to a new SubtitleList which then will take
        #       care of the correct order of the subtitles based on
        #       their timestamps. At the end we add the new subtitles
        #       from the add-section of the submod.
        sha256 = self._hash_subtitle_file(path_).lower()
        if sha256 != self.script['subtitle']['sha256']:
            raise ValueError(_('The subtitle has a wrong checksum ("{}")!')
                             .format(sha256))
        subtitle_list = subtitle_loader(path_)
        offset = 0.0
        for move in self.script['move']:
            by = Time.millis_from_str(move['by'])
            for i in self._get_valid_ids(subtitle_list, move['id']):
                if cuts is not None:
                    offset = self._get_run_offset(subtitle_list[i].start + by,
                                                  cuts)
                subtitle_list[i].start = (subtitle_list[i].start + by) - offset
                subtitle_list[i].end = (subtitle_list[i].end + by) - offset
        for update in self.script['update']:
            # Each update may contain any of end, start and text values
            # where the end/start-strings must be converted to number of
            # milliseconds.
            new_values = []
            for k, f in [('start', lambda x : self._get_run_time(
                                                Time.millis_from_str(x), cuts)),
                         ('end', lambda x : self._get_run_time(
                                                Time.millis_from_str(x), cuts)),
                         ('text', lambda x : x.encode('utf-8'))]:
                if k in update:
                    new_values.append((k, f(update[k],)))
            subtitles = self._get_subtitles_by_ids(subtitle_list, update['id'])
            for subtitle in subtitles:
                for k, v in new_values:
                    setattr(subtitle, k, v)
        # Gather ids of subs that should be removed and sort them desc.
        # The subs are removed in that order. Thus the sub with the 
        # biggest id will be removed first. If we would start with a
        # smaller id, then further ids that should be removed will point
        # to a wrong subtitle.
        to_remove = []
        for remove in self.script['remove']:
            to_remove += self._get_valid_ids(subtitle_list, remove['id'])
        for i in sorted(to_remove, reverse=True):
            subtitle_list.remove_subtitle(i)
        new_subtitle_list = SubtitleList()
        for subtitle in subtitle_list:
            new_subtitle_list.add_subtitle(subtitle)
        for add in self.script['add']:
            start = self._get_run_time(Time.millis_from_str(add['start']), cuts)
            end = self._get_run_time(Time.millis_from_str(add['end']), cuts)
            subtitle = Subtitle(start, end, add['text'].encode('utf-8'))
            new_subtitle_list.add_subtitle(subtitle)
        return new_subtitle_list