mirror of
				https://github.com/9001/copyparty.git
				synced 2025-11-03 21:43:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			132 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
 | 
						|
import json
 | 
						|
import re
 | 
						|
import os
 | 
						|
import sys
 | 
						|
import subprocess as sp
 | 
						|
 | 
						|
try:
 | 
						|
    from copyparty.util import fsenc
 | 
						|
except:
 | 
						|
 | 
						|
    def fsenc(p):
 | 
						|
        return p.encode("utf-8")
 | 
						|
 | 
						|
 | 
						|
_ = r"""
 | 
						|
inspects video files for errors and such
 | 
						|
plus stores a bunch of metadata to filename.ff.json
 | 
						|
 | 
						|
usage:
 | 
						|
  -mtp vidchk=t600,ay,p,bin/mtag/vidchk.py
 | 
						|
 | 
						|
explained:
 | 
						|
t600: timeout 10min
 | 
						|
  ay: only process files which contain audio (including video with audio)
 | 
						|
   p: set priority 1 (lowest priority after initial ffprobe/mutagen for base tags),
 | 
						|
       makes copyparty feed base tags into this script as json
 | 
						|
 | 
						|
if you wanna use this script standalone / separately from copyparty,
 | 
						|
provide the video resolution on stdin as json:  {"res":"1920x1080"}
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
FAST = True  # parse entire file at container level
 | 
						|
# FAST = False  # fully decode audio and video streams
 | 
						|
 | 
						|
 | 
						|
# warnings to ignore
 | 
						|
harmless = re.compile(
 | 
						|
    r"Unsupported codec with id |Could not find codec parameters.*Attachment:|analyzeduration"
 | 
						|
    + r"|timescale not set"
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
def wfilter(lines):
 | 
						|
    return [x for x in lines if x.strip() and not harmless.search(x)]
 | 
						|
 | 
						|
 | 
						|
def errchk(so, se, rc, dbg):
 | 
						|
    if dbg:
 | 
						|
        with open(dbg, "wb") as f:
 | 
						|
            f.write(b"so:\n" + so + b"\nse:\n" + se + b"\n")
 | 
						|
 | 
						|
    if rc:
 | 
						|
        err = (so + se).decode("utf-8", "replace").split("\n", 1)
 | 
						|
        err = wfilter(err) or err
 | 
						|
        return f"ERROR {rc}: {err[0]}"
 | 
						|
 | 
						|
    if se:
 | 
						|
        err = se.decode("utf-8", "replace").split("\n", 1)
 | 
						|
        err = wfilter(err)
 | 
						|
        if err:
 | 
						|
            return f"Warning: {err[0]}"
 | 
						|
 | 
						|
    return None
 | 
						|
 | 
						|
 | 
						|
def main():
 | 
						|
    fp = sys.argv[1]
 | 
						|
    zb = sys.stdin.buffer.read()
 | 
						|
    zs = zb.decode("utf-8", "replace")
 | 
						|
    md = json.loads(zs)
 | 
						|
 | 
						|
    fdir = os.path.dirname(os.path.realpath(fp))
 | 
						|
    flag = os.path.join(fdir, ".processed")
 | 
						|
    if os.path.exists(flag):
 | 
						|
        return "already processed"
 | 
						|
 | 
						|
    try:
 | 
						|
        w, h = [int(x) for x in md["res"].split("x")]
 | 
						|
        if not w + h:
 | 
						|
            raise Exception()
 | 
						|
    except:
 | 
						|
        return "could not determine resolution"
 | 
						|
 | 
						|
    # grab streams/format metadata + 2 seconds of frames at the start and end
 | 
						|
    zs = "ffprobe -hide_banner -v warning -of json -show_streams -show_format -show_packets -show_data_hash crc32 -read_intervals %+2,999999%+2"
 | 
						|
    cmd = zs.encode("ascii").split(b" ") + [fsenc(fp)]
 | 
						|
    p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
 | 
						|
    so, se = p.communicate()
 | 
						|
 | 
						|
    # spaces to tabs, drops filesize from 69k to 48k
 | 
						|
    so = b"\n".join(
 | 
						|
        [
 | 
						|
            b"\t" * int((len(x) - len(x.lstrip())) / 4) + x.lstrip()
 | 
						|
            for x in (so or b"").split(b"\n")
 | 
						|
        ]
 | 
						|
    )
 | 
						|
    with open(fsenc(f"{fp}.ff.json"), "wb") as f:
 | 
						|
        f.write(so)
 | 
						|
 | 
						|
    err = errchk(so, se, p.returncode, f"{fp}.vidchk")
 | 
						|
    if err:
 | 
						|
        return err
 | 
						|
 | 
						|
    if max(w, h) < 1280 and min(w, h) < 720:
 | 
						|
        return "resolution too small"
 | 
						|
 | 
						|
    zs = (
 | 
						|
        "ffmpeg -y -hide_banner -nostdin -v warning"
 | 
						|
        + " -err_detect +crccheck+bitstream+buffer+careful+compliant+aggressive+explode"
 | 
						|
        + " -xerror -i"
 | 
						|
    )
 | 
						|
 | 
						|
    cmd = zs.encode("ascii").split(b" ") + [fsenc(fp)]
 | 
						|
 | 
						|
    if FAST:
 | 
						|
        zs = "-c copy -f null -"
 | 
						|
    else:
 | 
						|
        zs = "-vcodec rawvideo -acodec pcm_s16le -f null -"
 | 
						|
 | 
						|
    cmd += zs.encode("ascii").split(b" ")
 | 
						|
 | 
						|
    p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
 | 
						|
    so, se = p.communicate()
 | 
						|
    return errchk(so, se, p.returncode, f"{fp}.vidchk")
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    print(main() or "ok")
 |