Skip to content

Instantly share code, notes, and snippets.

@abakum
Last active April 13, 2017 20:45
Show Gist options
  • Save abakum/827c89f1d9d5982c02bdff8a9b241678 to your computer and use it in GitHub Desktop.
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)
#!/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:boxcolor=yellow@0.6"'''
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")
@abakum
Copy link
Author

abakum commented Aug 18, 2016

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:# ffmpeg
ffmpeg version N-81387-ge2a39b1 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1
14.04.3)
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#

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment