diff --git a/options.py b/options.py index 85a859d..ad0803a 100644 --- a/options.py +++ b/options.py @@ -27,6 +27,7 @@ def __init__(self): self.debug = False # print debug information to terminal self.generate_track_info = False # generate track information from omxplayer output self.lang = "" + self.subtitles_lang = "" # create an options file if necessary confdir = os.path.expanduser("~") + '/.tboplayer' @@ -69,6 +70,7 @@ def read(self,filename): self.find_lyrics = int(config.get('config','find_lyrics',0)) self.autolyrics_coords = config.get('config','autolyrics_coords',0) self.lang = config.get('config','lang',0) + self.subtitles_lang = config.get('config','subtitles_lang',0) self.ytdl_update = int(config.get('config','ytdl_update',0)) if config.get('config','debug',0) == 'on': @@ -111,6 +113,7 @@ def create(self,filename): config.set('config','find_lyrics','0') config.set('config','autolyrics_coords','+350+350') config.set('config','lang','en') + config.set('config','subtitles_lang','en') config.set('config','ytdl_update','1') with open(filename, 'wb') as configfile: config.write(configfile) @@ -142,8 +145,8 @@ def save_state(self): config.set('config','find_lyrics',self.find_lyrics) config.set('config','autolyrics_coords',self.autolyrics_coords) config.set('config','lang',self.lang) + config.set('config','subtitles_lang',self.subtitles_lang) config.set('config','ytdl_update',self.ytdl_update) - with open(self.options_file, 'wb') as configfile: config.write(configfile) configfile.close() diff --git a/tboplayer.py b/tboplayer.py index 600ea64..cbd9d93 100644 --- a/tboplayer.py +++ b/tboplayer.py @@ -472,6 +472,13 @@ def go_ytdl(self, url, playlist=False): if self.ytdl_state==self._YTDL_STARTING: self.ytdl_state_machine() + def go_ytdl_subtitles(self, track): + self.ytdl.download_subtitles(self.options.subtitles_lang, track[2]) + while (not self.ytdl.subtitle_ready_signal and + not self.ytdl.download_subtitle_failed_signal): + sleep(0.2) + self.start_omx(track[0], skip_ytdl_check = True) + def ytdl_state_machine(self): if self.ytdl_state == self._YTDL_CLOSED: @@ -542,7 +549,7 @@ def treat_video_data(self, url, data): if tracks: for track in tracks: if track[1][0] == url: - self.playlist.replace(track[0],[media_url, data['title'],track[1][PlayList.LOCATION_BACKUP]]) + self.playlist.replace(track[0],[media_url, data['title'], url]) if self.play_state == self._OMX_STARTING: self.start_omx(media_url,skip_ytdl_check=True) self.refresh_playlist_display() @@ -556,7 +563,7 @@ def treat_youtube_playlist_data(self, data): media_url = self._treat_video_data(entry, data['extractor'], "medium") if not media_url: media_url = entry['url'] - self.playlist.append([media_url,entry['title'],'','']) + self.playlist.append([media_url,entry['title'],'']) self.playlist.select(self.playlist.length() - len(data['entries'])) self.refresh_playlist_display() self.root.after(3000, lambda: self.display_selected_track()) @@ -613,9 +620,19 @@ def start_omx(self, track, skip_ytdl_check=False): self.playlist.select(index) self.refresh_playlist_display() return + + if (self.options.omx_subtitles and + not self.ytdl.subtitle_ready_signal and + not self.ytdl.download_subtitle_failed_signal): + track = self.playlist.selected_track() + self.go_ytdl_subtitles(track) + return + track= "'"+ track.replace("'","'\\''") + "'" opts= (self.options.omx_user_options + " " + self.options.omx_audio_output + " " + - self.options.omx_subtitles + " --vol " + str(self.get_mB())) + " --vol " + str(self.get_mB()) + " " + self.options.omx_subtitles + " " + + " --subtitles " + self.ytdl._YTLAUNCH_SUB_DIR + "/subtitle." + self.options.subtitles_lang + ".srt" if self.ytdl.subtitle_ready_signal else "") + if self.media_is_video(): if not self.options.forbid_windowed_mode and not self.options.full_screen and '--win' not in opts: mc = self.RE_COORDS.match(self.options.windowed_mode_coords) @@ -632,7 +649,7 @@ def start_omx(self, track, skip_ytdl_check=False): self.monitor('starting omxplayer with args: "%s"' % (opts,)) - self.omx = OMXPlayer(track, args=opts, start_playback=True) + self.omx = OMXPlayer(track, args=opts, start_playback=True) self.monitor(" >Play: " + track + " with " + opts) @@ -1850,7 +1867,6 @@ def body(self, master): self.download_media_url_upon_var.set(_("when adding URL") if config.get('config','download_media_url_upon',0) == "add" else _("when playing URL")) om_download_media = OptionMenu(master, self.download_media_url_upon_var, _("when adding URL"), _("when playing URL")) om_download_media.grid(row=21, column=2, sticky=W) - Label(master, text="").grid(row=22, sticky=W) Label(master, text=_("Interface language:")).grid(row=23, column=0, sticky=W) @@ -1858,7 +1874,15 @@ def body(self, master): self.lang_var.set(config.get('config','lang',0)) om_lang = OptionMenu(master, self.lang_var, 'en', 'es' , 'fr', 'pt', 'ro', 'ru') om_lang.grid(row=23, column=2, sticky=W) - + + + Label(master, text="").grid(row=24, sticky=W) + Label(master, text=_("Subtitles language:")).grid(row=25, column=0, sticky=W) + self.subtitles_lang_var=StringVar() + self.subtitles_lang_var.set(config.get('config','subtitles_lang',0)) + om_lang = OptionMenu(master, self.subtitles_lang_var, 'ar','de', 'en', 'es' , 'fr', 'ko', 'pt', 'ro', 'ru', 'ch', 'ja') + om_lang.grid(row=25, column=2, sticky=W) + self.forbid_windowed_mode_var = IntVar() self.forbid_windowed_mode_var.set(int(config.get('config','forbid_windowed_mode',0))) @@ -1956,6 +1980,7 @@ def save_options(self): config.set('config','find_lyrics',self.find_lyrics_var.get()) config.set('config','autolyrics_coords',self.autolyrics_coords_var) config.set('config','lang',self.lang_var.get()) + config.set('config','subtitles_lang',self.lang_var.get()) config.set('config','ytdl_update',self.ytdl_update_var.get()) @@ -2228,7 +2253,7 @@ def nope(self): # *************************************** if __name__ == "__main__": - datestring="30 Out 2018" + datestring="15 Dec 2018" dbusif_tboplayer = None try: diff --git a/vtt2srt.py b/vtt2srt.py new file mode 100644 index 0000000..c856e6f --- /dev/null +++ b/vtt2srt.py @@ -0,0 +1,128 @@ +#!/usr/bin/python +#--------------------------------------- +# vtt-to-srt.py +# (c) Jansen A. Simanullang +# 02.04.2016 13:39 +# LAST CHANGE: +# 02.04.2016 16:56 +# recursively visit subdirectories +#--------------------------------------- +# usage: python vtt-to-srt.py +# +# example: +# python vtt-to-srt.py +# +# features: +# check a directory and all its subdirectories +# convert all vtt files to srt subtitle format +# +# real world needs: +# converting Coursera's vtt subtitle +# modified by Henio Tierra to provide funcionality to tboplayer + +import os, re, sys, io +from stat import * + + +def convertContent(fileContents): + + replacement = re.sub(r'([\d]+)\.([\d]+)', r'\1,\2', fileContents) + replacement = re.sub(r'WEBVTT\n\n', '', replacement) + replacement = re.sub(r'^\d+\n', '', replacement) + replacement = re.sub(r'\n\d+\n', '\n', replacement) + + return replacement + + + +def fileCreate(strNamaFile, strData): + #-------------------------------- + # fileCreate(strNamaFile, strData) + # create a text file + # + try: + + f = open(strNamaFile, "w") + f.writelines(str(strData)) + f.close() + + except IOError: + + strNamaFile = strNamaFile.split(os.sep)[-1] + f = open(strNamaFile, "w") + f.writelines(str(strData)) + f.close() + + print "file created: " + strNamaFile + "\n" + + + +def readTextFile(strNamaFile): + + f = open(strNamaFile, mode='r') + + print "file being read: " + strNamaFile + "\n" + + return f.read().decode("utf8").encode('ascii', 'ignore') + + + +def vtt_to_srt(strNamaFile): + + fileContents = readTextFile(strNamaFile) + + strData = "" + + strData = strData + convertContent(fileContents) + + strNamaFile = strNamaFile.replace(".vtt",".srt") + + print strNamaFile + + fileCreate(strNamaFile, strData) + + + +def walktree(TopMostPath, callback): + + '''recursively descend the directory tree rooted at TopMostPath, + calling the callback function for each regular file''' + + for f in os.listdir(TopMostPath): + + pathname = os.path.join(TopMostPath, f) + mode = os.stat(pathname)[ST_MODE] + + if S_ISDIR(mode): + + # It's a directory, recurse into it + walktree(pathname, callback) + + elif S_ISREG(mode): + + # It's a file, call the callback function + callback(pathname) + + else: + + # Unknown file type, print a message + print 'Skipping %s' % pathname + + + +def convertVTTtoSRT(file): + + if '.vtt' in file: + + vtt_to_srt(file) + +def vtt2srt(directory): + + #just edit the path below + + TopMostPath = directory + + walktree(TopMostPath, convertVTTtoSRT) + +if __name__ == '__main__': + vtt2srt(sys.argv[1]) diff --git a/ytdl.py b/ytdl.py index 09bec98..265f7ac 100644 --- a/ytdl.py +++ b/ytdl.py @@ -10,6 +10,8 @@ from threading import Thread from time import sleep +from vtt2srt import vtt2srt + # *************************************** # YTDL CLASS # *************************************** @@ -25,13 +27,18 @@ class Ytdl: _YTLAUNCH_ARGS_FORMAT = ' -j -f %s --youtube-skip-dash-manifest "%s"' _YTLAUNCH_PLST_CMD = '' _YTLAUNCH_PLST_ARGS_FORMAT = ' -J -f mp4 --youtube-skip-dash-manifest "%s"' + _YTLAUNCH_SUB_DIR = '/dev/shm/tbopsubs' + _YTLAUNCH_SUBT_ARGS_FORMAT = ' --write-sub --sub-lang %s --skip-download "%s" --output %s/subtitle' + _YTLAUNCH_AUTOSUBT_ARGS_FORMAT = ' --write-auto-sub --sub-lang %s --skip-download "%s" --output %s/subtitle' _FINISHED_STATUS = "\n" _WRN_STATUS = ".*WARNING:.*" _UPDATED_STATUS = ".*Restart youtube-dl to use the new version.*" _ERR_STATUS = ".*ERROR:.*" _SUDO_STATUS = ".*\[sudo\].*" - + _NO_SUB_STATUS = ".*WARNING: video doesn't have subtitles.*" + _FOUND_SUB_STATUS = ".*\[info\] Writing video subtitles to:.*" + _SERVICES_REGEXPS = () _ACCEPTED_LINK_REXP_FORMAT = "(http[s]{0,1}://(?:\w|\.{0,1})+%s\.(?:[a-z]{2,3})(?:\.[a-z]{2,3}){0,1}/)" @@ -47,10 +54,20 @@ class Ytdl: update_failed_signal = False password_requested_signal = False has_password_signal = False - + downloading_subtitle_signal = False + downloaded_subtitle_signal = False + downloaded_partial_subtitle_signal = False + download_subtitle_failed_signal = False + subtitle_ready_signal = False + _sudo_password = '' def __init__(self, options, yt_not_found_callback): + os.system("mount > /dev/null") + try: + os.mkdir(self._YTLAUNCH_SUB_DIR) + except: + pass self.set_options(options) self.yt_not_found_callback = yt_not_found_callback self.compile_regexps() @@ -144,7 +161,7 @@ def retrieve_youtube_playlist(self, url): def whether_to_use_youtube_dl(self, url): to_use = url[:4] == "http" and any(regxp.match(url) for regxp in self._SERVICES_REGEXPS) if to_use and not os.path.isfile(self._YTLOCATION): - self.yt_not_found_callback(); + self.yt_not_found_callback() return False return to_use @@ -173,6 +190,57 @@ def quit(self): except: return + def download_subtitles(self, lang, url): + self.downloaded_subtitle_signal = False + self.download_subtitle_failed_signal = False + self.subtitle_ready_signal = False + self.downloaded_partial_subtitle_signal = False + + if not os.path.isfile(self._YTLOCATION): + self.download_subtitle_failed_signal = True + return + ytcmd = self._YTLOCATION + ((self._YTLAUNCH_SUBT_ARGS_FORMAT) % (lang, url, self._YTLAUNCH_SUB_DIR)) + self._subtitled_process = pexpect.spawn(ytcmd) + self.downloading_subtitle_signal = True + Thread(target=self._download_subtitles,args=[lang, url]).start() + + def _download_subtitles(self, lang, url, trying = 1): + while self.downloading_subtitle_signal: + try: + index = self._subtitled_process.expect([self._FOUND_SUB_STATUS, + pexpect.EOF, + self._NO_SUB_STATUS, + pexpect.TIMEOUT]) + if index == 0: + self.downloaded_partial_subtitle_signal = True + elif index == 1: + if self.downloaded_partial_subtitle_signal: + self.downloaded_subtitle_signal = True + else: + self.download_subtitle_failed_signal = True + self.downloading_subtitle_signal = False + break + elif index in (2,3): + self.downloading_subtitle_signal = False + if trying == 2: + self.download_subtitle_failed_signal = True + break + sleep(0.2) + except Exception, e: + print e + self.download_subtitle_failed_signal = True + self.downloading_subtitle_signal = False + return + if trying == 1 and not self.downloaded_subtitle_signal: + self.downloading_subtitle_signal = True + ytcmd = self._YTLOCATION + ((self._YTLAUNCH_AUTOSUBT_ARGS_FORMAT) % (lang, url, self._YTLAUNCH_SUB_DIR)) + self._subtitled_process = pexpect.spawn(ytcmd) + self._download_subtitles(lang, url, trying = 2) + if self.downloaded_subtitle_signal: + vtt2srt(self._YTLAUNCH_SUB_DIR) + os.remove(self._YTLAUNCH_SUB_DIR + "/subtitle." + lang + ".vtt") + self.subtitle_ready_signal = True + def check_for_update(self): if not os.path.isfile(self._YTLOCATION): return @@ -225,7 +293,7 @@ def _check_for_update(self): except Exception, e: print e break - sleep(5) + sleep(0.5) if self.updated_signal: self.compile_regexps(updated=True) self.updating_signal = False