Skip to content

Instantly share code, notes, and snippets.

@saerdnaer
Last active May 29, 2021 08:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save saerdnaer/23ddea28f1ce8efca3377151c1c9f5c8 to your computer and use it in GitHub Desktop.
Save saerdnaer/23ddea28f1ce8efca3377151c1c9f5c8 to your computer and use it in GitHub Desktop.
download german transcript from youtube and convert to better readable text
'''
Downloads YouTube subtitles to human readable text, while fixing casing of german words.
Example usage:
python3 yt2text.py _RlwgfYd65Q > ultraschall5.txt
'''
import argparse
from youtube_transcript_api import YouTubeTranscriptApi
import hunspell # https://github.com/blatinier/pyhunspell/wiki/Documentation
import sys
parser = argparse.ArgumentParser()
parser.add_argument('id', help='id of a YouTube video e.g. wJz3iXdF9JI for https://www.youtube.com/watch?v=wJz3iXdF9JI')
def main():
args = parser.parse_args()
debug = False
#transcript_list = YouTubeTranscriptApi.list_transcripts(args.id)
transcript = testdata if debug else YouTubeTranscriptApi.get_transcript(args.id, languages=['de', 'en'])
# http://pankdm.github.io/hunspell.html
# https://extensions.libreoffice.org/en/extensions/show/german-de-de-frami-dictionaries
de = hunspell.HunSpell('/Library/Spelling/de_DE_frami.dic', '/Library/Spelling/de_DE_frami.aff')
en = hunspell.HunSpell('/Library/Spelling/en_US.dic', '/Library/Spelling/en_US.aff')
i = 0
sentence = ''
for line in transcript:
words = line['text'].split(' ')
for word in words:
# write first word of each sentence in upper case
if sentence == '':
sentence = words[0].title()
continue
# try different casing variants when word is not in dictionary
if not de.spell(word):
word_mod = word.title()
# check of title case word is valid, or if its an english word
if de.spell(word_mod) or en.spell(word):
word = word_mod
else:
# try upper casing the whole word e.g. usa to USA
word_mod = word.upper()
if de.spell(word_mod) or en.spell(word_mod):
word = word_mod
else:
sys.stderr.write(" unknown word: '%s', did you mean %s or %s\n" % (word, de.suggest(word), en.suggest(word)))
sentence += ' ' + word
if len(words) <= 2:
print(sentence + '.')
sentence = ''
i += 1
if debug and i > 50:
break
#export_csv('ultraschall5.csv', transcript)
def export_csv(filename, transcript):
import csv
with open(filename, 'w', newline='') as file:
writer = csv.DictWriter(file, ['text', 'start', 'duration'])
writer.writeheader()
writer.writerows(transcript)
print('wrote results to {}'.format(filename))
#id = '_RlwgfYd65Q'
testdata = [
{'text': '[Musik]', 'start': 1.5, 'duration': 9.02},
{'text': '[Applaus]', 'start': 7.35, 'duration': 3.17},
{'text': 'hallo', 'start': 13.07, 'duration': 4.36},
{'text': 'herzlich willkommen zu einer kleinen', 'start': 14.309, 'duration': 6.241},
{'text': 'einführung in ultraschall fünf unsere', 'start': 17.43, 'duration': 5.849},
{'text': 'nächste release jetzt eine etwas', 'start': 20.55, 'duration': 5.1},
{'text': 'sonderbare situation normalerweise 70', 'start': 23.279, 'duration': 3.811},
{'text': 'doch zumindest vor so einer handvoll', 'start': 25.65, 'duration': 2.16},
{'text': 'handeln', 'start': 27.09, 'duration': 2.759},
{'text': 'jetzt stehe ich also in meinen laptop', 'start': 27.81, 'duration': 3.99},
{'text': 'und weiß trotzdem dass auf der anderen', 'start': 29.849, 'duration': 4.441},
{'text': 'seite irgendwo menschen sitzen da', 'start': 31.8, 'duration': 4.439},
{'text': 'draußen aber das sind noch mal besondere', 'start': 34.29, 'duration': 4.289},
{'text': 'zeiten und wir haben es ja geschafft', 'start': 36.239, 'duration': 5.551},
{'text': 'vorm ersten coroner lockdown unsere', 'start': 38.579, 'duration': 5.461},
{'text': 'version 4 raus zu bringen und das ist', 'start': 41.79, 'duration': 4.109},
{'text': 'glaube ich vielen leuten sehr geholfen', 'start': 44.04, 'duration': 3.089},
{'text': 'weil sie einfach ein bisschen was zu', 'start': 45.899, 'duration': 3.271},
{'text': 'spielen hatten nur während sie alleine', 'start': 47.129, 'duration': 4.11},
{'text': 'irgendwo zu hause saßen und', 'start': 49.17, 'duration': 4.11},
{'text': 'gerüchtehalber soll jetzt der zweite', 'start': 51.239, 'duration': 3.901},
{'text': 'locker und auch etwas länger noch dauern', 'start': 53.28, 'duration': 5.099},
{'text': 'und von daher freut es euch vielleicht', 'start': 55.14, 'duration': 5.309},
{'text': 'dass er dann wieder etwas zu spielen hat', 'start': 58.379, 'duration': 4.051},
{'text': 'nämlich eine version 5 ich hab jetzt', 'start': 60.449, 'duration': 3.89},
{'text': 'ungefähr eine dreiviertel stunde zeit', 'start': 62.43, 'duration': 5.189},
{'text': 'ich habe nicht genau gemessen wie lange', 'start': 64.339, 'duration': 5.621},
{'text': 'der vortrag so wird schauen wir mal das', 'start': 67.619, 'duration': 3.421},
{'text': 'wird ein bisschen eine mischung zwischen', 'start': 69.96, 'duration': 5.01},
{'text': 'der keynote und einer demo werden', 'start': 71.04, 'duration': 5.61},
{'text': 'mein ziel ist also einfach mal so ein', 'start': 74.97, 'duration': 4.95},
{'text': 'paar maine features der nächsten version', 'start': 76.65, 'duration': 5.64},
{'text': 'schon mal an zu dieser natürlich wird es', 'start': 79.92, 'duration': 3.87},
{'text': 'dann zu offiziellen release auch immer', 'start': 82.29, 'duration': 5.66},
{'text': 'noch ein offizielles release video geben', 'start': 83.79, 'duration': 6.869},
{'text': 'ultraschall für diejenigen die überhaupt', 'start': 87.95, 'duration': 4.62},
{'text': 'noch gar nichts davon gehört haben', 'start': 90.659, 'duration': 4.35},
{'text': 'projektziel ist es die perfekte podcast', 'start': 92.57, 'duration': 5.38},
{'text': 'produktionssoftware anzubieten und das', 'start': 95.009, 'duration': 4.051},
{'text': 'insofern vielleicht ein bisschen', 'start': 97.95, 'duration': 2.97},
{'text': 'verwunderlich ist weil er der erste', 'start': 99.06, 'duration': 4.32},
{'text': 'instinkt sein sollte meine güte jeder', 'start': 100.92, 'duration': 3.629},
{'text': 'und seine mutter macht auch heutzutage', 'start': 103.38, 'duration': 3.419},
{'text': 'podcast das uns doch eigentlich ein', 'start': 104.549, 'duration': 5.131},
{'text': 'längst gelöstes problem sein da würde', 'start': 106.799, 'duration': 4.32},
{'text': 'ich sagen ja gar nicht so einfach ist es', 'start': 109.68, 'duration': 4.079},
{'text': 'nicht wenn man sich mal dieses schaubild', 'start': 111.119, 'duration': 4.351},
{'text': 'hier anguckt mit dem ich also jetzt auch', 'start': 113.759, 'duration': 5.101},
{'text': 'seit 40 jahren hausieren gehe wo ich', 'start': 115.47, 'duration': 4.469},
{'text': 'einfach mal versucht habe so die', 'start': 118.86, 'duration': 3.21},
{'text': 'gängigen tools mit denen man mehr oder', 'start': 119.939, 'duration': 3.96},
{'text': 'weniger gut podcast produzieren kann', 'start': 122.07, 'duration': 3.17},
{'text': 'abzubilden', 'start': 123.899, 'duration': 2.661},
{'text': 'dann sieht man dass gibt welche die sind', 'start': 125.24, 'duration': 3.45},
{'text': 'ziemlich teuer das sind die roten hier', 'start': 126.56, 'duration': 3.449},
{'text': 'muss man sich erst mal leisten können', 'start': 128.69, 'duration': 2.7},
{'text': 'dafür kriegt man auch ganz ordentlich', 'start': 130.009, 'duration': 4.621},
{'text': 'was und dann gibt es die open source', 'start': 131.39, 'duration': 5.88},
{'text': 'vertreter um die nichts kosten dafür', 'start': 134.63, 'duration': 4.56},
{'text': 'teilweise nicht so richtig gut zu', 'start': 137.27, 'duration': 3.299},
{'text': 'bedienen sind und teilweise auch nicht', 'start': 139.19, 'duration': 3.269},
{'text': 'so richtig viele features haben bezug', 'start': 140.569, 'duration': 5.371},
{'text': 'auf podcasting und dann gibt es zwei die', 'start': 142.459, 'duration': 5.341},
{'text': 'die ein bisschen raus stechen das sind', 'start': 145.94, 'duration': 3.629},
{'text': 'hier die blauen die sind so mittel teuer', 'start': 147.8, 'duration': 5.269},
{'text': 'nämlich die idee awi pa für 65 euro und', 'start': 149.569, 'duration': 6.361},
{'text': 'die hindenburg standard version für 85', 'start': 153.069, 'duration': 3.571},
{'text': 'euro', 'start': 155.93, 'duration': 3.21},
{'text': 'das kann man sich schon noch ganz gut', 'start': 156.64, 'duration': 4.78},
{'text': 'leisten finde ich die haben aber beide', 'start': 159.14, 'duration': 3.99},
{'text': 'auch so jährige schoss also sprich', 'start': 161.42, 'duration': 4.289},
{'text': 'normal ist standard reaper ist nahezu', 'start': 163.13, 'duration': 4.47},
{'text': 'unbenutzbar wenn man damit podcast', 'start': 165.709, 'duration': 3.631},
{'text': 'machen will was ich den entwicklerinnen', 'start': 167.6, 'duration': 3.96},
{'text': 'und entwickler noch niemals vorhalten', 'start': 169.34, 'duration': 4.29},
{'text': 'würde denn das ist eine musikproduktion', 'start': 171.56, 'duration': 4.05},
{'text': 'software und eben keine podcast', 'start': 173.63, 'duration': 4.52},
{'text': 'produktionssoftware das anderes hinten', 'start': 175.61, 'duration': 4.23},
{'text': 'hindenburg werden wir heute auch noch', 'start': 178.15, 'duration': 3.059},
{'text': 'mal darauf zu sprechen kommen', 'start': 179.84, 'duration': 4.11},
{'text': 'spezialisiert für radio features und', 'start': 181.209, 'duration': 4.481},
{'text': 'auch durchaus podcasting das heißt also', 'start': 183.95, 'duration': 4.02},
{'text': 'das ist von dem ganzen bands ihr', 'start': 185.69, 'duration': 4.32},
{'text': 'eigentlich dass wo es am ehesten heißt', 'start': 187.97, 'duration': 5.97},
{'text': 'jawohl wir kümmern uns auch wirklich ein', 'start': 190.01, 'duration': 5.58},
{'text': 'podcast von daher habe ich heute mal ein', 'start': 193.94, 'duration': 4.35},
{'text': 'besonders scharfen blick darauf was sich', 'start': 195.59, 'duration': 6.209},
{'text': 'die leute neues einfallen lassen so und', 'start': 198.29, 'duration': 5.37},
{'text': 'das ultraschall projektes steht unter', 'start': 201.799, 'duration': 3.601},
{'text': 'greifen darin diesen blauen pfeil hier', 'start': 203.66, 'duration': 3.63},
{'text': 'zu realisieren das heißt also aus einem', 'start': 205.4, 'duration': 4.53},
{'text': 'standard reaper in dem man viele dinge', 'start': 207.29, 'duration': 6.06},
{'text': 'mit anpasst und dazu programmiert was', 'start': 209.93, 'duration': 7.5},
{'text': 'bei rita sehr gut geht weil sap hat und', 'start': 213.35, 'duration': 5.639},
{'text': 'ein feeling schnittstelle man kann also', 'start': 217.43, 'duration': 4.05},
{'text': 'wirklich sehr sehr viel modifizieren und', 'start': 218.989, 'duration': 4.381},
{'text': 'das betreiben wir seit jahren kennt uns', 'start': 221.48, 'duration': 3.24},
{'text': 'mittlerweile gut aus in unserem', 'start': 223.37, 'duration': 6.03},
{'text': 'entwicklerteam dort also eine user', 'start': 224.72, 'duration': 6.75},
{'text': 'interface darüber zu stülpen und das mit', 'start': 229.4, 'duration': 4.08},
{'text': 'funktionen anzureichern die also auf', 'start': 231.47, 'duration': 4.019},
]
if __name__ == "__main__":
main()
@saerdnaer
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment