Seite 1 von 1

Fehler in Zusammenhang mit pytube

Verfasst: Dienstag 14. Juli 2020, 13:17
von JakobPrie
Hallo,
ich arbeite derzeit and einem Sprachassistenten namens luna, der zugleich via telepot als bot genutzt werden kann. Ich habe ein Modul mit Hilfe von anderen Forumnutzern entwickelt, was einen Link, der per Telegram geschickt wird, als vollwertige Videodatei zurücksendet.
Dazu habe ich 2 Fragen:
1. Wird immer die beste Qualität genommen? An sich nutze ich dafür ja get_highest_resolution(), allerdings werden manche Videos in 720p heruntergeladen, obwohl der Link auf Youtube auf in 1080p gibt.
2. ich bekomme folgenden Fehler und denke, dass das was mit der Verbindung zu tun hat. wie kann ich diesen beheben?

Code: Alles auswählen

--JAKOB-- (Telegram): schick video
Analyse: {'town': None, 'room': None, 'rooms': [None], 'datetime': datetime.datetime(2020, 7, 14, 12, 39, 55, 999897), 'time': {'day': 14, 'month': 7, 'year': 2020, 'hour': 12, 'minute': 39, 'second': 55}}
False
Traceback (most recent call last):
  File "LUNA_server.py", line 187, in query_threaded
    if module.isValid(text):
AttributeError: module 'foto_to_telegram' has no attribute 'isValid'
[ERROR] Modul foto_to_telegram konnte nicht abgefragt werden!
--Modul video_to_telegram gestartet--
--LUNA--@Jakob (Telegram): Wie lautet die URL von dem Youtube-Video, das ich dir schicken soll?
--JAKOB-- (Telegram): https://www.youtube.com/watch?v=I2zT_TJfm_s&t=
--LUNA--@Jakob (Telegram): Video wird heruntergeladen. Bitte warte einen Moment.
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2" progressive="True" type="video">
Video heruntergeladen.
/home/pi/Desktop/LUNA/server/modules/resources/YouTube.mp4
Abbruch durch Fehler: ('Connection aborted.', OSError("(104, 'ECONNRESET')"))
--LUNA--@Jakob (Telegram): Es gab einen Fehler. Bitte versuche es erneut.
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/contrib/pyopenssl.py", line 317, in _send_until_done
    return self.connection.send(data)
  File "/usr/lib/python3/dist-packages/OpenSSL/SSL.py", line 1737, in send
    self._raise_ssl_error(self._ssl, result)
  File "/usr/lib/python3/dist-packages/OpenSSL/SSL.py", line 1639, in _raise_ssl_error
    raise SysCallError(errno, errorcode.get(errno))
OpenSSL.SSL.SysCallError: (104, 'ECONNRESET')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 600, in urlopen
    chunked=chunked)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 354, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3.7/http/client.py", line 1244, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib/python3.7/http/client.py", line 1290, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.7/http/client.py", line 1239, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.7/http/client.py", line 1065, in _send_output
    self.send(chunk)
  File "/usr/lib/python3.7/http/client.py", line 987, in send
    self.sock.sendall(data)
  File "/usr/lib/python3/dist-packages/urllib3/contrib/pyopenssl.py", line 328, in sendall
    sent = self._send_until_done(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE])
  File "/usr/lib/python3/dist-packages/urllib3/contrib/pyopenssl.py", line 323, in _send_until_done
    raise SocketError(str(e))
OSError: (104, 'ECONNRESET')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/pi/Desktop/LUNA/server/modules/video_to_telegram.py", line 37, in handle
    send_video_to_telegram(luna, VIDEO_PATH)
  File "/home/pi/Desktop/LUNA/server/modules/video_to_telegram.py", line 54, in send_video_to_telegram
    luna.telegram.bot.sendVideo(uid, video=open(VIDEO_PATH, 'rb'), supports_streaming=True)
  File "/usr/local/lib/python3.7/dist-packages/telepot/_init_.py", line 588, in sendVideo
    return self._api_request_with_file('sendVideo', _rectify(p), 'video', video)
  File "/usr/local/lib/python3.7/dist-packages/telepot/_init_.py", line 499, in _api_request_with_file
    return self._api_request(method, _rectify(params), files, **kwargs)
  File "/usr/local/lib/python3.7/dist-packages/telepot/_init_.py", line 491, in _api_request
    return api.request((self._token, method, params, files), **kwargs)
  File "/usr/local/lib/python3.7/dist-packages/telepot/api.py", line 154, in request
    r = fn(*args, **kwargs)  # `fn` must be thread-safe
  File "/usr/lib/python3/dist-packages/urllib3/request.py", line 150, in request_encode_body
    return self.urlopen(method, url, **extra_kw)
  File "/usr/lib/python3/dist-packages/urllib3/poolmanager.py", line 323, in urlopen
    response = conn.urlopen(method, u.request_uri, **kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 638, in urlopen
    _stacktrace=sys.exc_info()[2])
  File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 367, in increment
    raise six.reraise(type(error), error, _stacktrace)
  File "/usr/lib/python3/dist-packages/six.py", line 692, in reraise
    raise value.with_traceback(tb)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 600, in urlopen
    chunked=chunked)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 354, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3.7/http/client.py", line 1244, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib/python3.7/http/client.py", line 1290, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.7/http/client.py", line 1239, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.7/http/client.py", line 1065, in _send_output
    self.send(chunk)
  File "/usr/lib/python3.7/http/client.py", line 987, in send
    self.sock.sendall(data)
  File "/usr/lib/python3/dist-packages/urllib3/contrib/pyopenssl.py", line 328, in sendall
    sent = self._send_until_done(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE])
  File "/usr/lib/python3/dist-packages/urllib3/contrib/pyopenssl.py", line 323, in _send_until_done
    raise SocketError(str(e))
urllib3.exceptions.ProtocolError: ('Connection aborted.', OSError("(104, 'ECONNRESET')"))
Video gelöscht
--Modul video_to_telegram beendet--
Hier das genannte Modul:

Code: Alles auswählen

#!/usr/bin/pythob
# -*- coding:utf-8 -*-

import pytube
import os
import traceback


def isValid(text):
    text = text.lower()

    if 'schicke' in text or 'schick' in text or 'sende' in text and 'video' in text:
        return True
    if 'lade' in text and 'youtubevideo' in text and 'herunter' in text:
        return True


def handle(text, luna, profile):
    SAVE_PATH = luna.path + '/modules/resources'
    VIDEO_PATH = SAVE_PATH + '/YouTube.mp4'
    luna.say('Wie lautet die URL von dem Youtube-Video, das ich dir schicken soll?', output='telegram')
    response = luna.listen(input='telegram')
    try:
        luna.say('Video wird heruntergeladen. Bitte warte einen Moment.', output='telegram')
        try:
            youtube = pytube.YouTube(response)
        except:
            luna.say('Der Link konnte keinem Video zugeordnet werden.')

        video = youtube.streams.get_highest_resolution()
        print(video)
        video.download(SAVE_PATH, filename="YouTube")
        print('Video heruntergeladen.')

        send_video_to_telegram(luna, VIDEO_PATH)

    except Exception as e:
        print(f"Abbruch durch Fehler: {e}")
        luna.say('Es gab einen Fehler. Bitte versuche es erneut.')
        traceback.print_exc()
    try:
        os.remove(VIDEO_PATH)
        print('Video gelöscht')
    except:
        print('Video konnte nicht gelöscht werden.')
        traceback.print_exc()
    print('Klasse abgeschlossen')


def send_video_to_telegram(luna, VIDEO_PATH):
    print(VIDEO_PATH)
    try:
        uid = luna.local_storage['LUNA_telegram_name_to_id_table'][luna.user]
        luna.telegram.bot.sendVideo(uid, video=open(VIDEO_PATH, 'rb'), supports_streaming=True)
    except KeyError as e:
        Log.write('WARNING', 'Der Text "{}" konnte nicht gesendet werden, da für den Nutzer "{}" keine Telegram-ID angegeben wurde'.format(text, user), conv_id=original_command, show=True)
        print(f"Abbruch durch Fehler: {e}")
        traceback.print_exc()
Vielen Dank für Eure Hilfe!
Liebe Grüße,
Jakob

Re: Fehler in Zusammenhang mit pytube

Verfasst: Sonntag 19. Juli 2020, 17:37
von __blackjack__
@JakobPrie: Ad 1. Ich vermute mal mit höchster Auflösung ist die Datei mit der höchsten Videroauflösung *und* mit Audio gemeint. Bei dem Beispielvideo in der Fehlermeldung gibt es zwar verschiedene 1080p-Varianten aber die sind alle ohne Ton:

Code: Alles auswählen

In [88]: yt = pytube.YouTube("https://www.youtube.com/watch?v=I2zT_TJfm_s")     

In [89]: yt.streams.filter(resolution="1080p").all()                            
Out[89]: 
[<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028" progressive="False" type="video">,
 <Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9" progressive="False" type="video">,
 <Stream: itag="299" mime_type="video/mp4" res="1080p" fps="60fps" vcodec="avc1.64002a" progressive="False" type="video">,
 <Stream: itag="303" mime_type="video/webm" res="1080p" fps="60fps" vcodec="vp9" progressive="False" type="video">]

In [90]: for stream in yt.streams.filter(resolution="1080p").all(): 
    ...:     print(stream.includes_audio_track) 
    ...:                                                                        
False
False
False
False
Ad 2.: Gute Frage. Da schliesst die Gegenseite die Verbindung. Warum auch immer.

Anmerkungen zum Quelltext: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Also `is_valid()` statt `isValid()`.

So eine Testfunktion sollte `True` oder `False` zurückgeben und nicht `True` oder `None`.

``"schicke" in text or "schick" in text`` ist zu umständlich denn wenn "schicke" in dem Text vorkommt, dann ist "schick" selbstverständlich auch in dem Text, denn "schick" ist in "schicke" enthalten. Oder anders herum: es reicht auf "schick" zu testen wenn "schicke" auch erfasst werden soll.

Pfade setzt man nicht mit ``+`` zusammen. Dafür gibt es das `pathlib`-Modul.

Man verwendet keine nackten ``except``\s ohne konkrete Ausnahmen. Das geht nur wenn man die Ausnahme erneut auslöst und/oder protokolliert. Sonst verschluckt man an der Stelle Ausnahmen und macht die Fehlersuche schwer bis unmöglich.

Die ”Fehlerbehandlung” beim erstellen des `Youtube`-Objekts ist zudem noch fehlerhaft weil danach so weiter gemacht wird als wäre nichts passiert. Was aber sicher nicht passiert ist, ist die Definition von `youtube`, was dazu führt das unweigerlich ein paar Zeilen weiter eine Ausnahme ausgelöst wird wenn auf den nicht definierten Namen zugegriffen wird. Da fehlt ein ``else``.

Beim zweiten ”nackten” ``except`` sollte ein `OSError` behandelt werden.

Die ganzen `print()`-Ausgaben sollten wohl eher Logging sein. Dann muss man auch nicht selbst mit dem `traceback`-Modul operieren.

Was soll die Ausgabe "Klasse abgeschlossen" bedeuten? Da ist nirgends eine Klasse und Klassen werden ja auch nicht ”abgeschlossen”. Das die `handle()`-Funktion durchgelaufen ist, sollte beim Aufrufer protokolliert werden, damit das nicht jede `handle()`-Funktion selbst machen muss. Das gilt im Grunde auch für eine generelle Ausnahmebehandlung der `handle()`-Funktion(en).

In der `send_video_to_telegram()` ist der ``try``-Block zu umfangreich für eine Ausnahmebehandlung die sich im Grunde nur auf die erste Zeile bezieht.

Im ``except`` werden Sachen verwendet die es nicht gibt: `Log`, `text`, `user`, und `original_command`. Da viele Ausnahmen nur selten auftreten, sollte das durch Unit-Tests abgedeckt sein.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import logging
from pathlib import Path

import pytube

LOG = logging.getLogger(__file__)


def is_valid(text):
    text = text.lower()
    return (
        any(term in text for term in ["schick", "sende"]) and "video" in text
    ) or all(term in text for term in ["lade", "youtubevideo", "herunter"])


def send_video_to_telegram(luna, video_file_path):
    LOG.debug(video_file_path)
    telegram_name_to_id = luna.local_storage["LUNA_telegram_name_to_id_table"]
    try:
        uid = telegram_name_to_id[luna.user]
    except KeyError:
        LOG.error(
            (
                "Das Video %r konnte nicht gesendet werden,"
                " da für den Nutzer %r keine Telegram-ID angegeben wurde"
            ),
            video_file_path,
            luna.user,
        )
    else:
        luna.telegram.bot.sendVideo(
            uid, video=video_file_path.open("rb"), supports_streaming=True
        )


def handle(_text, luna, _profile):
    #
    # TODO Would be nice if `luna.path` would already be a `Path` object.
    #
    luna.say(
        "Wie lautet die URL von dem Youtube-Video, das ich dir schicken soll?",
        output="telegram",
    )
    response = luna.listen(input="telegram")
    try:
        luna.say(
            "Video wird heruntergeladen. Bitte warte einen Moment.",
            output="telegram",
        )
        try:
            youtube = pytube.YouTube(response)
        except:  # FIXME This is way too broad!
            luna.say("Der Link konnte keinem Video zugeordnet werden.")
        else:
            stream = youtube.streams.get_highest_resolution()
            LOG.debug(stream)
            save_path = Path(luna.path) / "modules" / "resources"
            try:
                stream.download(str(save_path), filename="YouTube")
                LOG.info("Video heruntergeladen.")
                video_file_path = save_path / "YouTube.mp4"
                send_video_to_telegram(luna, video_file_path)
            finally:
                try:
                    video_file_path.unlink()
                except OSError:
                    LOG.exception("Video konnte nicht gelöscht werden.")
                else:
                    LOG.debug("Video gelöscht")
    except Exception:
        LOG.exception("Abbruch durch Fehler:")
        luna.say("Es gab einen Fehler. Bitte versuche es erneut.")