I am having issues with non ASCII characters.
I am using python 2.7.3
python -V
Python 2.7.3
I installed http://pymediainfo.readthedocs.org/en/latest/
via
easy_install pymediainfo
imported as below
from pymediainfo import MediaInfo
media_info = MediaInfo.parse(os.path.join(path, to, file))
using this with ascii characters is fine
for debugging I printed the 'command' from:
/usr/local/lib/python2.7/dist-packages/pymediainfo-1.3.5-py2.7.egg/pymediainfo/__init__.py
the class that 'runs' the mediainfo command in /usr/local/lib/python2.7/dist-packages/pymediainfo-1.3.5-py2.7.egg/pymediainfo/init.py
ENV_DICT = {
"PATH": "/usr/local/bin/:/usr/bin/",
"LD_LIBRARY_PATH": "/usr/local/lib/:/usr/lib/"}
#staticmethod
def parse(filename, environment=ENV_DICT):
command = ["mediainfo", "-f", "--Output=XML", filename]
print command
print repr(command)
fileno_out, fname_out = mkstemp(suffix=".xml", prefix="media-")
fileno_err, fname_err = mkstemp(suffix=".err", prefix="media-")
fp_out = os.fdopen(fileno_out, 'r+b')
fp_err = os.fdopen(fileno_err, 'r+b')
p = Popen(command, stdout=fp_out, stderr=fp_err, env=environment)
p.wait()
fp_out.seek(0)
xml_dom = MediaInfo.parse_xml_data_into_dom(fp_out.read())
fp_out.close()
fp_err.close()
return MediaInfo(xml_dom)
both print and print repr() display:
['mediainfo', '-f', '--Output=XML', "/mnt/path/Long 73\xc2\xb0 58' W.avi"]
the filename is:
Long 73° 58' W.avi
Looking at a UTF-8 table \xc2\xb0 corresponds to °
I am aware this might just be just the console not interpreting the encoding as it should but the output of mediainfo is just
<?xml version="1.0" encoding="UTF-8"?>
<Mediainfo version="0.7.58">
</Mediainfo>
which means "file not found"
os.path.isfile(os.path.join(path, to, file))
returns true for these files
and in bash using
mediainfo -f --Output=XML "/path/to/file"
works
i have goggled and searched around and cannot see the answer.
Any ideas?
Thanks
UPDATE
I used this new test script
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import os
import subprocess as sub
root = "/mnt/path"
for rootfldr in sorted(os.listdir(root)):
if os.path.isfile(os.path.join(root, rootfldr)):
command = ['mediainfo', '-f', '--Output=XML', rootfldr]
aa = sub.Popen(command, stdout=sub.PIPE, stderr=sub.PIPE, stdin=sub.PIPE)
result = aa.communicate()[0]
print rootfldr
print result
And the results were fine (some of then had non ASCII chars).
I then decided to change (from the pymedinfo parse function):
p = Popen(command, stdout=fp_out, stderr=fp_err, env=environment)
to
p = Popen(command, stdout=fp_out, stderr=fp_err)
and the problem was solved
I am guessing that something is missing and or wrong from
ENV_DICT = {
"PATH": "/usr/local/bin/:/usr/bin/",
"LD_LIBRARY_PATH": "/usr/local/lib/:/usr/lib/"}
The command looks ok. The filename is passed as a bytestring that contains text encoded using utf-8. If your filesystem uses utf-8 then it is correct filename:
>>> print "/mnt/path/Long 73\xc2\xb0 58' W.avi".decode('utf-8')
/mnt/path/Long 73° 58' W.avi
It might be a bug in pymediainfo. Try passing the environment argument explicitely as a workaround e.g., environment=os.environ.
Related
I have some trouble to flash an stm32 over a python script. I'm using the ST-LINK_CLI.exe, provided by the ST Link Utility tool, to flash the uC and it works by using the CMD in Windows, but not over the python tool.
The error I get back from the subprocess.run(...) is "Unable to open file!" for the path I provide, but the same path works fine in the CMD from Windows.
import subprocess
path = 'C:/Users/U1/Desktop/test.hex'
path = path.encode('utf-8')
stlink_output=[]
try:
stlink_output = subprocess.run(
["ST-LINK_CLI.exe", "-c", "ID=0", "SWD", "-P", str(path), "-V", "-HardRST", "-Rst"],
check=False,
stdout=subprocess.PIPE).stdout.decode().splitlines()
except:
print("An error occured")
print(stlink_output)
Has anyone an idea, what can be wrong with the provided path? Should I use a different encoding?
You are not decoding your path, just casting your bytes as string, so you get a path like
"b'C:/Users/U1/Desktop/test.hex'"
Try to decode instead to get proper string
stlink_output = subprocess.run(
["ST-LINK_CLI.exe", "-c", "ID=0", "SWD", "-P", path.decode(), "-V", "-HardRST", "-Rst"],
check=False,
stdout=subprocess.PIPE).stdout.decode().splitlines()
If You're sure the output values are text please consider using run text=True parameter (and encoding if needed).
Just define path as string and use it (no need to encode/decode).
Also for python 3.4+ it is recommended to use pathlib module (allows neat checks and user expansion in Your code later).
So the code would look something like:
import subprocess
import pathlib
# `~` gets converted to current user home with expanduser()
# i.e. `C:/Users/U1` in Your case
path = pathlib.Path('~/Desktop/test.hex').expanduser()
if not path.exists():
raise FileNotFoundError(path)
stlink_output = subprocess.run(
["ST-LINK_CLI.exe", "-c", "ID=0", "SWD", "-P", path, "-V", "-HardRST", "-Rst"],
check=False,
# text option without decoding requires py3.7+...
# text=True,
# stdout=subprocess.PIPE).stdout.splitlines()
# ...so this is variant pre python3.7:
stdout=subprocess.PIPE).stdout.decode().splitlines()
print(stlink_output)
i'm try to sign a file xml with a file JAR from python(Django)
to this i use the next code:
PATH_FIRMA_ABSOLUTA = os.path.join(os.path.dirname(os.path.dirname(__file__)),'static/SRI/betty_elena_romo_vega.p12')
PATH_FIRMAR_JAVA = os.path.join(os.path.dirname(os.path.dirname(__file__)),'static/SRI/firmaXadesBes.jar')
file_pk12 = PATH_FIRMA_ABSOLUTA
password = 'Rgberv1792'
JAR_PATH = PATH_FIRMAR_JAVA
JAVA_CMD = 'java'
ds_document = False
#xml_str = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>"
PATH_XML_ABSOLUTO = os.path.join(os.path.dirname(os.path.dirname(__file__)),path_xml_generado)
tree = etree.parse(str(PATH_XML_ABSOLUTO))
xml_str = etree.tostring(tree, encoding='utf8', method='xml')
command = [
JAVA_CMD,
'-jar',
JAR_PATH,
xml_str,
base64.b64encode(file_pk12),
base64.b64encode(password)
]
p = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True
)
res = p.communicate()
i get this error:
Usage: java [-options] class [args...] (to execute a class)
or java [-options] -jar jarfile [args...] (to execute a jar file)
where options include:
-d32\t use a 32-bit data model if available
-d64\t use a 64-bit data model if available
..
.
the error show when i try print :
doc_signed = res[0]
print doc_signed
in this case JAR file return a string xml with a sign include but dont return on get the error.
thanks for any suggest.
You must first install the 32-bit SDKor JRE and then select and install the 64-bit package on top of the 32-bit version. It probably means that, there is one entry point and that the selection is then done through -d32/-d64.
I want to change tags of my all mkv files.
I installed MKVToolNix.
My code is:
#!/usr/bin python3
# -*- coding: utf-8 -*-
import os
import subprocess
import re
neredebu='/home/pi/hrc2/Film-Dizi/duzenle'
for kokdizin, altdizinler, dosyalar in os.walk(neredebu):
for dosya in dosyalar:
if dosya.endswith('.mkv'):
isim=re.findall('([\w\d.]*)\.[\d]{4}\.m1080p.',dosya)[0]
isim=isim.replace(".", " ")
yil=re.split('[\w\d.]*\.([\d]{4})\.m1080p.',dosya)[1]
title=isim+" - "+yil+" _ www.netmanyagi.com"
dosyayolu=kokdizin
dosyatam=dosyayolu+"/"+dosya
bashCommand = "mkvpropedit "+dosyatam+" --edit info --set \"title="+title+"\""
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
#output = subprocess.check_output(['bash','-c', bashCommand])
print(output)
print("---")
I'm getting this output:
b"Error: More than one file name has been given ('/home/pi/hrc2/Film-Dizi/duzenle/The' and 'Hero').\n"
---
What is the cause of this error?
The issue is that one of your .mkv files contains a space in its filename. Thus when you split the bashCommand to create a list, you inadvertently split the filename into two pieces.
The easiest thing would be to rename the file so that it doesn't contain a space. It may also be safer to build the list manually, rather than relying upon split(), and ensure you wrap the filename in quotes.
bashCommand = [
'mkvpropedit',
'"{}"'.format(dosyatam),
'--edit',
'info',
'--set',
'"title={}"'.format(title)
]
process = subprocess.Popen(bashCommand, stdout=subprocess.PIPE)
I've installed ffprobe using the pip ffprobe command on my PC, and installed ffmpeg from here.
However, I'm still having trouble running the code listed here.
I try to use the following code unsuccessfully.
SyntaxError: Non-ASCII character '\xe2' in file GetVideoDurations.py
on line 12, but no encoding declared; see
http://python.org/dev/peps/pep-0263/ for details
Does anyone know what's wrong? Am I not referencing the directories correctly? Do I need to make sure the .py and video files are in a specific location?
import subprocess
def getLength(filename):
result = subprocess.Popen(["ffprobe", "filename"],
stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
return [x for x in result.stdout.readlines() if "Duration" in x]
fileToWorkWith = 'C:\Users\PC\Desktop\Video.mkv'
getLength(fileToWorkWith)
Apologies if the question is somewhat basic. All I need is to be able to iterate over a group of video files and get their start time and end time.
Thank you!
There is no need to iterate though the output of FFprobe. There is one simple command which returns only the duration of the input file:
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 <input_video>
You can use the following method instead to get the duration:
def get_length(input_video):
result = subprocess.run(['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', input_video], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
return float(result.stdout)
I'd suggest using FFprobe (comes with FFmpeg).
The answer Chamath gave was pretty close, but ultimately failed for me.
Just as a note, I'm using Python 3.5 and 3.6 and this is what worked for me.
import subprocess
def get_duration(file):
"""Get the duration of a video using ffprobe."""
cmd = 'ffprobe -i {} -show_entries format=duration -v quiet -of csv="p=0"'.format(file)
output = subprocess.check_output(
cmd,
shell=True, # Let this run in the shell
stderr=subprocess.STDOUT
)
# return round(float(output)) # ugly, but rounds your seconds up or down
return float(output)
If you want to throw this function into a class and use it in Django (1.8 - 1.11), just change one line and put this function into your class, like so:
def get_duration(file):
to:
def get_duration(self, file):
Note: Using a relative path worked for me locally, but the production server required an absolute path. You can use os.path.abspath(os.path.dirname(file)) to get the path to your video or audio file.
Using the python ffmpeg package (https://pypi.org/project/python-ffmpeg)
import ffmpeg
duration = ffmpeg.probe(local_file_path)["format"]["duration"]
where local_file_path is a relative or absolute path to your file.
I think Chamath's second comment answers the question: you have a strange character somewhere in your script, either because you are using a ` instead of a ' or you have a word with non-english accents, something like this.
As a remark, for what you are doing you can also try MoviePy which parses the ffmpeg output like you do (but maybe in the future I'll use Chamath's ffprobe method it looks cleaner):
import moviepy.editor as mp
duration = mp.VideoFileClip("my_video.mp4").duration
Updated solution using ffprobe based on #llogan guidance with the pointed link:
import subprocess
def get_duration(input_video):
cmd = ["ffprobe", "-i", input_video, "-show_entries", "format=duration",
"-v", "quiet", "-sexagesimal", "-of", "csv=p=0"]
return subprocess.check_output(cmd).decode("utf-8").strip()
Fragile Solution due to stderr output:
the stderr output from ffmpeg is not intended for machine parsing and
is considered fragile.
I get help from the following documentation (https://codingwithcody.com/2014/05/14/get-video-duration-with-ffmpeg-and-python/) and https://stackoverflow.com/a/6239379/2402577
Actually, sed is unnecessary: ffmpeg -i file.mp4 2>&1 | grep -o -P "(?<=Duration: ).*?(?=,)"
You can use the following method to get the duration in HH:MM:SS format:
import subprocess
def get_duration(input_video):
# cmd: ffmpeg -i file.mkv 2>&1 | grep -o -P "(?<=Duration: ).*?(?=,)"
p1 = subprocess.Popen(['ffmpeg', '-i', input_video], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p2 = subprocess.Popen(["grep", "-o", "-P", "(?<=Duration: ).*?(?=,)"], stdin=p1.stdout, stdout=subprocess.PIPE)
p1.stdout.close()
return p2.communicate()[0].decode("utf-8").strip()
Example output for both: 01:37:11.83
Have you tried adding the encoding? That error is typical of that, as Chamath said.
Add the utf-8 encoding to your script header:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
I like to build a shared library with ffmpeg, and load it in python.
C++ code:
#ifdef __WIN32__
#define LIB_CLASS __declspec(dllexport)
#else
#define LIB_CLASS
#endif
extern "C" {
#define __STDC_CONSTANT_MACROS
#include "libavformat/avformat.h"
}
extern "C" LIB_CLASS int64_t getDur(const char* url) {
AVFormatContext* pFormatContext = avformat_alloc_context();
if (avformat_open_input(&pFormatContext, url, NULL, NULL)) {
avformat_free_context(pFormatContext);
return -1;
}
int64_t t = pFormatContext->duration;
avformat_close_input(&pFormatContext);
avformat_free_context(pFormatContext);
return t;
}
Then use gcc to compile it and get a shared library.
Python code:
from ctypes import *
lib = CDLL('/the/path/to/your/library')
getDur = lib.getDur
getDur.restype = c_longlong
duration = getDur('the path/URL to your file')
It works well in my python program.
Python Code
<code>
cmnd = ['/root/bin/ffmpeg', '-i', videopath]
process = subprocess.Popen(cmnd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = process.communicate()
#This matches regex to get the time in H:M:S format
matches = re.search(r"Duration:\s{1}(?P<hours>\d+?):(?P<minutes>\d+?):(?P<seconds>\d+\.\d+?),", stdout, re.DOTALL).groupdict()
t_hour = matches['hours']
t_min = matches['minutes']
t_sec = matches['seconds']
t_hour_sec = int(t_hour) * 3600
t_min_sec = int(t_min) * 60
t_s_sec = int(round(float(t_sec)))
total_sec = t_hour_sec + t_min_sec + t_s_sec
#This matches1 is to get the frame rate of a video
matches1 = re.search(r'(\d+) fps', stdout)
frame_rate = matches1.group(0) // This will give 20fps
frame_rate = matches1.group(1) //It will give 20
</code>
we can also use ffmpeg to get the duration of any video or audio files.
To install ffmpeg follow this link
import subprocess
import re
process = subprocess.Popen(['ffmpeg', '-i', path_of_video_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = process.communicate()
matches = re.search(r"Duration:\s{1}(?P<hours>\d+?):(?P<minutes>\d+?):(?P<seconds>\d+\.\d+?),", stdout, re.DOTALL).groupdict()
print (matches['hours'])
print (matches['minutes'])
print (matches['seconds'])
I want a script where I can paste a windows path as argument, and then the script converts the path to unix path and open the path using nautilus.
I want to be able to use the script as follows:
mypythonscript.py \\thewindowspath\subpath\
The script currently looks like this:
import sys, os
path = "nautilus smb:"+sys.argv[1]
path = path.replace("\\","/")
os.system(path)
I almost works :)
The problem is that I have to add ' around the argument... like this:
mypythonscript.py '\\thewindowspath\subpath\'
Anyone who knows how I can write a script that allows that argument is without ' , ... i.e. like this:
mypythonscript.py \\thewindowspath\subpath\
EDIT: I think I have to add that the problem is that without ' the \ in the argument is treated as escape character. The solution does not necessarily have to be a python script but I want (in Linux) to be able to just paste a windows path as argument to a script.
Unless you're using a really early version of Windows: "/blah/whatever/" just works for your OP.
Actually I had something like this a while ago, I made a bash script to automatically download links I copy into clipboard, here it is edited to use your program (you first need to install xclip if you don't already have it):
#!/bin/bash
old=""
new=""
old="$(xclip -out -selection c)"
while true
do
new="$(xclip -out -selection c)"
if [ "$new" != "$old" ]
then
old="$new"
echo Found: $new
mypythonscript.py $new
fi
sleep 1
done
exit 0
Now whenever you copy something new into the clipboard, your Python script will be executed with an argument of whatever is in your clipboard.
To avoid dealing with escapes in the shell you could work with the clipboard directly:
import os
try:
from Tkinter import Tk
except ImportError:
from tkinter import Tk # py3k
# get path from clipboard
path = Tk().selection_get(selection='CLIPBOARD')
# convert path and open it
cmd = 'nautilus'
os.execlp(cmd, cmd, 'smb:' + path.replace('\\', '/'))
ntpath, urlparse, os.path modules might help to handle the paths more robustly.
#!/usr/bin/python
#! python3
#! python2
# -*- coding: utf-8 -*-
"""win2ubu.py changes WINFILEPATH Printing UBUNTU_FILEPATH
Author: Joe Dorocak aka Joe Codeswell (JoeCodeswell.com)
Usage: win2ubu.py WINFILEPATH
Example: win2ubu.py "C:\\1d\ProgressiveWebAppPjs\\Polymer2.0Pjs\\PolymerRedux\\zetc\\polymer-redux-polymer-2"
prints /mnt/c/1d/ProgressiveWebAppPjs/Polymer2.0Pjs/PolymerRedux/zetc/polymer-redux-polymer-2
N.B. spaceless path needs quotes in BASH on Windows but NOT in Windows DOS prompt!
"""
import sys,os
def winPath2ubuPath(winpath):
# d,p = os.path.splitdrive(winpath) # NG only works on windows!
d,p = winpath.split(':')
ubupath = '/mnt/'+d.lower()+p.replace('\\','/')
print (ubupath)
return ubupath
NUM_ARGS = 1
def main():
args = sys.argv[1:]
if len(args) != NUM_ARGS or "-h" in args or "--help" in args:
print (__doc__)
sys.exit(2)
winPath2ubuPath(args[0])
if __name__ == '__main__':
main()
may want to try
my_argv_path = " ".join(sys.argv[1:])
as the only reason it would split the path into separate args is spaces in pasted path
(eg: C:\Program Files would end up as two args ["c:\Program","Files"])