Python Forum

Full Version: problem with readline()
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi,
I just start to learn pyhton. I'm using 3.7.2 on windows. I have text files which contain full paths of video files, like:
video.lst:
Quote:c:\videos\video1.mp4
c:\videos\video2.mp4
c:\videos\video3.mp4
...


I want to find out how they are encoded with ffprobe. With the full given path of the video file everything works fine:

import os
import subprocess

filePath = r"c:\videos\video1.mp4"

ffprobeResult = subprocess.run(['ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=codec_name', '-of', 'default=noprint_wrappers=1:nokey=1', filePath], universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

print(ffprobeResult.stdout)
What doesn't work:
import os
import subprocess

with open(r"c:\video\video.lst","r") as f:
    filePath = 'r"' + str(f.readline()) + '"'
    
ffprobeResult = subprocess.run(['ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=codec_name', '-of', 'default=noprint_wrappers=1:nokey=1', filePath], universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

print(ffprobeResult.stdout)
It returns:
r"c:\videos\video1.mp4
"
:Invalid argument.

When I change it to
filePath = 'r"' + str(f.readline(18)) + '"'
it works again (18: the bytes of the path). readline seems to add a new line. Does anyone know how to change this or has something else up one's sleeve?

Cheers
Try
filepath = f.readline().strip()
readline reads the end of line character. You can remove it with f.readline().strip(). That should be all you need. You shouldn't need the 'r"' and the str.
It's not broken, readline does not remove newline.

In [21]: import io                                                                                                          

In [22]: io.TextIOWrapper.readline?                                                                                         
Signature: io.TextIOWrapper.readline(self, size=-1, /)
Docstring:
Read until newline or EOF.

Returns an empty string if EOF is hit immediately.
Type:      method_descriptor
The returned object from open() is a io.TextIOWrapper and io.BufferedReader in binary mode. All this objects inherits from io.IOBase, which is also a iterator (__next__ method is present). This method iterates one line by calling it.

This means, you can use the returned object from open direct in a for loop:

with open('some_file') as fd:
    for line in fd:
        print(line) # \n is still there!!!
To get rid of the white space (newline is also by definition a white space), use the method strip().


with open('some_file') as fd:
    for line in fd:
        print(line.strip())
        # white spaces on the left
        # and right side are removed
Instead of using subprocess.run, you can use subprocess.check_output.

from subprocess import check_output


with open('some_file') as fd:
    for line in fd:
        file = line.strip()
        result = check_output(['echo', file], encoding='utf8')
        # echo > file
        print(result)
To be more flexible, write small functions. Each function should solve one task.

from pathlib import Path


def read_video_list(file):
    with open(file) as fd:
        for line in fd:
            path = Path(line.strip())
            if path.exists() and path.is_file():
                # path exists and is a file
                # THIS DOES NOT GUARANTEE THAT YOU HAVE READ PERMISSION
                video_codec = video_info(str(path))
                # path is a Path object and must be converted to a
                # str, if you want to add this to the command
                print(path, '->', video_codec) # you can make an own function to print this out


def video_info(video_file):
    cmd = [
        'ffprobe', '-v', 'error', '-select_streams',
        'v:0', '-show_entries', 'stream=codec_name',
        '-of', 'default=noprint_wrappers=1:nokey=1',
        video_file,
    ]
    return check_output(cmd, encoding='utf8').rstrip()
    # rstrip, to remove the newline on the right side
For example, I did not made a function to print the data.
This should be in another function, so that you can exchange it independently.
In this case, the video_info should return both, codec_info and path.

Take also a closer look to pathlib.
In this example Path is just used as a shortcut. Otherwise I had to use os.path.exists and os.path.isfile.
Thank you very much for your quick replies/explanations. I'll try them out tomorrow.
Also you do not need stdout=subprocess.PIPE, stderr=subprocess.STDOUT in run().
Can use capture_output=True or can also use check_output() as @DeaD_EyE do.

Here is a test,code is formatted with black so i don't need to do that work,can aslo just use it online Black Playground
import os
import subprocess

with open('video_lst.txt') as f:
    for video_path in f:
        video_path = video_path.strip()
        ffprobeResult = subprocess.run(
            [
                "ffprobe",
                "-v",
                "error",
                "-select_streams",
                "v:0",
                "-show_entries",
                "stream=codec_name",
                "-of",
                "default=noprint_wrappers=1:nokey=1",
                video_path,
            ],
            capture_output=True, text=True
        )
        result = ffprobeResult.stdout.strip()
        print('-' * 15)
        print(f'Video {ffprobeResult.args[-1]} has encoding: {result}') 
Output:
Video G:\1_youtube\planet.mkv has encoding: h264 --------------- Video G:\1_youtube\sky_walk.mkv has encoding: h264
Thank you very much. Works like a charm :)