Ejemplo n.º 1
0
        /// <summary>
        /// Removes the overlappings in one list.
        ///
        /// Example:
        ///
        /// sub1 is in list1
        /// sub2 and sub3 are in list2
        ///
        /// sub1 overlaps with sub2, sub3
        ///  + sub2 and sub3 overlap
        ///
        /// An example of this in the real worlds are announcements while the main characters are speaking.
        /// We obviously only want to map the character-dialog and the announcement to its own translation.
        ///
        /// </summary>
        static void RemoveOverlappings(List <ExtendedLineInfo> primaryList)
        {
            foreach (ExtendedLineInfo eli in primaryList)
            {
                ExtendedLineInfo leastFittingLine;
                do
                {
                    leastFittingLine = null;

                    // the function will group all overlapping lines together, so we delete the worst
                    // line from that container and recalculate the containers (because overlapping could
                    // have been stopped)
                    var nonOverlappingLineContainers = UtilsSubtitle.GetNonOverlappingTimeSpans(eli.matchingLines, 0.2);
                    foreach (var lineContainer in nonOverlappingLineContainers)
                    {
                        if (lineContainer.TimeSpans.Count > 1)
                        {
                            leastFittingLine = GetLeastFittingLine(eli.lineInfo, lineContainer.TimeSpans);
                            break;
                        }
                    }

                    // rebuild matching lines without the removed line
                    if (leastFittingLine != null)
                    {
                        eli.matchingLines.Remove(leastFittingLine);
                        continue;
                    }
                } while(leastFittingLine != null);
            }
        }
        /// <summary>
        /// By calling this function, all references to LineInfo in "listToChange" passed in
        /// constructor can be changed. Retiming includes removing/introducing commercial breaks.
        ///
        /// By using the "noSplitting=true" option, the whole list gets shifted as one.
        /// </summary>
        public void Retime(bool noSplitting = false)
        {
            var stopwatch = new System.Diagnostics.Stopwatch();

            stopwatch.Start();

            var queue = new Queue <RemainingSlice>();

            // initilize first slice (with all lines)
            RemainingSlice fullSlice = new RemainingSlice(new List <LineInfo>(m_referenceList), new List <LineInfo>(m_listToChange));

            queue.Enqueue(fullSlice);

            // just find best offset
            if (noSplitting)
            {
                double bestOffset = FindBestOffset(fullSlice);
                UtilsSubtitle.ShiftByTime(m_listToChange, bestOffset);
                return;
            }


            // Only lines that are in the same slice (list of lines on both sides) can be matched.
            // When processing the slices, a consecutive row of lines is recognized as "good". Two new
            // slices (one left of the right of the row) are then created.
            //
            // This queue will process all these slices that have some elements in them.
            while (queue.Count > 0)
            {
                RemainingSlice slice = queue.Dequeue();
                //if(slice.listToChangeLines.Count == 0 || slice.referenceListLines.Count == 0) {
                //	// T O D O: these shouldn't just be moved out of the way but left in another slice
                //	// this is more debug code than anything else
                //	UtilsSubtitle.ShiftByTime(slice.listToChangeLines, 10000);
                //	UtilsSubtitle.ShiftByTime(slice.referenceListLines, 10000);
                //	continue;
                //}

                double bestOffset = FindBestOffset(slice);
                ApplyOffset(slice, bestOffset, queue);
            }

            Console.WriteLine(stopwatch.ElapsedMilliseconds);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Align subtitle line by constant value, to audio or to other subtitle based on settings in "perSubSettings".
        /// </summary>
        public static void AlignSub(List <LineInfo> lineList, List <LineInfo> referenceList, EpisodeInfo epInfo, Settings settings, PerSubtitleSettings thisSubSettings)
        {
            switch (thisSubSettings.AlignMode)
            {
            case PerSubtitleSettings.AlignModes.ByConstantValue:
                UtilsSubtitle.ShiftByTime(lineList, thisSubSettings.SubDelay);
                break;

            case PerSubtitleSettings.AlignModes.ToAudio:
                UtilsAlignSubToAudio alignToAudio = new UtilsAlignSubToAudio(lineList, epInfo.AudioFileDesc);
                UtilsSubtitle.ShiftByTime(lineList, alignToAudio.GetBestShiftValue());
                break;

            case PerSubtitleSettings.AlignModes.ToSubtitle:
                if (referenceList == null)
                {
                    throw new Exception("Can not align subtitle to other non-existent subtitle.");
                }
                UtilsAlignSubToSub alignToSub = new UtilsAlignSubToSub(lineList, referenceList);
                alignToSub.Retime();
                break;
            }
        }
Ejemplo n.º 4
0
        /** Generates a .tsv file */
        public void ExportTextFile(List <CardInfo> cardInfoList, Settings settings, InfoProgress progressInfo)
        {
            String tsvFilename = settings.OutputDirectoryPath + Path.DirectorySeparatorChar + settings.DeckName + ".tsv";

            Console.WriteLine(tsvFilename);

            // value that will be imported into Anki/SRS-Programs-Field => [sound:???.ogg] and <img src="???.jpg"/>
            var snapshotFields = new List <String>(cardInfoList.Count);
            var audioFields    = new List <String>(cardInfoList.Count);

            foreach (var cardInfo in cardInfoList)
            {
                if (cardInfo.HasImage())
                {
                    var outputSnapshotFilename = GetSnapshotFileName(settings, cardInfo);
                    snapshotFields.Add("<img src=\"" + outputSnapshotFilename + "\"/>"); // TODO: make this flexible
                }
                else
                {
                    snapshotFields.Add("");
                }

                if (cardInfo.HasAudio())
                {
                    var outputAudioFilename = GetAudioFileName(settings, cardInfo);
                    audioFields.Add("[sound:" + outputAudioFilename + "]"); // TODO: make this flexible
                }
                else
                {
                    audioFields.Add("");
                }
            }

            using (var outputStream = new StreamWriter(tsvFilename))
            {
                for (int i = 0; i < cardInfoList.Count; i++)
                {
                    CardInfo cardInfo = cardInfoList[i];

                    // XXX: performance analasys then --- generate a episode-filtered list for context card search (because it has O(n^2) steps)
                    var contextCardsTuple = UtilsSubtitle.GetContextCards(cardInfo.episodeInfo.Index, cardInfo, m_cardInfos);
                    var previousCards     = contextCardsTuple.Item1;
                    var nextCards         = contextCardsTuple.Item2;

                    var previousCardsNativeLanguage = UtilsSubtitle.CardListToMultilineString(previousCards, UtilsCommon.LanguageType.NATIVE);
                    var previousCardsTargetLanguage = UtilsSubtitle.CardListToMultilineString(previousCards, UtilsCommon.LanguageType.TARGET);

                    var nextCardsNativeLanguage = UtilsSubtitle.CardListToMultilineString(nextCards, UtilsCommon.LanguageType.NATIVE);
                    var nextCardsTargetLanguage = UtilsSubtitle.CardListToMultilineString(nextCards, UtilsCommon.LanguageType.TARGET);

                    String keyField   = cardInfo.GetKey();
                    String audioField = audioFields[i];
                    String imageField = snapshotFields[i];
                    String tags       = String.Format("SubtitleMemorize {0} ep{1:000} {2}", settings.DeckNameModified, cardInfo.episodeInfo.Number, InfoLanguages.languages[settings.TargetLanguageIndex].tag);
                    outputStream.WriteLine(UtilsCommon.HTMLify(keyField) + "\t" +
                                           UtilsCommon.HTMLify(imageField) + "\t" +
                                           UtilsCommon.HTMLify(audioField) + "\t" +
                                           UtilsCommon.HTMLify(cardInfo.ToSingleLine(UtilsCommon.LanguageType.TARGET)) + "\t" +
                                           UtilsCommon.HTMLify(cardInfo.ToSingleLine(UtilsCommon.LanguageType.NATIVE)) + "\t" +
                                           UtilsCommon.HTMLify(previousCardsTargetLanguage) + "\t" +
                                           UtilsCommon.HTMLify(previousCardsNativeLanguage) + "\t" +
                                           UtilsCommon.HTMLify(nextCardsTargetLanguage) + "\t" +
                                           UtilsCommon.HTMLify(nextCardsNativeLanguage) + "\t" +
                                           UtilsCommon.HTMLify(tags)
                                           );
                }
            }
        }
        /// <summary>
        /// This function will...
        /// a) move all lines to change in slice by offset
        /// b) match the lines in "listToChange" and "referenceList"
        /// c) find a threshold value for matchings
        /// d) find the longest row of consecutive matchings that are above the threshold
        /// e) create slices for lines before and lines after this "good row"
        /// f) reset offset of these remaining lines and put slice into queue
        /// </summary>
        private void ApplyOffset(RemainingSlice slice, double offset, Queue <RemainingSlice> queue)
        {
            UtilsSubtitle.ShiftByTime(slice.listToChangeLines, offset);

            // match lines
            var subtitleMatcherParameters = SubtitleMatcher.GetParameterCache(slice.referenceListLines, slice.listToChangeLines);
            var biMatchedLinesLinkedList  = SubtitleMatcher.MatchSubtitles(subtitleMatcherParameters);
            var biMatchedLinesList        = new List <SubtitleMatcher.BiMatchedLines>(biMatchedLinesLinkedList);

            biMatchedLinesList.Sort(delegate(SubtitleMatcher.BiMatchedLines x, SubtitleMatcher.BiMatchedLines y) {
                return(GetStartTime(x) < GetStartTime(y) ? -1 : 1);
            });


            // --------------------------------------------------
            // find threshold rating
            double averageRating = 0;
            int    numRatings    = 0;

            foreach (var biMatchedLines in biMatchedLinesList)
            {
                double rating = RateBiMatchedLines(biMatchedLines);
                if (rating > 0.0001)                  // ignore "zero" ratings
                {
                    averageRating += rating;
                    numRatings++;
                }
            }
            averageRating /= numRatings;
            double thresholdValue = averageRating * 0.8;

            // --------------------------------------------------
            // Find longest row over threshold rating.
            //
            // Zero ratings may be inbetween good ratings when some
            // lines couldn't get matched (for example street-sign
            // translations that aren't in subtitle file in native language).
            // These are stepped over: There can be a limited number of zero ratings
            // ratings in the row except at the beginning and the end (these will
            // get matched at a different time if possible).
            int numGoodMatched     = 0;
            int currentRowStart    = 0;
            int allowedZeroRatings = 0;


            int maxNumGoodMatched = -1;
            int bestRowStart      = 0;

            for (int index = 0; index < biMatchedLinesList.Count; index++)
            {
                var    biMatchedLines = biMatchedLinesList[index];
                double rating         = RateBiMatchedLines(biMatchedLines);

                if (rating < thresholdValue)
                {
                    // step over zero ratings
                    if (rating > 0.000001 || allowedZeroRatings-- < 0)
                    {
                        numGoodMatched = 0;                         // not a zero rating
                    }
                }
                else
                {
                    // update row start/end
                    if (numGoodMatched == 0)
                    {
                        currentRowStart = index;
                    }
                    numGoodMatched = index - currentRowStart + 1;

                    // save best value
                    if (numGoodMatched > maxNumGoodMatched)
                    {
                        maxNumGoodMatched = numGoodMatched;
                        bestRowStart      = currentRowStart;
                    }

                    allowedZeroRatings = 4;
                }
            }

            // could good row be found?
            if (maxNumGoodMatched == -1)
            {
                return;
            }

            // create new slices left and right
            RemainingSlice newSlice;

            // left slice
            newSlice = GetSubSlice(biMatchedLinesList, 0, bestRowStart);
            //UtilsSubtitle.ShiftByTime(newSlice.listToChangeLines, -offset); // by disabling this code, remaining slices are now embedded in greater slices
            queue.Enqueue(newSlice);

            // right slice
            newSlice = GetSubSlice(biMatchedLinesList, bestRowStart + maxNumGoodMatched + 1, biMatchedLinesList.Count);
            //UtilsSubtitle.ShiftByTime(newSlice.listToChangeLines, -offset);
            queue.Enqueue(newSlice);
        }