vivaldi, youtube-dl, irgendwas passt nicht mit den Pfaden

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
le_mon
User
Beiträge: 6
Registriert: Donnerstag 25. Mai 2017, 13:22

Ahoi zusammen.

Die Ausgangslage ist folgende:
Ich habe mir eine Extension für Chrome-ähnliche Browser gebastelt.
Ziel ist es, von einer beliebigen Video/Audio Seite eine mp3 zu erzeugen.
Das funktioniert mit NativeMessagingHosts.
Mein Host ist ein Python-Script, welches youtube.dl einbindet und darüber die videos/audios ergattert.

In Chrome/Chromium/Opera funktioniert der Spaß auch wie er sollte, in Vivaldi nicht ganz.
Hier mal der Host:
[codebox=python file=com.blabla.ytextract]
#!/usr/bin/env python

from __future__ import unicode_literals
import youtube_dl
import struct
import sys
import threading

# Thread that reads messages from the webapp.
def read_thread_func(queue):
message_number = 0
while 1:
# Read the message length (first 4 bytes).
text_length_bytes = sys.stdin.read(4)

if len(text_length_bytes) == 0:
sys.exit(0)

# Unpack message length as 4 byte integer.
text_length = struct.unpack('i', text_length_bytes.encode('utf-8'))[0]

# Read the text (JSON object) of the message.
text = sys.stdin.read(text_length)

# start download function
ytdl()

def logit(msg):
f = open('logfile', 'a')
f.write(msg+"\n")
f.close()

def ytdl():
url = "https://www.youtube.com/watch?v=B7bqAsxee4I"

# Logger Class to know what happens
class MyLogger(object):
def debug(self, msg):
logit("[debug]"+msg)
def warning(self, msg):
logit("[warning]"+msg)
def error(self, msg):
logit("[error]"+msg)

# youtube-dl parameter
path = "/home/user/Downloads/"
ydl_opts = {
'format': 'bestaudio',
'noplaylist' : True,
'outtmpl': path+'%(title)s.%(ext)s',
'verbose': True,
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
'progress_hooks': [my_hook],
'logger': MyLogger(),
}

with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])

def Main():
read_thread_func(None)
sys.exit(0)

if __name__ == '__main__':
Main()
[/code]
Videos werden geladen, aber er hängt sich auf, wenn es ans Extrahieren von Audio geht:
[codebox=bash file=logfile]
[download] 100% of 873.61KiB in 00:00
[debug][ffmpeg] Correcting container in "/home/user/Downloads/videotitle.m4a"
[debug][debug] ffmpeg command line: ffmpeg -y -i 'file:/home/user/Downloads/videotitle.m4a' -c copy -f mp4 'file:/home/user/Downloads/videotitle.m4a'
[error]ERROR: Did you mean file:file:/home/user/Downloads/videotitle.m4a?
[error]Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/youtube_dl/YoutubeDL.py", line 1942, in post_process
files_to_delete, info = pp.run(info)
File "/usr/lib/python3.6/site-packages/youtube_dl/postprocessor/ffmpeg.py", line 530, in run
self.run_ffmpeg(filename, temp_filename, options)
File "/usr/lib/python3.6/site-packages/youtube_dl/postprocessor/ffmpeg.py", line 208, in run_ffmpeg
self.run_ffmpeg_multiple_files([path], out_path, opts)
File "/usr/lib/python3.6/site-packages/youtube_dl/postprocessor/ffmpeg.py", line 204, in run_ffmpeg_multiple_files
raise FFmpegPostProcessorError(msg)
youtube_dl.postprocessor.ffmpeg.FFmpegPostProcessorError: Did you mean file:file:/home/user/Downloads/videotitle.m4a?
[/code]
Ich vermute, das hängt damit zusammen, dass irgendwo ein zweites "file:" in die Befehlszeile rutscht, was in den anderen Browsern nicht passiert.
Nun ist die Frage, warum.

Das Vivaldi-Forum scheint wenig Interesse daran zu haben und die youtube-dl community hat mein Issue geschlossen ohne groß drauf einzugehen.
Findet sich hier jemand, der sich mit der Materie auskennt?

Hat Vivaldi besondere Umgebungsvariablen, die dafür verantwortlich sind?
Wenn ich das script im terminal ausführe, funktioniert es problemlos.
Ich weiß nicht so recht, wo ich anfangen soll zu suchen.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

der verwendest ja Python 2, der Youtube Downloader, der importiert wird, ist aber für Python 3.6. Das passt nicht, normalerweise sollte der durch das `import` Statement gar nicht gefunden werden. Hast du dahingehend dein System irgendwie "verbogen"?

Gruß, noisefloor
le_mon
User
Beiträge: 6
Registriert: Donnerstag 25. Mai 2017, 13:22

Woran erkennt man, welche Version ich verwende?
Ich dachte eigentlich das läuft alles unter 3.6
Im System (Arch linux) ist 3.6 als Default angegeben.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Woran erkennt man, welche Version ich verwende?
Am einfachsten, indem du den Python-Interpreter in einem Terminal startest. Dann wird die Python-Version ausgegeben. Mit dem Aufruf von `python` bekommst du normalerweise Python 2 (bei den meisten Systemen heute 2.7.x), beim Aufruf von `python3` Python 3.x, bei dir also Python 3.6.

Die Zeile `#!/usr/bin/env python` lädt also den Python 2 Interpreter, normalerweise. Es sei denn, Arch Linux macht da was anders als Ubuntu, Debian, RedHat & Co.

Gruß, noisefloor
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@le_mon: python ist normalerweise für Python2 reserviert, nur Archlinux macht da sein eigenes Ding, trotzdem sollte die Shebang-Zeile »#!/usr/bin/env python3« heißen, damit es klar ist und auch auf anderen Systemen läuft. Nächster Punkt ist der __future__-Import, der bei Python3 überflüssig ist und daher sehr stark an Python2 erinnert.

Eingerückt wird immer mit 4 Leerzeichen, nicht mit 2. 1 sollte nicht für den Wahrheitswert True mißbraucht werden. Die ganze read_thread_func sieht seltsam aus. Du schreibst, Du willst Bytes lesen, liest aber Zeichen, und hoffst, dass das nicht irgendwie gültiges UTF8 ist und Du statt dessen 5, 6, oder mehr Bytes liest und Python3 sein Unicode-Wodoo machen kann. Am besten bekommt die Funktion als Argument ein file-objekt das Binärdaten liest, dann kann der Aufrufer entscheiden, wie er die Daten liefern will.
sys.exit sollte außerhalb der Main-Funktion nicht vorkommen. queue, message_number und text werden gar nicht benutzt, so dass die ganze Funktion unnötig ist.

Zum Loggen gibt es schon das logging-Modul, da braucht man nichts Neues erfinden. Klassen gehört auch nicht in eine Funktion; so kann man sie gar nicht testen.

youtube-dl ist so intelligent, die Fehlermeldung zu verstümmeln und nur die letzte Zeile auszugeben, so dass man garantiert nicht die Ursache finden kann. Was ist die Ausgabe, wenn Du ffmpeg direkt startest?
[codebox=bash file=Unbenannt.bsh]
ffmpeg -y -i 'file:/home/user/Downloads/videotitle.m4a' -c copy -f mp4 'file:/home/user/Downloads/videotitle.m4a'[/code]
le_mon
User
Beiträge: 6
Registriert: Donnerstag 25. Mai 2017, 13:22

Hu...
Danke vorerst.
Ich hab mir die Codeschnipsel irgendwie zusammen gesucht und muss gestehen, das dies mein 1. Pythonversuch ist.

Werde mir den Sonntag gönnen um alles nochmal auf den 3.6 Standard zu bringen und dann berichten.

Fühle mich hier gut aufgehoben und werde öfter reinschauen :D
le_mon
User
Beiträge: 6
Registriert: Donnerstag 25. Mai 2017, 13:22

back again,

Shebang angepasst, Leerzeichen sind nun alle korrekt und überflüssiges Zeug habe ich rausgeworfen. Ändert sich aber nichts am Verhalten, dass der Fehler mit dem file:file: auftaucht:

Code: Alles auswählen

youtube_dl.utils.DownloadError: ERROR: Did you mean file:file:/home/user/testvideo.m4a?
INFO: Dies tritt nur im Vivaldi Browser auf, Opera und Chrome/chromium tun was ihnen gesagt wird.

Bisher sieht der code folgendermaßen aus:

Code: Alles auswählen

#!/usr/bin/env python3
import sys
import json
import struct
import youtube_dl
import taglib
import logging

logging.basicConfig(filename='logfile',level=logging.DEBUG)

class MyLogger(object):
    def debug(self, msg):
        logging.debug(msg)
    def warning(self, msg):
        logging.warning(msg)
    def error(self, msg):
        logging.error(msg)

def getInfo(url):

   #kommt noch

def download(url):

    def my_hook(g):

        if g['status'] == 'finished':
            send_msg('{"status":"converting"}')

    path = "/home/user/"
    destination = path+'%(title)s.%(ext)s'
    ydl_opts = {
        'format': 'bestaudio',
        'noplaylist' : True,
        'outtmpl': destination,
        'verbose': True,
        'keepvideo': True,
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }],
        'progress_hooks': [my_hook],
        'logger': MyLogger(),
    }

    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(str(url), download=False)
        #download_target = ydl.prepare_filename(info)
        ydl.download([url])

# Function to send a message to chrome.
def send_msg(MSG_DICT):
    # Converts dictionary into string containing JSON format.
    msg_json = json.dumps([MSG_DICT], separators=(",", ":"))
    # Encodes string with UTF-8.
    msg_json_utf8 = msg_json.encode("utf-8")
    # Writes the message size. (Writing to buffer because writing bytes object.)
    sys.stdout.buffer.write(struct.pack("i", len(msg_json_utf8)))
    # Writes the message itself. (Writing to buffer because writing bytes object.)
    sys.stdout.buffer.write(msg_json_utf8)
    sys.stdout.flush()


# Function to read a message from chrome.
def read_msg():
    # Reads the first 4 bytes of the message (which designates message length).
    text_length_bytes = sys.stdin.buffer.read(4)
    # Unpacks the first 4 bytes that are the message length. [0] required because unpack returns tuple with required data at index 0.
    text_length = struct.unpack("i", text_length_bytes)[0]
    # Reads and decodes the text (which is JSON) of the message.
    text_undecoded = sys.stdin.buffer.read(text_length).decode("utf-8")
    # [...] Then use the data.

    msg = json.loads(text_undecoded)

    if 'getinfo' in msg:
        vidinfo = getInfo(msg['getinfo'])
        send_msg(vidinfo)

    if 'download' in msg:
        download(msg['download'])

def Main():
    read_msg()
    sys.exit(0)

if __name__ == '__main__':
  Main()

hier die ffmpeg line, wenn ich sie manuell ausführe:

Code: Alles auswählen

% ffmpeg -y -i 'file:/testvideo.m4a' -c copy -f mp4 'file:/testvideo.temp.m4a' 

ffmpeg version 3.3.1 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 6.3.1 (GCC) 20170306
  configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-avisynth --enable-avresample --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libass --enable-libbluray --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libschroedinger --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxvid --enable-netcdf --enable-shared --enable-version3
  libavutil      55. 58.100 / 55. 58.100
  libavcodec     57. 89.100 / 57. 89.100
  libavformat    57. 71.100 / 57. 71.100
  libavdevice    57.  6.100 / 57.  6.100
  libavfilter     6. 82.100 /  6. 82.100
  libavresample   3.  5.  0 /  3.  5.  0
  libswscale      4.  6.100 /  4.  6.100
  libswresample   2.  7.100 /  2.  7.100
  libpostproc    54.  5.100 / 54.  5.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'file:/testvideo.m4a':
  Metadata:
    major_brand     : dash
    minor_version   : 0
    compatible_brands: iso6mp41
    creation_time   : 2017-05-27T20:05:31.000000Z
  Duration: 00:00:23.66, start: 0.000000, bitrate: 95 kb/s
    Stream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 39 kb/s (default)
    Metadata:
      creation_time   : 2017-05-27T20:05:31.000000Z
      handler_name    : SoundHandler
Output #0, mp4, to 'file:/testvideo.temp.m4a':
  Metadata:
    major_brand     : dash
    minor_version   : 0
    compatible_brands: iso6mp41
    encoder         : Lavf57.71.100
    Stream #0:0(und): Audio: aac (LC) ([64][0][0][0] / 0x0040), 44100 Hz, mono, fltp, 39 kb/s (default)
    Metadata:
      creation_time   : 2017-05-27T20:05:31.000000Z
      handler_name    : SoundHandler
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help
size=     275kB time=00:00:23.63 bitrate=  95.4kbits/s speed=1.63e+03x    
video:0kB audio:270kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.739545%
Zur seltsamen read_msg:
So oder ähnlich habe ich das aus den examples vondeveloper.chrome.com übernommen.
Gesendet wird die Nachricht per
[codebox=javascript file=Unbenannt.js]
chrome.runtime.sendNativeMessage('com.my_company.my_application',
{ text: "Hello" },
function(response) {
console.log("Received " + response);
});[/code]

Ich verstehe das so, dass die ersten 4 "bytes" nur die Länge der Nachricht angeben und kein utf-8 enthalten können.
Oder bin ich da völlig unterbelichtet? Sagts mir. Ich bin neu in dieser Materie :)
Zuletzt geändert von le_mon am Sonntag 28. Mai 2017, 15:03, insgesamt 1-mal geändert.
le_mon
User
Beiträge: 6
Registriert: Donnerstag 25. Mai 2017, 13:22

Ich habe etwas nachgeforscht und herausgefunden, dass mein Script wunderbarst funktioniert, wenn ich das zusätzliche Paket entferne:

Code: Alles auswählen

aur/vivaldi-ffmpeg-codecs 58.0.3029.82-1 (70) (3,48)
    additional support for proprietary codecs for vivaldi
Sieht wohl so aus, als ob die ffmpeg-version, die dieses Paket bereit stellt, nicht das kann, was meine Version im System (local/ffmpeg 1:3.3.1-8)kann und dies zu Problemen mit den Pfaden führt. Außerdem hat die vivaldi-ffmpeg version manchmal ein Problem mit dem opus codec, welcher von ffprobe nciht korrekt erkannt wird, mit der System-Version allerdings schon.

Das selbe Spiel ist in Opera der Fall, nur mit Chromium scheint beides zu gehen.

Gibt es ne Möglichkeit, Python zu sagen, welche Umgebungspfade es nutzen soll?
le_mon
User
Beiträge: 6
Registriert: Donnerstag 25. Mai 2017, 13:22

Falls es jemand interessiert:

Ich hab mal alles auf GitHub gestellt:
https://github.com/le-mon/vid2mp3/
Antworten