Compare commits
6 Commits
f2b77697da
...
master
Author | SHA1 | Date | |
---|---|---|---|
605d777572
|
|||
f7efbab2aa
|
|||
ddbf16504a
|
|||
d82cb8a0b3
|
|||
b738a3106c
|
|||
c58ef4c305
|
42
README.md
42
README.md
@ -1,2 +1,44 @@
|
|||||||
# vid_convert
|
# vid_convert
|
||||||
|
|
||||||
|
vid_convert est un script qui utilise ffmpeg pour convertir des vidéos au
|
||||||
|
format [H.265](https://fr.wikipedia.org/wiki/H.265/HEVC), avec un audio en
|
||||||
|
[AAC-LC](https://fr.wikipedia.org/wiki/Advanced_Audio_Coding).
|
||||||
|
Cela permet d'optimiser le poids du fichier sans baisse visible de
|
||||||
|
la qualité avec les paramètres prévus dans le script.
|
||||||
|
Le but est de faire un script qui enchaine les différentes opérations, sans
|
||||||
|
laisser à l'utilisateur des choix ou des calculs fastidieux à faire.
|
||||||
|
|
||||||
|
Le format de sortie est un fichier MPEG-4 (.mp4), lisible sur n'importe quel
|
||||||
|
appareil (ou presque) disposant d'une puissance de décodage suffisante.
|
||||||
|
|
||||||
|
Le format d'entrée peut être n'importe lequel connu par ffmpeg. Pour les BluRay,
|
||||||
|
le 4K, HDR et HDR10+ sont gérés. Concernant le son, pas de prise en charge du
|
||||||
|
canal Atmos, mais le multi-canal (5.1, 7.1, ...) est géré. Toutes les pistes
|
||||||
|
(vidéo, audios et sous-titres) sont conservées.
|
||||||
|
|
||||||
|
## Installation et dépendances
|
||||||
|
|
||||||
|
- [Python](https://www.python.org/) (>= 3.5)
|
||||||
|
- [ffmpeg](https://ffmpeg.org/)
|
||||||
|
- [hdr10plus_tool](https://github.com/quietvoid/hdr10plus_tool)
|
||||||
|
|
||||||
|
## Utilisation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./vid_convert.py -h
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Les remarques et contributions sont les bienvenues. Par mail, avec un fichier de
|
||||||
|
patch si vous le souhaitez.
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
Licence [Expat](https://commons.wikimedia.org/wiki/Template:Expat).
|
||||||
|
|
||||||
|
La licence donne à toute personne recevant le logiciel (et ses fichiers) le
|
||||||
|
droit illimité de l'utiliser, le copier, le modifier, le fusionner, le
|
||||||
|
publier, le distribuer, le vendre et le « sous-licencier » (l'incorporer
|
||||||
|
dans une autre licence). La seule obligation est d'incorporer la notice de
|
||||||
|
licence et de copyright dans toutes les copies.
|
||||||
|
32
vid_convert.py
Executable file → Normal file
32
vid_convert.py
Executable file → Normal file
@ -97,7 +97,7 @@ def is_interlaced(file, infos):
|
|||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
pct = 100
|
pct = 100
|
||||||
if pct > 10:
|
if pct > 10:
|
||||||
logging.debug("Vidéo entrelacée à {pct}%")
|
logging.debug(f"Vidéo entrelacée à {pct}%")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logging.debug("Vidéo non entrelacée")
|
logging.debug("Vidéo non entrelacée")
|
||||||
@ -152,7 +152,7 @@ def extract_subs(file, track, lang):
|
|||||||
logging.debug(command)
|
logging.debug(command)
|
||||||
result = subprocess.getoutput(command)
|
result = subprocess.getoutput(command)
|
||||||
if result != "":
|
if result != "":
|
||||||
logging.info(result)
|
logging.error(result)
|
||||||
|
|
||||||
|
|
||||||
def convert_audio(file, track, volume_adj, channels, channel_layout, language, title):
|
def convert_audio(file, track, volume_adj, channels, channel_layout, language, title):
|
||||||
@ -163,10 +163,11 @@ def convert_audio(file, track, volume_adj, channels, channel_layout, language, t
|
|||||||
command = f'ffmpeg -loglevel error -i {file} -map 0:{track} -map_metadata -1 -vn -sn -c:a {codec} -mapping_family 1 -filter:a volume={volume_adj},aformat=channel_layouts={channel_layout} {metadatas} -y {file}_audio_{track}_{language}.mka'
|
command = f'ffmpeg -loglevel error -i {file} -map 0:{track} -map_metadata -1 -vn -sn -c:a {codec} -mapping_family 1 -filter:a volume={volume_adj},aformat=channel_layouts={channel_layout} {metadatas} -y {file}_audio_{track}_{language}.mka'
|
||||||
logging.debug(command)
|
logging.debug(command)
|
||||||
result = subprocess.getoutput(command)
|
result = subprocess.getoutput(command)
|
||||||
logging.info(result)
|
if result != "":
|
||||||
|
logging.error(result)
|
||||||
|
|
||||||
|
|
||||||
def convert_video(file, infos, start, crop, crf, animation):
|
def convert_video(file, infos, start, crop, crf, animation, interlaced, vhs):
|
||||||
str_start = "{:05d}".format(start)
|
str_start = "{:05d}".format(start)
|
||||||
output = f'{file}_video_t{str_start}.mkv'
|
output = f'{file}_video_t{str_start}.mkv'
|
||||||
fmt = "yuv420p10le"
|
fmt = "yuv420p10le"
|
||||||
@ -177,6 +178,10 @@ def convert_video(file, infos, start, crop, crf, animation):
|
|||||||
tune = "-tune animation"
|
tune = "-tune animation"
|
||||||
else:
|
else:
|
||||||
tune = ""
|
tune = ""
|
||||||
|
if interlaced:
|
||||||
|
crop = f"{crop},yadif"
|
||||||
|
if vhs:
|
||||||
|
crop = f"{crop},hqdn3d,unsharp=5:5:0.8:3:3:0.4"
|
||||||
if 'side_data_list' in infos['video'].keys():
|
if 'side_data_list' in infos['video'].keys():
|
||||||
try:
|
try:
|
||||||
light_level = f"{infos['video']['side_data_list'][1]['max_content']},{infos['video']['side_data_list'][1]['max_average']}"
|
light_level = f"{infos['video']['side_data_list'][1]['max_content']},{infos['video']['side_data_list'][1]['max_average']}"
|
||||||
@ -215,7 +220,8 @@ def convert_video(file, infos, start, crop, crf, animation):
|
|||||||
command = f'ffmpeg -loglevel error -i {file} -map 0:{track} -ss {start} -t 300 -an -sn -c:v {codec} {tune} {hdr} -crf {crf} -pix_fmt {fmt} -filter:v {crop} -y {output}'
|
command = f'ffmpeg -loglevel error -i {file} -map 0:{track} -ss {start} -t 300 -an -sn -c:v {codec} {tune} {hdr} -crf {crf} -pix_fmt {fmt} -filter:v {crop} -y {output}'
|
||||||
logging.debug(command)
|
logging.debug(command)
|
||||||
result = subprocess.getoutput(command)
|
result = subprocess.getoutput(command)
|
||||||
logging.info(result)
|
if result != "":
|
||||||
|
logging.error(result)
|
||||||
|
|
||||||
|
|
||||||
def create_mkv(filename):
|
def create_mkv(filename):
|
||||||
@ -270,6 +276,16 @@ def create_mkv(filename):
|
|||||||
remove(file)
|
remove(file)
|
||||||
|
|
||||||
|
|
||||||
|
def mkv_to_mp4(filename):
|
||||||
|
options = "-c:a copy -c:v copy -c:s copy -movflags faststart"
|
||||||
|
command = f"ffmpeg -loglevel error -i {filename}_FINAL.mkv -map 0 {options} -y NEW_{filename}.mp4"
|
||||||
|
result = subprocess.getoutput(command)
|
||||||
|
if result != "":
|
||||||
|
logging.error(result)
|
||||||
|
else:
|
||||||
|
remove(f"{filename}_FINAL.mkv")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import argparse
|
import argparse
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@ -278,6 +294,7 @@ if __name__ == '__main__':
|
|||||||
parser.add_argument("-s", "--stabilise", dest="stab", action="store_true")
|
parser.add_argument("-s", "--stabilise", dest="stab", action="store_true")
|
||||||
parser.add_argument("-t", "--starttime", dest="starttime")
|
parser.add_argument("-t", "--starttime", dest="starttime")
|
||||||
parser.add_argument("-a", "--animation", dest="animation", action="store_true")
|
parser.add_argument("-a", "--animation", dest="animation", action="store_true")
|
||||||
|
parser.add_argument("-c", "--vhs", dest="vhs", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if args.debug:
|
if args.debug:
|
||||||
logging.basicConfig(format='[%(asctime)s]\n%(message)s', level=logging.DEBUG, datefmt='%d/%m/%Y %H:%M:%S')
|
logging.basicConfig(format='[%(asctime)s]\n%(message)s', level=logging.DEBUG, datefmt='%d/%m/%Y %H:%M:%S')
|
||||||
@ -285,7 +302,7 @@ if __name__ == '__main__':
|
|||||||
logging.basicConfig(format='[%(asctime)s]\n%(message)s', level=logging.INFO, datefmt='%d/%m/%Y %H:%M:%S')
|
logging.basicConfig(format='[%(asctime)s]\n%(message)s', level=logging.INFO, datefmt='%d/%m/%Y %H:%M:%S')
|
||||||
file = args.f_input
|
file = args.f_input
|
||||||
infos = get_infos(file)
|
infos = get_infos(file)
|
||||||
# interlaced = is_interlaced(file, infos)
|
interlaced = is_interlaced(file, infos)
|
||||||
cropsize = cropping(file, infos)
|
cropsize = cropping(file, infos)
|
||||||
volumes = volume_audio(file, infos)
|
volumes = volume_audio(file, infos)
|
||||||
if args.stab:
|
if args.stab:
|
||||||
@ -305,6 +322,7 @@ if __name__ == '__main__':
|
|||||||
vid_part_time = 0
|
vid_part_time = 0
|
||||||
while vid_part_time < infos['duration']:
|
while vid_part_time < infos['duration']:
|
||||||
crf = 19
|
crf = 19
|
||||||
convert_video(file, infos, vid_part_time, cropsize, crf, animation)
|
convert_video(file, infos, vid_part_time, cropsize, crf, animation, interlaced, args.vhs)
|
||||||
vid_part_time += 300
|
vid_part_time += 300
|
||||||
create_mkv(file)
|
create_mkv(file)
|
||||||
|
mkv_to_mp4(file)
|
||||||
|
Reference in New Issue
Block a user