Parse WebVTT file with SwiftUI and show just the text and sync to audio

314 Views Asked by At

I'm re-learning how to program and am trying to make an interactive transcript document.

I really could use some pointers with how to display the text of a webVTT file ideally within a SwiftUI text view from a local file.

Secondly and probably more importantly at this step in the process I'm wanting to know how to parse the time stamps with the text so that I can sync that to audio that's playing.

I found this amazing example https://www.reddit.com/r/iOSProgramming/comments/10y0jcp/i_recreated_the_new_progressing_style_lyrics_from/ by Reddit u/mageshsridhar and while this isn't what I hope to achieve it shows text synced to audio.

I'm looking to do a page of text that you can read which would come from the webVTT file. If you tap anywhere on the text it would immediately start playing the audio from that sentence. Conversely if you are reading along I'd like for it to also highlight the text or sentence that's currently playing.

Using u/mageshsridhar example from Reddit I can manually add text and it works.

How on earth could I code this to programmatically show text based on time stamp from a local file?

Ideally a user could load an mp3 and vtt file and the program would be good to go but I'm lost how to parse and automate the text based on time and even how to show the text without the timestamp.

Any help is appreciated.

1

There are 1 best solutions below

0
Jason G On
import Foundation
import RegexBuilder


let testString = """
WEBVTT
 
00:00:00.000 --> 00:00:04.560
Did you know that up to 80 to 90 percent


00:00:04.560 --> 00:00:09.080
of those making a decision to Code will hit a wall and give up?


00:00:09.080 --> 00:00:14.920
What? Yes, it's true. Did you know that most people give up within the first year?


00:00:14.920 --> 00:00:19.400
Some extra dialog would go here


00:00:19.400 --> 00:00:23.360
also additional dialog would also go here


“””


let patternTest = Regex {
  /^/
  Capture {
    Regex {
      Repeat(count: 2) {
        One(.digit)
      }
      ":"
      Repeat(count: 2) {
        One(.digit)
      }
      ":"
      Repeat(count: 2) {
        One(.digit)
      }
      One(.anyOf(".,"))
      Repeat(count: 3) {
        One(.digit)
      }
    }
  }
  One(.whitespace)
  "-->"
  One(.whitespace)
  Capture {
    Regex {
      Repeat(count: 2) {
        One(.digit)
      }
      ":"
      Repeat(count: 2) {
        One(.digit)
      }
      ":"
      Repeat(count: 2) {
        One(.digit)
      }
      One(.anyOf(".,"))
      Repeat(count: 3) {
        One(.digit)
      }
    }
  }
  "\u{A}"
  Capture {
    Regex {
      ZeroOrMore {
        /./
      }
      ZeroOrMore {
        Regex {
          Optionally {
            "\u{D}"
          }
          "\u{A}"
          NegativeLookahead {
            Regex {
              Optionally {
                "\u{D}"
              }
              "\u{A}"
            }
          }
          ZeroOrMore {
            /./
          }
        }
      }
    }
  }
}
.anchorsMatchLineEndings()



let matches = testString.matches(of: patternTest)

// Creating an array of string type
// Using for loop to iterate through each element of the array
print("")
print("")
print("Array of Text elements are:")
for match14 in matches {
    let (notSureWhatGoesHere) = match14.output
    print(match14.output.3)
}

This will cycle through all the matches just for Text in a WebVTT formatted text within Swift 5.7 using the RegexBuilder code.

If you wish to cycle through all the matches for the first timestamp change the "print(match14.output.3" to "print(match14.output.1". If you needed all the end timestamps change it to "print(match14.output.2)".

This was something I got working in a swift playground and was working for me without an issue on iOS 16.4.1 and macOS 13.3.1

At the time of figuring this out I wasn't sure what went into one of the "let"'s so used "notSureWhatGoesHere", again learning as I go.....

This isn't the full solution to what I was looking for but it's a huge step in the right direction and hoping this helps someone else as well.