Last active
April 13, 2017 20:45
-
-
Save abakum/827c89f1d9d5982c02bdff8a9b241678 to your computer and use it in GitHub Desktop.
How to store, backup, cut (GOP-accurate with no quality loss) and tagging HD videos, convert it to MP3 (for music clips)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# coding=cp1251 | |
# | |
#pip install plumbum eyed3 | |
from __future__ import unicode_literals | |
from __future__ import print_function | |
#from __future__ import division | |
import platform | |
win=platform.DEV_NULL=="nul" | |
import codecs | |
import locale | |
import os | |
import subprocess | |
import sys | |
reload(sys) | |
coding="cp1251"#для win, ini и xml файлов | |
utf8="utf-8" #для linux, субтитров, плэйлистов и программ пишуших в консоль в UTF-8 | |
sde=(coding if win else utf8).lower() | |
print('sys.setdefaultencoding("%s")'%sde) | |
sys.setdefaultencoding(sde) | |
gpe=locale.getpreferredencoding(do_setlocale=True).lower() #eyed3 использует этот кодек для чтения USLT | |
cgw=locale.getdefaultlocale()[1].lower() #под mingw с LANG=ru_RU.UTF-8 выдаёт cp1251 | |
print("locale.getdefaultlocale()[1]", cgw) | |
if "LANG" in os.environ: #mingw или mintty -o Locale=ru_RU -o Charset=UTF-8 или mintty -o Locale=ru_RU -o Charset=CP1251 | |
cgw=os.environ["LANG"].split(".")[1].lower() | |
print('os.environ["LANG"].split(".")[1]', cgw) | |
if sys.stdout.isatty(): | |
soe=sys.stdout.encoding.lower() | |
print("sys.stdout.encoding", soe) | |
if soe=="cp866": #win или winpty под mingw | |
if win: subprocess.call(("chcp", sde[2:]), shell=1) | |
cgw=sde | |
sde="" | |
if cgw!=sde: | |
print('sys.stdout=codecs.getwriter("%s")(sys.stdout)'%cgw) | |
sys.stdout=codecs.getwriter(cgw)(sys.stdout) | |
#sys.tracebacklimit=0 | |
print(platform.uname()) | |
s=platform.python_compiler() | |
print(platform._sys_version_cache.keys()[0], sys.executable) | |
from plumbum import local as pl | |
from plumbum import path | |
import shlex | |
from datetime import datetime, timedelta | |
import json | |
from zlib import adler32 | |
from glob import glob | |
if win: from ctypes import windll, create_unicode_buffer | |
from PIL import Image, ImageDraw, ImageFont, ExifTags | |
#import pdb | |
#pdb.set_trace() | |
class g: | |
"выполняется один раз" | |
def __init__(self): | |
g.debug=0 | |
#%S будет заменён на 32 или 64 %s на "" или 64 %i на "86" или 64 | |
g.Yandex="y:" | |
g.mnt="/mnt/" | |
g.YandexWSL=g.mnt+"c/YandexDisk" | |
g.ls={x:g.Yandex+"\\"+y for x, y in dict(srt="Subs", txt="Lyrics", ini="Config").items()} | |
g.Google="z:" | |
g.GoogleWSL=g.mnt+"d/GoogleDrive" | |
g.ep=[] | |
g.ep.append("/usr/local/bin") | |
g.unx=len(g.ep) | |
g.ep.append(r"c:\msys%S\mingw%S\bin") | |
#g.ep.append("c:\\cygwin%s\\usr\\bin") # Лажа в r"\usr\" | |
g.ep.append(g.Yandex+r"\PortableApps\mkvToolNix%s") | |
g.ep.append(g.Yandex+r"\PortableApps\ffMpeg%s\bin") | |
g.ep.append(g.Yandex+r"\PortableApps\tsMuxeR") | |
g.ep.append(g.Yandex+r"\PortableApps\EXIFtool") | |
g.ep.append(g.Yandex+r"\PortableApps\ffms2\x%i") | |
g.ep.append(r"c:\Program Files\Solveig Multimedia\SolveigMM Video Splitter Business Edition") | |
g.ep.append(r"c:\Program Files (x86)\Solveig Multimedia\SolveigMM Video Splitter Business Edition") | |
g.ep.append(g.Yandex+r"\PortableApps\FRIM\x%i") | |
g.ep.append(g.Yandex+r"\PortableApps\MakeMKVPortable\App\MakeMKV") | |
g.lang="rus" | |
g.TPUB="Константин Абакумов" | |
g.scaleYTB='-vf scale="trunc(a*144/2)*2:144"' | |
g.drawtext='''-vf "scale=178:100,drawtext=text=%s:font=consolas:fontcolor=white:shadowx=4:shadowy=4:x=(w-tw)/2:y=h-th-4:fontsize=66,drawtext=text='%s':font=tahoma:y=h-th-1:fontsize=20:box=1:[email protected]"''' | |
g.mp3opt="-c:a libmp3lame -ac 2 -q:a 0" | |
g.sup='font-name="Impact", font-size=%s, font-color=0xffffffff, bottom-offset=%s, font-border=%s, fadein-time=0.25, fadeout-time=1, text-align=center, video-width=%s, video-height=%s, fps=%s,' | |
g.wav="-c:a pcm_s16le -ac %s -ar %s" | |
g.ac3="-c:a ac3 -ac %s -ar %s -b:a 256000" | |
g.ok=":)" | |
g.ob=";(" | |
g.err="" | |
g.rn="\r\n" | |
g.n="\n" | |
g.srt=5 | |
g.srtF=1 | |
g.srtD=3 | |
g.ion="descript.ion" | |
g.fromBegin="00:00:00.000" | |
g.bd=datetime(1963, 9, 27) | |
g.af=' -af "%s"' | |
g.volume='volume=%sdB' | |
g.ec="EmbeddedCover" | |
g.nod=not g.debug | |
g.bit=pl.path(pl.env.expand(r"%WINDIR%\SysWOW64")).is_dir() | |
for i, x in enumerate(g.ep): g.ep[i]=x.replace("%S", "64" if g.bit else "32").replace("%s", "64" if g.bit else "").replace("%i", "64" if g.bit else "86") | |
g.encoding=None | |
if not win: #wsl или linux | |
g.encoding=coding | |
for x in g.ls: g.ls[x]=wsl(g.ls[x]) | |
g.Yandex=g.YandexWSL | |
g.Google=g.GoogleWSL | |
print("g.encoding", g.encoding) | |
#print(sys.platform) | |
g.id3v23=("AENC","APIC","COMM","COMR","ENCR","EQUA","ETCO","GEOB","GRID","IPLS","LINK","MCDI","MLLT","OWNE","PRIV","PCNT","POPM","POSS","RBUF","RVAD","RVRB","SYLT","SYTC","TALB","TBPM","TCOM","TCON","TCOP","TDAT","TDLY","TENC","TEXT","TFLT","TIME","TIT1","TIT2","TIT3","TKEY","TLAN","TLEN","TMED","TOAL","TOFN","TOLY","TOPE","TORY","TOWN","TPE1","TPE2","TPE3","TPE4","TPOS","TPUB","TRCK","TRDA","TRSN","TRSO","TSIZ","TSRC","TSSE","TYER","TXXX","UFID","USER","USLT","WCOM","WCOP","WOAF","WOAR","WOAS","WORS","WPAY","WPUB","WXXX") | |
g.audio=("norm", "boost", "audio", "af", "filter_complex", "afout", "amix", "afin") | |
g.vprobe=("codec_name", "profile", "level", "width", "height", "pix_fmt", "r_frame_rate") | |
g.aprobe=("sample_rate", "bit_rate", "channels") | |
g.film=("film", "ts", "keep", "mkv", "last", "first") | |
g.jpg=("x", "y", "xc", "yc", "w", "o", "h") | |
g.etc=("nomp3", "sw", "ss", "3d", "kodi", "next")+g.film+g.jpg+g.vprobe+g.aprobe | |
try: import eyed3 #ffmpeg добавляет ID2v2.3 тэги USLT и COMM как TXXX. Буду использовать eyeD3 | |
except: | |
print("%s Нет %s из %s" % ( | |
g.ob, "eyeD3", "http://eyeD3.nicfit.net/")) | |
g.eyeD3="" | |
g.metadata="-id3v2_version 3" | |
else: | |
g.eyeD3=eyed3.__path__[0] | |
print("%s%s=%s" % ( | |
g.ok, "g.eyeD3", g.eyeD3)) | |
g.metadata="-map_metadata -1" | |
g.ext=("264", "mvc", "mpv", "ac3", "wav", "aac", "dts", "srt", "sup") | |
g.SI=("V_MPEG4/ISO/AVC", "V_MPEG4/ISO/MVC", "V_MPEG-2", "A_AC3", "A_LPCM", "A_AAC", "A_DTS", "S_TEXT/UTF8", "S_HDMV/PGS") | |
g.ext2SI=dict(zip(g.ext, g.SI)) | |
g.SI2ext=dict(zip(g.SI, g.ext)) | |
g.fps="24/1" | |
g.w="1920" | |
g.h="1080" | |
g.level="4.1" | |
g.profile="high" | |
g.vcodec="libx264" | |
g.makeMkvIso=1 | |
g.cfg={} | |
g.p={} | |
from ConfigParser import RawConfigParser | |
for x in ("cut", "uslt"): #конфиг свойств медиафайлов и стихов | |
g.cfg[x]=RawConfigParser() | |
g.p[x]=pl.path(g.ls["ini"]) / (x+".ini") | |
cRead(x) | |
if g.debug: | |
for x in dir(self): print("g.%s=%s"%(x,eval("self."+x))) | |
class m: | |
"выполняется с каждым медиафайлом s из списка параметров" | |
def __init__(self, s, next): | |
m.lp = pl.path(wsl(s)) #d:\av\2015\20150517 Отчетный\00005.MTS или /mnt/d/av/2015/20150517 Отчетный/00005.MTS | |
m.next=pl.path(wsl(next)) | |
print('cut("%s, %s")'%(m.lp, m.next)) | |
if not m.lp.is_file(): return | |
m.copyD=0 #копировать дескрипшен | |
m.copyC=1 #копировать куешит | |
m.doIt=0 #обновлять | |
m.found=0 #файл есть в дескрипшен | |
m.newD=0 #дескрипшен изменился | |
m.index=0 #de в c это m.deL[m.index] | |
m.deL=[] #новый список справочников куе [{}, {}] | |
m.csL=[] #старый список справочников куе | |
m.dur=0 #длительность медиафайла | |
if m.lp.dirname.lower()!=pl.cwd.lower(): | |
m.copyD=1 | |
try: pl.cwd.chdir(m.lp.dirname) #d:\av\2015\20150517 Отчетный | |
except: | |
print('%s pl.cwd.chdir("%s")'%( | |
g.ob, m.lp.dirname)) | |
return 1 | |
m.parent=pl.cwd.name #20150517 Отчетный | |
m.ptags=s2d(cGet(m.parent, "tags")) | |
da=s2d(cGet(m.parent, "aopt"), " ") #{"-c:a": "ac3", "-ac": "2", "-ar": "4800", "-b:a": "256000"} | |
dv=s2d(cGet(m.parent, "vopt"), " ") #{"-c:v": "libx264", "-profile:v": "high", "-level": "42" "-pix_fmt" "yuv420p"} | |
m.dpp=wsl(pl.cwd.dirname+"\\") #d:\av\2015\ | |
m.pp=m.dpp[2 if win else len(g.mnt)+1:]#\av\2015\ | |
m.name=m.lp.stem #00005 | |
m.ne=m.lp.name #00005.MTS.cut | |
#if m.lp.lower().endswith(".cut"): m.ne=m.lp.with_suffix("", depth=1).name #00005.MTS | |
m.pne=(m.parent+"\\"+m.ne) #секция для конфига | |
m.P1=wsl(m.pp+m.parent+"\\") #\av\2015\20150517 Отчетный\ | |
cueSheet=m.ne+".cut" #d:\av\2015\20150517 Отчетный\00005.MTS.cut | |
m.de=pl.path(g.Yandex+m.P1) / g.ion | |
m.src={x: m.de.up().up().up()/("-."+x) for x in ("png", "srt")} | |
m.all=m.src["png"].up() / "all" #шаблон заставки для всех аудиофайлов | |
m.alb=pl.cwd / "alb" #шаблон заставки для аудиофайлов этого альбома | |
m.lde=pl.cwd / g.ion | |
m.lne=pl.cwd / m.ne | |
m.cs=pl.path(g.Yandex+m.P1) / cueSheet | |
m.lcs=pl.cwd / cueSheet | |
m.neSnd=pl.path(m.ne+".mp3") | |
m.neVid=pl.path(m.ne+".mkv") | |
m.neAdd=pl.path(m.name+"_.ts") | |
#m.neAdd=pl.path(m.name+"_"+m.lne.suffix+".mkv") | |
#m.neAdd=pl.path(m.name+"_"+m.lne.suffix) | |
#m.exifTool={} | |
m.exif={} | |
m.iw, m.ih=(0, 0) | |
m.mm=m.dto="" | |
m.mpo=0 | |
try: im=Image.open(m.lne) | |
except: pass | |
else: | |
m.iw, m.ih=im.size | |
try: im.seek(1) | |
except: pass | |
else: m.mpo=1 | |
if hasattr(im, "_getexif"): | |
exif=im._getexif() | |
if exif: | |
m.exif=dict(exif.items()) | |
for x in ("Make", "Model", "DateTimeOriginal"): #определю mm | |
xKey=ExifTags.TAGS.keys()[ExifTags.TAGS.values().index(x)] | |
if xKey in m.exif: | |
if x.startswith("M"): m.mm+=" "+m.exif[xKey] | |
else: m.dto=m.exif[xKey] | |
m.mm=m.mm.strip().replace(" ", "_").replace("#", "№") | |
im.close() | |
print(m.lne, m.iw, m.ih, m.mm, m.dto) | |
if m.iw: pass | |
else: | |
if not cGet(m.pne, "d"): | |
if 1: | |
if m.lde.is_file(): | |
for line in m.lde.read(g.encoding).splitlines(): | |
if descr(line, 1): break | |
else: | |
if not m.lcs.is_file(): | |
print('%s Нет "%s" в "%s"'%(g.ob, m.ne, m.lde)) | |
return | |
else: | |
print('%s Нет "%s"'%(g.ob, m.lde)) | |
if not m.lcs.is_file(): | |
print('%s Нет "%s"'%(g.ob, m.lcs)) | |
return | |
#m.meD=json.loads(log(g.ffprobeU,'-v error -show_entries streams -of json "%s"', m.lne)) | |
m.meD=json.loads(log(g.ffprobeU,'-v error -show_streams -of json "%s"', m.lne).split("%s}"%g.rn)[0]+"%s}"%g.rn) #отрезаю Extra data | |
for x in m.meD["streams"]: | |
if "duration" in x: | |
cSet(m.pne, "d", x["duration"].rstrip("0")) | |
break | |
if "tags" in x: | |
if "DURATION" in x["tags"]: | |
cSet(m.pne, "d", sSec(x["tags"]["DURATION"])) | |
break | |
else: | |
cSet(m.pne, "nd", 1) | |
for x in m.meD["streams"]: | |
if x["codec_type"]=="video": | |
for y in g.vprobe: m.ptags[y]=m.ptags.get(y, "") or x.get(y, "") | |
if x["disposition"]["attached_pic"]: | |
cSet(m.pne, "att", x["index"]) | |
cSet(m.pne, "pic", x["codec_name"]) | |
break | |
for x in m.meD["streams"]: | |
if x["codec_type"]=="video": | |
if not x["disposition"]["attached_pic"]: | |
cSet(m.pne, "v", x["index"]) | |
break | |
for x in m.meD["streams"]: | |
if x["codec_type"]=="audio": | |
for y in g.aprobe: m.ptags[y]=m.ptags.get(y, "") or x.get(y, "") | |
cSet(m.pne, "a", x["codec_name"]) | |
da["-ac"]=da.get("-ac", m.ptags.get("channels", "")) | |
if x["codec_long_name"].startswith("PCM"): | |
da["-c:a"]=da.get("-c:a", "flac") | |
da.pop("-ar", "") | |
da.pop("-b:a", "") | |
da.pop("-q:a", "") | |
else: | |
if m.ptags.get("sample_rate", "") and m.ptags.get("bit_rate", ""): | |
da["-c:a"]=da.get("-c:a", cod2lib(x["codec_name"])) | |
da["-ar"]=da.get("-ar", m.ptags.get("sample_rate", "")) | |
da["-b:a"]=da.get("-b:a", m.ptags.get("bit_rate", "")) | |
da.pop("-q:a", "") | |
else: da["-c:a"]=da.get("-c:a", cod2lib("mp3")) | |
if da.get("-c:a","")=="libmp3lame": | |
da["-ac"]=da.get("-ac", 2) | |
da["-q:a"]=da.get("-q:a", 0) | |
da.pop("-ar", "") | |
da.pop("-b:a", "") | |
cDel(m.pne, "sr") | |
cDel(m.pne, "ch") | |
cDel(m.pne, "br") | |
cDel(m.pne, "aopt") | |
break | |
for x in g.vprobe+g.aprobe: | |
if not m.ptags.get(x, ""): del m.ptags[x] | |
if "profile" in m.ptags: | |
m.ptags["profile"]=m.ptags["profile"].lower() | |
m.ptags["profile"]=m.ptags["profile"].replace(" profile","") | |
cSet(m.parent, "tags", d2s(m.ptags)) | |
da.pop("", "") | |
cSet(m.parent, "aopt", "-c:a %s %s"%(da.pop("-c:a"), d2s(da, " "))) | |
h264=m.ptags.get("codec_name", "")=="h264" | |
dv["-c:v"]=dv.get("-c:v", cod2lib(m.ptags.get("codec_name", "")) if h264 else "libx264") | |
dv["-profile:v"]=dv.get("-profile:v", m.ptags.get("profile", "") if h264 else g.profile) | |
dv["-level"]=dv.get("-level", level() if h264 else g.level) | |
dv["-pix_fmt"]=dv.get("-pix_fmt", m.ptags.get("pix_fmt", "") if h264 else "yuv420p") | |
dv.pop("", "") | |
cSet(m.parent, "vopt", "-c:v %s %s"%(dv.pop("-c:v"), d2s(dv, " "))) | |
cWrite() | |
cRead() | |
if cGet(m.pne, "att"): | |
img=m.alb.with_suffix("."+cGet(m.pne, "pic")) #обложка альбома как заставка | |
if not img.is_file(): | |
log(g.ffmpegU, | |
'-v error -i "%s" -c:v:%s copy -frames:v 1 "%s"', | |
( m.lne, cGet(m.pne, "att"), img)) #извлекаю обложку из медиафайла | |
if cGet(m.pne, "nd"): | |
if not m.neSnd.is_file(): | |
mp3opt=g.mp3opt #+gain(m.lne, de["ft"], 1) | |
log(g.ffmpegU, | |
'-v error -i "%s" %s "%s"', ( | |
m.lne, mp3opt, m.neSnd)) | |
cSet(m.pne, "a", "mp3") | |
if not cGet(m.pne, "d"): | |
cSet(m.pne, "d", log(g.ffprobeU, '-v error -show_entries format=duration -of csv=p=0 "%s"', m.neSnd)) | |
cWrite() | |
fromCloud() | |
m.sw="" | |
for x in cOptions(m.parent): #групповые параметры 000 = film -21#ts 2 | |
if x=="tags": continue | |
if x==m.mm.lower() or m.name.lower().startswith(x): #совпадает имя камеры или начало имени файла | |
m.sw=cGet(m.parent, x) | |
print("m.sw", m.sw) | |
break | |
cs="" | |
m.ndur=not m.lcs.is_file() #пересоздать все файлы | |
m.vext="264" if "264" in cGet(m.parent, "vopt") else "mpv" | |
m.aext="ac3" if "ac3" in cGet(m.parent, "aopt") else "wav" | |
try: cs=m.lcs.read(g.encoding).rstrip(g.rn) | |
except: pass | |
print("cs:\n"+cs) | |
for line in cs.splitlines(): | |
m.csL.append({}) | |
if parse(len(m.csL)-1, line.replace("'", ";"), m.csL[-1], 1): del(m.csL[-1]) | |
m.doIt=0 | |
if m.lde.is_file(): | |
toClouD() | |
for line in m.lde.read(g.encoding).splitlines(): | |
if m.found: break | |
descr(line) | |
m.doIt=m.found | |
if not m.found: toClouC() | |
if m.doIt: | |
dtags=s2d(cGet(m.pne, "tags")) #справочник файла tags = boost -8#film -21#ts 0#sw 000 | |
sw=dtags.pop("sw", "") | |
if sw: cSet(m.parent, sw, d2s(dtags)) #справочник альбома 000 = boost -8#film -21#ts 0 | |
for x in range(len(m.deL)): #x это отрезаемая часть | |
m.index=x | |
try: csLx=m.csL[x] | |
except: | |
m.csL.append({}) | |
parse(len(m.csL)-1, "0 1 2 3", m.csL[-1]) | |
c(m.deL[x], m.csL[x]) | |
for x in ("cut", "uslt"): cWrite(x) | |
if g.debug: | |
for x in dir(self): print("m.%s=%s"%(x,eval("self."+x))) | |
class c: | |
"выполняется с каждой частью резки медиафайла из списка параметров" | |
def __init__(self, de, cs): | |
c.de=de | |
c.cs=cs | |
g.prev="" | |
c.film=de.get("film", "") | |
c.apic=stripp(de.get("APIC", "").replace(",", ".")) | |
c.fapic=pl.path(c.apic) | |
c.oapic=cs.get("APIC", "") | |
c.dur=sSec(de["to"])-sSec(de["from"])+(m.dur if "next" in de else 0) | |
c.nt=de.get("title", "-")=="-" | |
if c.nt: de["title"]=m.name | |
c.doCut=0 #не режу | |
c.newTags=0 #не тэггирую | |
if m.newD: #дескрипшен изменён | |
deT=dict(de) | |
for k in de.keys(): #удаляем параметры #e... | |
if k.islower(): del deT[k] | |
csT=dict(cs) | |
for k in cs.keys(): | |
if k.islower(): del csT[k] | |
c.newTags=deT!=csT #тэги изменёны | |
c.prvol="" | |
c.rg=("gain", "peak") | |
c.n=[] #старый шаблон | |
c.nni=[] #новый шаблон | |
c.ni=[] | |
c.pni=[] | |
for x in (de, cs): #шаблоны имён файлов | |
c.n.append(" ".join((m.name, str(x["index"]+1), x["title"]))) | |
c.nni.append(" ".join((m.name, x["TRCK"], x["title"]))) | |
c.ni.append(" ".join((x["TRCK"], x["title"]))) | |
c.pni.append(" ".join((m.par, x["TRCK"], x["title"]))) | |
c.pn=m.par+" "+c.ni[1] #секция для конфига | |
c.ts=int("-1" if c.de.get("next", "") else de.get("ts", "0")) | |
c.a={} | |
c.a={y:de[y] for y in g.audio if y in de} | |
c.afd={} | |
if "af" in c.a: c.afd["af"]=c.a["af"] | |
if "afin" in c.a: c.afd["af"]="afade=d=%s:curve=exp"%c.a["afin"] #ipar | |
if "afout" in c.a: c.afd["afout"]="afade=t=out:st=%s:d=%s:curve=exp"%(c.dur-float(c.a["afout"]), c.a["afout"]) | |
if "amix" in c.a: c.a["filter_complex"]="amix=duration=first:dropout_transition=%s"%c.a["amix"] | |
#переименовываем если изменился title | |
c.txt=ren(pl.path(g.ls["txt"]), cs["title"], de["title"], g.lang) #это стихи | |
c.p={} | |
c.lne=pl.path(m.lne+".jpeg") | |
if "last" in de: | |
slast="" | |
if de["last"]=="*": | |
if next: slast=m.next.with_suffix("") | |
else: de.pop("last") | |
else: slast=stripp(de["last"]) | |
if slast: | |
for x in ("jpeg", "3D.jpeg"): c.p[x]=slast.with_suffix("."+x) | |
c.tmp=("TS", "264", "ac3", "wav", "mpv", "yuv", "3D.yuv", "iso") | |
etc=("mka", "sup.ts", "3d.ts")+c.tmp | |
for x in ("xml", "JPG", "png", "tag", "meta", "xtl", "MKV", "MKA")+etc: #d:\AV\YYYY\YYYYMMDD album\in n title | |
c.p[x]=ren(pl.cwd, c.n[1], c.nni[1], x, "fake" in de) #со старого шаблона в новый | |
c.p[x]=ren(pl.cwd, c.nni[1], c.nni[0], x, "fake" in de) #удаляю если фэйк | |
#переименовываем если изменился title или TRCK | |
for x in ("mp4", "mKv"): #результат на GD g:\AV\YYYY\YYYYMMDD album trck title | |
c.p[x]=ren(pl.path(g.Google+m.pp), c.pni[1], c.pni[0], x) #берегу даже если фэйк | |
if c.film: | |
d=pl.cwd/"film" | |
d.mkdir() | |
for x in ("ts", "mkv", "srt", "txt")+etc: #film d:\AV\YYYY\YYYYMMDD album\film\YYYYMMDD album trck title | |
c.p[x]=ren(d, c.pni[1], c.pni[0], x, "fake" in de) #удаляю если фэйк | |
#d=pl.path("film")/m.label/"Scenes"/"scn" | |
d=d/"scn" | |
d.mkdir() | |
c.p["scn"]=ren(d, c.ni[1], c.ni[0], "png", "fake" in de) | |
else: | |
for x in ("mkv", "mp3"): #результат d:\AV\YYYY\YYYYMMDD album trck title | |
c.p[x]=ren(pl.cwd.up(), c.pni[1], c.pni[0], x, "fake" in de) #удаляю если фэйк | |
for x in ("srt", "txt"): # y:\Subs\YYYYMMDD album trck title.srt y:\Lyrics\YYYYMMDD album trck title.txt | |
c.p[x]=ren(pl.path(g.ls[x]), c.pni[1], c.pni[0], x) | |
d=pl.cwd/"jpg" | |
d.mkdir() | |
c.p["jpg"]=ren(d, c.ni[1], c.ni[0], "jpg", "fake" in de) #новый шаблон для APIC-кадра для вложения в mp3 или mkv | |
if c.p["JPG"].is_file() and not c.p["jpg"].is_file(): c.p["JPG"].rename(c.p["jpg"]) #копирую из каталога альбома | |
if de.get("jpg", ""): #сохраняю кадр из видео в jpg | |
if de["jpg"]=="-": jpeg(m.lne, "jpg") | |
else: jpg(m.lne) | |
return | |
(pl.cwd / (c.pni[0]+".srt")).delete() # удаляю старые srt | |
for x in ("%s %s.xml", "%s#%s.xml", "%s %s#.xml"): #удаляю старые тэги для mkv | |
(pl.cwd / (x%(m.name, de["title"]))).delete() | |
dice={x:de[x] for x in ("TIT2", "TCOM", "TEXT") if x in de} #если в тэгах есть названия песни, композитор, автор текста, запоминаем в конфиг | |
if dice: | |
cSet(de["title"], "tags", d2s(dice), "uslt") | |
cSet(de["title"], hash(m.pne), m.pne, "uslt") | |
for i in range(1, 4): | |
x="TPE%s"%i | |
if x in de: dice[x]=de[x] | |
for k, v in dice.items(): | |
for y in v.split("/"): cSet(k+" "+y, hash(de["title"]), de["title"], "uslt") #статистика | |
#TPE1=de.get("TPE1", "") | |
#if TPE1: cSet(hash(TPE1), "TPE1", TPE1, "uslt") | |
(lambda x: x and cSet(hash(x), "TPE1", x, "uslt"))(de.get("TPE1", "")) | |
for k, v in ( | |
("TALB", m.par), | |
("TYER", m.par[0:4]), | |
("TDAT", m.par[6:8]+m.par[4:6]), | |
("TPUB", g.TPUB), | |
("TIT2", de["title"]), #если нет названия песни то пишем начальные слова | |
("TIT3", de["title"])): | |
#if not k in de: de[k]=v | |
de[k]=de.get(k, v) | |
#kw & comm | |
c.KEYWORDS=[] | |
for x in de.keys(): | |
if x not in ("APIC", "TDAT", "TRCK"): #все тэги кроме этих | |
if not x.islower(): # не учитывать параметры | |
for y in de[x].split("/"): | |
if y not in c.KEYWORDS: c.KEYWORDS+=[y] #пишем в KEYWORDS | |
#if "COMM" in de: c.COMMENT=de["COMM"] | |
#else: c.COMMENT="" #пишем коммент для mkv из комента для mp3 | |
c.COMMENT=de.get("COMM", "") | |
de["COMM"]=", ".join(c.KEYWORDS) #в коммент для mp3 пишем kw | |
if de["TIT2"]==de["TIT3"]: de["TIT3"]=c.pni[0] #если не было названия песни то пишем альбом и начальные слова | |
stem=c.p["mkv"].stem | |
if "fake" in de: | |
cDelS(stem) | |
cDel(m.parent, hash(stem)) | |
return #закомментировано куе | |
tpe1=de.get("TPE1", "") | |
if tpe1: cSet(stem, "TPE1", hash(tpe1)) | |
#if (not c.nt) if c.film else (not tpe1 and not cHas(stem)): cSet(m.parent, hash(stem), "%s %s"%(m.name, int(de.get("index", ""))+1)) | |
if (de["from"], de["to"])!=(cs["from"], cs["to"]): c.doCut=1 | |
tg(de, c.nt, c.film, c.COMMENT, c.KEYWORDS, c.p["tag"]) | |
srt(c.ni[1], c.ni[0], de) | |
xml(de) | |
if c.film: m.aext="wav" | |
kodi() | |
if m.iw: | |
pic(de) | |
return | |
jpg(m.lne) | |
if "ss" in de: | |
if c.p["jpg"].is_file(): pic(de, c.p["jpg"]) | |
return #slideShow | |
vid(c.apic) | |
mkv(de) | |
g.prev=m.lne | |
if not c.prvol: | |
if cGet(c.pn, "max"): c.prvol+=" max=%s mean=%s"%(cGet(c.pn, "max"), cGet(c.pn, "mean")) | |
if cGet(c.pn, "volume"): c.prvol+=" volume=%s"%cGet(c.pn, "volume") | |
if cGet(c.pn, "peak"): c.prvol+=" peak=%s gain=%s"%(cGet(c.pn, "peak"), cGet(c.pn, "gain")) | |
if c.prvol: print(c.pn+c.prvol+" dur=%s"%c.dur) | |
#for k, v in de.items(): print("de[%s]=%s"%(k, v)) | |
#for k, v in cs.items(): print("cs[%s]=%s"%(k, v)) | |
if g.debug: | |
for x in dir(self): print("c.%s=%s"%(x,eval("self."+x))) | |
class f: | |
"Действия с фильмом" | |
def __init__(self, fmkv): | |
f.mkv=fmkv | |
f.cwd=fmkv.up() | |
pl.cwd.chdir(f.cwd) | |
f.name=f.cwd.up().name | |
f.ptags=s2d(cGet(fmkv.stem, "tags")) | |
print(fmkv.stem, f.ptags) | |
f.av=pl.path(f.cwd._path.replace(f.cwd.drive, g.Yandex)).up().up().up() | |
f.p={x: fmkv.with_suffix("."+x) for x in ("xml", "cha", "tag", "bdmd")} | |
f.p["srt"]=pl.path(g.ls["srt"])/(f.name+".srt") | |
f.p["mkv"]=f.cwd.up().with_suffix(".mkv") | |
f.p["m2ts"]=pl.path("BD")/"BDMV"/"STREAM"/"00001.m2ts" | |
f.ext=fmkv.suffix[1:] | |
f.tc={x: pl.path(x+"_track00.tc.txt") for x in ("m2ts", f.ext)} | |
f.kf={x: pl.path(x+"_track00.kf.txt") for x in ("m2ts", f.ext)} | |
f.BDedit=pl.path("chapter_00001_time.txt") | |
for x in ("tsMuxeR", "ac3", "cc", "ts"): f.p[x]=pl.path(x+".bat") | |
pngS=f.cwd/"scn" | |
f.iCN=[] | |
f.sup=[] | |
f.chap=[] | |
f.meta=tsmux('"%s"'%fmkv) | |
f.mvc=1 if f.meta.get("subTrack:", "") else 0 | |
if "PGS" in f.meta["Stream type:"]: f.sup+=[1] | |
if "SRT" in f.meta["Stream type:"]: f.iCN+=[f.meta["Stream type:"].index("SRT")-f.mvc] | |
#print(f.meta) | |
if 0: | |
for is_st in log(g.ffprobeU,'-v error -show_entries stream=index,codec_name -of csv=p=0 -select_streams s "%s"', fmkv).splitlines(): | |
if "subrip" in is_st: f.iCN=is_st.split(",") | |
elif "hdmv_pgs_subtitle" in is_st: f.sup=is_st.split(",") | |
tg({"TPUB":g.TPUB, "TALB":f.name, "TYER":f.name[:4], "TDAT":f.name[6:8]+f.name[4:6]}, 0, 1, "", (), f.p["tag"], 0) | |
if f.iCN: rSRTwCH() #читаю субтитры и пишу чаптеры | |
else: rCHwSRT() #читаю чаптеры и пишу субтитры | |
if f.mvc and not g.makeMkvIso: f.meta["Track ID:"]=("4113", "4114", "4352", "4608") | |
else: rw(f.cwd/"lof.bat", 'set lof="%s"'%f.mkv, g.encoding) | |
fp() | |
#for i, x in enumerate(("264", "snd", "sup")): f.p[x]=pl.path(f.name+".track_%s.%s"%(f.meta["Track ID:"][i], g.SI2ext[f.meta["Stream ID:"][i]])) | |
f.cha=f.chap if len(f.chap)>len(f.meta["Chapters:"]) else f.meta["Chapters:"] | |
if len(f.cha)<1 or f.cha[0]!=g.fromBegin: f.cha.insert(0, g.fromBegin) | |
f.ac3dts=f.p["snd"].suffix in (".ac3", ".dts") | |
#if not (f.mvc and not g.makeMkvIso): rw(f.cwd/"lof.bat", 'set lof="%s"'%f.mkv, g.encoding) | |
if date(f.mkv, f.p["tsMuxeR"]): | |
tsmuxer(f.meta, f.cwd) #пишу скрипт для BD без упаковки звука | |
rw(f.p["cc"], "set cc=--custom-chapters=%s"%";".join(f.cha)) #пишу чаптеры для скрипта | |
if date(f.mkv, f.p["264"]): | |
log(f.p["tsMuxeR"], "%s", "7 6 :EOF") #demux | |
if f.mvc and not g.makeMkvIso: fp(1) | |
opt="" | |
fac3=f.p["snd"].with_suffix(".ac3") | |
aopt=cGet(fmkv.stem, "aopt") | |
for x in ("flac", "aac", "mp3"): | |
f.x=x in aopt | |
if f.x: | |
fx=f.p["snd"].with_suffix("."+x) | |
break | |
if f.x and date(f.p["snd"], fx): log(g.ffmpegU,'-v error -y -i "%s" %s "%s"',( | |
f.p["snd"], aopt, fx)) | |
if f.ac3dts or f.x: | |
if date(f.mkv, f.p["m2ts"]): log(f.p["tsMuxeR"], "%s", "4 :EOF") #делаю BD | |
else: | |
f.meta["Stream ID:"][1+f.mvc]=g.ext2SI["ac3"] | |
#print(f.p["snd"], fac3, date(f.p["snd"], fac3)) | |
if date(f.p["snd"], fac3): | |
log(g.ffmpegU,'-v error -y -i "%s" %s "%s"',( | |
f.p["snd"], g.ac3%(f.ptags.get("channels", 2), f.ptags.get("sample_rate", "48000")), fac3)) | |
tsmuxer(f.meta, f.cwd, "ac3") #пишу скрипт для BD с упаковкой звука в ac3 | |
if date(fac3, f.p["m2ts"]): log(f.p["ac3"], "%s", "4 :EOF") #BD с упаковкой звука в ac3 | |
if date(f.mkv, f.p["mkv"]): # делаю новый mkv с субтитрами и сжатым звуком | |
f.p["sup"]=f.p["sup"].with_suffix(".sup") | |
if f.p["snd"].suffix==".wav": | |
if f.sup: opt=('-A ="%s" ="%s" --track-order 0:0,1:0,0:1,0:2'%(f.mkv, fx if f.x else fac3)) #сутитры были в mkv | |
elif f.p["sup"].is_file(): opt='-A ="%s" ="%s" ="%s" --track-order 0:0,1:0,2:0,0:2'%(f.mkv, fx if f.x else fac3, f.p["sup"]) | |
elif not f.sup and f.p["sup"].is_file(): opt='="%s" ="%s" --track-order 0:0,0:1,1:0,0:2'%(f.mkv, f.p["sup"]) | |
f.p["mkv"].delete() | |
if opt: log(g.mkvmergeU,'-q --disable-track-statistics-tags -o "%s" %s',( | |
f.p["mkv"], opt)) | |
else: f.mkv.symlink(f.p["mkv"]) | |
cha=fixChapters() | |
fps=float(f.meta["Frame rate:"]) | |
scenes="" | |
for pts, x in enumerate(cha): scenes+='<PTS%s PTS="%s" chk="false"/>\n '%( | |
pts, int(sSec(x)*90000)) | |
bdmd='''<?xml version="1.0" encoding="utf-8"?> | |
<BDMD_Project Version="16"> | |
<Properties> | |
<FirstPlay Type="TBDChapter" Name="Movie" Number="0"/> | |
<TopMenu Type=""/> | |
<VolumeLabel>%s</VolumeLabel> | |
<CopyPermission>false</CopyPermission> | |
<DefLangCode>%s</DefLangCode> | |
<CompileOutput>%s</CompileOutput> | |
<Profile>0</Profile> | |
<AudioTracks>1</AudioTracks> | |
<VideoMode>0</VideoMode> | |
<MenuSeamless>false</MenuSeamless> | |
<MenuAutoTruncate>false</MenuAutoTruncate> | |
<TruncVES>false</TruncVES> | |
<ProtectJAR>true</ProtectJAR> | |
<ActivatedENTER>true</ActivatedENTER> | |
<AutoClearActivated>true</AutoClearActivated> | |
<ActivatedDelayFr>6</ActivatedDelayFr> | |
<ResumeDelay>1000</ResumeDelay> | |
<Set2D3Dmode>0</Set2D3Dmode> | |
<UseReal2D>false</UseReal2D> | |
<ClearGPR>false</ClearGPR> | |
<DontClearPlayMovie>false</DontClearPlayMovie> | |
<DontClearStartMenu>false</DontClearStartMenu> | |
<UpdateAssetsRequired>true</UpdateAssetsRequired> | |
<AllowEmptyStates>false</AllowEmptyStates> | |
<SwitchAudioInMenu>false</SwitchAudioInMenu> | |
<MenuClips>99</MenuClips> | |
<ExtWAV>0</ExtWAV> | |
<FixSub>0</FixSub> | |
<SubMinLength>6</SubMinLength> | |
<SubMinDelta>6</SubMinDelta> | |
<RemoveZeroSubs>false</RemoveZeroSubs> | |
<HideLoadingTopMenu>false</HideLoadingTopMenu> | |
<ClearLoading>false</ClearLoading> | |
<LoadText>true</LoadText> | |
<Text1>Loading..</Text1> | |
<Text2>Loading...</Text2> | |
<Text3>Loading....</Text3> | |
<Text4>Loading.....</Text4> | |
<TextLeft>860</TextLeft> | |
<TextTop>800</TextTop> | |
<TextSize>36</TextSize> | |
<LoadImage>false</LoadImage> | |
<Image1></Image1> | |
<Image2></Image2> | |
<Image3></Image3> | |
<Image4></Image4> | |
<ImageLeft>0</ImageLeft> | |
<ImageTop>0</ImageTop> | |
<LoadBuffer>false</LoadBuffer> | |
<LoadBufferSize>1000000</LoadBufferSize> | |
<SyncDisplay>3</SyncDisplay> | |
<BDID>1</BDID> | |
<Mode3D>false</Mode3D> | |
<DisableNo3DWarning>false</DisableNo3DWarning> | |
<Startup3DMode>1</Startup3DMode> | |
</Properties> | |
<Menus/> | |
%s | |
<Movies> | |
<Movie Name="Movie" DisabledActions="0" AutoShowPopup="false" Group="Movie" ScnCount="%s"> | |
<EndAction Type="TBDChapter" Name="Movie" Number="0"/> | |
<PopupMenu %s/> | |
<Video Path="%s"/> | |
<Audio1 Path="%s" Lang="%s" Enabled="true" Offset="00:00:00:00"/> | |
<Subtitles1 Path="%s" Lang="%s" Enabled="true" Font.Name="Arial" Font.Size="45" Font.Color="16777215" Font.ShadowSize="4" Font.ShadowColor="0" CorrectTiming="true" Offset="20" Changed="true" Start="0" End="0"/> | |
<Chapters> | |
%s | |
</Chapters> | |
</Movie> | |
</Movies> | |
<Groups> | |
<Group Name="Movie" DefAudio="1" DefSub="1"/> | |
</Groups> | |
</BDMD_Project>''' | |
FileName=[pngS/(x+".png") for i, x in enumerate(f.p["srt"].read(utf8).splitlines()) if i%5==3] | |
try: im=Image.open(FileName[0]) | |
except: Width, Height=(100, 178) | |
else: Width, Height=im.size | |
ClipRight, ClipBottom=map(int,(f.meta["Width:"], f.meta["Height:"])) | |
chONpage=ClipBottom/(Height+8) | |
lastScene=(pts)/chONpage+1 | |
pts+=1 | |
Top=(ClipBottom/chONpage-Height)/2 #(1080/10-100)/2=4 (720/x-100)/2 | |
Bottom=Top*2+Height | |
Right=(ClipRight-Width)/lastScene | |
Left=(ClipRight-(Right*(lastScene-1)+Width))/2 | |
sels=f.av/"cur" | |
sels.mkdir() | |
imf=ImageFont.truetype("consolab.ttf", 30) | |
Wch=Width/3 | |
Hch=Height/3 | |
colorB=(0, 0, 0, 0) | |
colorR=(255,0,0,256) | |
im=Image.new('RGBA', (Wch, Hch), colorB) | |
imd=ImageDraw.Draw(im) | |
for x in range(1, pts+1): #чаптеры | |
fsel=sels/("%s.png"%x) #рисую номер чаптера | |
if not fsel.is_file(): | |
imd.ellipse((0, 0)+im.size,(255,0,0,96), colorR) | |
imd.multiline_text((2, 2),"%s\n\n222"%x, (0,0,0,255), imf, align="center") | |
imd.multiline_text((4, 4),"%s\n222"%x, (255,255,255,255), imf, align="center") | |
im.save(fsel) | |
if 0: | |
sel=f.av/"sel.png" #рисую селектор чаптера | |
if not sel.is_file(): | |
im=Image.new('RGBA', (Width+Top*2, Height+Top*2), colorB) | |
imd=ImageDraw.Draw(im) | |
imd.rectangle((0,0,Width+Top*2-1,Height+Top*2-1), (255,0,0,64), colorR) | |
imd.rectangle((Top*3,Top*3,Width+Top*2-Top*3,Height+Top*2-Top*3), (255,0,0,0)) | |
im.save(sel) | |
cur=f.av/"cur.png" #рисую картинку текущего чаптера | |
if not cur.is_file(): | |
im=Image.new('RGBA', (Height, Height), colorB) | |
imd=ImageDraw.Draw(im) | |
imd.polygon((0, 0, Height/2, Height/2, 0, Height), (0,255,0,64), (0,255,0,256)) | |
im.save(cur) | |
PopupMenus="<PopupMenus>\n" | |
for x in range(1, lastScene+1): #столбцы | |
PopupMenus+=''' <PopupMenu Name="Scenes %s" AutoClose="0" DrawMethod="3" OpenMenuSound=""> | |
<ClosePopupAnim AnimType="0" AnimTarget="0" AnimDirection="0" FadeFrom="0" FadeTo="0" AnimFrames="12" FromTop="0" FromLeft="0" ToTop="0" ToLeft="0" ClipLeft="0" ClipTop="0" ClipRight="%s" ClipBottom="%s" IncludeSelected="0" IncludeCurrent="0" Anchor="0" AnchorTop="0" AnchorLeft="0" FromScaleVert="100" FromScaleHoriz="100" ToScaleVert="100" ToScaleHoriz="100" SlideEffectName="linear" SlideEffectMode="0" IsCustom="false" Custom="" AnimOrder="0" SeqOrder="0"/> | |
<PopupMenu Type="TBDClosePopup"/> | |
<BDItems>\n'''%( x, | |
ClipRight, ClipBottom) | |
NormalLeft=Left+Right*(x-1) | |
for y in range(1, chONpage+1): #строки | |
btnChap=(x-1)*chONpage+y | |
if btnChap>pts: break | |
NormalTop=Top+Bottom*(y-1) | |
OnPressLeft=min(pts, pts/chONpage*chONpage+y) if btnChap-chONpage<1 else btnChap-chONpage | |
if (btnChap-1)/chONpage+1==lastScene-1: OnPressRight=pts if btnChap+chONpage>pts else btnChap+chONpage | |
else: OnPressRight=y if btnChap+chONpage>pts else btnChap+chONpage | |
OnPressUp=pts if btnChap-1<1 else btnChap-1 | |
OnPressDown=1 if btnChap+1>pts else btnChap+1 | |
# <Activated Top="%s" Left="%s" FileName="%s" Width="%s" Height="%s"/> | |
# NormalTop, NormalLeft, cur, Height, Height, | |
# NormalTop, NormalLeft+Width+Top-Wch, cur, Height, Height, | |
PopupMenus+=''' <BDItem Name="btnChap %s" Type="0" IsButton="true" IsAutoButton="false" AnimGroup="0" Highlight="3" HighlightValue="%s" HighlightGroup="Movie"> | |
<States> | |
<Normal Top="%s" Left="%s" FileName="%s" Width="%s" Height="%s"/> | |
<Selected Top="%s" Left="%s" FileName="%s" Width="%s" Height="%s"/> | |
<Current Top="%s" Left="%s" FileName="%s" Width="%s" Height="%s"/> | |
</States> | |
<Actions> | |
<OnPressLeft Type="TBDItem" MenuType="TBDPopupMenu" MenuName="Scenes %s" Name="btnChap %s"/> | |
<OnPressRight Type="TBDItem" MenuType="TBDPopupMenu" MenuName="Scenes %s" Name="btnChap %s"/> | |
<OnPressUp Type="TBDItem" MenuType="TBDPopupMenu" MenuName="Scenes %s" Name="btnChap %s"/> | |
<OnPressDown Type="TBDItem" MenuType="TBDPopupMenu" MenuName="Scenes %s" Name="btnChap %s"/> | |
<OnPressEnter Type="TBDChapter" Name="Movie" Number="%s"/> | |
</Actions> | |
</BDItem>\n'''%( btnChap, btnChap, | |
NormalTop, NormalLeft, FileName[btnChap-1], Width, Height, | |
NormalTop-Top, NormalLeft+Width+Top-Wch, sels/("%s.png"%btnChap), Wch, Hch, | |
NormalTop, NormalLeft-Top, cur, Height, Height, | |
(OnPressLeft-1)/chONpage+1, OnPressLeft, | |
(OnPressRight-1)/chONpage+1, OnPressRight, | |
(OnPressUp-1)/chONpage+1, OnPressUp, | |
(OnPressDown-1)/chONpage+1, OnPressDown, | |
btnChap-1) | |
PopupMenus+=''' </BDItems> | |
</PopupMenu>\n''' | |
PopupMenus+=" </PopupMenus>\n" | |
onPopupMenus='Type="TBDItem" MenuType="TBDPopupMenu" MenuName="Scenes 1" Name="btnChap 1"' | |
label=str(f.name).split()[0] | |
rw(f.p["bdmd"], bdmd%(label, g.lang, pl.path(label), PopupMenus, pts, onPopupMenus, f.p["264"], f.p["snd"], g.lang, f.p["sup"], g.lang, scenes)) | |
for x in (".bat", ".cmd"): | |
rw(pl.path(g.tsmuxerU).with_suffix(x), '''@echo off | |
chcp %s>nul | |
if "%%~2"=="" ( | |
%%~dpn0.exe %%* | |
) else ( | |
echo %%~dpn0.exe %%* | |
type "%%~1" | |
if /i "%%~x0"==".bat" ( | |
xcopy "%%~2\\..\\BD" "%%~2\\" /Y /T /E | |
xcopy "%%~2\\..\\BD\\BDMV\\PLAYLIST" "%%~2\\BDMV\\PLAYLIST\\" /Y /D | |
xcopy "%%~2\\..\\BD\\BDMV\\CLIPINF" "%%~2\\BDMV\\CLIPINF\\" /Y /D | |
xcopy "%%~2\\..\\BD\\BDMV\\BACKUP\\CLIPINF" "%%~2\\BDMV\\BACKUP\\CLIPINF\\" /Y /D | |
mklink "%%~2\\BDMV\\STREAM\\00001.m2ts" "%%~2\\..\\BD\\BDMV\\STREAM\\00001.m2ts" | |
mklink "%%~2\\BDMV\\STREAM\\00002.m2ts" "%%~2\\..\\BD\\BDMV\\STREAM\\00002.m2ts" | |
) else ( | |
move /Y "%%~2\\..\\BD\\BDMV" "%%~2\\" | |
) | |
)>"%%~2\\..\\%%~n2.log"'''%coding[2:], g.encoding) | |
def af(): | |
if c.prvol: print(c.prvol) | |
if c.afd: return g.af%",".join(c.afd.values()) | |
return "" | |
def aopt(): | |
return (g.wav%(c.de.get("channels", 2), c.de.get("sample_rate", "48000"))) if m.aext=="wav" else cGet(m.parent, "aopt") | |
def attrib(file, attr): | |
"""устанавливаю атрибут файла""" | |
#ub=create_unicode_buffer(file) | |
ub=file | |
pe('windll.kernel32.SetFileAttributesW("%s", %s)' % ( | |
file, attr)) | |
try: windll.kernel32.SetFileAttributesW(ub, attr) | |
except: print(g.ob) | |
else: print(g.ok) | |
def bSec(s): | |
return type(sSec(s))==type(0.0) or s=="-" | |
def cDel(s, o, x="cut"): | |
""" удаляю опцию o из секции s """ | |
#print("cDel", x, type(s), s, o) | |
se=s.encode(coding) if type(s) is unicode else unicode(s).encode(coding) | |
try: result=g.cfg[x].remove_option(se, o) | |
except: pass | |
def cDelS(s, x="cut"): | |
""" удаляю секцию s """ | |
#print("cDelЫ", x, type(s), s) | |
se=s.encode(coding) if type(s) is unicode else unicode(s).encode(coding) | |
try: result=g.cfg[x].remove_section(se) | |
except: pass | |
def cGet(s, o, x="cut"): | |
""" читаю опцию o из секции s """ | |
#print("cGet", x, type(s), s, o) | |
se=s.encode(coding) if type(s) is unicode else unicode(s).encode(coding) | |
try: result=g.cfg[x].get(se, o).decode(coding) | |
except: return "" | |
else: return result | |
def chapter(): | |
if c.de.get("jpg", ""): return | |
if c.film and date(c.p["jpg"], c.p["scn"]): #рисую чаптер для блюрэя | |
drawtext=g.drawtext%("%s-%s"%(c.de["TRCK"][:2], c.de["TRCK"][2:4]), c.de["title"]) | |
log(g.ffmpegU, '-v error -i "%s" -q:v 1 %s -y "%s"', ( | |
c.p["jpg"], drawtext, c.p["scn"])) | |
def cHas(s, x="cut"): | |
""" есть ли секция s """ | |
#print("cHas", x, type(s), s) | |
se=s.encode(coding) if type(s) is unicode else unicode(s).encode(coding) | |
return g.cfg[x].has_section(se) | |
def cRead(x="cut"): | |
g.cfg[x].read(g.p[x]) | |
def cOptions(s, x="cut"): | |
se=s.encode(coding) if type(s) is unicode else unicode(s).encode(coding) | |
if not g.cfg[x].has_section(se): return [] | |
return g.cfg[x].options(se) | |
def cSections(x="cut"): | |
return g.cfg[x].sections() | |
def cSet(s, o, v, x="cut"): | |
""" пишу v в опцию o из секции s """ | |
#print("cSet", x, type(s), s,o,v) | |
se=s.encode(coding) if type(s) is unicode else unicode(s).encode(coding) | |
ve=v.encode(coding) if type(v) is unicode else unicode(v).encode(coding) | |
if not g.cfg[x].has_section(se): g.cfg[x].add_section(se) | |
g.cfg[x].set(se, o, ve) | |
def cuvid(ext): | |
if not g.nv["dec"]: return "" | |
ext=ext.lower() | |
if ext in ("mts", "m2ts", "264"): return "-c:v h264_cuvid" | |
elif ext in ("hevc", "265"): return "-c:v hevc_cuvid" | |
elif ext in ("jpg", "jpeg") : return "-c:v mjpeg_cuvid" | |
elif ext in ("mp1", "m1v", "mpv", "dat") : return "-c:v mpeg1_cuvid" | |
elif ext in ("mpg", "mpg2", "m2v", "vob") : return "-c:v mpeg2_cuvid" | |
elif ext in ("ogg", "divx", "xvid") : return "-c:v mpeg4_cuvid" | |
elif ext in ("vc1", "wmva", "wvc1", "wmv3", "wmv9") : return "-c:v vc1_cuvid" | |
elif ext in ("vp8") : return "-c:v vp8_cuvid" | |
elif ext in ("vp9", "webm") : return "-c:v vp9_cuvid" | |
def cWrite(x="cut"): | |
ini=[] | |
for section in sorted(g.cfg[x].sections()): | |
ini+=["[%s]"%section.decode(coding)] | |
for option in sorted(g.cfg[x].options(section)): | |
ini+=["%s = %s"%(option, g.cfg[x].get(section, option).decode(coding))] | |
ini+=[""] | |
rw(g.p[x], g.rn.join(ini), g.encoding) | |
def d2s(d, sL="#", sKV=" "): | |
sl=[] | |
for x in d.items(): sl.append(("%s"+sKV+"%s")%x) | |
return sL.join(sl) | |
def date(s, t, p=0): | |
"""s моложе t?""" | |
if not p : m.doIt=0 | |
if not s.is_file(): m.doIt=0; return m.doIt | |
if not t.is_file(): m.doIt=1; return m.doIt | |
if round(s.stat().st_mtime,5) > round(t.stat().st_mtime,5): m.doIt=1 | |
return m.doIt | |
def desc2fl(d): | |
f=[] | |
try: l=d.read(g.encoding).splitlines() | |
except: l=[] | |
else: | |
for x in l: f+=[shlex.split(x.replace("'", ";"), comments=0, posix=1)[0]] | |
return f, l | |
def descAll(plp): | |
"читаю строки из дискрипшена plp и режу" | |
dl=desc2fl(plp) | |
p=plp.up() | |
return [p/x for x in dl[0]] | |
#for x in sorted(dl[0]): m(p/x) | |
def descNew(plp): | |
ok=0 | |
fion=plp/g.ion | |
dl, res=desc2fl(fion) | |
sdl=set(dl) | |
for x in ("m2ts", "mp4", "avi", "mov", "mpo", "jpg"): | |
pattern="%s\*."%plp | |
for l in x: pattern+=("["+l.lower()+l.upper()+"]") if l.lower()!=l.upper() else l | |
for y in sorted(glob(pattern)): | |
n=pl.path(y).name | |
if " " in n: n='"%s"'%n | |
if n in sdl: continue | |
b="0" if x in ("mpo", "jpg") else "0 - 0 "+pl.path(y).stem | |
try: cut=pl.path(y+".cut").read(g.encoding) | |
except:cut="" | |
if cut: b=(cut.replace(g.rn, r"\n")+"\x04В") if g.rn in cut else cut | |
res+=['%s %s'%(n, b)] | |
ok=1 | |
if not ok: return | |
if win and fion.is_file(): attrib(fion, 128) | |
s=g.rn.join(res)+g.rn | |
print(s) | |
rw(fion, s, g.encoding) | |
def descr(s, search=0): | |
"""читаю строку s из дискрипшена и пишу новый куешит""" | |
#print(type(s),s) | |
arg=shlex.split(s.replace("'", ";"), comments=0, posix=0) | |
if m.ne.lower()!=stripp(arg[0].lower()): return #не тот файл | |
if search: return 1 | |
m.found=1 | |
de=" ".join(arg[1:]) | |
if r"\n" in de: de=de[:-2].replace(r"\n",g.rn) | |
print("de:\n"+de) | |
for line in de.splitlines(): | |
m.deL.append({}) | |
if parse(len(m.deL)-1, line, m.deL[-1]): del(m.deL[-1]) | |
m.newD=m.deL!=m.csL | |
if not m.csL: m.csL=m.deL | |
if m.newD: rw(m.lcs, de, g.encoding) | |
def doId(d): | |
""" пишу тэги из d в mp3 """ | |
if c.txt.is_file(): | |
if date(c.txt, c.p["txt"], 1): rw(c.p["txt"], c.txt.read(g.encoding), utf8) #копия стихов для MiniLyrics в UTF-8 | |
if m.doIt: | |
if g.eyeD3: | |
c.eye=["-m eyed3.main"] | |
c.eye+=["--to-v2.3"] | |
for k, v in d.items(): | |
if k.startswith("T"): c.eye+=['--text-frame %s:"%s"'%( | |
k, v)] | |
else: | |
if k=="COMM": c.eye+=['--add-comment "%s"::%s'%(v, g.lang)] | |
c.eye+=["--remove-frame TSSE"] | |
replayGain() | |
for x in c.rg: | |
if c.replay[x]!="": c.eye+=['--user-text-frame REPLAYGAIN_TRACK_%s:"%s"'%(x.upper(), c.replay[x])] | |
lyrics=c.p["txt"] if gpe==utf8 else c.txt | |
if lyrics.is_file(): # на ЯД есть стихи | |
c.eye+=['--add-lyrics "%s"::%s'%(lyrics.replace(":",r"\:"), g.lang)] | |
log(pl.python, " ".join(c.eye)+' "%s"', c.p["mp3"]) | |
def dur(lt0, film): | |
"возвращаю m.dur длительность в секундах и m.sBegin время начала файла зависит от film" | |
if not m.iw and m.dur: return m.dur | |
sEnd=datetime.fromtimestamp(m.lne.stat().st_mtime) #время конца записи | |
if film: sEnd+=timedelta(seconds=float(film)) #коррекция часов камеры | |
d=3 if lt0[1]=="-" else sSec(lt0[1]) | |
d-=sSec(lt0[0]) | |
d=d if m.iw else cGet(m.pne, "d") | |
try: m.dur=float(d) | |
except: print('m.dur=float("%s")%s'%(d, g.ob)) | |
m.sBegin=sEnd if m.iw else sEnd-timedelta(seconds=m.dur) #время начала записи | |
#cSet(m.pne, "f", m.sBegin.strftime("%H%M%S")) | |
if cGet(m.pne, "f"): cDel(m.pne, "f") | |
#cSet(m.pne, "t", sEnd.strftime("%H%M%S")) | |
if cGet(m.pne, "t"): cDel(m.pne, "t") | |
print('%s-%s-%s'%(m.sBegin.strftime("%H%M%S"), m.dur, sEnd.strftime("%H%M%S"))) | |
return m.dur | |
def enc264(): | |
vopt=cGet(m.parent, "vopt") | |
if not vopt or not g.vcodec in vopt: vopt="-c:v %s -profile:v %s -level %s -pix_fmt yuv420p"%( | |
g.vcodec, g.profile, g.level) | |
return vopt.replace("-c:v libx264", g.nv["enc"])+enc264opt("h") if g.nv["enc"] else vopt+enc264opt("x") | |
def enc264opt(o): | |
fr=eval(c.de.get("r_frame_rate", g.fps)+".0") | |
h=int(c.de.get("height", g.h)) | |
nl=list(g.encL) | |
if fr>25: | |
if h>720: br=25 | |
else: br=20 | |
else: | |
if h>720: br=15 | |
else: br=10 | |
return g.enc[o]%((br,)+g.encL) | |
def fixChapters(fix=0): | |
if date(f.p["264"], f.BDedit): #чаптеры не проверены | |
if fix: | |
xml=f.p["xml"].read(g.encoding) | |
xmlN=xml | |
if date(f.mkv, f.tc[f.ext]): log(g.ffmsU, '-k -c -f -p "%s" ./%s', (f.mkv, f.mkv.suffix[1:])) | |
if date(f.p["264"], f.tc["m2ts"]): log(g.ffmsU, '-k -c -f -p "%s" ./%s', (f.p["m2ts"], f.p["m2ts"].suffix[1:])) | |
idr=map(int, f.kf[f.ext].read().splitlines()[2:]) | |
old=map(lambda x: int(float(x)), f.tc[f.ext].read().splitlines()[1:]) | |
new=map(lambda x: int(float(x)), f.tc["m2ts"].read().splitlines()[1:]) | |
offs=new[0] | |
if offs: new=map(lambda x: x-offs, new) | |
if not fix or old==new: | |
cha=f.cha | |
rw(f.BDedit, g.rn.join(cha)) | |
else: | |
cha=[] | |
i=0 | |
for z in f.cha: | |
x=int(sSec(z)*1000) #милисекунд | |
if x in old[i:]: i=old.index(x, i) #если есть совпадение впереди от прошлой находки | |
else: | |
for j in range(i+1, len(old)): #если нет совпадения ищем перебором вперёд от прошлой находки | |
if old[j]>x: #если больше то на предыдущий кадр | |
i=j-1 | |
break | |
else: i=-1 #если не нашли то последний кадр | |
if not i in idr: #найденный кадр не ключевой | |
for j in range(1, g.GOP): #ищем ключевой кадр проверяя +1 -1 +2 -2 ... | |
for k in (1, -1): | |
l=i+j*k | |
if l in idr: #нашли | |
i=l | |
break | |
if i==l: break | |
y=tSec(new[i]/1000.0) | |
cha.append(y) | |
y=tSec(old[i]/1000.0) | |
if z!=y: xmlN.replace(z, y) | |
if rw(f.p["xml"], xmlN, g.encoding): | |
mkvOpt="" | |
if f.p["tag"].is_file(): mkvOpt='-t global:"%s"'%f.p["tag"] | |
log(g.mkvpropeditU, '--delete-attachment name:%s.jpg --delete-track-statistics-tags -t all: -t global: -c "%s" %s -s title="%s" "%s"', ( | |
g.ec, f.p["xml"], mkvOpt, f.name, f.mkv)) | |
rw(f.BDedit, g.rn.join(cha)) | |
rw(f.p["cc"], "set cc=--custom-chapters=%s"%";".join(cha)) | |
if date(f.p["cc"], f.p["m2ts"]): log(f.p["tsMuxeR" if f.ac3dts else "ac3"], "%s", "4 :EOF") | |
else: cha=f.BDedit.read().splitlines() | |
return cha | |
def fp(ren=0): | |
for i, x in enumerate(f.meta["Stream ID:"]): | |
if x=="S_TEXT/UTF8": break | |
elif x in g.SI[:3]: s=g.SI2ext[x] | |
elif x in g.SI[-2:]: s="sup" | |
else: s="snd" | |
if ren: log("cmd", '/c ren "%s" "%s"',(f.p[s].replace(".track_", "*.track_"), f.p[s].name)) | |
else: f.p[s]=pl.path(f.name+".track_%s%s.%s"%(f.meta["Track ID:"][i], "_2" if s=="264" and f.mvc and g.makeMkvIso else "", g.SI2ext[f.meta["Stream ID:"][i]])) | |
def fromCloud(): | |
"""копирую дескрипшены и куешит из облака""" | |
if m.de.is_file(): | |
if m.copyD: | |
if date(m.de, m.lde): | |
if not ppuc(m.de, m.lde): m.copyD=0 | |
else: | |
if win: | |
attrib(m.lde, 128) | |
if not ppuc(m.de, m.lde): m.copyD=0 | |
#attrib(m.lde, 2) | |
else: | |
if m.copyC: | |
if date(m.cs, m.lcs): | |
if not ppuc(m.cs, m.lcs): m.copyC=0 | |
def gain(fi, ft, boost=0): | |
print("gain(%s, %s, %s"%(fi, ft, boost)) | |
if "boost" in c.a: | |
if float(c.a["boost"])<0: | |
if boost: return af() | |
#cSet(c.pn, "volume", c.a["boost"]) | |
c.afd["v"]=g.volume%c.a["boost"] | |
return af() | |
else: | |
if not "norm" in c.a: return af() | |
mm=("mean", "max") | |
ftls=ft.lstrip("--split parts:") | |
if ftls==cGet(c.pn, "ft"): | |
volume={} | |
for y in mm: | |
volume[y]=cGet(c.pn, y) | |
if volume[y]=="": break | |
else: | |
return gainCorr(volume, boost) | |
volume={} | |
for x in log(g.ffmpegU, '-i "%s" -vn -af volumedetect -sn -f null -', fi, 1).splitlines(): | |
if x.startswith("[Parsed_volumedetect"): | |
for y in mm: | |
z=y+"_volume:" | |
if z in x: | |
volume[y]=x.rstrip("dB").split(z)[1].strip() | |
try: fl=float(volume[y]) | |
except: return af() | |
cSet(c.pn, y, volume[y]) | |
#print("cut", c.pn, y, volume[y]) | |
for y in mm: | |
if y in volume: | |
if volume[y]=="": return af() | |
try: fl=float(volume[y]) | |
except: return af() | |
else: return af() | |
if ftls: cSet(c.pn, "ft", ftls) | |
return gainCorr(volume, boost) | |
def gainCorr(volume, boost): | |
c.prvol=c.pn+" max=%s mean=%s"%(volume["max"], volume["mean"]) | |
bt1=float(c.a["boost"]) if "boost" in c.a else float(c.a["norm"])-float(volume["mean"]) | |
bt=min(bt1, -float(volume["max"])) | |
if boost or (not "norm" in c.a): | |
if not bt>0: return af() | |
cSet(c.pn, "volume", bt) | |
c.afd["v"]=g.volume%bt | |
c.prvol+=" volume=%s"%bt | |
return af() | |
def hash(s): | |
se=s.encode(coding) if type(s) is unicode else unicode(s).encode(coding) | |
return hex(adler32(se)).lstrip("-0") | |
def ifEx(f): | |
""" замена аудиофайла с неопределённой длительностью, замена аудиофайла без видео """ | |
return f if f.is_file() else m.lne | |
def ifTouch(f): | |
if f.is_file(): f.touch() | |
def jpeg(f, j="jpeg"): | |
"пишу последний кадр клипа f в c.p[j] и в файл обложки если APIC это -" | |
if "last" in c.de or j=="jpg": | |
if date(f, c.p[j]) or c.p[j].name.lower()=="tmp.jpeg": | |
yuv="-s %s:%s"%(c.de.get("width", g.w), c.de.get("height", g.h)) | |
if "yuv" in f.suffix: log(g.ffmpegU, '-v error -y %s -i "%s" -vf reverse -frames:v 1 -q:v 1 "%s"', ( | |
yuv, f, c.p[j])) | |
else: log(g.ffmpegU, '-v error -y -sseof -1 -i "%s" -vf reverse -frames:v 1 -q:v 1 "%s"', ( | |
f, c.p[j])) | |
if c.apic=="-" and j=="jpeg" and c.p["jpeg"].is_file(): ppuc(c.p["jpeg"], c.p["jpg"]) | |
def jpegOld(f, j="jpeg"): | |
if "last" in c.de or j=="jpg": | |
if date(f, c.p[j]): | |
spf=round(1/eval(c.de.get("r_frame_rate", g.fps)+".0"),3) | |
for x in map(lambda x:-x*spf ,range(1, g.GOP)): | |
if date(f, c.p[j]): log(g.ffmpegU, '-v error -sseof %s -i "%s" -q:v 1 -vf scale=ih*dar:ih -frames:v 1 -y "%s"', ( | |
x, f, c.p[j])) | |
else: return | |
def jpg(f): | |
'пишу кадр указанный в c.apic в c.p["jpg"]' | |
if jpgNew(): | |
if bSec(c.apic): #обложка из видеофайла | |
if m.iw or cGet(m.pne, "v"): | |
fwh=(c.de.get("r_frame_rate", g.fps), c.de.get("width", g.w), c.de.get("height", g.h)) | |
yuv="-r %s -s %s:%s"%fwh if "yuv" in f.suffix else "" | |
loss="%s-q:v 1"%("" if c.ts!=7 else "-vf scale=%s:%s "%fwh[1:]) | |
ll="-bsf:v mjpeg2jpeg -c:v copy" if f==m.lne and c.de.get("codec_name", "")=="mjpeg" and c.ts!=7 else loss | |
sec=sSec(c.apic) | |
spf=round(1/eval(fwh[0]+".0"),3) | |
for x in map(lambda x:x*spf ,range(0, g.GOP)): | |
if date(f, c.p["jpg"]): | |
log(g.ffmpegU, '-v error -y %s %s -frames:v 1 %s "%s"', ( | |
yuv, tss(f, sec+x), ll, c.p["jpg"])) | |
ll=loss | |
else: break | |
if c.p["jpg"].is_file(): c.fapic=c.p["jpg"] | |
else: #файл обложки указан в тэге APIC | |
if c.fapic.is_file(): | |
if c.fapic!=c.p["jpg"]: ppuc(c.fapic, c.p["jpg"]) | |
ytb(c.fapic) | |
def jpgNew(): | |
if c.nt: return 0 | |
m.doIt=m.ndur | |
if m.found: | |
if c.apic!=c.oapic: m.doIt=1 #предыдущий apic отличается от текущего | |
if bool(c.apic)!=c.p["jpg"].is_file(): m.doIt=1 | |
else: date(m.lcs, c.p["jpg"], 1) | |
if not m.doIt: return 0 | |
if not c.apic: c.p["jpg"].delete() #обложка будет в alb all или 1x1 | |
return c.apic | |
def kodi(opt=0, pipe=""): | |
suff=c.de.get("kodi", "").lower() | |
c.sm=0 | |
c.vf="" | |
if not suff: return | |
ow, oh, fr=(c.de.get("width", g.w), c.de.get("height", g.h), c.de.get("r_frame_rate", g.fps)) | |
wh="%s:%s"%(ow, oh) | |
if suff in ("sbs", "hsbs", "lrq"): c.vf="hstack,scale=%s:%s/dar*2"%(ow, ow) | |
elif suff in ("fsbs", "lr"): c.vf="hstack,scale=%s*2:%s/dar*2"%(ow, ow) | |
elif suff in ("tab", "htab", "abq"): c.vf="vstack,scale=%s*dar*2:%s"%(oh, oh) | |
elif suff in ("ftab", "ab"): c.vf="vstack,scale=%s*dar*2:%s*2"%(oh, oh) | |
kodi2sm={"mvc":13, "sbs":1, "hsbs":1, "lrq":1, "fsbs":1, "lr":1} | |
c.sm=kodi2sm.get(suff, 3) | |
sm2fp={13:5, 1:3} | |
c.fp=sm2fp.get(c.sm, 4) | |
sm2ff={13:"block_lr", 1:"left_right"} | |
c.ff=sm2ff.get(c.sm, "top_bottom") | |
if opt==0: return | |
elif opt==1: | |
if c.vf: | |
if date(c.p["yuv"], c.p["264"]) or date(c.p["3D.yuv"], c.p["264"]): | |
log(g.ffmpegU,'-v error -y -s %s -i "%s" -s %s -i "%s" -filter_complex [0][1]%s -r %s %s "%s"', ( | |
wh, c.p["yuv"], wh, c.p["3D.yuv"], c.vf, fr, enc264(), c.p["264"])) | |
return | |
if pipe: | |
log("cmd",'/C %s | "%s" -sbs 2 -i - -o:mvc "%s" -w %s -h %s -f %s %s', ( | |
pipe, g.frimU, c.p["264"], ow, oh, fr, enc264opt("f"))) | |
else: | |
if date(c.p["yuv"], c.p["264"]) or date(c.p["3D.yuv"], c.p["264"]): | |
log(g.frimU,'-i "%s" "%s" -o:mvc "%s" -w %s -h %s -f %s %s', ( | |
c.p["yuv"], c.p["3D.yuv"], c.p["264"], ow, oh, fr, enc264opt("f"))) | |
def level(): | |
r=float(s2d(cGet(m.parent, "vopt"), " ").get("-level", g.level)) | |
if r==int(r): r=int(r) # 42->4.2 40->4 | |
return r | |
def log(c, s, p, hideSTE=0): | |
cmd=pl[str(c)] | |
pe("%s %s" % ( | |
c, s % p)) | |
g.err, sto, ste=cmd.run(shlex.split(s%p, comments=0, posix=1), retcode=None) | |
if g.err: print(g.ob) | |
else: print(g.ok) | |
try: sto=sto.decode(utf8, "backslashreplace").rstrip(g.rn) | |
except: pass | |
if sto: | |
try: print(sto) | |
except: pass | |
try: ste=ste.decode(utf8, "backslashreplace").rstrip(g.rn) | |
except: pass | |
if not hideSTE: | |
if ste: | |
try: print(ste) | |
except: pass | |
return sto+ste | |
def m3u(s): | |
"пишу плэйлист s" | |
#s="d:\AV\2016\20160827 Свадьба" | |
print('m3u("%s")'%s) | |
spl=pl.path(s) | |
pl.cwd.chdir(spl) | |
ffmkv="ffmkv.txt" | |
mt="mkv.txt" | |
sj="smmvs.jxtl" | |
bdsp=pl.path(s).name.split()[0] | |
tsl=[] | |
p={} | |
for x in ("mkv", "mp3", "mp4", "MKV"): | |
p["m3u"]=["#EXTM3U"] | |
p["xspf"]=['<?xml version="1.0" encoding="%s"?><playlist version="1" xmlns="http://xspf.org/ns/0/">'%( | |
utf8)] | |
p["xspf"]+=['<title>%s</title><trackList>'%( | |
s.stem)] | |
dpl=pl.path(s if x!="mp4" else g.Google+s[2:])# "d:\AV\2016\20160827 Свадьба" или "g:\AV\2016\20160827 Свадьба" | |
pll=dpl | |
if x=="MKV": #пишу начало скриптов для film | |
dpl=spl/"film" #"d:\AV\2016\20160827 Свадьба\film" | |
pll=dpl/spl.name #"d:\AV\2016\20160827 Свадьба\film\20160827 Свадьба" | |
sh="bat" if win else "sh" | |
eol=g.rn if win else g.n | |
for bat in ("mkv."+sh, "smmvs."+sh, "ffmkv."+sh): | |
if win: | |
p[bat]=["chcp 65001>nul"] | |
p[bat]+=['cd /d "%s"'%dpl] | |
else: p[bat]=['cd "%s"'%dpl] | |
if bat.startswith("f"): p[bat]+=['"%s" -f concat -safe 0 -i %s -c copy -map 0 -y "%s.%s"'%( | |
g.ffmpegU, ffmkv, pll, x)] | |
elif bat.startswith("m"): p[bat]+=['"%s" @%s'%( | |
g.mkvmergeU, mt)] | |
elif bat.startswith("s"): p[bat]+=['"%s" %s'%( | |
g.smmbsU, sj)] | |
if win: p[bat]+=["pause"] | |
p[ffmkv]=["#ffmkv."+sh] | |
p[mt]=["#mkv."+sh] | |
p[mt]+=["--disable-track-statistics-tags"] | |
p[mt]+=["-o"] | |
p[mt]+=[mkvOpt("%s.%s"%(spl.name, x))] | |
p[sj]= ['<timelines version="2" >'] | |
p[sj]+=[' <timeline>'] | |
p[sj]+=[' <group output="%s.%s" >'%( | |
pll, x )] | |
p[sj]+=[' <task type="joining" />'] | |
p[sj]+=[' <track video="-1" audio="-1" text="-1" flags="" >'] | |
pattern="%s *."%pll | |
for l in x: #[mM][kK][vV] | |
if l.lower()!=l.upper(): pattern+="["+l.lower()+l.upper()+"]" | |
else: pattern+=l | |
for y in sorted(glob(pattern)): # иду по файлам пишу середину скриптов | |
stem=pl.path(y).stem | |
talb, trck, title, film=stem2l(stem) | |
tit2=s2d(cGet(title, "tags", "uslt")).get("TIT2", "") or title | |
tpe1=(lambda x: x and cGet(x, "TPE1", "uslt"))(cGet(stem, "TPE1")) | |
if talb: | |
print(y) | |
if x=="mkv": | |
google=pl.path(g.Google+y[2:]) | |
mp4=google.with_suffix(".mp4") | |
if google.is_file(): | |
if not mp4.is_file(): google.rename(mp4) #меняю расширение с mkv на mp4 на GD | |
if x=="MKV": #film | |
ts=pl.path(y).with_suffix(".3d.ts") | |
if ts.is_file(): tsl+=['"%s"'%(ts.name if tsl else ts)] | |
if 0: | |
if tsl: tsl+=['"%s"'%ts.name] #для ts | |
else: tsl+=['"%s"'%ts] | |
p[ffmkv]+=["file '%s'"%pl.path(y).name] | |
p[mt]+=["-M"] | |
p[mt]+=["-T"] | |
p[mt]+=["--no-global-tags"] | |
p[mt]+=[("+" if len(p[mt])>8 else "")+mkvOpt(pl.path(y).name)] | |
p[sj]+=[' <clip src="%s" start="begin" stop="end" timeFormat="position" />'%pl.path(y).name] | |
p["m3u"]+=['#EXTINF:-1 group-title="%s", %s - %s\n%s'%( | |
talb, tpe1, tit2, pl.path(y).name)] | |
p["xspf"]+=["<track><trackNum>%s</trackNum><creator>%s</creator><title>%s</title><album>%s</album><location>%s</location></track>"%( | |
trck, tpe1, tit2, talb, pl.path(y).name)] | |
#пишу конец скриптов | |
p["xspf"]+=["</trackList></playlist>"] | |
for e in ("m3u", "xspf"): | |
f=pll.with_suffix("."+x+"."+e) | |
if len(p["m3u"])>2: rw(f, g.rn.join(p[e])) | |
else: f.delete() | |
if x=="MKV": # для фильма | |
if 0 and len(p[ffmkv])>1: | |
for ffm in ("ffmkv."+sh, ffmkv): rw(dpl/ffm, eol.join(p[ffm])) | |
if len(p[mt])>4: | |
for mkv in ("mkv."+sh, mt): rw(dpl/mkv, eol.join(p[mkv])) | |
if 0 and len(p[sj])>5: | |
p[sj]+=[" </track>"] | |
p[sj]+=[" </group>"] | |
p[sj]+=[" </timeline>"] | |
p[sj]+=["</timelines>"] | |
if win: p["smmvs."+sh][0]="chcp %s>nul"%coding[2:] | |
for smmvs in ("smmvs."+sh, sj): rw(dpl/smmvs, eol.join(p[smmvs]), coding) | |
if tsl and c.sm==13: rw(dpl/"lof.bat", "set lof=%s"%"+".join(tsl), g.encoding) | |
for e in ("m3u", "xspf", "m3u8"): (pl.cwd / (s.stem+"."+x+"."+e)).delete() #удаляю старые плэйлисты | |
(pl.cwd / (x+".xspf")).delete() #удаляю старые плэйлисты | |
dpl.with_suffix("."+x+".m3u8").delete() #удаляю старые плэйлисты | |
def mkv(de): | |
if c.doCut: print(g.ok+"c.doCut") | |
m.doIt=m.ndur or c.doCut or (c.apic=="-" and c.oapic!="-") #изменились точки резки или apic стал - | |
doMP3=1 | |
for x in (ifEx(m.neVid), c.p["srt"]): #изменились источник, субтитры | |
if m.doIt: break | |
date(x, c.p["mkv"], 1) | |
if m.doIt: print(g.ok+x) | |
mkvOpt="" | |
mp3opt="-c:a copy" if cGet(m.pne, "a")=="mp3" else g.mp3opt | |
map="-map 0:1 "+mp3opt | |
if m.src["png"]!=c.img: #не вкладываем пустую обложку | |
if c.img.is_file(): #вкладываем обложку | |
ec=g.ec+c.img.suffix | |
mkvOpt='--attachment-name %s --attach-file "%s"'%(ec, c.img) if m.doIt else '--delete-attachment name:%s --attachment-name %s --add-attachment "%s"'%(ec, ec, c.img) | |
map='-i "%s" -map 0:1 %s -map 1:0 -c:v copy'%( | |
c.img, mp3opt) | |
if m.doIt: #новый mkv | |
mkvIn=ifEx(m.neVid) | |
demkv=de.get("mkv", "")+sm() | |
if c.ts: | |
ts(m.lne, de["from"], de["to"]) | |
mkvIn=c.p["TS"] | |
de["ft"]="" | |
de["sync"]="" | |
demkv=sm() | |
neSnd=m.neSnd | |
if c.a: #режу mkvmerge | |
if c.ts: log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" ="%s" %s ', ( | |
c.p["MKV"], mkvIn, de["ft"])) | |
else: log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" ="%s" ="%s" %s ', ( | |
c.p["MKV"], mkvIn, neSnd, de["ft"])) | |
neSnd=c.p["MKA"] | |
mkvIn=c.p["MKV"] | |
tss="" | |
ffmIn=mkvIn | |
ffmFT=de["ft"] | |
if "audio" in c.a: #"file.mp3" 00:00:01.230 00:00:02.380 | |
repl=shlex.split(c.a["audio"].replace(",","."), comments=1, posix=1) | |
print(repl) | |
repl[0]=pl.path(stripp(repl[0])) | |
if repl[0].is_file(): | |
ffmIn=repl[0] | |
ffmFT="" | |
tss="-t %s"%c.dur | |
if len(repl)>2: | |
if sSec(repl[2]): tss="-t %s"%min(c.dur, sSec(repl[2])-sSec(repl[1])) | |
if len(repl)>1: | |
if sSec(repl[1]): tss+=" -ss %s"%repl[1] | |
ffmFT="%s-"%repl[1] | |
if len(repl)>2: | |
ffmFT="%s-%s"%tuple(repl[1:]) | |
ffmSnd=neSnd | |
if "filter_complex" in c.a: ffmSnd=pl.path(str(neSnd).replace(".MKA","_.MKA")) | |
log(g.ffmpegU, | |
'-v error %s -i "%s" -vn -sn %s %s -map_metadata -1 -y "%s"', ( | |
tss, ffmIn, aopt(), gain(ffmIn, ffmFT), ffmSnd)) | |
if "filter_complex" in c.a: | |
log(g.ffmpegU, | |
'-v error -i "%s" -i "%s" -vn -sn %s -filter_complex "%s" -map_metadata -1 -y "%s"', ( | |
c.p["MKV"], ffmSnd, aopt(), c.a["filter_complex"], neSnd)) | |
ffmSnd.delete() | |
de["ft"]="" | |
de["sync"]="" | |
if cGet(m.pne, "nd") or c.a: | |
if 0: log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" %s --default-language %s -A ="%s" --chapters "%s" %s --sub-charset 0:%s --default-track 0:yes %s ="%s" -D -S -B -T ="%s" --global-tags "%s" --title "%s" --track-order 0:0,2:0,1:0,0:2 %s', ( | |
c.p["mkv"], demkv, g.lang, mkvIn, c.p["xml"], de["ft"], utf8, de["sync"], c.p["srt"], neSnd, c.p["tag"], de["TIT2"], mkvOpt)) | |
else: log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" %s --default-language %s -A ="%s" --chapters "%s" %s --sub-charset 0:%s %s ="%s" -D -S -B -T ="%s" --global-tags "%s" --title "%s" --track-order 0:0,2:0,0:2,1:0 %s', ( | |
c.p["mkv"], demkv, g.lang, mkvIn, c.p["xml"], de["ft"], utf8, de["sync"], c.p["srt"], neSnd, c.p["tag"], de["TIT2"], mkvOpt)) | |
if not "keep" in de: | |
c.p["MKV"].delete() | |
c.p["MKA"].delete() | |
else: | |
if 0: log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" %s --default-language %s ="%s" --chapters "%s" %s --sub-charset 0:%s --default-track 0:yes %s ="%s" --global-tags "%s" --title "%s" --track-order 0:0,0:1,1:0,0:2 %s', ( | |
c.p["mkv"], demkv, g.lang, mkvIn, c.p["xml"], de["ft"], utf8, de["sync"], c.p["srt"], c.p["tag"], de["TIT2"], mkvOpt)) | |
else: log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" %s --default-language %s ="%s" --chapters "%s" %s --sub-charset 0:%s %s ="%s" --global-tags "%s" --title "%s" --track-order 0:0,0:1,0:2,1:0 %s', ( | |
c.p["mkv"], demkv, g.lang, mkvIn, c.p["xml"], de["ft"], utf8, de["sync"], c.p["srt"], c.p["tag"], de["TIT2"], mkvOpt)) | |
if not "keep" in de: | |
for x in ("TS", "264", "ac3", "wav", "flac", "sup.ts"): | |
if x in c.p: c.p[x].delete() | |
if not c.sm: jpeg(c.p["mkv"]) #last | |
else: #старый mkv | |
doMP3=not g.eyeD3 | |
if date(c.p["jpg"], c.p["mkv"]): #изменилась обложка | |
doMP3=1 | |
print(g.ok+c.p["jpg"]) | |
if c.newTags: print(g.ok+"c.newTags") | |
m.doIt=doMP3 or c.newTags | |
for x in (c.p["tag"], c.p["xml"]): #изменились тэги, чаптеры | |
if m.doIt: break | |
date(x, c.p["mkv"], 1) | |
if m.doIt: print(g.ok+x) | |
if m.doIt: | |
log(g.mkvpropeditU, '"%s" -t global:"%s" -c "%s" -s title="%s" %s -e track:v1 -s stereo-mode=%s', ( | |
c.p["mkv"], c.p["tag"], c.p["xml"], de["TIT2"], mkvOpt, c.sm)) | |
chapter() | |
#mp3 | |
if "nomp3" in de: return | |
m.doIt=m.ndur or c.newTags | |
if doMP3 or not c.p["mp3"].is_file(): | |
if date(c.p["mkv"], c.p["mp3"]): | |
log(g.ffmpegU, '-v error -i "%s" %s %s -y "%s"', ( | |
c.p["mkv"], map, g.metadata, c.p["mp3"])) | |
replayGain(0) | |
m.doIt=1 | |
doId(de) | |
def mkvOpt(s): | |
return s.replace("\\", "\\\\").replace(" ", "\\s").replace('"', "\\2").replace(":", "\\c").replace("#", "\\h").replace("[", "\\b").replace("]", "\\B") | |
def ms(fl): | |
return round(fl*1000)/1000 | |
def muxTS(wid, hei, fr, sh=0, out=0): | |
fps=round(eval(fr+".0"), 3) | |
mc=["MUXOPT --no-pcr-on-video-pid --vbr --vbv-len=500"] | |
res=("TS", "3d.ts", "iso", "sup.ts") | |
m.doIt=0 | |
if out==2: mc[0]+=' --blu-ray --label="BD3d"' | |
ext=g.ext[3:] if out==3 else g.ext | |
for x in ext: | |
if not x in c.p: continue | |
f=c.p[x] | |
s="" | |
if x in g.ext[:3]: #("264", "mpv") | |
#s="fps=%s,"%fps | |
if c.sm==13: s+=" level=%s,"%level() | |
elif not x in g.ext[-2:]: | |
if sh: s="timeshift=%sms,"%sh #("ac3", "wav") | |
elif x=="srt": | |
f=m.src[x] | |
tr="" | |
if "mka" in c.p: | |
if date(c.p["srt"], c.p["mka"]): log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" --sub-charset 0:%s ="%s"', ( | |
c.p["mka"], utf8, c.p["srt"])) | |
if c.p["mka"].is_file(): | |
f=c.p["mka"] | |
tr="track=1, " | |
s=tr+sup(wid, hei, fps) | |
if f.is_file(): | |
date(f, c.p[res[out]], 1) | |
if x=="264" and out in (1, 2): | |
mc+=['%s, "%s", %s lang=%s, subTrack=1'%( | |
g.ext2SI["mvc"], f, s, g.lang)] | |
mc+=['%s, "%s", %s lang=%s, subTrack=2'%( | |
g.ext2SI[x], f, s, g.lang)] | |
else: mc+=['%s, "%s", %s lang=%s'%( | |
g.ext2SI[x], f, s, g.lang)] | |
if m.doIt: | |
rw(c.p["meta"], g.n.join(mc), g.encoding) | |
log(g.tsmuxerU, '"%s" "%s"', ( | |
c.p["meta"], c.p[res[out]])) | |
def parse(n, s, d, cs=0): | |
"""пишу куе s в словарь d""" | |
#s должно быть похоже на "a b c d ...#[TAG1] a1 b1 ...#TAG2 a2 b2 ..." | |
lt=s.split("#") #lt[0]="a b c d ..." lt[1]="[TAG1] a1 b1 ..." | |
#if len(lt)<2: return 1 #не тот формат дискрипшена - нет "[TAG1] a1 b1 ..." | |
#lt0=lt[0].split(" ", 2) #lt0[0]="a" lt0[1]="b" lt0[2]="c d ..." | |
lt0=shlex.split(lt[0], comments=1, posix=1) #lt0[0]="a" lt0[1]="b" lt0[2]="c" lt0[3]="d" | |
if len(lt0)<3: | |
if m.iw: | |
if len(lt0)<1: return 1 | |
if len(lt0)<2: lt0.append("4") #для фоток по умолчении 4 секунды | |
if len(lt0)<3: lt0.append("0") | |
if len(lt0)<4: lt0.append("-") | |
else: return 1 #не тот формат дискрипшена - нет "c" | |
for x in range(3): lt0[x]=lt0[x].replace(",", ".") | |
if bSec(lt0[2]): #"c" это APIC | |
d["APIC"]=lt0[2] | |
st=1 | |
else: st=0 | |
if 0: | |
d2=pl.cwd / lt0[2+st] | |
if d2.is_file(): #"c" (или "d" если есть APIC) это файл который будет добавлен в конец | |
cc='0 - %s#%s%s'%( | |
" ".join([d.get("APIC", "")]+lt0[3+st:]), "#".join(lt[1:]), g.rn) | |
cf=pl.path(m.neAdd+".cut") | |
if m.neAdd.is_file(): | |
if not cf.is_file() or cf.read(g.encoding)!=cc: rw(cf, cc, g.encoding) | |
m(m.neAdd) | |
else: | |
if 0: | |
log(g.mkvmergeU, | |
'-q --disable-track-statistics-tags -o "%s" -S ="%s" -S +"%s"', ( | |
m.neAdd, m.lne, d2)) | |
else: | |
if 0: | |
log(g.ffmpegU, | |
'-v error -i "concat:%s|%s" -codec:v copy -codec:a copy -codec:s copy "%s"', ( | |
m.lne.name, d2.name, m.neAdd)) | |
else: | |
c=[] | |
for x in (m.lne, d2): | |
ts=pl.path(x+".ts") | |
if not ts.is_file(): log(g.ffmpegU, | |
'-v error %s -i "%s" %s -c copy "%s"', ( | |
"" if c else "-ss "+lt0[0], x, ("-t "+lt0[1]) if c else "", ts)) | |
c+=[ts] | |
if not m.neAdd.is_file(): log(g.ffmpegU, | |
'-v error -i "concat:%s" -c copy "%s"', ( | |
"|".join(c), m.neAdd)) | |
if not "keep" in d: | |
for x in c: x.delete() | |
#os.utime(str(m.neAdd), (d2.stat().st_atime, d2.stat().st_mtime)) | |
os.utime(str(m.neAdd), (m.lne.stat().st_atime, m.lne.stat().st_mtime)) | |
print('%s Файл "%s" добавлен после файла "%s" и записан в "%s"'%( | |
g.ok, d2, m.lne, m.neAdd)) | |
rw(cf, cc, g.encoding) | |
m(m.neAdd) | |
return 1 | |
d["title"]=" ".join(lt0[2+st:]) #d["title"]="c d ..." | |
d["index"]=n | |
if lt0[0].startswith(";"): | |
d["fake"]=1 | |
d["from"]=lt0[0][1:] | |
else: d["from"]=lt0[0] | |
df=d["from"] | |
mSec=round(sSec(d["from"])*1000) | |
if not mSec: d["from"]=g.fromBegin | |
if mSec: | |
d["ft"]="--split parts:%s-"%d["from"] | |
d["sync"]="--sync 0:%s"%mSec | |
else: | |
d["ft"]="" | |
d["sync"]="" | |
for x in cGet(d["title"], "tags", "uslt").split("#")+m.sw.split("#")+cGet(m.parent, "tags").split("#")+cGet(m.pne, "tags").split("#")+lt[1:]: #тэги песни + тэги альбома + тэги | |
if x: | |
kv=x.split(" ",1) #ABCD. efgh | |
kv0l=kv[0].lower() #abcd. | |
kv0rs=kv0l.rstrip(".") #abcd | |
if not kv0rs in g.etc+g.audio: kv0rs=kv0rs.upper() #ABCD | |
if kv0l.endswith("."): | |
par=m.parent if kv0l.endswith("..") else m.pne | |
if cGet(par, kv0rs): cDel(par, kv0rs) #удаляю старые тэги | |
tags=cGet(par, "tags").split("#") | |
for i, t in enumerate(tags): | |
if t.startswith(kv0rs+" "): | |
if len(kv)<2: tags[i]="" #удаляю родительский тэг | |
else: | |
tags[i]=" ".join((kv0rs, kv[1])) | |
d[kv0rs]=kv[1] | |
break | |
else: | |
if len(kv)>1: | |
tags.append(" ".join((kv0rs, kv[1]))) | |
d[kv0rs]=kv[1] | |
for x in range(tags.count("")): tags.remove("") | |
if not cs and tags: cSet(par, "tags", "#".join(tags)) | |
else: | |
if len(kv)<2: #удаляю тэг | |
if kv0rs in d: del d[kv0rs] | |
else: d[kv0rs]=kv[1] | |
if not "TPE1" in d: d["nomp3"]=1 | |
m.par=d.get("TALB", m.parent) #ожидаю альбом вида YYYYMMDD Событие | |
m.label=m.par.split()[0] | |
film=d.get("film", "") | |
dur(lt0, film) | |
sBegin=m.sBegin+timedelta(seconds=0 if m.iw else sSec(d["from"])) | |
d["TRCK"]=d.get("TRCK", sBegin.strftime("%H%M%S" if film else "%H%M")) | |
dt=lt0[1] | |
d["to"]=lt0[1] if ":" in lt0[1] else "0:"+lt0[1] | |
#if d.get("APIC", "")=="-": d["APIC"]=tSec(m.dur) | |
if lt0[1]=="-": | |
d["to"]=tSec(m.dur) | |
else: | |
if sSec(lt0[1]): | |
if d["ft"]: d["ft"]+=d["to"] | |
else: d["ft"]="--split parts:-"+d["to"] | |
#for k in d.keys(): print("d(%s)"%k, d[k]) | |
if d.get("APIC", ""): return 0 | |
if film and d.get("title", "-")!="-": d["APIC"]=d["from"] | |
if df==dt: | |
d["jpg"]=dt | |
d["APIC"]=d["to"] | |
return 0 | |
def pe(s): | |
print(s, end="") | |
def pic(d, lne=""): | |
if lne: dur=ms(float(d.get("ss", "1"))) | |
else: | |
dur=ms(m.dur) | |
lne=m.lne | |
s3d=int(d.get("3d", "") or "0") | |
if not s3d and c.sm: s3d=1 | |
loop=[-1, 1] if s3d else [0] | |
for x in loop: | |
sh=x*s3d | |
j={} | |
jd={"x": "0", "y": "0", "fr": d.get("r_frame_rate", g.fps), "ow": d.get("width", g.w), "oh": d.get("height", g.h), "w": "(iw-x*2)", "o": "0"} | |
#Sony i(23*p) M=1, N=12 cabac=1 / ref=2 / bframes=0 / keyint=24 Максимальный битрейт: 26,0 Мбит/сек | |
#Pana i(7*bbp)bb M=3, N=12 cabac=1 / ref=2 / bframes=1 / keyint=24 Максимальный битрейт: 25,0 Мбит/сек | |
zoom="" | |
for z in ("x", "y", "w", "xc", "yc", "h"): zoom+=d.get(z, "") #любой параметр приведёт к зуму | |
if zoom: d["zoom"]=zoom | |
if not d.get("w", "") and d.get("h", ""): d["w"]=str(int(d["h"])*int(jd["ow"])/int(jd["oh"])) | |
if d.get("xc", ""): | |
d["w"]=d.get("w", "ow") | |
d["x"]="(%s-%s/2)"%(d["xc"], d["w"]) | |
if d.get("yc", ""): | |
d["w"]=d.get("w", "ow") | |
d["y"]="(%s-%s*%s/%s/2)"%(d["yc"], d["w"], d.get("oh", jd["oh"]), d.get("ow", jd["ow"])) | |
for z in jd.keys(): j[z]=d.get(z, jd[z]) | |
j["o"]=int(j["o"]) | |
if d.get("first", ""): | |
flast=pl.path(stripp(d["first"])) #prev.jpeg | |
if sh>0: flast=flast.with_suffix(".3D.jpeg") | |
else: flast=lne.with_suffix(".3D.jpeg" if sh>0 else ".jpeg") | |
if m.ndur or m.newD or date(lne, c.p["mkv"]) or date(flast, c.p["mkv"]): | |
fps=round(eval(j["fr"]+".0"),3) | |
f=int(dur*fps) #количество кадров | |
vf=[] | |
j["iw"], j["ih"]=m.iw, m.ih | |
if not j["o"]: | |
if m.exif: | |
oKey=ExifTags.TAGS.keys()[ExifTags.TAGS.values().index("Orientation")] | |
if oKey in m.exif: | |
j["o"]=m.exif[oKey] | |
if j["o"] is 6: vf+=["transpose=clock"] #vf+=["rotate=PI/2:ih:iw"] | |
elif j["o"] is 8: vf+=["transpose=cclock"] #vf+=["rotate=-PI/2:ih:iw"] | |
elif j["o"] is 3: vf+=["rotate=PI"] | |
elif j["o"] is 2: vf+=["hflip"] | |
elif j["o"] is 5: vf+=["transpose=cclock_flip"] #vf+=["rotate=-PI/2:ih:iw,hflip"] | |
elif j["o"] is 7: vf+=["transpose=clock_flip"] #vf+=["rotate=PI/2:ih:iw,hflip"] | |
elif j["o"] is 4: vf+=["vflip"] | |
if j["o"] in (6, 8, 5, 7): | |
j["ih"], j["iw"]=m.iw, m.ih | |
d["x"], d["y"]=d["y"], d["x"] | |
for z in ("ih", "iw"): j[z]=str(j[z]) | |
for z in ("x", "y", "ow", "oh", "ih", "iw"): | |
if z!="x" and z in j["x"]: j["x"]=j["x"].replace(z, j[z]) | |
if z!="y" and z in j["y"]: j["y"]=j["y"].replace(z, j[z]) | |
if z in j["w"]: j["w"]=j["w"].replace(z, j[z]) | |
#print(z, j[z]) | |
w=int(j["iw"])*2-int(j["ih"])*int(j["ow"])/int(j["oh"]) | |
h=m.iw!=int(j["ow"]) | |
if not m.src["srt"].is_file(): m.src["srt"].write("1\n00:00:00,000 --> 00:00:00,500\n\n") | |
if sh>0: | |
if m.mpo: | |
if date(lne, c.lne): | |
ll="-vf reverse -frames:v 1 -q:v 1" if 0 else "-ss 0.001 -frames:v 1 -bsf:v mjpeg2jpeg -c:v copy" | |
log(g.ffmpegU, '-v error -y -f mjpeg -i "%s" %s "%s"', ( | |
lne, ll, c.lne)) | |
else: | |
j["x"]=int(eval(j["x"]))+sh | |
c.lne=lne | |
sou=c.lne if sh>0 else lne | |
tar=c.p["yuv" if sh<0 else "3D.yuv"] | |
if zoom: | |
ffsou='-i "%s"'%sou | |
inp="0" #hflip,[0]hstack | |
if vf: | |
inp="r" | |
vf+=["split[%s]"%inp] #vflip,split[r],hflip,[r]hstack | |
vf+=["hflip"] | |
vf+=["[%s]hstack"%inp] | |
if w<0: vf+=["pad=ih*%s/%s:ih:0:0:gray"%( | |
j["ow"], j["oh"])] | |
if w>0: vf+=["crop=ih*%s/%s:ih:0:0"%( | |
j["ow"], j["oh"])] | |
vf+=["zoompan=zoom+(iw/%s-1)/%s:x+%s/%s:y+%s/%s:%s:%sx%s:%s"%( | |
j["w"], f, j["x"], f, j["y"], f, f, j["ow"], j["oh"], j["fr"])] | |
else: #scroll | |
v="sqrt(%s-(n-%s)*(n-%s))"%(f*f, f ,f) # сектор от 45 до 0 минут | |
if float(m.iw)/m.ih==float(j["ow"])/float(j["oh"]): w=0 | |
if w<0: vf+=["pad=ih*%s/%s:ih:0:0:gray"%( | |
j["ow"], j["oh"])] | |
if w>0: vf+=["crop=ih*%s/%s:ih:0:0"%( | |
j["ow"], j["oh"])] | |
if h: vf+=["scale=%s:%s"%( | |
j["ow"], j["oh"])] | |
if flast.is_file(): #A из #last или first | |
ffsou='-loop 1 -r %s -i "%s" -r %s -i "%s"'%( | |
j["fr"], sou, j["fr"], flast) | |
inp="1" | |
vf+=["[%s]hstack"%inp] | |
if w<0: vf+=["pad=ih*%s/%s:ih:0:0:gray"%( | |
j["ow"], j["oh"])] | |
vf+=["crop=%s:%s:ow/%s*%s"%( | |
j["ow"], j["oh"], f, v)] | |
else: #A это отражение B | |
ffsou='-loop 1 -r %s -i "%s"'%( | |
j["fr"], sou) | |
inp="0" #hflip[f],[f][0]hstack | |
if vf: | |
inp="r" | |
vf+=["split[%s]"%inp] #vflip,split[r],hflip[f],[f][r]hstack | |
vf+=["hflip[f]"] | |
vf+=["[f][%s]hstack"%inp] | |
vf+=["crop=%s:%s:ow/%s*%s"%( | |
j["ow"], j["oh"], f, v)] | |
if m.ndur or date(sou, c.p["264"]): | |
if sh: | |
if date(sou, tar): log(g.ffmpegU,'-v error -y %s -filter_complex "%s" -pix_fmt yuv420p -frames:v %s "%s"', ( | |
ffsou, ",".join(vf), f, tar)) | |
if sh>0: | |
jpeg(tar, "3D.jpeg") #last.3d.jpeg | |
if c.apic!="-": jpg(tar) | |
kodi(1) | |
else: | |
jpeg(tar) #last.jpeg | |
continue | |
else: log(g.ffmpegU,'-v error -y %s -filter_complex "%s" -pix_fmt yuv420p %s -frames:v %s "%s"', ( | |
ffsou, ",".join(vf), enc264(), f, c.p["264"])) | |
shift=0 | |
if m.ndur or not c.p[m.aext].is_file(): | |
ch=d.get("channels", 2) | |
sr=int(d.get("sample_rate", "48000")) | |
if m.aext=="wav": | |
c.p["ac3"].delete() | |
opt="-t %s"%dur | |
else: | |
c.p["wav"].delete() | |
afps=sr/1536.0 | |
dur=round(dur*afps) #3*31.25=93.75 => 94=> 94/31.25=3.008 | |
dur=dur*afps//1 #3*31.25=93.75 => 93=> 93/31.25=2.976 | |
shift=int((m.dur-dur/afps)*1000) #(3.0-2.976)*1000=24 timeshift=24ms | |
shift=int((dur-dur/afps)*1000) #(3.0-2.976)*1000=24 timeshift=24ms | |
opt="-frames:a %s"%dur | |
log(g.ffmpegU, '-v error -y -f lavfi -i anullsrc=%s:%s %s %s "%s"', ( #пустой звук | |
ch, sr, opt, aopt(), c.p[m.aext])) | |
if m.ndur or date(c.p["264"], c.p["mkv"]): | |
title00=c.p["TS"].up()/"title00.mkv" | |
if c.sm==13: | |
for x in (g.makeMkvIso+1, 3): muxTS(jd["ow"], jd["oh"], jd["fr"], shift, x) | |
if g.makeMkvIso and c.sm==13: | |
log(g.makemkvU, 'mkv iso:"%s" all "%s"', ( #из iso в makemkv | |
c.p["iso"], title00.up())) | |
nt=('-A ="%s" ="%s"'%(c.p["sup.ts"], m.src["srt"])) if c.nt else ' --global-tags "%s" --chapters "%s" -A ="%s" --sub-charset 0:%s ="%s"'%( | |
c.p["tag"], c.p["xml"], c.p["sup.ts"], utf8, c.p["srt"]) | |
log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" --default-language %s -S ="%s" %s', ( #из makemkv в mkv | |
c.p["mkv"], g.lang, title00, nt)) | |
else: | |
muxTS(jd["ow"], jd["oh"], jd["fr"], shift) #в TS | |
#--engage force_passthrough_packetizer | |
nt=('="%s"'%m.src["srt"]) if c.nt else ' --global-tags "%s" --chapters "%s" --sub-charset 0:%s ="%s"'%( | |
c.p["tag"], c.p["xml"], utf8, c.p["srt"]) | |
if not g.makeMkvIso and c.sm==13: nt="" | |
log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" --default-language %s %s ="%s" %s', ( #из TS в mkv | |
c.p["mkv"], g.lang, sm(), c.p["TS"], nt)) | |
#log(g.ffmpegU,'-i "%s" -sn -an -c copy -metadata stereo_mode=block_lr "%s"',(c.p["TS"], c.p["mkv"])) | |
#log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" --default-language %s %s ="%s"', (c.p["mkv"], g.lang, sm, c.p["TS"])) | |
if not g.makeMkvIso and c.sm==13: | |
log(g.makemkvU, 'mkv file:"%s" all "%s"', ( #из mkv в makemkv | |
c.p["mkv"], title00.up())) | |
nt=('-A ="%s" ="%s"'%(c.p["sup.ts"], m.src["srt"])) if c.nt else ' --global-tags "%s" --chapters "%s" -A ="%s" --sub-charset 0:%s ="%s"'%( | |
c.p["tag"], c.p["xml"], c.p["sup.ts"], utf8, c.p["srt"]) | |
log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" --default-language %s -S ="%s" %s', ( #из makemkv в mkv | |
c.p["mkv"], g.lang, title00, nt)) | |
title00.delete() | |
if c.nt: c.p["sup.ts"].delete() | |
if not c.sm: jpeg(c.p["mkv"]) #last.jpeg | |
if not c.sm: jpg(c.p["mkv"]) | |
chapter() | |
if not "keep" in d: | |
for x in c.tmp: | |
if x in c.p: c.p[x].delete() | |
g.prev=lne | |
return 1 | |
def cod2lib(s): | |
c2l={"mp3": "libmp3lame", "h264": "libx264"} | |
return c2l.get(s, s) | |
def ppuc(s, t): | |
"""s копирую в t""" | |
pe('path.utils.copy("%s", "%s")' % ( | |
s, t)) | |
try: path.utils.copy(s, t) | |
except: | |
print(g.ob) | |
return 1 | |
else: print(g.ok) | |
return 0 | |
def ren(d, s, t, e, k=0): | |
result=d/(t+"."+e) | |
if k: result.delete() | |
else: | |
if s!=t: | |
tmp=d/(s+"."+e) | |
if tmp.is_file(): tmp.rename(result) | |
return result | |
def replayGain(r=1): | |
if r: | |
c.replay={} | |
for y in c.rg: | |
c.replay[y]=cGet(c.pn, y) | |
if c.replay[y]=="": break | |
else: | |
return | |
c.replay={} | |
for x in log(g.ffmpegU, '-i "%s" -vn -af replaygain -sn -f null -', c.p["mp3"], 1).splitlines(): | |
if x.startswith("[Parsed_replaygain"): | |
for y in c.rg: | |
z="track_"+y+" =" | |
if z in x: | |
c.replay[y]=x.split(z)[1].strip() | |
cSet(c.pn, y, c.replay[y]) | |
for y in c.rg: | |
if not y in c.replay: c.replay[y]="" | |
return | |
def rCHwSRT(): | |
"читаю чаптеры и пишу субтитры" | |
srtI=1 | |
srtB=[] | |
if 0: | |
if date(f.mkv, f.p["xml"]): #извлекаю чаптеры | |
srtD={} | |
log(g.mkvextractU, 'chapters "%s" -r "%s"', ( | |
f.mkv, f.p["xml"])) | |
for lch in f.p["xml"].read(utf8).splitlines(): | |
lch=lch.strip() | |
for ch in ("TimeStart", "String"): | |
if lch.startswith("<Chapter%s>"%ch): | |
srtD[ch]=lch[len(ch)+9:][:-len(ch)-10] | |
if ch=="String": | |
srtB+=[str(srtI)] | |
srtI+=1 | |
srtB+=["%s --> %s"%( | |
tSec(sSec(srtD["TimeStart"])+g.srtF, 1), tSec(sSec(srtD["TimeStart"])+g.srtF+g.srtD, 1))] | |
srtB+=[str(f.name)] | |
srtB+=[srtD["String"]] | |
srtB+=[""] | |
chap+=[tSec(sSec(srtD["TimeStart"]))] | |
else: | |
if date(f.mkv, f.p["cha"]): | |
rw(f.p["cha"], log(g.ffprobeU, '-v error -show_chapters -show_entries chapter=start_time -pretty -of csv=p=0 "%s"', f.mkv)) | |
for lch in f.p["cha"].read(utf8).splitlines(): | |
srtD=lch.split("000,") | |
srtB+=[str(srtI)] | |
srtI+=1 | |
srtB+=["%s --> %s"%( | |
tSec(sSec(srtD[0])+g.srtF, 1), tSec(sSec(srtD[0])+g.srtF+g.srtD, 1))] | |
srtB+=[str(f.name)] | |
srtB+=[srtD[1]] | |
srtB+=[""] | |
chap+=[tSec(sSec(srtD[0]))] | |
if not srtB: return | |
rw(f.p["srt"], g.n.join(srtB)) | |
fmkv_=pl.path(str(f.mkv).replace(".mkv","_.mkv")) | |
mkvOpt=('-t global:"%s"'%f.p["tag"]) if f.p["tag"].is_file() else "" | |
log(g.mkvmergeU, | |
'-q --disable-track-statistics-tags -o "%s" -M -T --no-global-tags ="%s" --sub-charset 0:%s --default-track 0:yes ="%s" %s --track-order 0:0,0:1,0:2,1:0', ( | |
fmkv_, f.mkv, utf8, f.p["srt"], mkvOpt)) | |
if fmkv_.is_file(): fmkv_.rename(f.mkv) | |
f.p["srt"].touch() | |
f.p["cha"].touch() | |
def rSRTwCH(): | |
"читаю субтитры пишу чаптеры" | |
if date(f.mkv, f.p["srt"]): | |
f.p["srt"].delete() | |
if 1: log(g.mkvextractU, 'tracks "%s" %s:"%s"', ( | |
f.mkv, f.iCN[0], f.p["srt"])) | |
else: log(g.ffmpegU, '-v error -i "%s" -map 0:%s "%s"', ( #быстрее но без BOM | |
f.mkv, f.iCN[0], f.p["srt"])) | |
if not date(f.p["srt"], f.p["xml"]): return | |
pmx=['<?xml version="1.0" encoding="%s"?><Chapters><EditionEntry>'%coding] | |
for i, lsrt in enumerate(f.p["srt"].read(utf8).splitlines()): | |
if i%5==1: | |
tc=lsrt.split(" --> ") | |
sec=max(sSec(tc[0])-g.srtF, 0) | |
mxdur=g.bd+timedelta(seconds=sec) | |
mxdurR=g.bd+timedelta(seconds=round(sec)) #mxdurR.strftime("%H:%M:%S")+" "+lsrt | |
elif i%5==3: | |
f.chap+=[mxdur.strftime("%H:%M:%S.%f")[:12]] | |
pmx+=['<ChapterAtom><ChapterTimeStart>%s</ChapterTimeStart><ChapterDisplay><ChapterString>%s</ChapterString><ChapterLanguage>%s</ChapterLanguage></ChapterDisplay></ChapterAtom>'%( | |
mxdur.strftime("%H:%M:%S.%f")[:12], lsrt, g.lang)] | |
if len(pmx)<2: return | |
pmx+=["</EditionEntry></Chapters>"] | |
rw(f.p["xml"], g.rn.join(pmx), g.encoding) | |
mkvOpt=('-t global:"%s"'%f.p["tag"]) if f.p["tag"].is_file() else "" | |
log(g.mkvpropeditU, '--delete-attachment name:%s.jpg --delete-track-statistics-tags -t all: -t global: -c "%s" %s -s title="%s" "%s"', ( | |
g.ec, f.p["xml"], mkvOpt, f.name, f.mkv)) | |
f.p["srt"].touch() | |
f.p["xml"].touch() | |
def rw(f, t, e="utf-8", old=""): | |
if old: s=old | |
else: | |
try: s=f.read(e) | |
except: s="" | |
if s==t: return 0 | |
f.write(t, e) | |
return 1 | |
def s2d(s, sL="#", sKV=" "): | |
d=[] | |
ss=s.split(sL) | |
if sL==sKV: | |
for i, x in enumerate(ss): | |
if i%2: d.append([k, x]) | |
else: k=x | |
if not i%2: d.append([k, ""]) | |
else: | |
for x in ss: | |
if x: | |
if sKV in x: d.append(x.split(sKV, 1)) | |
else: d.append([x, ""]) | |
return dict(d) | |
def sm(): | |
return (" --stereo-mode 0:%s"%c.sm) if c.sm else "" | |
def srt(s, t, d): | |
""" пишу субтитры для mkv""" | |
if c.nt: return | |
m_srt=g.srtF if c.film else g.srt | |
ftT=tSec(m_srt, 1), tSec(min(m_srt+g.srtD, c.dur-0.1), 1), m.par, t | |
if c.p["srt"].is_file(): | |
srS=c.p["srt"].read(utf8) | |
srSsl=srS.splitlines() | |
ftS=srSsl[1].split(" --> ") | |
ftS.append(srSsl[2]) | |
ftS.append(srSsl[3]) #s | |
srT=srS | |
for x in range(0, len(ftT)): | |
if ftS[x]!=ftT[x]: srT=srT.replace(ftS[x], ftT[x], 1) | |
rw(c.p["srt"], srT, old=srS) | |
else: c.p["srt"].write("1\n%s --> %s\n%s\n%s\n"%ftT, utf8) | |
def sSec(s): | |
""" из 00:00:00.001 в 0.001 """ | |
try: return sum([60**(2-i)*k for i, k in enumerate(map(float, (("0:0:"+s.replace(",", ".")).split(':'))[-3:]))]) | |
except: return 0 | |
def stem2l(s): #stem2l("20160827 Свадьба 134612 Мерс уехал") | |
for x in s.split(): | |
if x.isdigit() and len(x) in (4, 6): | |
trck=x | |
film=len(x)==6 | |
talb=s.partition(" %s "%x)[0] | |
title=s.partition(" %s "%x)[2] | |
return talb, trck, title, film | |
return "", "", "", "" | |
def stripp(s): | |
return s.strip("'").strip('"') | |
def sup(wid, hei, fps): | |
return g.sup%tuple([int(float(hei)*x/1080) for x in (65, 24, 5)]+[wid, hei, fps]) | |
def tg(d, nt, film, COMMENT, KEYWORDS, p, tr=1): # tg(d, c.nt, c.film, c.COMMENT, c.KEYWORDS, c.p["tag"]) | |
""" пишу tag файл для mkv""" | |
if nt: return | |
ALBUM=50 | |
TargetType="MOVIE" if film else "ALBUM" | |
l=['<?xml version="1.0" encoding="%s"?><Tags><Tag><Targets><TargetTypeValue>%s</TargetTypeValue><TargetType>%s</TargetType></Targets>'%( | |
coding, ALBUM, TargetType)] #пишу в список строк заголовок | |
tag={} | |
tag["TITLE"]=d["TALB"] | |
tag["PUBLISHER"]=d["TPUB"] | |
tag["DATE_RECORDED"]="%s-%s-%s"%(d["TYER"], d["TDAT"][2:4], d["TDAT"][0:2]) | |
for k, v in tag.items(): #пишу в список строк тело альбома | |
l+=["<Simple><Name>%s</Name><String>%s</String></Simple>"%(k, v)] | |
if tr: | |
TRACK=30 | |
TargetType="CHAPTER" if film else "TRACK" | |
l+=["</Tag><Tag><Targets><TargetTypeValue>%s</TargetTypeValue><TargetType>%s</TargetType></Targets>"%( | |
TRACK, TargetType)] #пишу в список строк конец альбома | |
tag={} #словарь mkv тэгов | |
mkv2id3v23={"ARTIST":"TPE1", #соответствие тэгов | |
"ACCOMPANIMENT":"TPE2", | |
"COMPOSER":"TCOM", | |
"CONDUCTOR":"TPE3", | |
"LYRICIST":"TEXT", | |
"PART_NUMBER":"TRCK", | |
"SUBTITLE":"TIT3", | |
"TITLE":"TIT2"} | |
for k, v in mkv2id3v23.items(): #пишу в словарь mkv тэгов совпадающие тэги | |
if v in d and d[v]: tag[k]=d[v] | |
if COMMENT: tag["COMMENT"]=COMMENT | |
tag["KEYWORDS"]=", ".join(KEYWORDS) | |
for k, v in d.items(): #пишу в словарь mkv тэгов не id3v23 тэги | |
if k.isupper(): | |
if k not in g.id3v23: | |
tag[k]=v | |
for k, v in tag.items(): #пишу в список строк тело | |
#print("%s=%s"%(k, v)) | |
l+=["<Simple><Name>%s</Name><String>%s</String></Simple>"%(k, v)] | |
l+=["</Tag></Tags>"] #пишу в список строк конец | |
rw(p, g.rn.join(l), coding) | |
def ts(s, f, t): | |
shift=0 | |
sec=sSec(f) | |
ow, oh, fr=(c.de.get("width", g.w), c.de.get("height", g.h), c.de.get("r_frame_rate", g.fps)) | |
fps=round(eval(fr+".0"), 3) | |
fvb=sec*fps//1 #первый кадр | |
sfvb=ms(fvb/fps) #время первого кадра | |
dur=ms(sSec(t)-sec) | |
fv=round(dur*fps) #длительность в кадрах | |
fve=fvb+fv #последний кадр | |
sfve=ms(fve/fps) #время последнего кадра | |
ss=("-ss %s"%sfvb) if sfvb else "" | |
print("%s=>%s %s+%s=%s=>ms(%s)=%s"%(sec, sfvb, fvb, fv, fve, fve/fps, sfve)) | |
if c.ts==-1: | |
next=c.de.get("next", "") | |
fnext=m.next if next=="*" else pl.path(stripp(next)) | |
if not fnext.is_file(): return | |
lts=[] | |
for x in (m.lne, fnext): | |
ts=pl.path(x+".ts") | |
opt=('-i "%s" -t %s'%(x, t)) if lts else tss(x, sfvb) | |
if date(x, ts): log(g.ffmpegU, | |
'-v error %s -c copy "%s"', ( | |
opt, ts)) | |
lts+=[ts] | |
if date(lts[0], c.p["TS"]) or date(lts[1], c.p["TS"]): | |
log(g.ffmpegU, '-v error -i "concat:%s" -c copy "%s"', ( | |
"|".join(lts), c.p["TS"])) | |
if not "keep" in c.de: | |
for x in lts: x.delete() | |
return | |
elif c.ts==1: #tsMuxeR~ts | |
meta=tsmux('"%s"'%s, 1) | |
mc=["MUXOPT --no-pcr-on-video-pid --vbr --cut-start=%sms --cut-end=%sms --vbv-len=500"%( | |
int(sfvb*1000), int(sfve*1000) )] | |
for i, x in enumerate(meta["Stream ID:"]): | |
mc+=['%s, "%s", track=%s'%( | |
x, s, meta["Track ID:"][i])] | |
rw(c.p["meta"], g.n.join(mc), g.encoding) | |
log(g.tsmuxerU, '"%s" "%s"', ( | |
c.p["meta"], c.p["TS"])) | |
elif c.ts in (2, 5): | |
#2: smmbs~mkv~ffmpeg(264, wav)~tsMuxeR~ts | |
#5: smmbs~mkv~mkvmerge~ts | |
xtl =['<timelines version="2" >'] | |
xtl+=[' <timeline>'] | |
xtl+=[' <group output="%s" out_type="matroska" >'%c.p["TS"]] | |
xtl+=[' <track video="1" audio="1" text="0" obey_sample_times="1" accuracy="frame" flags="interlaced_fields_alignment,keep_mpeg_closedcaptions,keep_rtp_hint_tracks" >'] if 0 else [' <track video="1" audio="1" accuracy="frame">'] | |
xtl+=[' <clip src="%s" start="%s" stop="%s" timeFormat="time10ms" />'%( | |
s, f, t)] | |
xtl+=[' </track>'] | |
xtl+=[' </group>'] | |
xtl+=[' </timeline>'] | |
xtl+=['</timelines>'] | |
rw(c.p["xtl"], g.rn.join(xtl), g.encoding) | |
log(g.smmbsU, '"%s"', c.p["xtl"]) | |
if date(c.p["TS"], c.p[m.vext]): #демукс в 264 и wav | |
aopt="" if m.aext=="wav" else "-c:a copy" | |
if aopt: log(g.mkvextractU, 'tracks "%s" 0:"%s" 1:"%s"', ( | |
c.p["TS"], c.p[m.vext], c.p[m.aext])) | |
else: log(g.ffmpegU, '-v error -y -i "%s" -map 0:v -c:v copy "%s" -map 0:a %s "%s"', ( | |
c.p["TS"], c.p[m.vext], aopt, c.p[m.aext])) | |
elif c.ts in (3, 4, 6): | |
#3: ffmpegCopy(264, wav)~tsMuxeR~ts | |
#4: ffmpegEnc(264, wav)~tsMuxeR~ts | |
#6: ffmpegCopy(264, wav)~mkvmerge~ts | |
if date(s, c.p[m.vext]): log(g.ffmpegU, '-v error -y %s -i "%s" %s -frames:v %s -map 0:v %s "%s"', ( | |
cuvid(m.vext), s, ss, int(fv), enc264() if c.ts==4 else "-c:v copy", c.p[m.vext])) | |
if not m.aext=="ac3": | |
c.p["ac3"].delete() | |
if date(s, c.p[m.aext]): log(g.ffmpegU, '-v error -y %s -i "%s" %s -t %s -map 0:a "%s"', ( | |
cuvid(m.vext), s, ss, ms(fv/fps), c.p[m.aext])) | |
else: | |
c.p["wav"].delete() | |
afps=int(c.de.get("sample_rate", "48000"))/1536.0 | |
fab=sfvb*afps//1 | |
if (dur*1000)%(1000/afps): #не целое количество аудиокадров | |
fab+=1 #первый аудиокадр выбираем после видеокадра | |
sfab=ms(fab/afps) #время первого кадра | |
lGap=ms(sfab-sfvb) | |
fa=(sfve-sfab)*afps//1 #длительность в кадрах с отбрасыванием дробной части | |
sfae=ms((fab+fa)/afps) | |
rGap=ms(sfve-sfae) | |
shift=int((lGap if 0 else ms(lGap+rGap))*1000) | |
print("shift=", shift) | |
print(sec, "=>", sfvb, "+", lGap, "+", fa/afps, "+", rGap, "=", sfvb+lGap+fa/afps+rGap) | |
else: #целое количество аудиокадров | |
sfab=ms(fab/afps) #время первого кадра | |
fa=(dur*1000)//(1000/afps) | |
sfae=ms((fab+fa)/afps) | |
print("%s=>%s %s+%s=%s=>ms(%s)=%s"%(sec, sfab, fab, fa, fab+fa, (fab+fa)/afps, sfae)) | |
ss=("-ss %s"%sfab) if sfab else "" | |
if date(s, c.p[m.aext]): log(g.ffmpegU, '-v error -y %s -i "%s" %s -frames:a %s -map 0:a -c:a copy "%s"', ( | |
cuvid(m.vext), s, ss, int(fa), c.p[m.aext])) | |
elif c.ts in (7, 8): | |
m.aext="wav" | |
if not c.sm: | |
c.de["kodi"]="mvc" | |
kodi() | |
if date(s, c.p[m.aext]): log(g.ffmpegU, '-v error -y %s %s -t %s -map 0:a "%s"', ( #достаю звук | |
cuvid(m.vext), tss(s, sfvb), ms(fv/fps), c.p[m.aext])) | |
scale="" if c.ts==8 else "-vf scale=%s:%s"%(ow, oh) | |
for i, x in enumerate(("yuv", "3D.yuv")): #достаю левый и правый ракурсы | |
if date(s, c.p[x]): log(g.ffmpegU, '-v error -y %s %s -frames:v %s -map 0:v:%s -pix_fmt yuv420p %s "%s"', ( | |
cuvid(m.vext), tss(s, sfve-1), int(fps), i, scale, c.p[x])) | |
jpeg(c.p[x], "3D.jpeg" if i else "jpeg") | |
c.p[x].delete() | |
if c.vf: | |
if date(s, c.p["264"]): log(g.ffmpegU,'-v error -y %s %s -frames:v %s -filter_complex [0:v:0][0:v:1]%s %s -x264opts frame-packing=%s -metadata stereo_mode=%s "%s"', ( | |
cuvid(m.vext), tss(s, sfvb), int(fv), c.vf, enc264(), c.fp, c.ff, c.p["264"])) | |
else: | |
if 0: | |
for i, x in enumerate(("yuv", "3D.yuv")): | |
if date(s, c.p[x]): log(g.ffmpegU, '-v error -y %s %s -frames:v %s -map 0:v:%s -pix_fmt yuv420p -vf scale=%s:%s "%s"', ( | |
cuvid(m.vext), tss(s, sfvb), int(fv), i, ow, oh, c.p[x])) | |
kodi(2) | |
if 1: | |
if date(s, c.p["264"]): kodi(2, '"%s" -v error -y %s %s -frames:v %s -filter_complex [0:v:0][0:v:1]hstack,scale=%s*2:%s/dar*2 -pix_fmt yuv420p -f rawvideo -'%( | |
g.ffmpegU, cuvid(m.vext), tss(s, sfvb), int(fv), ow, ow )) | |
title00=c.p["TS"].up()/"title00.mkv" | |
for x in (g.makeMkvIso+1, 3): muxTS(ow, oh, fr, shift, x) | |
if g.makeMkvIso: | |
log(g.makemkvU, 'mkv iso:"%s" all "%s"', ( #из iso в makemkv | |
c.p["iso"], title00.up())) | |
log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" --default-language %s -S ="%s" -A ="%s"', ( #из makemkv в mkv | |
c.p["TS"], g.lang, title00, c.p["sup.ts"])) | |
if not "keep" in c.de: | |
for x in c.tmp: | |
if x!="TS" and x in c.p: c.p[x].delete() | |
return | |
if c.ts in (5, 6): | |
#log(g.ffmpegU, '-y -i "%s" -i "%s" -i "%s" -c copy -f matroska "%s"',(c.p[m.vext], c.p[m.aext], c.p["srt"], c.p["TS"])) | |
muxTS(ow, oh, fr, shift, 3) | |
log(g.mkvmergeU, '-q --disable-track-statistics-tags -o "%s" ="%s" ="%s" -A ="%s"', ( | |
c.p["TS"], c.p[m.vext], c.p[m.aext], c.p["sup.ts"])) | |
return | |
muxTS(ow, oh, fr, shift) | |
def tss(s, sfvb, i=1.0): | |
_ss=ss_="" | |
if sfvb: | |
_ss=("-ss %s"%(sfvb-i)) if sfvb>i else "" | |
ss_="-ss %s"%((i if sfvb>i else sfvb)-0.0001) | |
return '%s -i "%s" %s'%(_ss, s, ss_) | |
def tsmux(s, isTS=""): | |
meta={} | |
li=("Track ID:", "Stream type:", "Stream ID:", "Stream info:", "Stream lang:", "subTrack:") | |
for x in log(g.tsmuxerU, "%s", s).splitlines(): | |
for y in li+("Duration:", "Marks:"): | |
if x.startswith(y): | |
if not y in meta: meta[y]=[] | |
meta[y].append(x[len(y):].strip()) | |
if isTS: | |
for i, x in enumerate(meta["Stream type:"]): | |
if x=="PGS": | |
for y in li: del meta[y][i] | |
for x in meta["Stream info:"]: | |
for y in ("Profile:", "Resolution:", "Frame rate:", "Bitrate:", "Sample Rate:", "Channels:"): | |
if y in x: | |
if not y in meta: meta[y]=x.partition(y+" ")[2].split(" ")[0] | |
if "Resolution:" in meta: | |
meta["Width:"], meta["Height:"]=meta["Resolution:"].split(":") | |
meta["Height:"]=meta["Height:"].rstrip("ip") | |
meta["Chapters:"]=[] | |
if "Marks:" in meta: | |
for x in meta["Marks:"]: meta["Chapters:"]+=x.split(" ") | |
return meta | |
def tsmuxer(meta, plp, name="tsMuxeR"): | |
plpn=plp.up().name | |
mc=[] | |
mc2=[] | |
for i, x in enumerate(meta["Stream ID:"]): | |
if i>(2+f.mvc): break #mvc avc snd sup | |
track="track=%s,"%meta["Track ID:"][i] | |
s="" | |
ex=g.SI2ext[x] | |
if x in g.SI[:3]: # ex in ("mvc", "264", "mpv") | |
s="%sei% %sps%" | |
if g.makeMkvIso and f.mvc: track+=" subTrack=%s,"%meta["subTrack:"][i] | |
elif ex=="sup": s=" fps=%s,"%meta["Frame rate:"] | |
elif ex=="srt": s=sup(meta["Width:"], meta["Height:"], meta["Frame rate:"]) | |
mc2+=['echo %s, %%lof%%,%s %s lang=%s'%( | |
x, s, track, meta["Stream lang:"][i])] | |
if ex=="srt": | |
ex="sup" | |
x=g.ext2SI[ex] | |
s=" fps=%s,"%meta["Frame rate:"] | |
fil=["%%label%%%s.track_%s%s.%s"%(y, meta["Track ID:"][i], "_2" if ex=="264" and f.mvc and g.makeMkvIso else "" ,ex) for y in ("", "*")] | |
mc+=['ren "%s" "%s"'%( | |
fil[1], fil[0])] | |
mc+=['if EXIST "%s" echo %s, "%s",%s lang=%s'%( | |
fil[0], x, fil[0], s, meta["Stream lang:"][i])] | |
rw(plp/("%s.bat"%name), '''@echo off | |
chcp %s>nul | |
set demux=1 | |
set sei= | |
set sps= | |
set nap= | |
cd /d "%%~dp0" | |
set meta=%%~n0.meta | |
set label=%s | |
set iso=%s | |
call lof.bat | |
goto first | |
:loop | |
SHIFT | |
:first | |
echo 1).ts | |
echo 2).m2ts | |
echo 3).iso | |
echo 4)Blu-Ray | |
echo 5)avcHD | |
if not DEFINED demux echo 6)deMux | |
if DEFINED demux echo 7)Источником будут файлы после deMux | |
if not DEFINED demux echo 7)Источником будут файлы до deMux | |
if not DEFINED sei echo 8)insertSEI будет установлен | |
if "%%sei%%"=="insertSEI," echo 8)forceSEI будет установлен | |
if "%%sei%%"=="forceSEI," echo 8)forceSEI будет снят | |
if DEFINED sps echo 9)contSPS будет снят | |
if not DEFINED sps echo 9)contSPS будет установлен | |
if DEFINED nap echo 0)new-audio-pes будет снят | |
if not DEFINED nap echo 0)new-audio-pes будет установлен | |
set gt=%%~1 | |
if not DEFINED gt set /p gt=Нажми цифру от 0 до 9 и нажми Enter^> | |
if not DEFINED gt goto :EOF | |
set pref=MUXOPT --no-pcr-on-video-pid %%nap%% | |
set suff=--vbr --vbv-len=500 | |
set out= | |
set prm= | |
goto %%gt%% | |
:1 | |
set out=%%label%%.ts | |
goto doit | |
:2 | |
set out=%%label%%.m2ts | |
goto doit | |
:3 | |
call cc.bat | |
set out=%%label%%.iso | |
set prm=--blu-ray %%cc%% --label="%%iso%%" | |
goto doit | |
:4 | |
call cc.bat | |
set out=BD | |
set prm=--blu-ray --mplsOffset=1 --m2tsOffset=1 %%cc%% | |
goto doit | |
:5 | |
call cc.bat | |
set out=AVCHD | |
set prm=--avchd %%cc%% | |
goto doit | |
:6 | |
set out= | |
set prm=--demux | |
del "%%label%%.track_*" | |
goto doit | |
:7 | |
if DEFINED demux set demux=&goto loop | |
if not DEFINED demux set demux=1 | |
goto loop | |
:8 | |
if not DEFINED sei set sei=insertSEI,&goto loop | |
if "%%sei%%"=="insertSEI," set sei=forceSEI,&goto loop | |
if "%%sei%%"=="forceSEI," set sei= | |
goto loop | |
:9 | |
if DEFINED sps set sps=&goto loop | |
if not DEFINED sps set sps=contSPS, | |
goto loop | |
:0 | |
if DEFINED nap set nap=&goto loop | |
if not DEFINED nap set nap=--new-audio-pes | |
goto loop | |
:doit | |
if DEFINED demux ( | |
echo %%pref%% %%prm%% %%suff%% | |
%s | |
)>%%meta%% else ( | |
echo %%pref%% %%prm%% %%suff%% | |
%s | |
)>%%meta%% | |
type %%meta%% | |
echo on | |
"%s" %%meta%% "%%out%%" | |
@echo off | |
goto loop | |
'''%(coding[2:], plpn, plpn.split()[0], g.rn.join(mc), g.rn.join(mc2), g.tsmuxerU), g.encoding) | |
def tSec(s, d=0): | |
return (g.bd+timedelta(seconds=s)).strftime("%H:%M:%S"+("," if d else ".")+"%f")[:12] | |
def title(s): | |
if win: | |
#ub=create_unicode_buffer(s) | |
ub=s | |
pe('windll.kernel32.SetConsoleTitleW(%s)'%s) | |
try: windll.kernel32.SetConsoleTitleW(ub) | |
except: print(g.ob) | |
else: print(g.ok) | |
def toClouC(): | |
"""копирую куешит в облако""" | |
if m.lcs.is_file(): | |
if m.copyC: | |
if date(m.lcs, m.cs): | |
if not ppuc(m.lcs, m.cs): m.copyC=0 | |
m.deL=m.csL | |
m.doIt=1 | |
def toClouD(): | |
"""копирую дескрипшены в облако""" | |
if m.copyD: | |
if date(m.lde, m.de): | |
if not ppuc(m.lde, m.de): m.copyD=0 | |
else: | |
if win: | |
attrib(m.de, 128) | |
if not ppuc(m.lde, m.de): m.copyD=0 | |
def unxU(exe, desc, ex=1): | |
s=(r"\%s.exe" if win else "/%s")%exe | |
for i, x in enumerate(g.ep): | |
try: | |
if i<g.unx: | |
if win: continue | |
else: result=pl.get(x+s) | |
else: | |
if win: result=pl.get(x+s) | |
else: break | |
except: pass | |
else: | |
pe(g.ok) | |
print("%sU=%s" % ( | |
exe, result)) | |
return result | |
sys.tracebacklimit=0 | |
msg="%s Нет %s из %s"%( | |
g.ob, s[1:], desc) | |
if ex: sys.exit(msg) | |
else: print(msg) | |
return "" | |
def unx_U(exe, desc, ex=1): | |
"ищу exe" | |
s=r"\%s.exe" % exe | |
try: | |
if win: result=pl.get(g.ep["mingw"]+s, g.ep["cygwin"]+s, g.ep["mkvTN"]+s, g.ep["ffMPG"]+s, g.ep["tsmuxer"]+s, g.ep["SMM_BatchSplit"]+s) | |
else: result=pl.get(g.ep["wsl"]+"/"+exe) | |
except: | |
sys.tracebacklimit=0 | |
msg="%s Нет %s из %s"%( | |
g.ob, s[1:], desc) | |
if ex: sys.exit(msg) | |
else: print(msg) | |
return "" | |
else: pe(g.ok) | |
print("%sU=%s" % ( | |
exe, result)) | |
return result | |
def vid(s): | |
""" выбираю видеозаставку для аудиофайла """ | |
for x in ("jpg", "png"): | |
if s: | |
if ytb(c.p[x]): return | |
else: c.p[x].delete() | |
for y in (m.alb, m.all): | |
if ytb(y.with_suffix("."+x)): return | |
if not m.src["png"].is_file(): m.src["png"].write("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x04\x00\x00\x00\xb5\x1c\x0c\x02\x00\x00\x00\x0bIDATx\xdacd\xf8\x0f\x00\x01\x05\x01\x01'\x18\xe3f\x00\x00\x00\x00IEND\xaeB`\x82") | |
ytb(m.src["png"]) | |
def wsl(s): | |
"""замена путей для wsl""" | |
if win: return s | |
result=s.replace(g.Yandex, g.YandexWSL) | |
result=result.replace(g.Google, g.GoogleWSL) | |
result=result.replace("\\", "/") | |
if result[1]==":": result=g.mnt+result[0].lower()+result[2:] | |
return result | |
def xml(d): | |
""" пишу чаптеры для mkv""" | |
if c.nt: return | |
f=g.fromBegin | |
if c.a or c.ts: t=tSec(c.dur) | |
#elif c.ts: t=tSec(m.dur) | |
else: f, t=d["from"], d["to"] | |
rw(c.p["xml"], g.rn.join(('<?xml version="1.0" encoding="%s"?><Chapters><EditionEntry><ChapterAtom>'%( | |
coding), | |
"<ChapterTimeStart>%s</ChapterTimeStart>"%( | |
f), | |
"<ChapterTimeEnd>%s</ChapterTimeEnd><ChapterDisplay>"%( | |
tSec(sSec(t)-1)), | |
"<ChapterString>%s</ChapterString><ChapterLanguage>%s</ChapterLanguage></ChapterDisplay></ChapterAtom></EditionEntry></Chapters>"%( | |
c.pni[0], g.lang))), coding) | |
def ytb(img): | |
""" пишу аудиофайл как видеофайл """ | |
if img.is_file(): | |
c.img=img | |
if not cGet(m.pne, "v"): | |
if date(c.img, m.neVid): log(g.ffmpegU, | |
'-v error -framerate 1 -loop 1 -i "%s" -i "%s" %s -c:v libx264 -tune stillimage -pix_fmt yuv420p -force_key_frames "expr:gte(t,n_forced)" -c:a copy -shortest -y "%s"', ( | |
c.img, ifEx(m.neSnd), g.scaleYTB, m.neVid)) | |
return 1 | |
else: return 0 | |
if __name__ == "__main__": | |
g() | |
#if len(sys.argv)<2: sys.argv=["cut.py", r"d:\AV\2015\20150517 Отчетный\00000.MTS"] | |
title('"%s"' % '" "'.join(sys.argv)) | |
g.tsmuxerU=unxU("tsMuxeR", "http://forum.doom9.org/showthread.php?t=168539", 0) | |
if 1 : print(r''' | |
01)Пусть файл "d:\av\2015\20150517 Отчетный\00005.MTS" из видеокамеры | |
01+)Если файл "d:\av\2015\20150517 Отчетный\descript.ion" содержит комментарий для 01): | |
00005.MTS 00:01:01.880 00:02:49.000 Не слышно песен#TPE1 Юлия Абакумова#THANKS_TO Елена Михеева#TIT2 Люби Россию в непогоду#TCOM Эдуард Артемьев#TEXT Валерий Шумилин\n00:02:54.200 00:05:30.200 Под высоким куполом#TPE1 TPE1 Хор Миллеровской ДШИ#TPE2 Галина Брылёва#TPE3 Елена Михеева#TIT2 Россия матушка | |
02)Или если комментария нет, но файл "d:\av\2015\20150517 Отчетный\00005.MTS.cut" содержит: | |
00:01:01.880 00:02:49.000 Не слышно песен#TPE1 Юлия Абакумова#THANKS_TO Елена Михеева#TIT2 Люби Россию в непогоду#TCOM Эдуард Артемьев#TEXT Валерий Шумилин | |
00:02:54.200 00:05:30.200 Под высоким куполом#TPE1 Хор Миллеровской ДШИ#TPE2 Галина Брылёва#TPE2 Елена Михеева#TIT2 Россия матушка | |
Если строка 01+) 02) начинается с ; то эта часть не выполняется (;00:02:54.200 ...) | |
Если часть начинается с начала файла вместо 00:00:00.000 ставь 0 (0 00:02:49.000 ...) | |
Если часть идёт до конца файла ставь (00:01:01.880 - ...) | |
Если часть заканчивается в следующем файле ставь (00:01:01.880 00:22:49.000 ...#next 00006.MTS) | |
Где время 00:01:01.880 это начало клипа в 00005.MTS, время 00:22:49.000 это конец клипа в 00006.MTS а обложка 07+) это время в результирующем файое | |
Если несколько исполнителей отделяй их / (... #TPE1 Юлия Абакумова/Хор Миллеровской ДШИ). | |
Если тэга #TPE1 Исполнитель нет, то файлы 09) не создадутся. Про тэги mp3 см. в http://id3.org/id3v2.3.0 | |
После запуска cut.py с параметром 01) (например через sendTo), | |
или после запуска cut.py с параметром 02) (через sendTo или через ассоциацию .cut файла c cut.py): | |
или после запуска cut.py с параметром 01+) (через sendTo или через ассоциацию .ion файла c cut.py): | |
02+)Будет создан файл 02) (в этом случае редактировать его нет смысла). Удаление 02+) приведёт к обновление 04)-09) | |
03) 01+) и 02) будут синхронизированы с облачным диском для проектов "'''+g.Yandex+r'''\av\2015\20150517 Отчетный\" | |
04)Появятся файлы субтитров "20150517 Отчетный 1430 Не слышно песен.srt" и "20150517 Отчетный 1432 Под высоким куполом.srt" в UTF-8 на "'''+g.ls["srt"]+r'''" | |
Советую добавить в MPC-HC->Options->Subtitles->Misc->Autoload paths-> и в VLC->Tools->Preferences->Video->Subtitles / OSD->Subtitles autodetection paths->"'''+g.ls["srt"]+r'''" | |
05)Появятся файлы тэгов "00005 1 Не слышно песен.tag" и "00005 2 Под высоким куполом.tag". Cм. в https://www.matroska.org/technical/specs/tagging/index.html | |
06)Появятся файлы чаптеров "00005 1 Не слышно песен.xml" и "00005 2 Под высоким куполом.xml" | |
07)Появятся файлы jpg "00005 1 Не слышно песен.jpg" и "00005 2 Под высоким куполом.jpg" эти кадры видео станут обложками для 08) 09) | |
07+)Обложку можно указать третьим параметром (00:01:01.880 00:02:49.000 00:01:10.000 ...) или тэгом #APIC "file.jpg" если обложка не указана тогда используются alb.X а если его нет то "'''+g.Yandex+r'''\av\all.X" (где X это jpg или png) | |
07++)Если в 00005.MTS была вложена обложка то появится файл "d:\av\2015\20150517 Отчетный\alb.png" | |
07+++)Если в 00005.MTS нет видео то появится файл 00005.MTS.mkv и "'''+g.Yandex+r'''\av\1x1.png" если нет "'''+g.Yandex+r'''\av\all.X" или "alb.X" или файлов 07) 07++) | |
08)Появятся файлы "d:\av\2015\20150517 Отчетный 1430 Не слышно песен.mkv" и "d:\av\2015\20150517 Отчетный 1432 Под высоким куполом.mkv" | |
09)Появятся файлы "d:\av\2015\20150517 Отчетный 1430 Не слышно песен.mp3" и "d:\av\2015\20150517 Отчетный 1432 Под высоким куполом.mp3" | |
10)Если существует файл "'''+g.ls["txt"]+r'''\Под высоким куполом.'''+g.lang+r'''" | |
11)то появится файл стихов "'''+g.ls["txt"]+r'''\20150517 Отчетный 1432 Под высоким куполом.txt" для показа через MiniLyrics, содержимое также будет добавлено в тэг USLT | |
12)После запуска cut.py с параметром "d:\av\2015\20150517 Отчетный" | |
13)Файлы на облачном диске для медиафайлов "'''+g.Google+r'''\av\2015\20150517 Отчетный*.mkv" будут переименованы в *.mp4 | |
14)В плэйлисты "20150517 Отчетный.X.Y" (где X mkv, mp3 и mp4 а Y m3u и xspf) добавятся файлы 08) 09) | |
Файлы 04) и 10) можно редактировать и запускать cut.py | |
14+)В файлах uslt.ini в секции песни (пусть [Не слышно песен]) и cut.ini в секции альбома (пусть [20150517 Отчетный]) можно указывать tags=TAG1 тэг1#TAG2 тэг2... | |
Если указать тэг (пусть #TPE1. Юлия Абакумова) в descript.ion (или .cut) тогда он запишется в секцию [20150517 Отчетный\00005.MTS] и будет действовать для всего медиафайла | |
Если указать тэг (пусть #TPE1.. Юлия Абакумова) в descript.ion (или .cut) тогда он запишется в секцию [20150517 Отчетный] и будет действовать для всего альбома | |
Тэги берутся из [Не слышно песен] заменяются (если есть) тэгами из [20150517 Отчетный] затем [20150517 Отчетный\00005.MTS] и наконец из descript.ion (или .cut) | |
14++)Кроме тэгов можно указывать параметры например #ts X (где X 0 для резки mkvmerge, 1 для резки tsMuxeR, 2 для резки с точностью до кадра SMM_BatchSplit, 3 для резки ffmpeg, 4 энкодинг) | |
Для мастеринга Blu-Ray диска сделанного из записей с нескольких камер и фотографий подходит только #ts 4 | |
Для отладки можно задать #keep 1 тогда файлы с промежуточными результатами не будут удалятся. | |
Параметр #film.. Z (где Z коррекция в секундах часов камеры). Чтобы удалить параметр из родительской секции нужно указать его пустым. Например #film.. | |
14+++)Тогда вместо "20150517 Отчетный 1430 Не слышно песен.mkv" будет создан "20150517 Отчетный 143059 Не слышно песен.mkv" в каталоге film | |
15)Файлы 14+++) добавятся в список объединения "film\SMMVS.jxtl" для объединения программой http://www.solveigmm.com/en/products/video-splitter | |
16)И в списки объединения "film\X.txt" (где X mkv, ffmkv, avi) для объединения с помощью mkvmerge, ffmpeg 17) | |
17)Появятся скрипты "film\X.Y" (где X как в 16) и где Y для linux будет sh а для windows bat) | |
18)После запуска скрипта 17) появится фильм "d:\av\2015\20150517 Отчетный\film\20150517 Отчетный.MKV" | |
19)Запуск cut.py с параметром 18) приведёт к создании 05) и 06) для фильма 18) | |
19+)Для изготовления Blu-Ray диска в каталоге film появятся tsMuxeR.bat, ac3.bat, '''+pl.path(g.tsmuxerU).with_suffix(".X")+''' (где X это bat для теста и cmd для финала), 20150517 Отчетный.bdmd это проект для Blu-Disk Studio http://blu-disc.net/ | |
19++)Рецепт изготовления Blu-Ray диска с всплывающим меню сцен: 20150517\0.bdmd, File~Option~tsMuxer~Path~Y:\PortableApps\tsMuxeR\tsMuxeR.X, Run tsMuxer in external console window+, Project~Mux by tsMuxeR | |
20)Параметры #norm X или #boost Y для коррекции громкости звука через ffmpeg -af volume=YdB или Y=min(X-MEAN,0-MAX) (где MEAN и MAX из ffmpeg -af volumedetect). | |
21)Параметр #af afade=st=X:d=Y (где после X секунд тишины в течении Y секунд звук будет увеличиваться до номинального) и #afout Z чтоб за Z секунд до конца уменьшить звук до тишины | |
22)Параметр #audio "file.mp3" 00:00:01.000 00:00:5.000 Тогда вместо исходного звука будет использовано 4 секунды из "file.mp3" с учётом 20) 21) | |
23)Параметр #filter_complex amix=duration=first:dropout_transition=Y тогда к исходному звуку добавится 22) где Y как в 21) (см. https://ffmpeg.org/ffmpeg-filters.html) | |
Вместо 23) можно указать #amix Y. Вместо #af afade=d=Y можно указать #afin Y. Параметр #mkv X вставит X перед медиафайлом при вызове mkvmerge. Например #mkv -S исключит субтитры | |
Для 09) выполняется ffmpeg -af replaygain и записывается в тэги REPLAYGAIN_TRACK_GAIN и REPLAYGAIN_TRACK_PEAK | |
24)Параметры #x X#y Y#w W #h H применяют для записи клипа из фотографи a.P (где P это .jpg или .mpo) которая зуммируется в (X, Y) с шириной W(если не указана то W=(iw-X*2)) или высотой H если не указана ширина. | |
Вместо координат левого верхнего угла можно указать координаты центра #xc XC#yc YC с шириной W(если не указана то W=ow). Где iw это ширина фотографии, а ow ширина клипа. | |
25)По умолчанию клип из фотографии длится 4 секунды. В в descript.ion (или .cut) можно указать просто (0), что будет значить (0 4 0 -). Можно (1) для 3 секундного клипа или (2) для 2 секундного. | |
26)Если для a.M (где M расширение фотограции или видеофайла) задать параметр #last b появится последнией кадр клипа b.jpeg и тогда он может быть использован в клипе из b.P 24) | |
27)Параметр #first a.jpeg для b.P создаст клип в котором ab скролируется от a.jpeg до b.P Параметры 24) не должны быть указаны для b.P. Неплохо указывать последний кадр 26) | |
28)Можно не хранить последние кадры задав #last.. tmp#first.. tmp.jpeg Тогда для каждого a.M будет создан последний кадр клипа tmp.jpeg и каждая следующая b.P альбома его может использовать | |
29)Если для a.M (за которым идёт b.P) задать параметр #last * тогда последний кадр клипа a.M запишется в b.jpeg. Можно хранить все последние кадры задав #last.. * | |
30)Параметр kodi X (где X mvc,sbs,tab) задаст формат 3D клипа из .mpo или .avi файлов от FinePix REAL 3D. Для таких .avi надо указать #ts 7 (размер видео будет зависить от #width и #height) или #ts 8 | |
31)Параметр #3d X сдвинет на X пикселей влево a.jpg для правого глаза a.3D.jpeg чтоб получить псевдо 3D | |
''') | |
g.mkvmergeU=unxU("mkvmerge", "https://mkvtoolnix.download/windows") | |
g.mkvpropeditU=unxU("mkvpropedit", "https://mkvtoolnix.download/windows") | |
g.mkvextractU=unxU("mkvextract", "https://mkvtoolnix.download/windows", 0) | |
g.ffmpegU=unxU("ffmpeg", "https://ffmpeg.zeranoe.com/builds") | |
g.nvInfo=pl.path(g.ffmpegU).with_suffix(".log") | |
g.nv={"enc":".264", "dec":".jpg"} | |
g.nvOpt={"enc":"-c:v h264_nvenc", "dec":"-c:v h264_cuvid", "jpg":"-c:v mjpeg_cuvid"} | |
g.GOP=12 | |
g.encL=(40, 30, g.GOP, 0, 2, 4) | |
g.enc={"h": " -preset bd -b:v %sM -maxrate %sM -bufsize %sM -g %s -bf %s -refs %s -slices %s", # -b:v 26M -cbr 1 -rc constqp -global_quality %s | |
"x": " -bluray-compat 1 -b:v %sM -maxrate %sM -bufsize %sM -g %s -bf %s -refs %s -slices %s -x264opts force-cfr=1", #-weightp none -aud 1 -b-pyramid strict -qp %s | |
"f": "-vbr %s000 %s000 -cpbsize %s000 -gop %s 1 %s -rf %s -l %s"} | |
for x in g.nv: g.nv[x]=g.nvInfo.with_suffix(g.nv[x]) | |
if not g.nvInfo.isfile(): | |
g.nvInfo.write(log(g.ffmpegU, '-f lavfi -i color %s -frames:v 1 -y "%s"', (g.nvOpt["enc"], g.nv["enc"]))) | |
if g.err: g.nv["enc"].delete() | |
if g.nv["enc"].is_file(): log(g.ffmpegU, '-v error %s -i "%s" -frames:v 1 -y "%s"', (g.nvOpt["dec"], g.nv["enc"], g.nv["dec"])) | |
for x in g.nv: g.nv[x]=g.nvOpt[x] if g.nv[x].is_file() else "" | |
g.ffprobeU=unxU("ffprobe", "https://ffmpeg.zeranoe.com/builds") | |
g.smmbsU=unxU("SMM_BatchSplit", "http://www.solveigmm.com", 0) | |
g.ffmsU=unxU("ffmsindex", "https://github.com/FFMS/ffms2/releases", 0) | |
g.frimU=unxU("FRIMencode", "https://www.videohelp.com/software/FRIM", 0) | |
g.makemkvU=unxU("makemkvcon%s"%("64" if g.bit else ""), "http://www.makemkv.com", 0) | |
#g.exiftoolU=unxU("exiftool", "http://www.sno.phy.queensu.ca/~phil/exiftool", 0) | |
m3=[] | |
qm=[] | |
for x in sys.argv[1:]: | |
x=stripp(x) | |
plp=pl.path(wsl(x)) | |
if plp.lower().endswith(".cut"): plp=plp.with_suffix("") | |
print(repr(str(plp))) | |
if plp.is_file(): #режим резки | |
if plp.up().up().name==plp.with_suffix("").name: f(plp) #фильм | |
elif plp.name.lower()==g.ion: qm+=descAll(plp) #резка по дескрипшену | |
else: qm+=[plp] #m(x) | |
m3+=[plp.up()] | |
else: | |
descNew(plp) | |
m3+=[plp] | |
qmd=list(filter(lambda x: x.is_file(), descAll(qm[0].up()/g.ion))) | |
qmd.sort(key=os.path.getmtime) | |
qm=list(filter(lambda x: x.is_file(), set(qm))) | |
qm.sort(key=os.path.getmtime) | |
for x in qm: | |
try: next=qmd[qmd.index(x)+1] | |
except: next="" | |
m(x, next) | |
for x in set(m3): m3u(x) | |
print("\a") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For Windows10-64bit:
apt-get update
apt-get upgrade
apt-get install git
apt-get install python-pip
pip install -U pip
cut.py
pip install plumbum
pip install eyed3
root@WIN64:~# python -m eyed3.main --version
eyeD3 0.7.9-final (C) Copyright 2002-2014 Travis Shirk
ffmpeg
apt-get install yasm
apt-get install libmp3lame-dev
apt-get install libx264-dev
apt-get install pkg-config
cd ~
git clone git://source.ffmpeg.org/ffmpeg.git
cd ffmpeg
./configure --disable-ffplay --disable-ffserver --enable-libmp3lame --enable-libx264 --enable-gpl
make && make install
root@WIN64:
# ffmpeg14.04.3)ffmpeg version N-81387-ge2a39b1 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1
configuration: --disable-ffplay --disable-ffserver --enable-libmp3lame --enable-libx264 --enable-gpl
libavutil 55. 29.100 / 55. 29.100
libavcodec 57. 54.100 / 57. 54.100
libavformat 57. 47.101 / 57. 47.101
libavdevice 57. 0.102 / 57. 0.102
libavfilter 6. 52.100 / 6. 52.100
libswscale 4. 1.100 / 4. 1.100
libswresample 2. 1.100 / 2. 1.100
libpostproc 54. 0.100 / 54. 0.100
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...
mkvtoolnix
apt-get install ruby
apt-get install autoconf
apt-get install libboost-dev libboost-filesystem-dev libboost-regex-dev libboost-date-time-dev libboost-system-dev
apt-get install libmagic-dev
apt-get install libogg-dev
apt-get install libvorbis-dev
apt-get install libbz2-dev liblzo2-dev zlib1g-dev
apt-get install libflac-dev
apt-get install libcurl4-gnutls-dev
cd ~
git clone git://github.com/mbunkus/mkvtoolnix.git
cd mkvtoolnix
./autogen.sh
git submodule init
git submodule update
./configure --with-boost=/usr
./drake && ./drake install
root@WIN64:~# mkvmerge -V
mkvmerge v9.3.1 ('Mask Machine') 64bit
root@WIN64:/usr/local/bin#