用于合并Video和subs的脚本然后删除现有文件(非递归)

我一直在使用mkvmerge合并video文件和字幕,我正在使用以下命令

 mkvmerge -o output.mkv video.mp4 subtitles.srt 

哪个工作正常,但你可能会猜到这是非常慢的操作

  • 我要在存储电影的每个目录中打开终端。
  • 然后我将复制并粘贴命令中的文件名
  • 然后我要重命名输出文件
  • 然后删除我使用的原始文件

这是一个漫长的过程。


我想要的理想情况。

  • 我将进入包含电影文件和同名字幕文件的文件夹。
  • 我选择一个电影文件和一个字幕文件,右键单击并在它们上运行一个nautilus脚本。
  • 他们合并了
  • 原始文件被删除。
  • 使用带有mkv扩展名的orignal文件名重命名合并文件

这不需要递归,我不希望我的硬盘创建和删除100的文件。 电影文件和字幕文件通常具有相同的名称,但有些时候将“_en”post固定到字幕文件。 如果你有另一种做法,我也会参与其中。

我已经决定获得一些Bash的经验,并编写了下面的脚本,它有下一个function:

  • 它可以处理放在当前目录中的所有文件和文件夹。
  • 如果有多个字幕或video文件(示例文件除外),脚本将要求手动交互。
  • 在所有其他情况下,脚本会自动将所有文件和文件夹移动到备份目录中运行它时要小心! ),它将被移动到用户的垃圾文件夹中,而不是被删除。
  • 它可以处理多种类型的video和字幕扩展。
  • 它使用mkvmerge将video与字幕文件合并,并使用notify-send在GUI中显示一些消息。 它还使用gvfs-trash移动用户的垃圾文件夹中的文件。
  • 它可以用作Nautilus脚本或常规shell脚本,但需要GIU环境,而notify-send命令存在于脚本的主体中。
  • 输出文件的名称可以基于目录名称(默认)或源video文件的名称。
  • 此外,当仅选择两个文件一个video和一个字幕文件 )时, 它们将被合并,并且将保留文件夹内容的其余部分。 输出文件将以源video文件命名。 仅当脚本用作Nautilus脚本时,此选项才可用。

剧本:

 #!/bin/bash -e # Check if all tools are available [ -x /usr/bin/notify-send ] || (echo "Please, install 'notify-send'"; exit 1) [ -x /usr/bin/mkvmerge ] || (echo "Please, install 'mkvmerge'"; exit 1) # Allowed video and subtitle file extensions EXT_VIDEO=("mp4 avi mpg mov mkv wmv") EXT_SUB=("sub str srt vtt") # Files, which names contains some of next strings will be removed in auto mode FILTER=("sample Sample SAMPLE") # Log file MERGE_LOG="/tmp/merge-video-sub.log" echo > "$MERGE_LOG" # # Functions # function get-video-and-sub-file-names { # Get the names of the video and subtitle files and move the rest of the files into the Backup directory for ((i=0; i<${#FILE_LIST[@]}; i++)); do FILE_NAME="${FILE_LIST[$i]%.*}" FILE_EXT="${FILE_LIST[$i]##*.}" if [[ "${EXT_SUB[@]}" == *"$FILE_EXT"* ]]; then SUB_FULL_FILE_NAME="${FILE_LIST[$i]}" SUB_FILE_NAME="${FILE_NAME}" SUB_FILE_EXT="${FILE_EXT}" elif [[ "${EXT_VIDEO[@]}" == *"$FILE_EXT"* ]]; then VIDEO_FULL_FILE_NAME="${FILE_LIST[$i]}" VIDEO_FILE_NAME="${FILE_NAME}" VIDEO_FILE_EXT="${FILE_EXT}" else # We need 'find' to manipulate only with files, because "$BACKUP_DIR" is in the queue find ./* -maxdepth 0 -type f -name "${FILE_LIST[$i]}" -exec mv "{}" "$BACKUP_DIR" \; -exec echo -e "The file {} was REMOVED.\n" >> "$MERGE_LOG" \; fi done } function get-the-content-of-the-current-directory { # Get the content of the current directory shopt -s nullglob FILE_LIST=(*) shopt -u nullglob } function mkvmerge-video-and-sub-files { # Create merged file mkvmerge -o "$OUTPUT_FILE" "$VIDEO_FULL_FILE_NAME" "$SUB_FULL_FILE_NAME" sleep 3 } # # Scenario 1: If exactly two files are selected in Nautilus! Then check if they are 1 video and 1 subtitle files, if yes - merge and remove them # Scenario 2: Else run the standard procedure # # Get the files, selected in Nautilus as file list. Use next command to check the result: notify-send "MESSAGE" "`echo -e "${#FILE_LIST[@]}"; printf '%s\n' "${FILE_LIST[@]}"`" IFS_BAK=$IFS IFS=$'\t\n' FILE_LIST=($NAUTILUS_SCRIPT_SELECTED_FILE_PATHS) IFS=$IFS_BAK if [ "${#FILE_LIST[@]}" -eq "2" ] then # Scenario 1 # Get the names of the video and subtitle files get-video-and-sub-file-names if [[ "${EXT_SUB[@]}" == *" $SUB_FILE_EXT "* ]] && [[ "${EXT_VIDEO[@]}" == *" $VIDEO_FILE_EXT "* ]] then notify-send "OK" "`echo -e "The following files will be MERGED and MOVED to trash:"; printf '\t-\ %s\n' "${FILE_LIST[@]##*/}"`" # Construct the name of the merged file. OUTPUT_FILE="${VIDEO_FILE_NAME}.sub.mkv" # Merge the files mkvmerge-video-and-sub-files # Move video and subtitle files into user's trash directory and create trash infofile if [ -f "$OUTPUT_FILE" ] then gvfs-trash "$VIDEO_FULL_FILE_NAME" gvfs-trash "$SUB_FULL_FILE_NAME" notify-send "OK" "`echo -e "THE NAME OF THE NEW MERGED FILE IS:\n${OUTPUT_FILE##*/}"`" else notify-send "ERROR 1" "`echo "Something went wrong!"`" fi else notify-send "ERROR" "`echo -e "\n\t\nTo use this function, please select exactly:\n\t- 1 video file and\n\t- 1 subtitle file!\n\t\nYou are selected these files:"; printf '\t-\ %s\n' "${FILE_LIST[@]##*/}"`" fi else # Scenario 2 # Get the current directory name DIR_NAME="${PWD##*/}" # Create Backup sub-directory BACKUP_DIR="${DIR_NAME}.backup" [ -d "${BACKUP_DIR}" ] || mkdir "$BACKUP_DIR" && echo "The directory $BACKUP_DIR was CREATED.\n" > "$MERGE_LOG" # Move all sub-directories into the Backup directory shopt -s dotglob find ./* -maxdepth 0 -type d ! -name "*$BACKUP_DIR*" -prune -exec mv "{}" "$BACKUP_DIR" \; -exec echo "The directory {} was REMOVED.\n" >> "$MERGE_LOG" \; shopt -u dotglob # Move all files and folders, whose names contains a string, that exists in $FILTER[@] for f in $FILTER; do shopt -s dotglob find ./* -maxdepth 0 ! -name "*$BACKUP_DIR*" -type f -name "*$f*" -exec mv "{}" "$BACKUP_DIR" \; -exec echo "The file {} was REMOVED.\n" >> "$MERGE_LOG" \; shopt -u dotglob done # Get the entire content of the current directory get-the-content-of-the-current-directory # Get the names of the video and subtitle files and move the rest of the files into the Backup directory get-video-and-sub-file-names # Construct the name of the merged file. It could be based on the parent directory or on the video file name Make your choice and comment/uncomment next lines #OUTPUT_FILE="${VIDEO_FILE_NAME}.sub.mkv" OUTPUT_FILE="${DIR_NAME}.sub.mkv" # Get the entire content of the current directory after the filtering get-the-content-of-the-current-directory echo -e "$(cat $MERGE_LOG)" && notify-send "OK" "`echo -e "$(cat $MERGE_LOG)"`" && echo > "$MERGE_LOG" # Check the current structure of the directory if [ "${#FILE_LIST[@]}" -ne "3" ]; then echo "The content structure must consists of next 3 items:" > "$MERGE_LOG" echo "\t- 1 movie file,\n\t- 1 subtitle file and\n\t- 1 backup directory." >> "$MERGE_LOG" echo "\n\t\nThe current number of contained items is ${#FILE_LIST[@]}." >> "$MERGE_LOG" && echo >> "$MERGE_LOG" echo "\n\t\nPLEASE RESOLVE THIS MANUALLY!" >> "$MERGE_LOG" echo -e "$(cat $MERGE_LOG)" && notify-send "ERROR" "`echo -e "$(cat $MERGE_LOG)"`" else echo "The directory structure looks good, is contains ${#FILE_LIST[@]} items." > "$MERGE_LOG" echo " - The source VIDEO file is: ${VIDEO_FULL_FILE_NAME::21}... .${VIDEO_FULL_FILE_NAME##*.}" >> "$MERGE_LOG" echo " - The source SUB file is: ${SUB_FULL_FILE_NAME::25}... .${SUB_FULL_FILE_NAME##*.}" >> "$MERGE_LOG" echo "They has been merged and removed!" | tr /az/ /AZ/ >> "$MERGE_LOG" # Merge the files mkvmerge-video-and-sub-files # Move video and subtitle files into the Backup directory mv "$VIDEO_FULL_FILE_NAME" "$BACKUP_DIR" mv "$SUB_FULL_FILE_NAME" "$BACKUP_DIR" # Move the Backup directory to trash and create trash infofile if [ -f "$OUTPUT_FILE" ]; then gvfs-trash "$BACKUP_DIR" echo "\n\t\nThe Backup directory has been MOVED to Trash!\n\t\n" >> "$MERGE_LOG" echo "The name of the new merged file is:" | tr /az/ /AZ/ >> "$MERGE_LOG" echo "${OUTPUT_FILE##*/}" >> "$MERGE_LOG" echo -e "$(cat $MERGE_LOG)" && notify-send "OK" "`echo -e "$(cat $MERGE_LOG)"`" else echo "Something went wrong!" && notify-send "ERROR 2" "`echo "Something went wrong!"`" fi fi fi rm "$MERGE_LOG" exit 1 

建立:

  • 创建可执行文件并将上述内容粘贴到其中。 我们称这个文件为merge-video-sub

     touch merge-video-sub chmod +x merge-video-sub nano merge-video-sub 
  • 将此文件复制(或ln -s )到~/.local/share/nautilus/scripts文件夹中,使其可用作当前用户的Nautilus脚本。

  • 目前,我无法找到一种方法如何在系统范围内使用它作为Nautilus脚本。

  • 将文件复制到~/bin (如果需要,将export PATH=$PATH:~/bin~/.bashrc的底部),使其可用作当前用户的shell命令。

  • 将文件复制到/usr/local/bin以使其可用作系统范围的shell命令。

  • 简短的方法是将我的PasteBin脚本直接卷入nautilus/scripts文件夹:

     curl https://pastebin.com/raw/HrLTibuR | sed -e 's/\r$//' > $HOME/.local/share/nautilus/scripts/merge-video-sub chmod +x $HOME/.local/share/nautilus/scripts/merge-video-sub 

演示:

在此处输入图像描述

  • 上一个演示

在这里,我为此制作了一个脚本。 只需确保电影文件夹中只有两个文件,一个是电影,另一个是副标题(它们不需要具有相同的名称)。

在Desktop创建一个名为merge的文件并粘贴此代码:

 #!/usr/bin/env python import subprocess, os, sys def main(): movieName = "" subtitleName = "" for name in os.listdir(os.getcwd()): if (name != sys.argv[0]): if ".srt" in name: subtitleName = name else: movieName = name start(movieName, subtitleName) def start(movieName, subtitleName): if (subprocess.call(["mkvmerge", "-o", "merging", movieName, subtitleName]) == 0): subprocess.call(["rm", movieName, subtitleName]) subprocess.call(["mv", "merging", movieName]) else: for name in os.listdir(os.getcwd()): if name == "merging": subprocess.call("rm", "merging") print "Sorry, Something went wrong." if __name__ == "__main__": main() 

现在,在你保存marge的目录下打开一个终端(在这种情况下在桌面上)并放入mv merge ~/.local/share/nautilus/scripts/merge并按Enter键。

现在把cd ~/.local/share/nautilus/scripts/然后按回车键。

现在把chmod +x merge并点击回车。

现在转到电影和字幕所在的文件夹,右键单击任何文件,然后选择scripts > merge

完成。

如果您有任何问题,请告诉我。

在此处输入图像描述

我制作了另一个有GUI的脚本。 它可以在文件夹和子文件夹中找到所有字幕文件及其相关video(搜索相同名称),并将它们合并为一个。

这是脚本的GitHub链接: https : //github.com/bhaktanishant/Total-Subtitle-Merger

在此处输入图像描述

这是代码:

 #!/usr/bin/env python from Tkinter import Tk, Listbox, Button, Scrollbar, Canvas, Frame, Label from subprocess import call from threading import Thread import os, tkMessageBox from time import sleep class MergeApp: def __init__(self, root): self.root = root self.title = "Subtitle Merger By - Nishant Bhakta" self.messageBoxTitle = "Message Box" self.cancelWarning = "The video which has been started to merge will be merge. Rest will be cancel." self.movieListBox = Listbox(self.root) self.scrollBar = Scrollbar(self.root) self.startButton = Button(self.root, text = "start", state = "disable", command = self.startMerging) self.cancelButton = Button(self.root, text = "Stop", state = "disable", command = self.stopMerging) self.finishButton = Button(self.root, text = "Exit", state = "normal", command = self.endApplication) self.loadingLabel = Label(self.root) self.processState = Label(self.root) self.movieMap = {} self.keyList = [] self.loadingIcons = ["--", "\\", "|", "/"] self.wantToMerge = True self.loading = False self.warningMessageLoaded = False def start(self): screen_width = self.root.winfo_screenwidth() screen_height = self.root.winfo_screenheight() # calculate position x and y coordinates x = (screen_width/2) - (700/2) y = (screen_height/2) - (300/2) self.root.geometry('%dx%d+%d+%d' % (700, 300, x, y)) self.root.title(self.title) self.movieListBox.config(width = 68, yscrollcommand = self.scrollBar.set) self.movieListBox.pack(side = "left", fill = "y") self.scrollBar.config(command = self.movieListBox.yview) self.scrollBar.pack(fill = "y", side = "left") self.startButton.pack(fill = "x") self.cancelButton.pack(fill = "x") self.finishButton.pack(fill = "x") self.processState.pack(fill = "x", side = "bottom") self.loadingLabel.pack(fill = "x", side = "bottom") Thread(target = self.createMovieMap).start() self.mainThread = Thread(target = self.startMerge) self.root.protocol("WM_DELETE_WINDOW", self.ifCloseWindow) self.root.mainloop() def createMovieMap(self): #Looking for subtitle index = 0 Thread(target = self.startLoading, args = (True, )).start() self.processState.config(text = "Searching Videos..") for oneWalk in os.walk(os.getcwd()): for fileName in oneWalk[2]: if ".srt" in fileName: subtitleName = fileName #Now looking for movie with the name of subtitle for oneWalk in os.walk(os.getcwd()): for fileName in oneWalk[2]: if ".srt" not in fileName: key = subtitleName.replace(".srt", "") if key in fileName: movieName = fileName if key not in self.movieMap: self.movieMap[key] = dict([("subtitleUri", oneWalk[0] + "/" + subtitleName) , ("movieUri", oneWalk[0] + "/" + movieName) , ("moviePath", oneWalk[0])]) self.movieListBox.insert(index, " Queued - " + key) self.keyList.append(key) index += 1 self.startButton.config(state = "normal") self.processState.config(text = "Search Complete.") self.loading = False def startMerge(self): self.changeButtonState() for key, value in self.movieMap.iteritems(): if self.wantToMerge: self.processState.config(text = "Merging Video..") Thread(target = self.startLoading, args = (True, )).start() index = self.keyList.index(key) self.movieListBox.delete(index) self.movieListBox.insert(index, " Merging - " + key) self.movieListBox.itemconfig(index, bg = "yellow") if (call(["mkvmerge", "-o", value['moviePath'] + "/merging", value['movieUri'], value['subtitleUri']]) == 0): call(["rm", value['movieUri'], value['subtitleUri']]) call(["mv", value['moviePath'] + "/merging", value['moviePath'] + "/"+ key + ".mkv"]) self.movieListBox.delete(index) self.movieListBox.insert(index, " Successful - " + key) self.movieListBox.itemconfig(index, bg = "green") else: for name in os.listdir(value['moviePath'] + "/"): if name == "merging": call(["rm", value['moviePath'] + "/merging"]) self.movieListBox.delete(index) self.movieListBox.insert(index, " Failed - "+ key) self.movieListBox.itemconfig(index, bg = "red", foreground = "white") else: break self.loading = False self.cancelButton.config(state = "disable") self.finishButton.config(state = "normal") if self.wantToMerge: self.processState.config(text = "Merge Complete.") def startLoading(self, loadOrNot): self.loading = loadOrNot while self.loading: for icon in self.loadingIcons: self.loadingLabel.config(text = icon) sleep(.2) def startMerging(self): self.mainThread.start() def changeButtonState(self): self.startButton.config(state = "disable") self.cancelButton.config(state = "normal") self.finishButton.config(state = "disable") def stopMerging(self): self.wantToMerge = False self.startButton.config(state = "disable") self.cancelButton.config(state = "disable") self.finishButton.config(state = "normal") self.processState.config(text = "Merge Canceled.") if not self.warningMessageLoaded: tkMessageBox.showwarning(self.messageBoxTitle, self.cancelWarning) self.warningMessageLoaded = True def endApplication(self): self.root.destroy() def ifCloseWindow(self): if self.mainThread.is_alive(): self.stopMerging() self.endApplication() if __name__ == "__main__": tk = Tk() app = MergeApp(tk) app.start() 

如何将它用作nautilus脚本:

将此代码粘贴到文件并将其命名为merge

现在,在保存文件marge的目录中打开一个终端并放入mv merge ~/.local/share/nautilus/scripts/merge并按Enter键。

现在把cd ~/.local/share/nautilus/scripts/然后按回车键。

现在把chmod +x merge并点击回车。

现在转到电影和字幕所在的根文件夹,右键单击任何文件或文件夹,然后选择scripts > merge

完成。