AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / ubuntu / 问题 / 939821
Accepted
Sumeet Deshmukh
Sumeet Deshmukh
Asked: 2017-07-26 20:14:14 +0800 CST2017-07-26 20:14:14 +0800 CST 2017-07-26 20:14:14 +0800 CST

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

  • 772

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

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

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

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

这是一个漫长的过程。


我想要的理想情况。

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

这不需要递归,我不希望我的 HDD 创建和删除 100 个文件。电影文件和字幕文件通常具有相同的名称,但有时会将“_en”后缀固定到字幕文件。如果您有另一种方法,我也同意。

command-line
  • 3 3 个回答
  • 3850 Views

3 个回答

  • Voted
  1. Best Answer
    pa4080
    2017-07-28T14:00:01+08:002017-07-28T14:00:01+08:00

    我决定获得一些使用 Bash 的经验并编写了以下脚本,该脚本具有以下功能:

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

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

    • 简短的方法是将我的 PasteBincurl中的脚本直接放入文件夹: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
      

    演示:

    在此处输入图像描述

    • 之前的演示

    附加参考:

    • 使用 mkvmerge 编码强制字幕
    • 如何在几秒钟内将 srt、idx 或字幕与 MKV 合并
    • ToolSlick: SUB to SRT Converter and SubtitleTools: Convert Sub/Idx to Srt
    • 7
  2. Nishant Bhakta
    2017-07-31T07:45:53+08:002017-07-31T07:45:53+08:00

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

    这是脚本的 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输入并按回车键。

    现在放cd ~/.local/share/nautilus/scripts/并按回车。

    现在放chmod +x merge并按回车。

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

    完毕。

    • 2
  3. Nishant Bhakta
    2017-07-26T21:46:38+08:002017-07-26T21:46:38+08:00

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

    创建一个名为mergeDesktop 的文件并粘贴以下代码:

    #!/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

    完毕。

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

    在此处输入图像描述

    • 1

相关问题

  • 如何从命令行仅安装安全更新?关于如何管理更新的一些提示

  • 如何从命令行刻录双层 dvd iso

  • 如何从命令行判断机器是否需要重新启动?

  • 文件权限如何工作?文件权限用户和组

  • 如何在 Vim 中启用全彩支持?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何运行 .sh 脚本?

    • 16 个回答
  • Marko Smith

    如何安装 .tar.gz(或 .tar.bz2)文件?

    • 14 个回答
  • Marko Smith

    如何列出所有已安装的软件包

    • 24 个回答
  • Marko Smith

    无法锁定管理目录 (/var/lib/dpkg/) 是另一个进程在使用它吗?

    • 25 个回答
  • Martin Hope
    Flimm 如何在没有 sudo 的情况下使用 docker? 2014-06-07 00:17:43 +0800 CST
  • Martin Hope
    Ivan 如何列出所有已安装的软件包 2010-12-17 18:08:49 +0800 CST
  • Martin Hope
    La Ode Adam Saputra 无法锁定管理目录 (/var/lib/dpkg/) 是另一个进程在使用它吗? 2010-11-30 18:12:48 +0800 CST
  • Martin Hope
    David Barry 如何从命令行确定目录(文件夹)的总大小? 2010-08-06 10:20:23 +0800 CST
  • Martin Hope
    jfoucher “以下软件包已被保留:”为什么以及如何解决? 2010-08-01 13:59:22 +0800 CST
  • Martin Hope
    David Ashford 如何删除 PPA? 2010-07-30 01:09:42 +0800 CST

热门标签

10.10 10.04 gnome networking server command-line package-management software-recommendation sound xorg

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve