aboutsummaryrefslogtreecommitdiff
path: root/youtube_dl/extractor/lbry.py
blob: 6177297ab627e15af6e37b48eda906efbe30375e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# coding: utf-8
from __future__ import unicode_literals

import json

from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    determine_ext,
    ExtractorError,
    int_or_none,
    mimetype2ext,
    try_get,
)


class LBRYIE(InfoExtractor):
    IE_NAME = 'lbry.tv'
    _VALID_URL = r'https?://(?:www\.)?(?:lbry\.tv|odysee\.com)/(?P<id>@[^:]+:[0-9a-z]+/[^:]+:[0-9a-z])'
    _TESTS = [{
        # Video
        'url': 'https://lbry.tv/@Mantega:1/First-day-LBRY:1',
        'md5': '65bd7ec1f6744ada55da8e4c48a2edf9',
        'info_dict': {
            'id': '17f983b61f53091fb8ea58a9c56804e4ff8cff4d',
            'ext': 'mp4',
            'title': 'First day in LBRY? Start HERE!',
            'description': 'md5:f6cb5c704b332d37f5119313c2c98f51',
            'timestamp': 1595694354,
            'upload_date': '20200725',
        }
    }, {
        # Audio
        'url': 'https://lbry.tv/@LBRYFoundation:0/Episode-1:e',
        'md5': 'c94017d3eba9b49ce085a8fad6b98d00',
        'info_dict': {
            'id': 'e7d93d772bd87e2b62d5ab993c1c3ced86ebb396',
            'ext': 'mp3',
            'title': 'The LBRY Foundation Community Podcast Episode 1 - Introduction, Streaming on LBRY, Transcoding',
            'description': 'md5:661ac4f1db09f31728931d7b88807a61',
            'timestamp': 1591312601,
            'upload_date': '20200604',
        }
    }, {
        'url': 'https://odysee.com/@BrodieRobertson:5/apple-is-tracking-everything-you-do-on:e',
        'only_matching': True,
    }, {
        'url': "https://odysee.com/@ScammerRevolts:b0/I-SYSKEY'D-THE-SAME-SCAMMERS-3-TIMES!:b",
        'only_matching': True,
    }]

    def _call_api_proxy(self, method, display_id, params):
        return self._download_json(
            'https://api.lbry.tv/api/v1/proxy', display_id,
            headers={'Content-Type': 'application/json-rpc'},
            data=json.dumps({
                'method': method,
                'params': params,
            }).encode())['result']

    def _real_extract(self, url):
        display_id = self._match_id(url).replace(':', '#')
        uri = 'lbry://' + display_id
        result = self._call_api_proxy(
            'resolve', display_id, {'urls': [uri]})[uri]
        result_value = result['value']
        if result_value.get('stream_type') not in ('video', 'audio'):
            raise ExtractorError('Unsupported URL', expected=True)
        streaming_url = self._call_api_proxy(
            'get', display_id, {'uri': uri})['streaming_url']
        source = result_value.get('source') or {}
        media = result_value.get('video') or result_value.get('audio') or {}
        signing_channel = result_value.get('signing_channel') or {}

        return {
            'id': result['claim_id'],
            'title': result_value['title'],
            'thumbnail': try_get(result_value, lambda x: x['thumbnail']['url'], compat_str),
            'description': result_value.get('description'),
            'license': result_value.get('license'),
            'timestamp': int_or_none(result.get('timestamp')),
            'tags': result_value.get('tags'),
            'width': int_or_none(media.get('width')),
            'height': int_or_none(media.get('height')),
            'duration': int_or_none(media.get('duration')),
            'channel': signing_channel.get('name'),
            'channel_id': signing_channel.get('claim_id'),
            'ext': determine_ext(source.get('name')) or mimetype2ext(source.get('media_type')),
            'filesize': int_or_none(source.get('size')),
            'url': streaming_url,
        }