def directory(app): #app = QApplication(sys.argv) QCoreApplication.setApplicationVersion(QT_VERSION_STR) parser = QCommandLineParser() parser.setApplicationDescription("File Directory") parser.addHelpOption() parser.addVersionOption() dontUseCustomDirectoryIconsOption = QCommandLineOption( 'C', "Set QFileIconProvider.DontUseCustomDirectoryIcons") parser.addOption(dontUseCustomDirectoryIconsOption) parser.addPositionalArgument('', "The directory to start in.") parser.process(app) try: rootPath = parser.positionalArguments().pop(0) except IndexError: rootPath = None model = QFileSystemModel() model.setRootPath('') filter = ['*.db'] #filtering out just by db model.setNameFilters(filter) model.setNameFilterDisables(0) #Only show the filtered .db paths #filename = model.filePath() #print(filename) if parser.isSet(dontUseCustomDirectoryIconsOption): model.iconProvider().setOptions( QFileIconProvider.DontUseCustomDirectoryIcons) tree = QTreeView() tree.setModel(model) if rootPath is not None: rootIndex = model.index(QDir.cleanPath(rootPath)) if rootIndex.isValid(): tree.setRootIndex(rootIndex) # Demonstrating look and feel features. tree.setAnimated(False) tree.setIndentation(20) tree.setSortingEnabled(True) availableSize = QApplication.desktop().availableGeometry(tree).size() tree.resize(availableSize / 2) tree.setColumnWidth(0, tree.width() / 3) tree.setWindowTitle("Directory View") tree.show() sys.exit(app.exec_())
parser.addVersionOption() dontUseCustomDirectoryIconsOption = QCommandLineOption( "c", "Set QFileIconProvider.DontUseCustomDirectoryIcons") parser.addOption(dontUseCustomDirectoryIconsOption) parser.addPositionalArgument("directory", "The directory to start in.") parser.process(app) try: rootPath = parser.positionalArguments().pop(0) except IndexError: rootPath = None model = QFileSystemModel() model.setRootPath("") if parser.isSet(dontUseCustomDirectoryIconsOption): model.iconProvider().setOptions( QFileIconProvider.DontUseCustomDirectoryIcons) tree = QTreeView() tree.setModel(model) if rootPath is not None: rootIndex = model.index(QDir.cleanPath(rootPath)) if rootIndex.isValid(): tree.setRootIndex(rootIndex) # Demonstrating look and feel features. tree.setAnimated(False) tree.setIndentation(20) tree.setSortingEnabled(True) availableSize = QApplication.desktop().availableGeometry(tree).size() tree.resize(availableSize / 2) tree.setColumnWidth(0, tree.width() / 3)
parser.addVersionOption() dontUseCustomDirectoryIconsOption = QCommandLineOption('c', "Set QFileIconProvider.DontUseCustomDirectoryIcons") parser.addOption(dontUseCustomDirectoryIconsOption) parser.addPositionalArgument('directory', "The directory to start in.") parser.process(app) try: rootPath = parser.positionalArguments().pop(0) except IndexError: rootPath = None model = QFileSystemModel() model.setRootPath('') if parser.isSet(dontUseCustomDirectoryIconsOption): model.iconProvider().setOptions( QFileIconProvider.DontUseCustomDirectoryIcons) tree = QTreeView() tree.setModel(model) if rootPath is not None: rootIndex = model.index(QDir.cleanPath(rootPath)) if rootIndex.isValid(): tree.setRootIndex(rootIndex) # Demonstrating look and feel features. tree.setAnimated(False) tree.setIndentation(20) tree.setSortingEnabled(True) availableSize = QApplication.desktop().availableGeometry(tree).size() tree.resize(availableSize / 2) tree.setColumnWidth(0, tree.width() / 3)
class SearchFileWidget(QWidget): language_filter_change = pyqtSignal(list) def __init__(self): QWidget.__init__(self) self._refreshing = False self.fileModel = None self.proxyFileModel = None self.videoModel = None self._state = None self.timeLastSearch = QTime.currentTime() self.ui = Ui_SearchFileWidget() self.setup_ui() def set_state(self, state): self._state = state self._state.login_status_changed.connect(self.on_login_state_changed) self._state.interface_language_changed.connect( self.on_interface_language_changed) def get_state(self): return self._state def setup_ui(self): self.ui.setupUi(self) settings = QSettings() self.ui.splitter.setSizes([600, 1000]) self.ui.splitter.setChildrenCollapsible(False) # Set up folder view lastDir = settings.value("mainwindow/workingDirectory", QDir.homePath()) log.debug('Current directory: {currentDir}'.format(currentDir=lastDir)) self.fileModel = QFileSystemModel(self) self.fileModel.setFilter(QDir.AllDirs | QDir.Dirs | QDir.Drives | QDir.NoDotAndDotDot | QDir.Readable | QDir.Executable | QDir.Writable) self.fileModel.iconProvider().setOptions( QFileIconProvider.DontUseCustomDirectoryIcons) self.fileModel.setRootPath(QDir.rootPath()) self.fileModel.directoryLoaded.connect(self.onFileModelDirectoryLoaded) self.proxyFileModel = QSortFilterProxyModel(self) self.proxyFileModel.setSortRole(Qt.DisplayRole) self.proxyFileModel.setSourceModel(self.fileModel) self.proxyFileModel.sort(0, Qt.AscendingOrder) self.proxyFileModel.setSortCaseSensitivity(Qt.CaseInsensitive) self.ui.folderView.setModel(self.proxyFileModel) self.ui.folderView.setHeaderHidden(True) self.ui.folderView.hideColumn(3) self.ui.folderView.hideColumn(2) self.ui.folderView.hideColumn(1) index = self.fileModel.index(lastDir) proxyIndex = self.proxyFileModel.mapFromSource(index) self.ui.folderView.scrollTo(proxyIndex) self.ui.folderView.expanded.connect(self.onFolderViewExpanded) self.ui.folderView.clicked.connect(self.onFolderTreeClicked) self.ui.buttonFind.clicked.connect(self.onButtonFind) self.ui.buttonRefresh.clicked.connect(self.onButtonRefresh) # Set up introduction self.showInstructions() # Set up video view self.ui.filterLanguageForVideo.set_unknown_text(_('All languages')) self.ui.filterLanguageForVideo.selected_language_changed.connect( self.on_language_combobox_filter_change) # self.ui.filterLanguageForVideo.selected_language_changed.connect(self.onFilterLanguageVideo) self.videoModel = VideoModel(self) self.ui.videoView.setHeaderHidden(True) self.ui.videoView.setModel(self.videoModel) self.ui.videoView.activated.connect(self.onClickVideoTreeView) self.ui.videoView.clicked.connect(self.onClickVideoTreeView) self.ui.videoView.customContextMenuRequested.connect(self.onContext) self.videoModel.dataChanged.connect(self.subtitlesCheckedChanged) self.language_filter_change.connect( self.videoModel.on_filter_languages_change) self.ui.buttonSearchSelectVideos.clicked.connect( self.onButtonSearchSelectVideos) self.ui.buttonSearchSelectFolder.clicked.connect( self.onButtonSearchSelectFolder) self.ui.buttonDownload.clicked.connect(self.onButtonDownload) self.ui.buttonPlay.clicked.connect(self.onButtonPlay) self.ui.buttonIMDB.clicked.connect(self.onViewOnlineInfo) self.ui.videoView.setContextMenuPolicy(Qt.CustomContextMenu) # Drag and Drop files to the videoView enabled self.ui.videoView.__class__.dragEnterEvent = self.dragEnterEvent self.ui.videoView.__class__.dragMoveEvent = self.dragEnterEvent self.ui.videoView.__class__.dropEvent = self.dropEvent self.ui.videoView.setAcceptDrops(1) # FIXME: ok to drop this connect? # self.ui.videoView.clicked.connect(self.onClickMovieTreeView) self.retranslate() def retranslate(self): introduction = '<p align="center"><h2>{title}</h2></p>' \ '<p><b>{tab1header}</b><br/>{tab1content}</p>' \ '<p><b>{tab2header}</b><br/>{tab2content}</p>'\ '<p><b>{tab3header}</b><br/>{tab3content}</p>'.format( title=_('How To Use {title}').format(title=PROJECT_TITLE), tab1header=_('1st Tab:'), tab2header=_('2nd Tab:'), tab3header=_('3rd Tab:'), tab1content=_('Select, from the Folder Tree on the left, the folder which contains the videos ' 'that need subtitles. {project} will then try to automatically find available ' 'subtitles.').format(project=PROJECT_TITLE), tab2content=_('If you don\'t have the videos in your machine, you can search subtitles by ' 'introducing the title/name of the video.').format(project=PROJECT_TITLE), tab3content=_('If you have found some subtitle somewhere else that is not in {project}\'s database, ' 'please upload those subtitles so next users will be able to ' 'find them more easily.').format(project=PROJECT_TITLE)) self.ui.introductionHelp.setHtml(introduction) @pyqtSlot(Language) def on_interface_language_changed(self, language): self.ui.retranslateUi(self) self.retranslate() @pyqtSlot(str) def onFileModelDirectoryLoaded(self, path): settings = QSettings() lastDir = settings.value('mainwindow/workingDirectory', QDir.homePath()) qDirLastDir = QDir(lastDir) qDirLastDir.cdUp() if qDirLastDir.path() == path: index = self.fileModel.index(lastDir) proxyIndex = self.proxyFileModel.mapFromSource(index) self.ui.folderView.scrollTo(proxyIndex) self.ui.folderView.setCurrentIndex(proxyIndex) @pyqtSlot(int, str) def on_login_state_changed(self, state, message): log.debug( 'on_login_state_changed(state={state}, message={message}'.format( state=state, message=message)) if state in (State.LOGIN_STATUS_LOGGED_OUT, State.LOGIN_STATUS_BUSY): self.ui.buttonSearchSelectFolder.setEnabled(False) self.ui.buttonSearchSelectVideos.setEnabled(False) self.ui.buttonFind.setEnabled(False) elif state == State.LOGIN_STATUS_LOGGED_IN: self.ui.buttonSearchSelectFolder.setEnabled(True) self.ui.buttonSearchSelectVideos.setEnabled(True) self.ui.buttonFind.setEnabled( self.get_current_selected_folder() is not None) else: log.warning('unknown state') @pyqtSlot(Language) def on_language_combobox_filter_change(self, language): if language.is_generic(): self.language_filter_change.emit( self.get_state().get_permanent_language_filter()) else: self.language_filter_change.emit([language]) def on_permanent_language_filter_change(self, languages): selected_language = self.ui.filterLanguageForVideo.get_selected_language( ) if selected_language.is_generic(): self.language_filter_change.emit(languages) @pyqtSlot() def subtitlesCheckedChanged(self): subs = self.videoModel.get_checked_subtitles() if subs: self.ui.buttonDownload.setEnabled(True) else: self.ui.buttonDownload.setEnabled(False) def showInstructions(self): self.ui.stackedSearchResult.setCurrentWidget(self.ui.pageIntroduction) def hideInstructions(self): self.ui.stackedSearchResult.setCurrentWidget(self.ui.pageSearchResult) @pyqtSlot(QModelIndex) def onFolderTreeClicked(self, proxyIndex): """What to do when a Folder in the tree is clicked""" if not proxyIndex.isValid(): return index = self.proxyFileModel.mapToSource(proxyIndex) settings = QSettings() folder_path = self.fileModel.filePath(index) settings.setValue('mainwindow/workingDirectory', folder_path) # self.ui.buttonFind.setEnabled(self.get_state().) def get_current_selected_folder(self): proxyIndex = self.ui.folderView.currentIndex() index = self.proxyFileModel.mapToSource(proxyIndex) folder_path = self.fileModel.filePath(index) if not folder_path: return None return folder_path def get_current_selected_item_videomodel(self): current_index = self.ui.videoView.currentIndex() return self.videoModel.getSelectedItem(current_index) @pyqtSlot() def onButtonFind(self): now = QTime.currentTime() if now < self.timeLastSearch.addMSecs(500): return folder_path = self.get_current_selected_folder() settings = QSettings() settings.setValue('mainwindow/workingDirectory', folder_path) self.search_videos([folder_path]) self.timeLastSearch = QTime.currentTime() @pyqtSlot() def onButtonRefresh(self): currentPath = self.get_current_selected_folder() if not currentPath: settings = QSettings() currentPath = settings.value('mainwindow/workingDirectory', QDir.homePath()) self._refreshing = True self.ui.folderView.collapseAll() currentPath = self.get_current_selected_folder() if not currentPath: settings = QSettings() currentPath = settings.value('mainwindow/workingDirectory', QDir.homePath()) index = self.fileModel.index(currentPath) self.ui.folderView.scrollTo(self.proxyFileModel.mapFromSource(index)) @pyqtSlot(QModelIndex) def onFolderViewExpanded(self, proxyIndex): if self._refreshing: expandedPath = self.fileModel.filePath( self.proxyFileModel.mapToSource(proxyIndex)) if expandedPath == QDir.rootPath(): currentPath = self.get_current_selected_folder() if not currentPath: settings = QSettings() currentPath = settings.value('mainwindow/workingDirectory', QDir.homePath()) index = self.fileModel.index(currentPath) self.ui.folderView.scrollTo( self.proxyFileModel.mapFromSource(index)) self._refreshing = False @pyqtSlot() def onButtonSearchSelectFolder(self): settings = QSettings() path = settings.value('mainwindow/workingDirectory', QDir.homePath()) folder_path = QFileDialog.getExistingDirectory( self, _('Select the directory that contains your videos'), path) if folder_path: settings.setValue('mainwindow/workingDirectory', folder_path) self.search_videos([folder_path]) @pyqtSlot() def onButtonSearchSelectVideos(self): settings = QSettings() currentDir = settings.value('mainwindow/workingDirectory', QDir.homePath()) fileNames, t = QFileDialog.getOpenFileNames( self, _('Select the video(s) that need subtitles'), currentDir, SELECT_VIDEOS) if fileNames: settings.setValue('mainwindow/workingDirectory', QFileInfo(fileNames[0]).absolutePath()) self.search_videos(fileNames) def search_videos(self, paths): if not self.get_state().connected(): QMessageBox.about( self, _("Error"), _('You are not connected to the server. Please reconnect first.' )) return self.ui.buttonFind.setEnabled(False) self._search_videos_raw(paths) self.ui.buttonFind.setEnabled(True) def _search_videos_raw(self, paths): # FIXME: must pass mainwindow as argument to ProgressCallbackWidget callback = ProgressCallbackWidget(self) callback.set_title_text(_("Scanning...")) callback.set_label_text(_("Scanning files")) callback.set_finished_text(_("Scanning finished")) callback.set_block(True) try: local_videos, local_subs = scan_videopaths(paths, callback=callback, recursive=True) except OSError: callback.cancel() QMessageBox.warning(self, _('Error'), _('Some directories are not accessible.')) if callback.canceled(): return callback.finish() log.debug("Videos found: %s" % local_videos) log.debug("Subtitles found: %s" % local_subs) self.hideInstructions() QCoreApplication.processEvents() if not local_videos: QMessageBox.about(self, _("Scan Results"), _("No video has been found!")) return total = len(local_videos) # FIXME: must pass mainwindow as argument to ProgressCallbackWidget # callback = ProgressCallbackWidget(self) # callback.set_title_text(_("Asking Server...")) # callback.set_label_text(_("Searching subtitles...")) # callback.set_updated_text(_("Searching subtitles ( %d / %d )")) # callback.set_finished_text(_("Search finished")) callback.set_block(True) callback.set_range(0, total) callback.show() callback.set_range(0, 2) download_callback = callback.get_child_progress(0, 1) # videoSearchResults = self.get_state().get_OSDBServer().SearchSubtitles("", videos_piece) remote_subs = self.get_state().get_OSDBServer().search_videos( videos=local_videos, callback=download_callback) self.videoModel.set_videos(local_videos) # self.onFilterLanguageVideo(self.ui.filterLanguageForVideo.get_selected_language()) if remote_subs is None: QMessageBox.about( self, _("Error"), _("Error contacting the server. Please try again later")) callback.finish() # TODO: CHECK if our local subtitles are already in the server, otherwise suggest to upload # self.OSDBServer.CheckSubHash(sub_hashes) @pyqtSlot() def onButtonPlay(self): settings = QSettings() programPath = settings.value('options/VideoPlayerPath', '') parameters = settings.value('options/VideoPlayerParameters', '') if programPath == '': QMessageBox.about( self, _('Error'), _('No default video player has been defined in Settings.')) return selected_subtitle = self.get_current_selected_item_videomodel() if isinstance(selected_subtitle, SubtitleFileNetwork): selected_subtitle = selected_subtitle.get_subtitles()[0] if isinstance(selected_subtitle, LocalSubtitleFile): subtitle_file_path = selected_subtitle.get_filepath() elif isinstance(selected_subtitle, RemoteSubtitleFile): subtitle_file_path = QDir.temp().absoluteFilePath( 'subdownloader.tmp.srt') log.debug( 'Temporary subtitle will be downloaded into: {temp_path}'. format(temp_path=subtitle_file_path)) # FIXME: must pass mainwindow as argument to ProgressCallbackWidget callback = ProgressCallbackWidget(self) callback.set_title_text(_('Playing video + sub')) callback.set_label_text(_('Downloading files...')) callback.set_finished_text(_('Downloading finished')) callback.set_block(True) callback.set_range(0, 100) callback.show() try: subtitle_stream = selected_subtitle.download( self.get_state().get_OSDBServer(), callback=callback) except ProviderConnectionError: log.debug('Unable to download subtitle "{}"'.format( selected_subtitle.get_filename()), exc_info=True) QMessageBox.about( self, _('Error'), _('Unable to download subtitle "{subtitle}"').format( subtitle=selected_subtitle.get_filename())) callback.finish() return callback.finish() write_stream(subtitle_stream, subtitle_file_path) video = selected_subtitle.get_parent().get_parent().get_parent() def windows_escape(text): return '"{text}"'.format(text=text.replace('"', '\\"')) params = [windows_escape(programPath)] for param in parameters.split(' '): param = param.format(video.get_filepath(), subtitle_file_path) if platform.system() in ('Windows', 'Microsoft'): param = windows_escape(param) params.append(param) pid = None log.info('Running this command: {params}'.format(params=params)) try: log.debug('Trying os.spawnvpe ...') pid = os.spawnvpe(os.P_NOWAIT, programPath, params, os.environ) log.debug('... SUCCESS. pid={pid}'.format(pid=pid)) except AttributeError: log.debug('... FAILED', exc_info=True) except Exception as e: log.debug('... FAILED', exc_info=True) if pid is None: try: log.debug('Trying os.fork ...') pid = os.fork() if not pid: log.debug('... SUCCESS. pid={pid}'.format(pid=pid)) os.execvpe(os.P_NOWAIT, programPath, params, os.environ) except: log.debug('... FAIL', exc_info=True) if pid is None: QMessageBox.about(self, _('Error'), _('Unable to launch videoplayer')) @pyqtSlot(QModelIndex) def onClickVideoTreeView(self, index): data_item = self.videoModel.getSelectedItem(index) if isinstance(data_item, SubtitleFile): self.ui.buttonPlay.setEnabled(True) else: self.ui.buttonPlay.setEnabled(False) if isinstance(data_item, VideoFile): video = data_item if True: # video.getMovieInfo(): self.ui.buttonIMDB.setEnabled(True) self.ui.buttonIMDB.setIcon(QIcon(':/images/info.png')) self.ui.buttonIMDB.setText(_('Movie Info')) elif isinstance(data_item, RemoteSubtitleFile): self.ui.buttonIMDB.setEnabled(True) self.ui.buttonIMDB.setIcon( QIcon(':/images/sites/opensubtitles.png')) self.ui.buttonIMDB.setText(_('Subtitle Info')) else: self.ui.buttonIMDB.setEnabled(False) def onContext(self, point): # FIXME: code duplication with Main.onContext and/or SearchNameWidget and/or SearchFileWidget menu = QMenu('Menu', self) listview = self.ui.videoView index = listview.currentIndex() data_item = listview.model().getSelectedItem(index) if data_item is not None: if isinstance(data_item, VideoFile): video = data_item movie_info = video.getMovieInfo() if movie_info: subWebsiteAction = QAction(QIcon(":/images/info.png"), _("View IMDB info"), self) subWebsiteAction.triggered.connect(self.onViewOnlineInfo) else: subWebsiteAction = QAction(QIcon(":/images/info.png"), _("Set IMDB info..."), self) subWebsiteAction.triggered.connect(self.onSetIMDBInfo) menu.addAction(subWebsiteAction) elif isinstance(data_item, SubtitleFile): downloadAction = QAction(QIcon(":/images/download.png"), _("Download"), self) # Video tab, TODO:Replace me with a enum downloadAction.triggered.connect(self.onButtonDownload) playAction = QAction(QIcon(":/images/play.png"), _("Play video + subtitle"), self) playAction.triggered.connect(self.onButtonPlay) menu.addAction(playAction) subWebsiteAction = QAction( QIcon(":/images/sites/opensubtitles.png"), _("View online info"), self) menu.addAction(downloadAction) subWebsiteAction.triggered.connect(self.onViewOnlineInfo) menu.addAction(subWebsiteAction) elif isinstance(data_item, Movie): subWebsiteAction = QAction(QIcon(":/images/info.png"), _("View IMDB info"), self) subWebsiteAction.triggered.connect(self.onViewOnlineInfo) menu.addAction(subWebsiteAction) # Show the context menu. menu.exec_(listview.mapToGlobal(point)) def onButtonDownload(self): # We download the subtitle in the same folder than the video subs = self.videoModel.get_checked_subtitles() replace_all = False skip_all = False if not subs: QMessageBox.about(self, _("Error"), _("No subtitles selected to be downloaded")) return total_subs = len(subs) answer = None success_downloaded = 0 # FIXME: must pass mainwindow as argument to ProgressCallbackWidget callback = ProgressCallbackWidget(self) callback.set_title_text(_("Downloading...")) callback.set_label_text(_("Downloading files...")) callback.set_updated_text(_("Downloading subtitle {0} ({1}/{2})")) callback.set_finished_text( _("{0} from {1} subtitles downloaded successfully")) callback.set_block(True) callback.set_range(0, total_subs) callback.show() for i, sub in enumerate(subs): if callback.canceled(): break destinationPath = self.get_state().getDownloadPath(self, sub) if not destinationPath: break log.debug("Trying to download subtitle '%s'" % destinationPath) callback.update(i, QFileInfo(destinationPath).fileName(), i + 1, total_subs) # Check if we have write permissions, otherwise show warning window while True: # If the file and the folder don't have write access. if not QFileInfo( destinationPath).isWritable() and not QFileInfo( QFileInfo(destinationPath).absoluteDir().path() ).isWritable(): warningBox = QMessageBox( _("Error write permission"), _("%s cannot be saved.\nCheck that the folder exists and you have write-access permissions." ) % destinationPath, QMessageBox.Warning, QMessageBox.Retry | QMessageBox.Default, QMessageBox.Discard | QMessageBox.Escape, QMessageBox.NoButton, self) saveAsButton = warningBox.addButton( _("Save as..."), QMessageBox.ActionRole) answer = warningBox.exec_() if answer == QMessageBox.Retry: continue elif answer == QMessageBox.Discard: break # Let's get out from the While true # If we choose the SAVE AS elif answer == QMessageBox.NoButton: fileName, t = QFileDialog.getSaveFileName( self, _("Save subtitle as..."), destinationPath, 'All (*.*)') if fileName: destinationPath = fileName else: # If we have write access we leave the while loop. break # If we have chosen Discard subtitle button. if answer == QMessageBox.Discard: continue # Continue the next subtitle optionWhereToDownload = QSettings().value( "options/whereToDownload", "SAME_FOLDER") # Check if doesn't exists already, otherwise show fileExistsBox # dialog if QFileInfo(destinationPath).exists( ) and not replace_all and not skip_all and optionWhereToDownload != "ASK_FOLDER": # The "remote filename" below is actually not the real filename. Real name could be confusing # since we always rename downloaded sub to match movie # filename. fileExistsBox = QMessageBox( QMessageBox.Warning, _("File already exists"), _("Local: {local}\n\nRemote: {remote}\n\nHow would you like to proceed?" ).format(local=destinationPath, remote=QFileInfo(destinationPath).fileName()), QMessageBox.NoButton, self) skipButton = fileExistsBox.addButton(_("Skip"), QMessageBox.ActionRole) # skipAllButton = fileExistsBox.addButton(_("Skip all"), QMessageBox.ActionRole) replaceButton = fileExistsBox.addButton( _("Replace"), QMessageBox.ActionRole) replaceAllButton = fileExistsBox.addButton( _("Replace all"), QMessageBox.ActionRole) saveAsButton = fileExistsBox.addButton(_("Save as..."), QMessageBox.ActionRole) cancelButton = fileExistsBox.addButton(_("Cancel"), QMessageBox.ActionRole) fileExistsBox.exec_() answer = fileExistsBox.clickedButton() if answer == replaceAllButton: # Don't ask us again (for this batch of files) replace_all = True elif answer == saveAsButton: # We will find a uniqiue filename and suggest this to user. # add .<lang> to (inside) the filename. If that is not enough, start adding numbers. # There should also be a preferences setting "Autorename # files" or similar ( =never ask) FIXME suggBaseName, suggFileExt = os.path.splitext( destinationPath) fNameCtr = 0 # Counter used to generate a unique filename suggestedFileName = suggBaseName + '.' + \ sub.get_language().xxx() + suggFileExt while (os.path.exists(suggestedFileName)): fNameCtr += 1 suggestedFileName = suggBaseName + '.' + \ sub.get_language().xxx() + '-' + \ str(fNameCtr) + suggFileExt fileName, t = QFileDialog.getSaveFileName( None, _("Save subtitle as..."), suggestedFileName, 'All (*.*)') if fileName: destinationPath = fileName else: # Skip this particular file if no filename chosen continue elif answer == skipButton: continue # Skip this particular file # elif answer == skipAllButton: # count += percentage # skip_all = True # Skip all files already downloaded # continue elif answer == cancelButton: break # Break out of DL loop - cancel was pushed QCoreApplication.processEvents() # FIXME: redundant update? callback.update(i, QFileInfo(destinationPath).fileName(), i + 1, total_subs) try: if not skip_all: log.debug("Downloading subtitle '%s'" % destinationPath) download_callback = ProgressCallback() # FIXME data_stream = sub.download( provider_instance=self.get_state().get_OSDBServer(), callback=download_callback, ) write_stream(data_stream, destinationPath) except Exception as e: log.exception('Unable to Download subtitle {}'.format( sub.get_filename())) QMessageBox.about( self, _("Error"), _("Unable to download subtitle %s") % sub.get_filename()) callback.finish(success_downloaded, total_subs) def onViewOnlineInfo(self): # FIXME: code duplication with Main.onContext and/or SearchNameWidget and/or SearchFileWidget # Tab for SearchByHash TODO:replace this 0 by an ENUM value listview = self.ui.videoView index = listview.currentIndex() data_item = self.videoModel.getSelectedItem(index) if isinstance(data_item, VideoFile): video = data_item movie_info = video.getMovieInfo() if movie_info: imdb = movie_info["IDMovieImdb"] if imdb: webbrowser.open("http://www.imdb.com/title/tt%s" % imdb, new=2, autoraise=1) elif isinstance(data_item, RemoteSubtitleFile): sub = data_item webbrowser.open(sub.get_link(), new=2, autoraise=1) elif isinstance(data_item, Movie): movie = data_item imdb = movie.IMDBId if imdb: webbrowser.open("http://www.imdb.com/title/tt%s" % imdb, new=2, autoraise=1) @pyqtSlot() def onSetIMDBInfo(self): #FIXME: DUPLICATED WITH SEARCHNAMEWIDGET QMessageBox.about(self, _("Info"), "Not implemented yet. Sorry...")
class SearchFileWidget(QWidget): language_filter_change = pyqtSignal(list) def __init__(self): QWidget.__init__(self) self._refreshing = False self.fileModel = None self.proxyFileModel = None self.videoModel = None self._state = None self.timeLastSearch = QTime.currentTime() self.ui = Ui_SearchFileWidget() self.setup_ui() QTimer.singleShot(0, self.on_event_loop_started) @pyqtSlot() def on_event_loop_started(self): lastDir = self._state.get_video_path() index = self.fileModel.index(str(lastDir)) proxyIndex = self.proxyFileModel.mapFromSource(index) self.ui.folderView.scrollTo(proxyIndex) def set_state(self, state): self._state = state self._state.signals.interface_language_changed.connect( self.on_interface_language_changed) self._state.signals.login_status_changed.connect( self.on_login_state_changed) def setup_ui(self): self.ui.setupUi(self) self.ui.splitter.setSizes([600, 1000]) self.ui.splitter.setChildrenCollapsible(False) # Set up folder view self.fileModel = QFileSystemModel(self) self.fileModel.setFilter(QDir.AllDirs | QDir.Dirs | QDir.Drives | QDir.NoDotAndDotDot | QDir.Readable | QDir.Executable | QDir.Writable) self.fileModel.iconProvider().setOptions( QFileIconProvider.DontUseCustomDirectoryIcons) self.fileModel.setRootPath(QDir.rootPath()) self.fileModel.directoryLoaded.connect(self.onFileModelDirectoryLoaded) self.proxyFileModel = QSortFilterProxyModel(self) self.proxyFileModel.setSortRole(Qt.DisplayRole) self.proxyFileModel.setSourceModel(self.fileModel) self.proxyFileModel.sort(0, Qt.AscendingOrder) self.proxyFileModel.setSortCaseSensitivity(Qt.CaseInsensitive) self.ui.folderView.setModel(self.proxyFileModel) self.ui.folderView.setHeaderHidden(True) self.ui.folderView.hideColumn(3) self.ui.folderView.hideColumn(2) self.ui.folderView.hideColumn(1) self.ui.folderView.expanded.connect(self.onFolderViewExpanded) self.ui.folderView.clicked.connect(self.onFolderTreeClicked) self.ui.buttonFind.clicked.connect(self.onButtonFind) self.ui.buttonRefresh.clicked.connect(self.onButtonRefresh) # Setup and disable buttons self.ui.buttonFind.setEnabled(False) self.ui.buttonSearchSelectFolder.setEnabled(False) self.ui.buttonSearchSelectVideos.setEnabled(False) # Set up introduction self.showInstructions() # Set unknown text here instead of `retranslate()` because widget translates itself self.ui.filterLanguageForVideo.set_unknown_text(_('All languages')) self.ui.filterLanguageForVideo.set_selected_language( UnknownLanguage.create_generic()) self.ui.filterLanguageForVideo.selected_language_changed.connect( self.on_language_combobox_filter_change) # Set up video view self.videoModel = VideoModel(self) self.videoModel.connect_treeview(self.ui.videoView) self.ui.videoView.setHeaderHidden(True) self.ui.videoView.clicked.connect(self.onClickVideoTreeView) self.ui.videoView.selectionModel().currentChanged.connect( self.onSelectVideoTreeView) self.ui.videoView.customContextMenuRequested.connect(self.onContext) self.ui.videoView.setUniformRowHeights(True) self.videoModel.dataChanged.connect(self.subtitlesCheckedChanged) self.language_filter_change.connect( self.videoModel.on_filter_languages_change) self.ui.buttonSearchSelectVideos.clicked.connect( self.onButtonSearchSelectVideos) self.ui.buttonSearchSelectFolder.clicked.connect( self.onButtonSearchSelectFolder) self.ui.buttonDownload.clicked.connect(self.onButtonDownload) self.ui.buttonPlay.clicked.connect(self.onButtonPlay) self.ui.buttonIMDB.clicked.connect(self.onViewOnlineInfo) self.ui.videoView.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.buttonPlay.setEnabled(False) # Drag and Drop files to the videoView enabled # FIXME: enable drag events for videoView (and instructions view) self.ui.videoView.__class__.dragEnterEvent = self.dragEnterEvent self.ui.videoView.__class__.dragMoveEvent = self.dragEnterEvent self.ui.videoView.__class__.dropEvent = self.dropEvent self.ui.videoView.setAcceptDrops(1) self.retranslate() def retranslate(self): introduction = '<p align="center"><h2>{title}</h2></p>' \ '<p><b>{tab1header}</b><br/>{tab1content}</p>' \ '<p><b>{tab2header}</b><br/>{tab2content}</p>'\ '<p><b>{tab3header}</b><br/>{tab3content}</p>'.format( title=_('How To Use {title}').format(title=PROJECT_TITLE), tab1header=_('1st Tab:'), tab2header=_('2nd Tab:'), tab3header=_('3rd Tab:'), tab1content=_('Select, from the Folder Tree on the left, the folder which contains the videos ' 'that need subtitles. {project} will then try to automatically find available ' 'subtitles.').format(project=PROJECT_TITLE), tab2content=_('If you don\'t have the videos in your machine, you can search subtitles by ' 'introducing the title/name of the video.').format(project=PROJECT_TITLE), tab3content=_('If you have found some subtitle somewhere else that is not in {project}\'s database, ' 'please upload those subtitles so next users will be able to ' 'find them more easily.').format(project=PROJECT_TITLE)) self.ui.introductionHelp.setHtml(introduction) @pyqtSlot(Language) def on_interface_language_changed(self, language): self.ui.retranslateUi(self) self.retranslate() @pyqtSlot(str) def onFileModelDirectoryLoaded(self, path): lastDir = str(self._state.get_video_path()) qDirLastDir = QDir(lastDir) qDirLastDir.cdUp() if qDirLastDir.path() == path: index = self.fileModel.index(lastDir) proxyIndex = self.proxyFileModel.mapFromSource(index) self.ui.folderView.scrollTo(proxyIndex) self.ui.folderView.setCurrentIndex(proxyIndex) @pyqtSlot() def on_login_state_changed(self): log.debug('on_login_state_changed()') nb_connected = self._state.providers.get_number_connected_providers() if nb_connected: self.ui.buttonSearchSelectFolder.setEnabled(True) self.ui.buttonSearchSelectVideos.setEnabled(True) self.ui.buttonFind.setEnabled( self.get_current_selected_folder() is not None) else: self.ui.buttonSearchSelectFolder.setEnabled(False) self.ui.buttonSearchSelectVideos.setEnabled(False) self.ui.buttonFind.setEnabled(False) @pyqtSlot(Language) def on_language_combobox_filter_change(self, language): if language.is_generic(): self.language_filter_change.emit( self._state.get_download_languages()) else: self.language_filter_change.emit([language]) def on_permanent_language_filter_change(self, languages): selected_language = self.ui.filterLanguageForVideo.get_selected_language( ) if selected_language.is_generic(): self.language_filter_change.emit(languages) @pyqtSlot() def subtitlesCheckedChanged(self): subs = self.videoModel.get_checked_subtitles() if subs: self.ui.buttonDownload.setEnabled(True) else: self.ui.buttonDownload.setEnabled(False) def showInstructions(self): self.ui.stackedSearchResult.setCurrentWidget(self.ui.pageIntroduction) def hideInstructions(self): self.ui.stackedSearchResult.setCurrentWidget(self.ui.pageSearchResult) @pyqtSlot(QModelIndex) def onFolderTreeClicked(self, proxyIndex): """What to do when a Folder in the tree is clicked""" if not proxyIndex.isValid(): return index = self.proxyFileModel.mapToSource(proxyIndex) folder_path = self.fileModel.filePath(index) self._state.set_video_paths([folder_path]) def get_current_selected_folder(self): proxyIndex = self.ui.folderView.currentIndex() index = self.proxyFileModel.mapToSource(proxyIndex) folder_path = Path(self.fileModel.filePath(index)) if not folder_path: return None return folder_path def get_current_selected_item_videomodel(self): current_index = self.ui.videoView.currentIndex() return self.videoModel.getSelectedItem(current_index) @pyqtSlot() def onButtonFind(self): now = QTime.currentTime() if now < self.timeLastSearch.addMSecs(500): return folder_path = self.get_current_selected_folder() self._state.set_video_paths([folder_path]) self.search_videos([folder_path]) self.timeLastSearch = QTime.currentTime() @pyqtSlot() def onButtonRefresh(self): currentPath = self.get_current_selected_folder() self._refreshing = True self.ui.folderView.collapseAll() currentPath = self.get_current_selected_folder() if not currentPath: self._state.set_video_paths([currentPath]) index = self.fileModel.index(str(currentPath)) self.ui.folderView.scrollTo(self.proxyFileModel.mapFromSource(index)) @pyqtSlot(QModelIndex) def onFolderViewExpanded(self, proxyIndex): if self._refreshing: expandedPath = self.fileModel.filePath( self.proxyFileModel.mapToSource(proxyIndex)) if expandedPath == QDir.rootPath(): currentPath = self.get_current_selected_folder() if not currentPath: currentPath = self._state.get_video_path() index = self.fileModel.index(str(currentPath)) self.ui.folderView.scrollTo( self.proxyFileModel.mapFromSource(index)) self._refreshing = False @pyqtSlot() def onButtonSearchSelectFolder(self): paths = self._state.get_video_paths() path = paths[0] if paths else Path() selected_path = QFileDialog.getExistingDirectory( self, _('Select the directory that contains your videos'), str(path)) if selected_path: selected_paths = [Path(selected_path)] self._state.set_video_paths(selected_paths) self.search_videos(selected_paths) @pyqtSlot() def onButtonSearchSelectVideos(self): paths = self._state.get_video_paths() path = paths[0] if paths else Path() selected_files, t = QFileDialog.getOpenFileNames( self, _('Select the video(s) that need subtitles'), str(path), get_select_videos()) if selected_files: selected_files = list(Path(f) for f in selected_files) selected_dirs = list(set(p.parent for p in selected_files)) self._state.set_video_paths(selected_dirs) self.search_videos(selected_files) def search_videos(self, paths): if not self._state.providers.get_number_connected_providers(): QMessageBox.about( self, _('Error'), _('You are not connected to a server. Please connect first.')) return self.ui.buttonFind.setEnabled(False) self._search_videos_raw(paths) self.ui.buttonFind.setEnabled(True) def _search_videos_raw(self, paths): # FIXME: must pass mainwindow as argument to ProgressCallbackWidget callback = ProgressCallbackWidget(self) callback.set_title_text(_('Scanning...')) callback.set_label_text(_('Scanning files')) callback.set_finished_text(_('Scanning finished')) callback.set_block(True) callback.show() try: local_videos, local_subs = scan_videopaths(paths, callback=callback, recursive=True) except OSError: callback.cancel() QMessageBox.warning(self, _('Error'), _('Some directories are not accessible.')) if callback.canceled(): return callback.finish() log.debug('Videos found: {}'.format(local_videos)) log.debug('Subtitles found: {}'.format(local_subs)) self.hideInstructions() if not local_videos: QMessageBox.information(self, _('Scan Results'), _('No video has been found.')) return total = len(local_videos) # FIXME: must pass mainwindow as argument to ProgressCallbackWidget # callback = ProgressCallbackWidget(self) # callback.set_title_text(_('Asking Server...')) # callback.set_label_text(_('Searching subtitles...')) # callback.set_updated_text(_('Searching subtitles ( %d / %d )')) # callback.set_finished_text(_('Search finished')) callback.set_block(True) callback.set_range(0, total) callback.show() try: remote_subs = self._state.search_videos(local_videos, callback) except ProviderConnectionError: log.debug( 'Unable to search for subtitles of videos: videos={}'.format( list(v.get_filename() for v in local_videos))) QMessageBox.about(self, _('Error'), _('Unable to search for subtitles')) callback.finish() return self.videoModel.set_videos(local_videos) if remote_subs is None: QMessageBox.about( self, _('Error'), _('Error contacting the server. Please try again later')) callback.finish() # TODO: CHECK if our local subtitles are already in the server, otherwise suggest to upload @pyqtSlot() def onButtonPlay(self): selected_item = self.get_current_selected_item_videomodel() log.debug('Trying to play selected item: {}'.format(selected_item)) if selected_item is None: QMessageBox.warning(self, _('No subtitle selected'), _('Select a subtitle and try again')) return if isinstance(selected_item, SubtitleFileNetwork): selected_item = selected_item.get_subtitles()[0] if isinstance(selected_item, VideoFile): subtitle_file_path = None video = selected_item elif isinstance(selected_item, LocalSubtitleFile): subtitle_file_path = selected_item.get_filepath() video = selected_item.get_super_parent(VideoFile) elif isinstance(selected_item, RemoteSubtitleFile): video = selected_item.get_super_parent(VideoFile) subtitle_file_path = Path( tempfile.gettempdir()) / 'subdownloader.tmp.srt' log.debug('tmp path is {}'.format(subtitle_file_path)) log.debug( 'Temporary subtitle will be downloaded into: {temp_path}'. format(temp_path=subtitle_file_path)) # FIXME: must pass mainwindow as argument to ProgressCallbackWidget callback = ProgressCallbackWidget(self) callback.set_title_text(_('Playing video + sub')) callback.set_label_text(_('Downloading files...')) callback.set_finished_text(_('Downloading finished')) callback.set_block(True) callback.set_range(0, 100) callback.show() try: selected_item.download( subtitle_file_path, self._state.providers.get(selected_item.get_provider), callback) except ProviderConnectionError: log.debug('Unable to download subtitle "{}"'.format( selected_item.get_filename()), exc_info=sys.exc_info()) QMessageBox.about( self, _('Error'), _('Unable to download subtitle "{subtitle}"').format( subtitle=selected_item.get_filename())) callback.finish() return callback.finish() else: QMessageBox.about( self, _('Error'), '{}\n{}'.format(_('Unknown Error'), _('Please submit bug report'))) return # video = selected_item.get_parent().get_parent().get_parent() # FIXME: download subtitle with provider + use returned localSubtitleFile instead of creating one here if subtitle_file_path: local_subtitle = LocalSubtitleFile(subtitle_file_path) else: local_subtitle = None try: player = self._state.get_videoplayer() player.play_video(video, local_subtitle) except RuntimeError as e: QMessageBox.about(self, _('Error'), e.args[0]) @pyqtSlot(QModelIndex) def onClickVideoTreeView(self, index): data_item = self.videoModel.getSelectedItem(index) if isinstance(data_item, VideoFile): video = data_item if True: # video.getMovieInfo(): self.ui.buttonIMDB.setEnabled(True) self.ui.buttonIMDB.setIcon(QIcon(':/images/info.png')) self.ui.buttonIMDB.setText(_('Movie Info')) elif isinstance(data_item, RemoteSubtitleFile): self.ui.buttonIMDB.setEnabled(True) self.ui.buttonIMDB.setIcon( QIcon(':/images/sites/opensubtitles.png')) self.ui.buttonIMDB.setText(_('Subtitle Info')) else: self.ui.buttonIMDB.setEnabled(False) @pyqtSlot(QModelIndex) def onSelectVideoTreeView(self, index): data_item = self.videoModel.getSelectedItem(index) self.ui.buttonPlay.setEnabled(True) # if isinstance(data_item, SubtitleFile): # self.ui.buttonPlay.setEnabled(True) # else: # self.ui.buttonPlay.setEnabled(False) def onContext(self, point): # FIXME: code duplication with Main.onContext and/or SearchNameWidget and/or SearchFileWidget menu = QMenu('Menu', self) listview = self.ui.videoView index = listview.currentIndex() data_item = listview.model().getSelectedItem(index) if data_item is not None: if isinstance(data_item, VideoFile): video = data_item video_identities = video.get_identities() if any(video_identities.iter_imdb_identity()): online_action = QAction(QIcon(":/images/info.png"), _("View IMDb info"), self) online_action.triggered.connect(self.onViewOnlineInfo) else: online_action = QAction(QIcon(":/images/info.png"), _("Set IMDb info..."), self) online_action.triggered.connect(self.on_set_imdb_info) menu.addAction(online_action) elif isinstance(data_item, SubtitleFile): play_action = QAction(QIcon(":/images/play.png"), _("Play video + subtitle"), self) play_action.triggered.connect(self.onButtonPlay) menu.addAction(play_action) if isinstance(data_item, RemoteSubtitleFile): download_action = QAction(QIcon(":/images/download.png"), _("Download"), self) download_action.triggered.connect(self.onButtonDownload) menu.addAction(download_action) online_action = QAction( QIcon(":/images/sites/opensubtitles.png"), _("View online info"), self) online_action.triggered.connect(self.onViewOnlineInfo) menu.addAction(online_action) # Show the context menu. menu.exec_(listview.mapToGlobal(point)) def _create_choose_target_subtitle_path_cb(self): def callback(path, filename): selected_path = QFileDialog.getSaveFileName( self, _('Choose the target filename'), str(path / filename)) return selected_path return callback def onButtonDownload(self): # We download the subtitle in the same folder than the video rsubs = self.videoModel.get_checked_subtitles() sub_downloader = SubtitleDownloadProcess(parent=self.parent(), rsubtitles=rsubs, state=self._state, parent_add=True) sub_downloader.download_all() new_subs = sub_downloader.downloaded_subtitles() self.videoModel.uncheck_subtitles(new_subs) def onViewOnlineInfo(self): # FIXME: code duplication with Main.onContext and/or SearchNameWidget and/or SearchFileWidget # Tab for SearchByHash TODO:replace this 0 by an ENUM value listview = self.ui.videoView index = listview.currentIndex() data_item = self.videoModel.getSelectedItem(index) if isinstance(data_item, VideoFile): video = data_item video_identities = video.get_identities() if any(video_identities.iter_imdb_identity()): imdb_identity = next(video_identities.iter_imdb_identity()) webbrowser.open(imdb_identity.get_imdb_url(), new=2, autoraise=1) else: QMessageBox.information(self.parent(), _('imdb unknown'), _('imdb is unknown')) elif isinstance(data_item, RemoteSubtitleFile): sub = data_item webbrowser.open(sub.get_link(), new=2, autoraise=1) @pyqtSlot() def on_set_imdb_info(self): # FIXME: DUPLICATED WITH SEARCHNAMEWIDGET QMessageBox.about(self, _("Info"), "Not implemented yet. Sorry...")