web-dev-qa-db-fra.com

Script pour fusionner la vidéo et les sous-programmes puis supprimer les fichiers existants (non récursif)

J'utilise mkvmerge pour fusionner un fichier vidéo et des sous-titres, j'utilise la commande suivante

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

Ce qui fonctionne bien, mais comme vous pouvez probablement en déduire qu'il s'agit d'un fonctionnement très lent

  • Je dois ouvrir un terminal dans chacun de ces répertoires où les films sont stockés.
  • Ensuite, je dois copier et coller les noms des fichiers dans la commande
  • Ensuite, je dois renommer le fichier de sortie
  • Puis supprimez les fichiers originaux que j'ai utilisés

C'est un long processus.


Situation idéale que je veux.

  • Je vais aller dans le dossier qui contient un fichier vidéo et un fichier de sous-titres du même nom.
  • Je sélectionne un fichier de film et un fichier de sous-titres, un clic droit et exécute un script Nautilus sur eux.
  • Ils se confondent
  • Le fichier original est supprimé.
  • Renommez le fichier fusionné avec le nom du fichier d'origine avec l'extension mkv.

Cela n'a pas besoin d'être récursif, je ne veux pas que mon disque dur crée et supprime des centaines de fichiers. Les fichiers de film et les fichiers de sous-titre ont généralement le même nom, mais parfois, la publication "_en" est fixée aux fichiers de sous-titre. Si vous avez une autre approche, je suis également de la partie.

7
Sumeet Deshmukh

J'ai décidé d'acquérir de l'expérience avec Bash et j'ai écrit le script ci-dessous, qui présente les fonctionnalités suivantes:

  • Il peut gérer tous les fichiers et dossiers placés dans le répertoire en cours.
  • S'il existe plusieurs sous-titres ou fichiers vidéo (à l'exception des exemples de fichiers), le script demande une interaction manuelle.
  • Dans tous les autres cas, le script déplacera automatiquement tous les fichiers et dossiers dans un répertoire de sauvegarde (. Faites attention à l'endroit où vous l'exécutez. it! ), qui sera déplacé dans la corbeille de l'utilisateur au lieu d'être supprimé.
  • Il peut gérer plusieurs types d'extensions de vidéo et de sous-titres.
  • Il utilise mkvmerge pour fusionner des vidéos avec des fichiers de sous-titres. Il utilise également notify-send pour afficher certains messages dans l'interface graphique. De plus, il utilise gvfs-trash pour déplacer les fichiers dans le dossier Corbeille de l'utilisateur.
  • Il peut être utilisé en tant que Nautilus script ou en tant que script Shell standard, mais l'environnement GIU est requis, mais la commande notify-send figure dans le corps du script.
  • Le nom du fichier de sortie peut être basé sur le nom du répertoire (par défaut) ou sur le nom du fichier vidéo source.
  • De plus, lorsque seulement deux fichiers sont sélectionnés (, une vidéo et un fichier de sous-titres ), ils seront fusionnés et le reste du contenu du dossier sera conservé. Le fichier de sortie sera nommé d'après le fichier vidéo source. Cette option est disponible uniquement lorsque le script est utilisé en tant que script Nautilus.

Le script:

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

Configuration:

  • Créez un fichier exécutable et collez le contenu ci-dessus à l'intérieur. Appelons ce fichier merge-video-sub:

    touch merge-video-sub 
    chmod +x merge-video-sub
    nano merge-video-sub
    
  • Copiez (ou ln -s) ce fichier dans le dossier ~/.local/share/nautilus/scripts pour le rendre disponible en tant que script Nautilus pour l'utilisateur actuel.

  • Pour le moment, je ne trouve aucun moyen de le rendre accessible à l'ensemble du système sous forme de script Nautilus.

  • Copiez le fichier dans ~/bin (et ajoutez export PATH=$PATH:~/bin au bas de ~/.bashrc si nécessaire) pour le rendre disponible en tant que commande Shell pour l'utilisateur actuel.

  • Copiez le fichier dans /usr/local/bin pour le rendre disponible en tant que système de commande Shell.

  • Le chemin court consiste à curl le script de my Pastebin directement dans le dossier 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
    

Démo:

enter image description here

Références supplémentaires:

5
pa4080

Ici, j'ai fait un script pour ça. Assurez-vous simplement qu'il n'y a que deux fichiers dans votre dossier de films, l'un est votre film et l'autre, le sous-titre (ils n'ont pas besoin du même nom).

Créez un fichier appelé merge sur le bureau et collez ce code:

#!/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 __== "__main__":
    main()

Maintenant, ouvrez un terminal dans le répertoire où vous avez enregistré marge (dans ce cas sur le bureau), mettez mv merge ~/.local/share/nautilus/scripts/merge et appuyez sur Entrée.

maintenant, mettez cd ~/.local/share/nautilus/scripts/ et appuyez sur Entrée.

maintenant, mettez chmod +x merge et appuyez sur Entrée.

maintenant, allez dans le dossier où se trouvent le film et les sous-titres et cliquez avec le bouton droit sur n’importe quel fichier, puis sélectionnez scripts > merge

Terminé.

Si vous avez un problème, faites le moi savoir.

enter image description here

1
Nishant Bhakta

J'ai fait un autre script qui a une interface graphique. Il peut trouver tous les fichiers de sous-titres avec leurs vidéos associées (recherche du même nom) dans un dossier et un sous-dossier et les fusionner.

Voici le lien GitHub pour le script: https://github.com/bhaktanishant/Total-Subtitle-Merger

enter image description here

Voici le code:

#!/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 __== "__main__":
    tk = Tk()
    app = MergeApp(tk)
app.start()

Comment l'utiliser comme script nautilus:

collez ce code dans un fichier et nommez-le merge.

Maintenant, ouvrez un terminal dans le répertoire où vous avez enregistré le fichier marge, mettez mv merge ~/.local/share/nautilus/scripts/merge et appuyez sur Entrée.

maintenant, mettez cd ~/.local/share/nautilus/scripts/ et appuyez sur Entrée.

maintenant, mettez chmod +x merge et appuyez sur Entrée.

maintenant, allez dans le dossier racine où se trouvent les films et les sous-titres, cliquez avec le bouton droit de la souris sur un fichier ou un dossier, puis sélectionnez scripts > merge.

Terminé.

1
Nishant Bhakta