Beispiel #1
0
def parse(app: FastFlixApp, **_):
    source = app.fastflix.current_video.source
    if source.name.lower().endswith("txt"):
        source = get_first_concat_item(source)
        app.fastflix.current_video.concat = True
    data = probe(app, source)
    if "streams" not in data:
        raise FlixError(f"Not a video file, FFprobe output: {data}")
    streams = Box({"video": [], "audio": [], "subtitle": [], "attachment": [], "data": []})
    for track in data.streams:
        if track.codec_type == "video" and (
            track.get("disposition", {}).get("attached_pic")
            or track.get("tags", {}).get("MIMETYPE", "").startswith("image")
        ):
            streams.attachment.append(track)
        elif track.codec_type in streams:
            streams[track.codec_type].append(track)
        else:
            logger.error(f"Unknown codec: {track.codec_type}")

    if not streams.video:
        raise FlixError(f"There were no video streams detected: {data}")

    for stream in streams.video:
        if "bits_per_raw_sample" in stream:
            stream.bit_depth = int(stream.bits_per_raw_sample)
        else:
            stream.bit_depth = guess_bit_depth(stream.pix_fmt, stream.get("color_primaries"))

    app.fastflix.current_video.streams = streams
    app.fastflix.current_video.video_settings.selected_track = streams.video[0].index
    app.fastflix.current_video.format = data.format
    app.fastflix.current_video.duration = float(data.format.get("duration", 0))
Beispiel #2
0
def probe(app: FastFlixApp, file: Path) -> Box:
    """Run FFprobe on a file"""
    command = [
        f"{app.fastflix.config.ffprobe}",
        "-v",
        "quiet",
        "-loglevel",
        "panic",
        "-print_format",
        "json",
        "-show_format",
        "-show_streams",
        f"{clean_file_string(file)}",
    ]
    result = execute(command)
    if result.returncode != 0:
        raise FlixError(f"Error code returned running FFprobe: {result.stdout} - {result.stderr}")

    if result.stdout.strip() == "{}":
        raise FlixError(f"No output from FFprobe, not a known video type. stderr: {result.stderr}")

    try:
        return Box.from_json(result.stdout)
    except BoxError:
        logger.error(f"Could not read output: {result.stdout} - {result.stderr}")
        raise FlixError(result.stderr)
Beispiel #3
0
def ffprobe_configuration(app, config: Config, **_):
    """Extract the version of ffprobe"""
    res = execute([f"{config.ffprobe}", "-version"])
    if res.returncode != 0:
        raise FlixError(f'"{config.ffprobe}" file not found')
    try:
        version = res.stdout.split(" ", 4)[2]
    except (ValueError, IndexError):
        raise FlixError(f'Cannot parse version of ffprobe from "{res.stdout}"')
    app.fastflix.ffprobe_version = version
Beispiel #4
0
def ffmpeg_configuration(app, config: Config, **_):
    """Extract the version and libraries available from the specified version of FFmpeg"""
    res = execute([f"{config.ffmpeg}", "-version"])
    if res.returncode != 0:
        raise FlixError(f'"{config.ffmpeg}" file not found')
    config = []
    try:
        version = res.stdout.split(" ", 4)[2]
    except (ValueError, IndexError):
        raise FlixError(f'Cannot parse version of ffmpeg from "{res.stdout}"')
    line_denote = "configuration: "
    for line in res.stdout.split("\n"):
        if line.startswith(line_denote):
            config = [x[9:].strip() for x in line[len(line_denote) :].split(" ") if x.startswith("--enable")]
    app.fastflix.ffmpeg_version = version
    app.fastflix.ffmpeg_config = config
Beispiel #5
0
 def s(a, v, base=50_000):
     upper, lower = [int(x) for x in a.get(v, "0/0").split("/")]
     if lower != base:
         upper *= base / lower
     value = int(upper)
     if value < 0 or value > 4_294_967_295:  # 32-bit unsigned int max size
         raise FlixError("HDR value outside expected range")
     return value
Beispiel #6
0
def get_concat_item(file, location=0):
    all_items = get_all_concat_items(file)
    if not all_items:
        raise FlixError("concat file must start with `file` on each line.")
    if location == 0:
        return all_items[0]
    section = len(all_items) // 10
    item_num = int((location * section)) - 1
    if item_num >= len(all_items):
        return all_items[-1]
    return all_items[item_num]
Beispiel #7
0
def get_all_concat_items(file):
    items = []
    with open(file) as f:
        for line in f:
            if line.strip().startswith("#"):
                continue
            elif line.strip().startswith("file"):
                filename = Path(line.strip()[5:].strip("'\""))
                if not filename.exists():
                    raise FlixError(f'No file "{filename}" exists')
                items.append(filename)
    return items
Beispiel #8
0
def parse(app: FastFlixApp, **_):
    data = probe(app, app.fastflix.current_video.source)
    if "streams" not in data:
        raise FlixError("Not a video file")
    streams = Box({
        "video": [],
        "audio": [],
        "subtitle": [],
        "attachment": [],
        "data": []
    })
    covers = []
    for track in data.streams:
        if track.codec_type == "video" and track.get("disposition",
                                                     {}).get("attached_pic"):
            streams.attachment.append(track)
        elif track.codec_type in streams:
            streams[track.codec_type].append(track)
        else:
            logger.error(f"Unknown codec: {track.codec_type}")

    if not streams.video:
        raise FlixError("There were no video streams detected")

    for stream in streams.video:
        if "bits_per_raw_sample" in stream:
            stream.bit_depth = int(stream.bits_per_raw_sample)
        else:
            stream.bit_depth = guess_bit_depth(stream.pix_fmt,
                                               stream.get("color_primaries"))

    app.fastflix.current_video.streams = streams
    app.fastflix.current_video.video_settings.selected_track = streams.video[
        0].index
    app.fastflix.current_video.width, app.fastflix.current_video.height = determine_rotation(
        streams)
    app.fastflix.current_video.format = data.format
    app.fastflix.current_video.duration = float(data.format.get("duration", 0))
Beispiel #9
0
def probe(app: FastFlixApp, file: Path) -> Box:
    """ Run FFprobe on a file """
    command = [
        f"{app.fastflix.config.ffprobe}",
        "-v",
        "quiet",
        "-loglevel",
        "panic",
        "-print_format",
        "json",
        "-show_format",
        "-show_streams",
        f"{file}",
    ]
    result = execute(command)
    try:
        return Box.from_json(result.stdout)
    except BoxError:
        logger.error(
            f"Could not read output: {result.stdout} - {result.stderr}")
        raise FlixError(result.stderr)
Beispiel #10
0
def get_first_concat_item(file):
    all_items = get_all_concat_items(file)
    if not all_items:
        raise FlixError("concat file must start with `file` on each line.")
    return all_items[0]