Я использовал mkvmerge
для слияния видеофайла и подзаголовков я использую следующую команду
mkvmerge -o output.mkv video.mp4 subtitles.srt
Который хорошо работает, но поскольку можно, вероятно, предположить, что это - действительно медленная операция
Это - долгий процесс.
Идеальная ситуация, что я хочу.
mkv
расширение Это не должно быть рекурсивно, я не хочу свое создание жесткого диска и удаление 100 файлов в. Файлы ролика и файлы Подзаголовка обычно имеют то же имя, но несколько раз имеют сообщение "_en", прикрепленное к файлам подзаголовка. Если у Вас есть другой подход выполнения его, я на борту с этим также.
Я решил получить некоторый опыт с Bash и записал ниже сценария, который имеет следующие функции:
mkvmerge
объединить видео с файлами подзаголовка также использует notify-send
показать некоторые сообщения в GUI. Также это использует gvfs-trash
перемещать файлы в папку "Удаленные" пользователя.notify-send
команда существует в тело сценария.Сценарий:
#!/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
сделать это доступным как сценарий Наутилуса для текущего пользователя.
В данный момент я не могу найти путь, как сделать его доступным в масштабе всей системы как сценарий Наутилуса.
Скопируйте файл в ~/bin
(и добавить export PATH=$PATH:~/bin
к нижней части ~/.bashrc
если необходим) сделать это доступным как команда оболочки для текущего пользователя.
Скопируйте файл в /usr/local/bin
сделать это доступным как система команд оболочки широкий.
Короткий путь к curl
сценарий от моего 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
Демонстрация:
Дополнительные ссылки:
Здесь, я сделал сценарий для этого. Просто удостоверьтесь, что существует только два файла в Вашей папке с фильмом, каждый - Ваш фильм, и другой подзаголовок (у них не должно быть того же имени).
Создайте названный файл merge
в Desktop и вставке этот код:
#!/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
и хит входит.
теперь помещенный cd ~/.local/share/nautilus/scripts/
и хит входит.
теперь помещенный chmod +x merge
и хит входит.
теперь перейдите к папке, где фильм и подзаголовок, и щелчок правой кнопкой по любому файлу затем выбирают scripts > merge
Готово.
Если у Вас есть какая-либо проблема, сообщить мне.
Я сделал другой сценарий, который имеет 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()
Как использовать его в качестве сценария наутилуса:
вставьте этот код в файл и назовите его merge
.
Теперь, откройте терминал в каталоге, где Вы сохранили файл marge
и помещенный mv merge ~/.local/share/nautilus/scripts/merge
и хит входит.
теперь помещенный cd ~/.local/share/nautilus/scripts/
и хит входит.
теперь помещенный chmod +x merge
и хит входит.
теперь перейдите к корневой папке, где фильмы и подзаголовки и щелкают правой кнопкой по любому файлу, или папка затем выбирают scripts > merge
Готово.