diff --git a/README.md b/README.md
index 20d40b0..77477b4 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# VideoCut
-Version 2.1.0
+Version 2.1.1
-![Download](https://github.com/kanehekili/VideoCut/releases/download/2.1.0/videocut2.1.0.tar)
+![Download](https://github.com/kanehekili/VideoCut/releases/download/2.1.1/videocut2.1.1.tar)
MP2/MP4 Cutter for Linux on base of mpv and ffmpeg. Cutting is lossless, the target file will not be reencoded.
@@ -17,6 +17,7 @@ The current version is written in python3 and uses the qt5 widget kit.
### Prerequisites
* Arch: python3, python-pillow and mpv
* Debian/Mint/Ubuntu: python3 python3-pil libmpv1 python3-pyqt5.qtopengl (no-recommends)
+ #not working for Ubuntu 18.4: libmpv - use python3-opencv instead
* Fedora: python3-pillow-qt and mpv-libs.x86_64
* ffmpeg > 3.X to 5.X
* python3-pyqt5
@@ -73,7 +74,7 @@ MPV supports audio streams while playing. Unfortunately it relies on the audio s
##Install
-#### Install via ppa on Linux Mint or Ubuntu (focal/Mint20 only)
+#### Install via ppa on Linux Mint or Ubuntu (focal/jammy/Mint20 only)
```
sudo add-apt-repository ppa:jentiger-moratai/mediatools
sudo apt update
@@ -97,10 +98,11 @@ Remove with:
Select video and open it with "Open with ->VideoCut", oder via terminal "VideoCut"
-#### Install dependencies manually on Linux Mint or Ubuntu (tested from 16.04 to 22.04)
+#### Install dependencies manually on Linux Mint or Ubuntu (tested from 20.04 to 22.04)
```
sudo apt –no-install-recommends install python3-pyqt5 ffmpeg python3-pil libmpv1
```
+libmpv1 won't work on Ubuntu 18.04 - no bindings for the old libs - use opencv instead
#### Install dependencies on Fedora
```
@@ -136,12 +138,14 @@ remux5 is a c binary based on the libavcodec library, but uses an integrated app
### Exact frame cut for one GOP only?
Can't be really implemented with the ffmpeg ABI. The transcoded part will have different coding parameters than the rest of the stream. A decoder cannot handle that change. On the other hand there is no way to transcode the GOP with the exact parameters of the original stream, since only a subset of h264 paramenters are accepted by the ffmpeg ABI.
-### Legacy Opencv
+### Legacy Opencv (for Ubuntu 18.04 and older)
Since Videocut ran with OpenCV for many years it is still available. If needed it has to be downloaded
* python3-opencv
* hdf5 (Arch only)
-Create a .desktop file with the line "Exec= python3 .../VideoCut.py -p cv %f". Opencv will not be displaying subtitles nor frametypes.
+Copy the .desktop file and change the exec line to "Exec= python3 .../VideoCut.py -p cv %f"
+
+Opencv will not be displaying subtitles nor frametypes.
### Changes
08.07.2016
@@ -251,4 +255,8 @@ Create a .desktop file with the line "Exec= python3 .../VideoCut.py -p cv %f". O
05.04.2022
* Support for wayland (OpenGL Widget)
-* Tune MPV settings for older mpv versions
\ No newline at end of file
+* Tune MPV settings for older mpv versions
+
+04.05.2022
+* Fixed ffmpeg3 build for older distros
+* revamped logging
\ No newline at end of file
diff --git a/build/build.properties b/build/build.properties
index 100baa1..9f60a2c 100644
--- a/build/build.properties
+++ b/build/build.properties
@@ -1,4 +1,5 @@
-version=2.1.0
+version=2.1.1
+ubu0=bionic
ubu1=focal
ubu2=jammy
pkgrelease=1
\ No newline at end of file
diff --git a/build/build.xml b/build/build.xml
index 89a7218..b807a6b 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -10,8 +10,10 @@
+
-
+
+
@@ -26,6 +28,7 @@
+
@@ -64,6 +67,7 @@
+
diff --git a/src/.gitignore b/src/.gitignore
index 581d446..4b777a7 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -2,3 +2,4 @@
/VideoCut.py.old
/Configuration.py
/__pycache__/
+/VideoCut.log
diff --git a/src/CvPlayer.py b/src/CvPlayer.py
index 8a91de7..1533465 100644
--- a/src/CvPlayer.py
+++ b/src/CvPlayer.py
@@ -33,7 +33,7 @@
Compat layer for cv2 and 3
'''
-Log=FFMPEGTools.Log
+Log = FFMPEGTools.Log
class OpenCV2():
@@ -231,7 +231,7 @@ def _captureFromFile(self, rotation):
return False
self._capture = OPENCV.getCapture();
if not self._capture.open(self._file):
- Log.logError("STREAM NOT OPENED")
+ Log.error("STREAM NOT OPENED")
return False
self.frameWidth = OPENCV.getFrameWidth()
@@ -243,10 +243,10 @@ def _captureFromFile(self, rotation):
duration = self._streamProbe.formatInfo.getDuration()
ff_fps= self._streamProbe.getVideoStream().frameRateMultiple()
ff_FrameCount = round(ff_fps*duration)
- Log.logInfo("Analyze %s frameCount:%d fps:%.3f ffmpeg frameCount:%d fps:%.3f"%(self._file,self.framecount,self.fps,ff_FrameCount,ff_fps))
+ Log.info("Analyze %s frameCount:%d fps:%.3f ffmpeg frameCount:%d fps:%.3f"%(self._file,self.framecount,self.fps,ff_FrameCount,ff_fps))
fps_check= (self.fps/ff_fps)
if abs(fps_check -1.0)>10.0:
- Log.logInfo("Irregular data, ratio: %.3f"%(fps_check))
+ Log.info("Irregular data, ratio: %.3f"%(fps_check))
self.framecount=ff_FrameCount
self.fps=ff_fps
@@ -264,12 +264,12 @@ def _captureFromFile(self, rotation):
def _sanityCheck(self):
if self._streamProbe is None or not self._streamProbe.isKnownVideoFormat():
print ("STREAM NOT KNOWN")
- Log.logInfo("STREAM NOT KNOWN")
+ Log.info("STREAM NOT KNOWN")
return False
if len(self._streamProbe.video)!=1:
print("Zero or more than 1 video stream")
- Log.logInfo("Zero or more than 1 video stream")
+ Log.info("Zero or more than 1 video stream")
return False
return True
@@ -312,7 +312,7 @@ def getNextFrame(self):
return frame
self.framecount = self.getCurrentFrameNumber()
- Log.logInfo("No more frames @" + str(self.framecount + 1));
+ Log.info("No more frames @" + str(self.framecount + 1));
return self.__getLastFrame(self.framecount, 0)
# A test to paint on a frame. Has artefacts..
@@ -354,7 +354,7 @@ def getFrameAt(self, frameNumber):
self.setFrameAt(frameNumber)
return self.getNextFrame()
except:
- Log.logException("Error Frame")
+ Log.exception("Error Frame")
return None
def getCurrentFrameNumber(self):
diff --git a/src/FFMPEGTools.py b/src/FFMPEGTools.py
index 87ade8c..5d82824 100644
--- a/src/FFMPEGTools.py
+++ b/src/FFMPEGTools.py
@@ -16,41 +16,50 @@
from itertools import tee
import configparser
from shutil import which
+import gzip
+
+
+def setupRotatingLogger(logName,logConsole):
+ logSize=5*1024*1024 #5MB
+ #if OSTools().isRoot(): won't work if you call it from commandline
+ # folder=OSTools().joinPath("/var","log")
+ # OSTools().ensureDirectory(folder)
+ #else:
+ folder = OSTools().moduleDir()
+ if not OSTools().canWriteToFolder(folder):
+ folder= OSTools().joinPathes(OSTools().getHomeDirectory(),".config",logName)
+ OSTools().ensureDirectory(folder)
+ logPath = OSTools().joinPathes(folder,logName+".log")
+ fh= RotatingFileHandler(logPath,maxBytes=logSize,backupCount=5)
+ fh.rotator=OSTools().compressor
+ fh.namer=OSTools().namer
+ logHandlers=[]
+ logHandlers.append(fh)
+ if logConsole:
+ logHandlers.append(logging.StreamHandler(sys.stdout))
+ logging.basicConfig(
+ handlers=logHandlers,
+ #level=logging.INFO
+ level=logging.DEBUG,
+ format='%(asctime)s %(levelname)s : %(message)s'
+ )
+ #console - only if needed
+ '''
+ if logConsole:
+ cons = logging.StreamHandler(sys.stdout)
+ logger.addHandler(cons)
+ '''
+def setLogLevel(levelString):
+ if levelString == "Debug":
+ Log.setLevel(logging.DEBUG)
+ elif levelString == "Info":
+ Log.setLevel(logging.INFO)
+ elif levelString == "Warning":
+ Log.setLevel(logging.WARNING)
+ elif levelString == "Error":
+ Log.setLevel(logging.ERROR)
-class Logger():
- def __init__(self):
- homeDir = os.path.expanduser("~")
- self.LogDir =OSTools().joinPathes(homeDir,".config","VideoCut")
- OSTools().ensureDirectory(self.LogDir, None)
- self.mainHandler=None
- self.setupLogging()
-
-
- def setupLogging(self):
- logpath = os.path.join(self.LogDir,"VC.log")
- self.mainHandler = RotatingFileHandler(logpath, mode='a', maxBytes=10000000, backupCount=2)
- handler = logging.StreamHandler(sys.stdout)
- handler.setLevel(logging.DEBUG)
- logging.basicConfig(
- handlers=[handler,self.mainHandler],
- level=logging.DEBUG,
- format='%(asctime)s %(message)s'
- )
- self.logger = logging.getLogger(__name__)
-
- def logInfo(self, aString):
- self.logger.log(logging.INFO, aString)
-
- def logError(self, aString):
- self.logger.log(logging.ERROR, aString)
-
- def logClose(self):
- logging.shutdown()
-
- def logException(self, text):
- self.logger.exception(text)
-
class OSTools():
__instance=None
@@ -89,6 +98,9 @@ def getWorkingDirectory(self):
dname = os.path.dirname(abspath)
return dname
+ def moduleDir(self):
+ return os.path.dirname(__file__)
+
#location of "cwd", i.e where is bash..
def getActiveDirectory(self):
return os.getcwd()
@@ -97,6 +109,7 @@ def getActiveDirectory(self):
def isAbsolute(self,path):
return os.path.isabs(path)
+ #The users home directory - not where the code lies
def getHomeDirectory(self):
return os.path.expanduser("~")
@@ -113,7 +126,13 @@ def removeFile(self, path):
if self.fileExists(path):
os.remove(path)
- def ensureDirectory(self, path, tail):
+ def canWriteToFolder(self,path):
+ return os.access(path,os.W_OK)
+
+ def canReadFromFolder(self,path):
+ return os.access(path,os.R_OK)
+
+ def ensureDirectory(self, path, tail=None):
# make sure the target dir is present
if tail is not None:
path = os.path.join(path, tail)
@@ -144,13 +163,36 @@ def __pairwise(self,iterable):
next(b, None)
return list(zip(a, b))
+ def isRoot(self):
+ return os.geteuid()==0
+
+ def countFiles(self,aPath,searchString):
+ log_dir=os.path.dirname(aPath)
+ cnt=0
+ for f in os.listdir(log_dir):
+ if searchString is None or searchString in f:
+ cnt+=1
+ return cnt
+
+ #logging rotation & compression
+ def compressor(self,source, dest):
+ with open(source,'rb') as srcFile:
+ data=srcFile.read()
+ bindata = bytearray(data)
+ with gzip.open(dest,'wb') as gz:
+ gz.write(bindata)
+ os.remove(source)
+
+ def namer(self,name):
+ return name+".gz"
class ConfigAccessor():
- __SECTION = "videocut"
+ __SECTION = "default"
homeDir = OSTools().getHomeDirectory()
- def __init__(self, filePath):
- self._path = OSTools().joinPathes(self.homeDir,".config","VideoCut",filePath)
+ def __init__(self, folder,filePath,section="default"):
+ self.__SECTION=section
+ self._path = OSTools().joinPathes(self.homeDir,".config",folder,filePath)
self.parser = configparser.ConfigParser()
self.parser.add_section(self.__SECTION)
@@ -191,8 +233,7 @@ def store(self):
BIN = "ffmpeg"
-Log = Logger()
-
+Log=logging.getLogger("Main")
def parseCVInfos(cvtext):
lines = cvtext.splitlines(False)
@@ -240,13 +281,13 @@ def timedeltaToString2(deltaTime):
so = str(s).rjust(2, '0')
mso = str(ms).rjust(3, '0')
return '%s.%s' % (so, mso)
-
+'''
def log(*messages): #text value only...
# Hook for logger...
# cnt = len(messages)
#print("{0} {1}".format(*messages))
Log.logInfo("{0} {1}".format(*messages))
- '''
+
cnt = len(messages)
r= range(cnt)
tmp=[]
@@ -256,8 +297,8 @@ def log(*messages): #text value only...
tmp.append('}')
fmt=''.join(tmp)
Log.logInfo(fmt.format(*messages))
- '''
-
+
+'''
# execs an command, yielding the lines to caller. Throws exception on error
def executeAsync(cmd, commander):
@@ -351,7 +392,7 @@ def _readData(self, seekTo, count):
if count is not None:
cmd = cmd + ["-read_intervals", seekTo + "%+#" + str(count)]
cmd.extend(("-show_packets", "-select_streams", "v:0", "-show_entries", "packet=pts,pts_time,dts,dts_time,flags", "-of", "csv" , self.path, "-v", "quiet"))
- log("FFPacket:", cmd)
+ Log.debug("FFPacket:%s", cmd)
result = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
if len(result[0]) == 0:
raise IOError('No such media file ' + self.path)
@@ -658,6 +699,43 @@ def _readData(self):
def sanityCheck(self):
if self.getVideoStream() is None:
raise IOError("No video stream available")
+ if logging.root.level!=logging.DEBUG:
+ return
+ Log.debug("-------- Video -------------")
+ s = self.getVideoStream()
+ Log.debug("Index: %d", s.getStreamIndex())
+ Log.debug("codec %s", s.getCodec())
+ Log.debug("getCodecTimeBase: %s", s.getCodecTimeBase())
+ Log.debug("getTimeBase: %s", s.getTimeBase())
+ Log.debug("getAspect %s", s.getAspectRatio())
+ Log.debug("getFrameRate_r: %.3f", s.frameRateMultiple())
+ Log.debug("getAVGFrameRate: %3.f", s.frameRateAvg()) # Common denominator
+ Log.debug("getDuration: %.3f", s.duration())
+ Log.debug("getWidth: %s", s.getWidth())
+ Log.debug("getHeight: %s", s.getHeight())
+ Log.debug("isAudio: %r", s.isAudio())
+ Log.debug("isVideo: %r", s.isVideo())
+
+ Log.debug("-------- Audio -------------")
+ s = self.getAudioStream()
+ if not s:
+ Log.debug("No audio")
+ exit(0)
+ Log.debug("Index:%d", s.getStreamIndex())
+ Log.debug("getCodec:%s", s.getCodec())
+ Log.debug("bitrate(kb) %d", s.getBitRate())
+ Log.debug("getCodecTimeBase: %s", s.getCodecTimeBase())
+ Log.debug("getTimeBase: %s", s.getTimeBase())
+ Log.debug("getDuration: %.3f", s.duration())
+ Log.debug("isAudio: %r", s.isAudio())
+ Log.debug("isVideo: %r", s.isVideo())
+ Log.debug("-----------Formats-----------")
+ f=self.formatInfo
+ Log.debug("Fmt Names:%s",f.formatNames())
+ Log.debug("Fmt bitrate;%d",f.getBitRate())
+ Log.debug("Fmt dur: %.3f",f.getDuration())
+ Log.debug("Fmt size %.3f",f.getSizeKB())
+ Log.debug("-----------EOF---------------")
def getVideoStream(self):
if len(self.video) == 0:
@@ -712,21 +790,19 @@ def getAspectRatio(self):
This filter is required for copying an AAC stream from
a raw ADTS AAC or an MPEG-TS container to MP4A-LATM.
'''
-
def needsAudioADTSFilter(self):
if self.getAudioStream() is None:
return False
- return self.getAudioStream().getCodec() == "aac" and (self.isH264() or self.isMP4())
+ return self.getAudioStream().getCodec() == "aac" and (self.isH264() or self.isMP4Container())
'''
check if needs the h264_mp4toannexb filter-
- Used on mp4 or h264, for converting INTO transport streams
+ Use on: MP4 file(container) containing an H.264 stream to mpegts format
'''
-
def needsH264Filter(self):
if self.isTransportStream():
return False;
- return self.isH264()
+ return self.isH264Codec() #also works work mkv
# VideoFormat is format info....
def getFormatNames(self):
@@ -781,16 +857,16 @@ def isTransportStream(self):
is MP4? Since its a formatcheck it can't be mp4-TS
'''
- def isMP4(self):
+ def isMP4Container(self):
return self.hasFormat("mp4")
- def isMPEG2(self):
+ def isMPEG2Codec(self):
return "mpeg" in self.getVideoStream().getCodec()
- def isH264(self):
+ def isH264Codec(self):
return "h264" == self.getVideoStream().getCodec()
- def isVC1(self):
+ def isVC1Codec(self):
return "vc1" == self.getVideoStream().getCodec()
@@ -883,10 +959,11 @@ def __init__(self, dataArray):
def _parse(self, dataArray):
for entry in dataArray:
+ val=self.NA
try:
(key, val) = entry.strip().split('=')
except:
- log("Error in entry:", entry)
+ Log.error("Error in entry:%s", entry)
if self.NA != val:
if self.TAG in key:
key = key.split(':')[1]
@@ -948,7 +1025,7 @@ def _parse(self, dataArray):
try:
(key, val) = entry.strip().split('=')
except:
- log("Error in entry:", entry)
+ Log.error("Error in entry:%s", entry)
if self.NA != val:
if self.TAG in key:
@@ -970,6 +1047,9 @@ def getAspectRatio(self):
return 1.0
def getRotation(self):
+ result = self.dataDict.get('rotation',None)
+ if result:
+ return int(result)
result = self.dataDict.get('TAG:rotate',None)
if result:
return int(result)
@@ -1109,7 +1189,6 @@ def _readDataByLines(self):
break
if re.match('\[\/FRAME\]', line):
proc += 1
- log("p ", proc)
# dataBucket = self.__processLine(line,dataBucket)
# if len(dataBucket)==0:
@@ -1272,13 +1351,13 @@ def cutPart(self, startTimedelta, endTimedelta, index=0, nbrOfFragments=1):
durString = timedeltaToFFMPEGString(timedelta(seconds=deltaSeconds, microseconds=deltaMillis))
# fast search - which is key search
- log('Prefetch seek/dur: ', prefetchString, ">>", durString)
+ Log.info('Prefetch seek/dur:%s >> %s ', prefetchString, durString)
if nbrOfFragments == 1:
fragment = self.targetPath()
else:
ext = self.retrieveConcatExtension()
fragment = self._getTempPath() + str(index) + ext
- log("generate file:", fragment)
+ Log.debug("generate file:%s", fragment)
self.say("Cutting part:" + str(index))
cmdExt = self._videoMode()
@@ -1293,7 +1372,7 @@ def cutPart(self, startTimedelta, endTimedelta, index=0, nbrOfFragments=1):
cmd.extend(self.langMappings)
cmdExt.extend(["-avoid_negative_ts", "1", "-shortest", fragment])
cmd.extend(cmdExt)
- log("cut:", cmd)
+ Log.debug("cut:%s", cmd)
prefix = "Cut part " + str(index) + ":"
try:
for path in executeAsync(cmd, self):
@@ -1325,7 +1404,7 @@ def _audioMode(self, mode):
if self._config.streamData.getAudioStream() is None:
return []
targetFmt = FORMATS.fromFilename(self.targetPath())
- log("audio:", self._config.streamData.getAudioStream().getCodec()," targets:",targetFmt) #TODO logging
+ Log.debug("audio: %s targets:%s", self._config.streamData.getAudioStream().getCodec(),targetFmt) #TODO logging
container=targetFmt.format
lib = "copy"
if self._config.reencode:
@@ -1335,7 +1414,7 @@ def _audioMode(self, mode):
if self._config.streamData.needsAudioADTSFilter():
return ["-c:a", lib, "-bsf:a", "aac_adtstoasc"]
- if self._config.streamData.isMPEG2() and container=='mpegts' and (self._fragmentCount == 1 or mode == self.MODE_JOIN):
+ if self._config.streamData.isMPEG2Codec() and container=='mpegts' and (self._fragmentCount == 1 or mode == self.MODE_JOIN):
return ["-c:a", lib, "-f", "dvd"]
# This must be configurable - at least for tests. mp2 seems to work for kodi+mp4
@@ -1354,7 +1433,7 @@ def _videoMode(self):
srcContainer=srcFmt.format
- log("video:", videoStream.getCodec())
+ Log.debug("video %s:", videoStream.getCodec())
lib="copy"
bsf=None
codec=None
@@ -1420,10 +1499,10 @@ def _subtitleMode(self):
subSrc= self._config.streamData.subtitleCodec()
subTarget= targetFmt.subtitleFormat()
if not FORMATS.sameSubGroup(subSrc,subTarget): #TODO ? Only if we can't copy them.
- log("subtitle:","src and target are not both text or image")
+ Log.warning("subtitle: src and target are not same text or image")
return []
if subTarget is None:
- log("subtitle:","container does not support subtitles")
+ Log.warning("subtitle: container does not support subtitles")
return[]
scopy=subTarget
@@ -1511,7 +1590,7 @@ def join(self):
if len(self.langMappings) > 0:
cmd.extend(["-map", "0"])
cmd.append(self.targetPath())
- log("join:", cmd)
+ Log.info("join:%s", cmd)
prefix = "Join:"
try:
for path in executeAsync(cmd, self):
@@ -1546,7 +1625,7 @@ def parseAndDispatch(self, prefix, text):
if len(text) > 5:
print ("<" + text.rstrip())
if "failed" in text:
- print ("ERR:", text)
+ Log.error("FFmpeg error: %s", text)
self.say(prefix + " !Conversion failed!")
self.warn(text);
return False
@@ -1605,7 +1684,7 @@ def stopCurrentProcess(self):
# Stop button has been pressed....
self.killed = True
if self.runningProcess is None:
- self.log("Can't kill proc!", "-Error")
+ Log.warning("Can't kill proc! -Error")
self.warn("Can't kill proc!", "-Error")
else:
print("FFMPEGCutter - stop process")
@@ -1681,7 +1760,7 @@ def cut(self, cutlist):
cmd = cmd + [self.config.targetPath]
print(cmd)
- log("cut file:", cmd)
+ Log.debug("cut file:%s", cmd)
try:
start = time.monotonic()
for path in executeAsync(cmd, self):
@@ -1694,7 +1773,7 @@ def cut(self, cutlist):
except Exception as error:
self.warn("Remux failed: %s" % (error))
- log("Remux failed", error)
+ Log.error("Remux failed %s", str(error))
self.runningProcess = None
return False
@@ -1742,7 +1821,7 @@ def parseAndDispatch(self, prefix, text, showProgress):
aLine= text.rstrip()
print ("<" + aLine )
if "Err:" in aLine:
- log(">", aLine)
+ Log.debug(">%s", aLine)
if not "muxing" in aLine:
self.warn(aLine);
return False
@@ -1756,7 +1835,7 @@ def stopCurrentProcess(self):
# Stop button has been pressed....
self.killed = True
if self.runningProcess is None:
- self.log("Can't kill proc!", "-Error")
+ Log.error("Can't kill proc! -Error")
else:
print("VCCutter - stop process")
self.runningProcess.kill()
@@ -1793,7 +1872,7 @@ def figureItOut(self):
g1 = m.group(0)
print(g1)
self.version = float(g1)
- log("FFmepg Version:", float(g1))
+ Log.info("FFmepg Version:%.3f", float(g1))
class FFmpegPicture():
diff --git a/src/MpvPlayer.py b/src/MpvPlayer.py
index eabcc27..ee57acb 100644
--- a/src/MpvPlayer.py
+++ b/src/MpvPlayer.py
@@ -230,7 +230,7 @@ def _hookEvents(self):
if nbr>30:
self.mediaPlayer.demuxer_max_back_bytes='10000MiB'
self.mediaPlayer.demuxer_cache_wait='no'
- Log.logInfo("applied demuxer settings for mpv > 3.x")
+ Log.info("applied demuxer settings for mpv > 3.x")
observe=[]#"seeking","time-pos"...
#ignore=["mouse-pos",""]
@@ -278,7 +278,7 @@ def open(self,filePath):
self.mediaPlayer.loadfile(filePath)
self._getReady()
except Exception as ex:
- Log.logException("Open mpv file")
+ Log.exception("Open mpv file")
print(ex)
#Test, not activated
@@ -312,7 +312,7 @@ def calcPosition(self,frameNumber):
#performance tweak: fastseek should have a low demuxer seek offset: tune if fast seek.
def seek(self,frameNumber,fast=False):
if self.mediaPlayer.seeking is None:
- Log.logError("No seek! Aborting")
+ Log.error("No seek! Aborting")
return
step = frameNumber - self.getCurrentFrameNumber()
if abs(step) < 20: #mpv hack: mpegts small distances
@@ -360,7 +360,7 @@ def seekStep(self,dialStep):
self._waitSeekDone()
#print("seekStep2 %f dial: %d currTime:%f"%(nxt,dialStep,self.timePos()))
else:
- Log.logInfo("MPV: Seek none!")
+ Log.info("MPV: Seek none!")
def screenshotAtFrame(self,frameNumber):
secs = self.calcPosition(frameNumber)
@@ -382,7 +382,7 @@ def _onPropertyChange(self,name,pos):
def _onDuration(self,name,val):
if val is not None:
self.duration=val
- Log.logInfo("durance detected:%.3f"%(val))
+ Log.info("durance detected:%.3f"%(val))
def _onFrameInfo(self,name,val):
if val is not None:
@@ -398,7 +398,7 @@ def setFPS(self,newFPS):
'''
def _onFps(self,name,val):
if val is not None:
- Log.logInfo("fps detected: %.5f"%(val))
+ Log.info("fps detected: %.5f"%(val))
self.mediaPlayer.unobserve_property("estimated-vf-fps",self._onFps)
self.setFPS(val)
'''
@@ -475,7 +475,7 @@ def _onReadyWait(self,name,val):
def _passLog(self,loglevel, component, message):
msg='{}: {}'.format(component, message)
- Log.logError(">"+msg)
+ Log.error(">%s",msg)
with self.seekLock:
#TODO: file not recognized seems not be working"
if message in self.ERR_IDS:
@@ -494,21 +494,21 @@ def connectTo(self,func):
#tweak for transport streams
def tweakTansportStreamSettings(self,isInterlaced):
- Log.logInfo("Transport stream. Setting seek offset to high and interlacing: %d"%(isInterlaced))
+ Log.info("Transport stream. Setting seek offset to high and interlacing: %d"%(isInterlaced))
self._demuxOffset=1.5#Solution for mpegts seek
if isInterlaced:
self.mediaPlayer.deinterlace="yes"
def tweakUHD(self):
- Log.logInfo("UHD, set stream size")
+ Log.info("UHD, set stream size")
self.mediaPlayer.stream_buffer_size='255MiB'
def tweakVC1(self):
- Log.logInfo("Set VC1 codec in MPV explictly")
+ Log.info("Set VC1 codec in MPV explictly")
self.mediaPlayer.hwdec_codecs="vc1"
def tweakMPG(self):
- Log.logInfo("MP2: Setting frame offset in mpg (mpv bug) and seek offset to high")
+ Log.info("MP2: Setting frame offset in mpg (mpv bug) and seek offset to high")
self._frameOffset=1
self._demuxOffset=1.5 #mpeg step seek
@@ -573,9 +573,9 @@ def shutDown(self):
def createWidget(self,showGL,parent):
self.showGL=showGL;
if showGL:
- Log.logInfo("create GL Widget")
+ Log.info("create GL Widget")
return self._createGLWidget(parent)
- Log.logInfo("create X11 Widget")
+ Log.info("create X11 Widget")
return self._createPlainwidget(parent)
def _createPlainwidget(self,parent):
@@ -643,26 +643,26 @@ def _sanityCheck(self,streamData):
fps=1.0
#rot = streamData.getRotation()
#ratio = streamData.getAspectRatio()
- Log.logInfo("Analyze MPV frameCount:%d fps:%.3f /FFMPEG frameCount:%d fps:%.3f, interlaced:%d"%(frameCount,fps,ff_FrameCount,ff_fps,interlaced))
+ Log.info("Analyze MPV frameCount:%d fps:%.3f /FFMPEG frameCount:%d fps:%.3f, interlaced:%d"%(frameCount,fps,ff_FrameCount,ff_fps,interlaced))
fps_check= abs(self._secureDiv(self.player.fps,ff_fps)-1)
#if fps_check >0.1:
- Log.logInfo("Setting FPS into MPV, ratio: %.3f setting fps %.3f"%(fps_check,ff_fps))
+ Log.info("Setting FPS into MPV, ratio: %.3f setting fps %.3f"%(fps_check,ff_fps))
self.player.setFPS(ff_fps)
fcCheck= self._secureDiv(self.player.framecount,ff_FrameCount)
if fcCheck < 0.9 or fcCheck > 1.1:
- Log.logInfo("Irregular count, ratio: %.3f, setting framecount %d"%(fcCheck,ff_FrameCount))
+ Log.info.logInfo("Irregular count, ratio: %.3f, setting framecount %d"%(fcCheck,ff_FrameCount))
self.player.framecount=max(1,ff_FrameCount)
#Transport stream handling:
- if streamData.isTransportStream():
+ if streamData.isTransportStream() or interlaced:
self.player.tweakTansportStreamSettings(interlaced)
if isUHD:
self.player.tweakUHD()
- if streamData.isVC1():
+ if streamData.isVC1Codec():
self.player.tweakVC1()
- if streamData.isMPEG2():
+ if streamData.isMPEG2Codec():
self.player.tweakMPG()
def _secureDiv(self,nominator,denominator):
diff --git a/src/VideoCut.py b/src/VideoCut.py
index da74959..d01068f 100644
--- a/src/VideoCut.py
+++ b/src/VideoCut.py
@@ -23,7 +23,6 @@
from datetime import timedelta
from FFMPEGTools import FFMPEGCutter, FFStreamProbe, CuttingConfig, OSTools, ConfigAccessor, VCCutter,FFmpegVersion
import FFMPEGTools
-import threading
from time import sleep, time
import xml.etree.cElementTree as CT
#####################################################
@@ -776,7 +775,7 @@ def showCodecInfo(self):
text2 = ''.join(entries)
except:
- Log.logException("Invalid codec format")
+ Log.exception("Invalid codec format")
text = "
No Information
"
text2= "
Please select a file first"
self.__getInfoDialog(text + text2).show()
@@ -853,7 +852,7 @@ def closeEvent(self,event):
try:
super(MainFrame, self).closeEvent(event)
except:
- Log.logException("Error Exit")
+ Log.exception("Error Exit")
'''
class LanguageModel():
@@ -1320,7 +1319,7 @@ def setFile(self, filePath):
self.gui.updateWindowTitle(OSTools().getFileNameOnly(filePath))
self._initVideoViews()
except Exception as ex:
- Log.logException("Setting file")
+ Log.exception("Setting file")
if not OSTools().fileExists(filePath):
self.lastError = "File not found"
else:
@@ -1390,7 +1389,7 @@ def restoreVideoCuts(self):
cutList = XMLAccessor(self.currentPath).readXML()
except Exception as error:
print(error)
- Log.logException("Error restore:")
+ Log.exception("Error restore:")
return
for cut in cutList:
mode = VideoCutEntry.MODE_STOP
@@ -1424,7 +1423,7 @@ def gotoCutIndex(self, index):
# callback from stop button
def killSaveProcessing(self):
if self.cutter is None:
- Log.logInfo("Can't kill process")
+ Log.info("Can't kill process")
else:
self.cutter.stopCurrentProcess()
@@ -1435,7 +1434,7 @@ def saveVideo(self, path):
if cutEntry.isStartMode():
if block:
- Log.logInfo("Start invalid: %s" % (cutEntry.getTimeString()))
+ Log.info("Start invalid: %s" % (cutEntry.getTimeString()))
else:
block = []
block.append(cutEntry)
@@ -1445,7 +1444,7 @@ def saveVideo(self, path):
spanns.append(block)
block = None
else:
- Log.logInfo("Stop ignored:" + cutEntry.getTimeString())
+ Log.info("Stop ignored:" + cutEntry.getTimeString())
#src = self.player._file
src = self._currentFile
# need that without extension!
@@ -1509,7 +1508,7 @@ def __makeCuts(self, srcPath, targetPath, spanns, settings):
t2 = cutmark[1].timeDelta()
hasSucess = self.cutter.cutPart(t1, t2, index, slices)
if not hasSucess:
- Log.logError("***Cutting failed***")
+ Log.error("***Cutting failed***")
return
self.cutter.join()
@@ -1671,7 +1670,7 @@ def run(self):
try:
self.function(*self.arguments)
except Exception as ex:
- Log.logException("***Error in LongRunningOperation***")
+ Log.exception("***Error in LongRunningOperation***")
self.msg = "Error while converting: "+str(ex)
finally:
self.signal.emit(self)
@@ -1749,15 +1748,15 @@ def handle_exception(exc_type, exc_value, exc_traceback):
infoText = str(exc_value)
detailText = "*".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
WIN.getErrorDialog("Unexpected error", infoText, detailText).show()
- Log.logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
+ Log.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
def parseOptions(args):
res={}
res["mpv"]=True
- res["log"]="debug"
+ res["logConsole"]=False
res["file"]=None
try:
- opts,args=getopt.getopt(args[1:], "l:p:", ["log=","player="])
+ opts,args=getopt.getopt(args[1:], "cdp:", ["console","debug","player="])
if len(args)==1:
res["file"]=args[0]
except getopt.GetoptError as err:
@@ -1765,11 +1764,13 @@ def parseOptions(args):
sys.exit(2)
for o,a in opts:
- if o in ("-l","--log"):
- print("log:",o," val:",a)
+ if o in ("-d","--debug"):
+ FFMPEGTools.setLogLevel("Debug")
elif o in ("-p","--player"):
if a in "cv":
res["mpv"]=False
+ elif o in ("-c","--console"):
+ res["logConsole"]=True
else:
print("Undef:",o)
return res
@@ -1782,14 +1783,14 @@ def main():
localPath = OSTools().getActiveDirectory() #won't work after setting WD
OSTools().setCurrentWorkingDirectory()
#Log.logInfo('*** VC located in %s***' % OSTools().getWorkingDirectory())
- vc_config = ConfigAccessor("vc.ini")
+ vc_config = ConfigAccessor("VideoCut","vc.ini","videocut") #folder,name§ion
vc_config.read();
argv = sys.argv
- #parseOptions(argv)
app = QApplication(argv)
app.setWindowIcon(getAppIcon())
res=parseOptions(argv)
+ FFMPEGTools.setupRotatingLogger("VideoCut",res["logConsole"])
VideoPlugin=setUpVideoPlugin(res["mpv"])
fn =res["file"]
if fn is None:
@@ -1799,9 +1800,9 @@ def main():
fn=OSTools().joinPathes(localPath,fn)
WIN = MainFrame(app,fn)
app.exec_()
- Log.logClose()
+ #logging.shutdown()
except:
- Log.logException("Error in main:")
+ Log.exception("Error in main:")
#traceback.print_exc(file=sys.stdout)
#TODO: Respect theme
diff --git a/src/ffmpeg/bin/V3/remux5 b/src/ffmpeg/bin/V3/remux5
index 51247dd..2e6daf8 100755
Binary files a/src/ffmpeg/bin/V3/remux5 and b/src/ffmpeg/bin/V3/remux5 differ
diff --git a/src/ffmpeg/src/makefile3 b/src/ffmpeg/src/makefile3
new file mode 100644
index 0000000..052364d
--- /dev/null
+++ b/src/ffmpeg/src/makefile3
@@ -0,0 +1,10 @@
+remux5: remux5.o
+ gcc -o ../bin/V3/remux5 remux5.o -g -lavutil -lavformat -lavcodec
+
+remux5.o: remux5.c
+ gcc -c remux5.c
+
+.PHONY: clean
+
+clean:
+ rm -f *.o remux5
diff --git a/src/installOnDebian.txt b/src/installOnDebian.txt
index 02322a2..1bd67d3 100644
--- a/src/installOnDebian.txt
+++ b/src/installOnDebian.txt
@@ -1,6 +1,8 @@
-sudo apt-get install python3-opencv (includes numpy)
-sudo apt-get install python3-pyqt5
-sudo apt-get install qt5-style-plugins
+sudo apt –no-install-recommends install python3-pyqt5 ffmpeg python3-pil libmpv1
+
+optional:
+sudo apt install python3-opencv (includes numpy)
+sudo apt install qt5-style-plugins
on gtk base DE add to: ~/.profile or /etc/environment
export QT_QPA_PLATFORMTHEME=gtk2