Internet radio browser GUI for music/video streams from various directory services.

⌈⌋ ⎇ branch:  streamtuner2


Check-in [f7c2123fa9]

Overview
Comment:new channel: peertube. Deprecated plugin: youtube
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f7c2123fa9e4daad03330344b30b50903372107e
User & Date: mario on 2020-05-15 16:04:33
Original Comment: new channel: peertube. Deprecated plugin: youtube
Other Links: manifest | tags
Context
2020-05-15
17:21
Update documentation (and html/ version) to reflect plugin changes and deprecations. check-in: 0ea7545d8e user: mario tags: trunk
16:04
new channel: peertube. Deprecated plugin: youtube check-in: f7c2123fa9 user: mario tags: trunk
2020-05-14
23:08
Adapt yelp pages to plugin deprecations (dirble, streamlicensing, tuner2, etc.) and liveradio now being standard channel. check-in: fcd345af8a user: mario tags: trunk
Changes

Added channels/peertube.py version [101375306a].





















































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# encoding: UTF-8
# api: streamtuner2
# title: Peertube
# description: Video browser for Peertube servers.
# type: channel
# version: 0.2
# priority: extra
# url: https://joinpeertube.org/
# category: video
# config:
#    { name: peertube_srv,  type: select,  value: "peertube.live",  select: "peertube.live|peertube.anarchmusicall.net|hostyour.tv|peertube.co.uk|troo.tubevideo.ploud.fr|video.ploud.fr|peertube.bittube.tv|bittube.video|peertube.video|libretube.net",  description: "Primary instance/server to query", category: server }
# priority: default
# png:
#    iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3Ccul
#    E8AAABO1BMVEUAAAAhHyAnIiAjICDxaA3uZw0eGx13d3dzc3Nwc3YhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAhHyAiHyAgHyA5KB7cYQ//bQwe
#    HiCbShXwaA3xaA3xaA0hHyAhHyAhHyAAACn2ag3xaA3xaA3xaA3xaA3xaA0gHh8hHyDxaA3xaA3xaA3xaA3xaA0sKislIyQfHR4dGxzxaA3xaA3xaA1paWlubm
#    50dHR5eXnxaA3xaA10dHRzc3Nzc3Nzc3Nzc3PxaA3xaA3xaA1zc3Nzc3Nzc3Nzc3M4eKPzaAvxaA3xaA1zc3Nxc3S9bDfxaA3xaA3xaA1zc3ODcmbkaRf/ZwBz
#    c3Nzc3Nyc3RtdHpzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3MhHyDxaA1zc3MAAABrSqpYAAAAZXRSTlMAAAAAAAAAAAAAA4ROBgK56YIX/EXoGfaNOAd+P8
#    SFF/u7SAQl2/u6RwQC7Sba6oMZA31IBfrAQwODRAXDPQK56oMa5oAcAvu7QwQl/L/nhEDEgRr1kjYE6XwdAfq9RwV9FwOGUtq6k/IAAAABYktHRACIBR1IAAAA
#    CXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH5AUPCjUlNSD/NQAAAMJJREFUGNNjYODi5uFlZEABfPwCgkKMjKhCqcL8IigKgUKpqaLcYkzMqEKp4hKSUiwoQg
#    LSMrJy8iwscCEFRSVlFVU1dQ1NiJgWvzbQRlYd3bQ0PX0DsEJDI2MTNgYWkFCaqZk5SMjC0sqaHSqkZmMLErKzd3B04gALObu4gjW62ae7e3h6eev6+MKMBwql
#    p/v5BwQGBcMcARZKDwkNC4c7FSwUERkVzcmAJBQTGxfPgeRtN3uHhERkAQaGpOQUFCUMAJsmJ8NW/rFWAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIwLTA1LTE1VD
#    A4OjUzOjM3KzAyOjAw7qvI1wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMC0wNS0xNVQwODo1MzozNyswMjowMJ/2cGsAAAAASUVORK5CYII=
# depends: bin:youtube-dl
# extraction-method: json
#
#
# Browser for PeerTube instances (configurable in settings) and
# video categories. Even loads video snapshots as favicons, hencewhy
# the display is slightly stretched in comparison to radio channels.
#
# The chosen main server has some influence over sorting, but usually
# includes entries from peered instances.
#
# Currently there's no player for PeerTube /embed/ links, so the
# video/youtube MIME should be combined with `youtuble-dl` even
# for VLC. Else just use the [Station] homepage for the web view.
#
# Per default fetches 200 entries (not conf.max_streams), because
# it's a little slower on some servers. And the standard sorting
# is publication time (newest videos atop).
#


from config import *
from channels import *

import ahttp
import json
import re



# Peertube
#
# /video queries are public, so we don't even need OAuth for most
# of the instances.
# The API and resultsets are also quite simple to work with.
# Apart from the /embed/ url not being useful for direct playback.
#
# API
#
#  · https://peer.tube/api/v1/oauth-clients/local
#     {"client_id":"3ouzl1…","client_secret":"oLFjYTCjw…"}
#
#  · https://peer.tube/api/v1/videos/categories
#     {"1":"Music","2":"Films","3":"Vehicles","4":"Art","5":"Sports",…}
#
#  · https://peer.tube/api/v1/videos?categoryOneOf=1
#     data[
#      {
#         "publishedAt" : "2020-05-10T07:01:18.843Z",
#         "embedPath" : "/videos/embed/b60bae06-82bf-4925-b1ef-b8fc6903ae28",
#         "originallyPublishedAt" : "2014-03-31T07:00:42.000Z",
#         "nsfw" : false,
#         "language" : {
#            "id" : "te",
#            "label" : "Telugu"
#         },
#         "createdAt" : "2020-05-10T07:01:18.843Z",
#         "privacy" : {
#            "label" : "Public",
#            "id" : 1
#         },
#         "id" : 184339,
#         "views" : 1,
#         "duration" : 349,
#         "thumbnailPath" : "/static/thumbnails/b60bae06-82bf-4925-b1ef-b8fc6903ae28.jpg",
#         "description" : "Lyrics : Lakshmi Valli Devi Bijibilla\nMusic : Kanakesh Rathod\nPublisher : Bijibilla Rama Rao ",
#         "dislikes" : 0,
#         "uuid" : "b60bae06-82bf-4925-b1ef-b8fc6903ae28",
#         "likes" : 0,
#         "category" : {
#            "label" : "Music",
#            "id" : 1
#         },
#         "updatedAt" : "2020-05-10T08:02:35.008Z",
#         "licence" : {
#            "id" : 7,
#            "label" : "Public Domain Dedication"
#         },
#         "channel" : {
#            "avatar" : null,
#            "url" : "https://peertube.slat.org/video-channels/sudhanva_sankirtanam",
#            "displayName" : "Sudhanva Sankirtanam",
#            "id" : 16607,
#            "name" : "sudhanva_sankirtanam",
#            "host" : "peertube.slat.org"
#         },
#         "isLocal" : false,
#         "name" : "Alayam Devaalayam-Kanakesh Rathod",
#         "account" : {
#            "id" : 25475,
#            "name" : "bijibilla_rama_rao",
#            "host" : "peertube.slat.org",
#            "displayName" : "Bijibilla Rama Rao",
#            "url" : "https://peertube.slat.org/accounts/bijibilla_rama_rao",
#            "avatar" : null
#         },
#         "previewPath" : "/static/previews/b60bae06-82bf-4925-b1ef-b8fc6903ae28.jpg"
#      },
#
#  · peertube.mygaia.org/videos/embed/f1208e4f-425f-473b-9449-bc168798d604
#
# INTERNA
#
# The /embed/ section of the url can sometimes be substituted with:
#  · /videos/watch/UUID
#  · /static/streaming-playlists/hls/UUID/master.m3u8
#  · /static/webseed/UUID.mp4
# Though that's sometimes blocked / or not consistently supported on all instances.
# Which is why resoslve_urn does an extra /api/v1/videos/uuid lookup.
#
class peertube (ChannelPlugin):

    # control attributes
    listformat = "srv"
    has_search = True
    audioformat = "video/youtube"
    titles = dict( genre="Channel", title="Title", playing="Description", bitrate=False, listeners=False )
    srv = conf.peertube_srv
    image_resize = 48
    fixed_size = [48,32]

    categories = [] 
    catmap = { "Music": "1" }


    # just a static list for now
    def update_categories(self):
        cats = self.api("videos/categories")    # { "1": "Music" }
        self.catmap = dict((k,int(v)) for v,k in cats.items())   # { "Music": 1 }
        self.categories = sorted(self.catmap, key=self.catmap.get)   # sort by value


    # retrieve and parse
    def update_streams(self, cat, search=None):
        if search:
            params = {
                "tagsOneOf": search,
                "count": 100,
                "sort": "-name",
                "nsfw": "false"
            }
        elif not cat in self.catmap:
            return []
        elif cat:
            params = {
                "categoryOneOf": self.catmap[cat],
                "count": 100,
                "sort": "-publishedAt",
                "nsfw": "false"
            }

        # fetch + map
        entries = []
        for video in self.api("videos", params):
            #log.DATA(video)
            entries.append(self.map_data(video))
        #log.EN(json.dumps(entries, indent=4))
        return entries

    # peertube entry to streamtunter2 dict
    def map_data(self, v):
        url = "http://" + v["channel"]["host"]
        return dict(
            uuid = v["uuid"],
            genre = v["category"]["label"],
            title = v["name"],
            playing = re.sub("\s+", " ", v["description"]) if v.get("description") else "",
            url = "urn:peertube:{}".format(v["uuid"]),
            homepage = url + v["embedPath"].replace("/embed/", "/watch/"),
            #homepage = v["channel"]["url"],
            format = self.audioformat,
            img = url + v["thumbnailPath"]
        )


    # fetch one or multiple pages from API
    def api(self, method, params={}, debug=False, count=200, **kw):
        r = []
        for i in range(0, 5):
            add = json.loads(
                ahttp.get("http://{}/api/v1/{}".format(conf.peertube_srv, method), params, **kw)
            )
            if not add.get("data"):
                return add
            else:
                r += add["data"]
                params["start"] = 100 * (i+1);
            if len(r) >= count:
                break
        return r


    # from uuid to downloadUrl
    def resolve_urn(self, row):
        video = self.api("videos/" + row["uuid"])
        #log.DATA(video)
        search = (
            ("streamingPlaylists", "playlistUrl", "audio/mpeg-url"),
            ("files", "fileDownloadUrl", "video/mpeg"),
            ("files", "fileUrl", "video/mpeg")
        )
        for section, entry, format in search:
            for row in video.get(section, []):
                if row.get(entry):
                    row["url"] = row[entry]
                    row["format"] = format
                    return row
        # else use /watch/ url
        row["url"] = row["homepage"]
        return row

Modified contrib/youtube.py from [f32eca050a] to [a528707a1d].

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
# encoding: UTF-8
# api: streamtuner2
# title: Youtube
# description: Channel, playlist and video browsing for youtube.
# type: channel
# version: 0.3
# url: http://www.youtube.com/
# category: video
# config:

#    { name: youtube_channels,  type: text,  value: "Key Of Awesome, Pentatonix",  description: "Preferred channels to list videos from.",  category: select }
#    { name: youtube_region,  type: select,  select: "=No Region|AR=Argentina|AU=Australia|AT=Austria|BE=Belgium|BR=Brazil|CA=Canada|CL=Chile|CO=Colombia|CZ=Czech Republic|EG=Egypt|FR=France|DE=Germany|GB=Great Britain|HK=Hong Kong|HU=Hungary|IN=India|IE=Ireland|IL=Israel|IT=Italy|JP=Japan|JO=Jordan|MY=Malaysia|MX=Mexico|MA=Morocco|NL=Netherlands|NZ=New Zealand|PE=Peru|PH=Philippines|PL=Poland|RU=Russia|SA=Saudi Arabia|SG=Singapore|ZA=South Africa|KR=South Korea|ES=Spain|SE=Sweden|CH=Switzerland|TW=Taiwan|AE=United Arab Emirates|US=United States",  value: GB,  description: "Filter by region id.",  category: auth }
#    { name: youtube_wadsworth,  type: boolean,  value: 0,  description: "Apply Wadsworth constant.",  category: filter }
# priority: default
# png:
#   iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAYNJREFUOI3Fks9LVFEUxz/nzrPx+WN0xhAUgoT6A6y/wFb+C4IbIQhcBm36H1obVNtoGYS0TFoIQstazBgNBaELQdTx
#   vea9uffbwufw3mTRzi8cLnzv955z7vccuG7YXmtyBlgBbgFTQB3Q3/RAHzgD9oHdyMNTg01gshD8DwScCJ7bx+bEN7Cl0Xt5D2aYc//Iq67LYDFHXEamgGZmmd94SHzvPoMoIguerKQZamExykS9kjQIN3eThcdP
#   WAiBo/fbHLx5Te/LZzQYgFW6qbsMKEcf+CWRpCm+2aK5ts6drZfMP9okH4/pSxV91NeI4RLmA0mS4ns9JHGaJvzMc1Lpwo3Smyi7wl6FwHmScNzt8mPnA4fv3lLrtJkIHqt+AXvViFPB+JCQ0HQDrTyg127jvu4T
#   D3Jqzg0LDLWQ2lYj7oDulmlJZCEwZuD+GGMlRae2eiNqeVgOUA9AAAuAmSEzCq4cKs5TwYvIwzPBJ+A2F2s8XZQcXedL7qwY1neDHa4dvwFfDLdx6YbozgAAAABJRU5ErkJggg==
# depends: bin:youtube-dl
# extraction-method: json
#



# 









# Lists recently popular youtube videos by category or channels.

#
# Introduces the faux MIME type "video/youtube" for player and recording
# configuration; both utilizing `youtube-dl`. But VLC can consume Youtube
# URLs directly anyhow.
#
# For now custom channel names must be configured in the settings dialog
# text entry, and applied using Channel > Update categories..


from config import *
from channels import *

import ahttp
import json









>



|








>
>
>
|
>
>
>
>
>
>
>
>
>
|
>




<
<
<







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
# encoding: UTF-8
# api: streamtuner2
# title: Youtube
# description: Channel, playlist and video browsing for youtube.
# type: channel
# version: 0.3
# url: http://www.youtube.com/
# category: video
# config:
#    { name: youtube_apikey,  type: text,  value: "",  description: "Youtube Data API key",  category: auth }
#    { name: youtube_channels,  type: text,  value: "Key Of Awesome, Pentatonix",  description: "Preferred channels to list videos from.",  category: select }
#    { name: youtube_region,  type: select,  select: "=No Region|AR=Argentina|AU=Australia|AT=Austria|BE=Belgium|BR=Brazil|CA=Canada|CL=Chile|CO=Colombia|CZ=Czech Republic|EG=Egypt|FR=France|DE=Germany|GB=Great Britain|HK=Hong Kong|HU=Hungary|IN=India|IE=Ireland|IL=Israel|IT=Italy|JP=Japan|JO=Jordan|MY=Malaysia|MX=Mexico|MA=Morocco|NL=Netherlands|NZ=New Zealand|PE=Peru|PH=Philippines|PL=Poland|RU=Russia|SA=Saudi Arabia|SG=Singapore|ZA=South Africa|KR=South Korea|ES=Spain|SE=Sweden|CH=Switzerland|TW=Taiwan|AE=United Arab Emirates|US=United States",  value: GB,  description: "Filter by region id.",  category: auth }
#    { name: youtube_wadsworth,  type: boolean,  value: 0,  description: "Apply Wadsworth constant.",  category: filter }
# priority: deprecated
# png:
#   iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAYNJREFUOI3Fks9LVFEUxz/nzrPx+WN0xhAUgoT6A6y/wFb+C4IbIQhcBm36H1obVNtoGYS0TFoIQstazBgNBaELQdTx
#   vea9uffbwufw3mTRzi8cLnzv955z7vccuG7YXmtyBlgBbgFTQB3Q3/RAHzgD9oHdyMNTg01gshD8DwScCJ7bx+bEN7Cl0Xt5D2aYc//Iq67LYDFHXEamgGZmmd94SHzvPoMoIguerKQZamExykS9kjQIN3eThcdP
#   WAiBo/fbHLx5Te/LZzQYgFW6qbsMKEcf+CWRpCm+2aK5ts6drZfMP9okH4/pSxV91NeI4RLmA0mS4ns9JHGaJvzMc1Lpwo3Smyi7wl6FwHmScNzt8mPnA4fv3lLrtJkIHqt+AXvViFPB+JCQ0HQDrTyg127jvu4T
#   D3Jqzg0LDLWQ2lYj7oDulmlJZCEwZuD+GGMlRae2eiNqeVgOUA9AAAuAmSEzCq4cKs5TwYvIwzPBJ+A2F2s8XZQcXedL7qwY1neDHa4dvwFfDLdx6YbozgAAAABJRU5ErkJggg==
# depends: bin:youtube-dl
# extraction-method: json
#
# DEPRECATED. You're going to need your own Youtube API key now to use
# this channel. This isn't very much worth the effort, but see:
# https://developers.google.com/youtube/v3/getting-started
#
# WHY? The usage quota has been exceeded (fairly unlikely that it was from
# regular streamtuner2 usage; more plausible that someone looked up the
# existing API key and used it for different purposes). And the Youtube
# "support" people kept demanding a video recording of ST2 usage (due to
# comprehension issues or whatever). Which wasn't a strong indicator for
# a sensible or technical audit/quota usage review.
#
# Which is why there's now a PeerTube channel plugin instead.
# 
# Lists recently popular youtube videos from a selection of categories
# or customly configured channel names.
#
# Introduces the faux MIME type "video/youtube" for player and recording
# configuration; both utilizing `youtube-dl`. But VLC can consume Youtube
# URLs directly anyhow.





from config import *
from channels import *

import ahttp
import json
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
                "v": 2,
                "alt": "json",
                "max-results": 50,
            }
        ],
        3: [ "https://www.googleapis.com/youtube/v3/",
            {
                "key": "AIzaSyAkbLSLn1VgsdFXCJjjdZtLd6W8RqtL4Ag",
                "maxResults": 50,
                "part": "id,snippet",
                "fields": "pageInfo,nextPageToken,items(id,snippet(title,thumbnails/default/url,channelTitle))",
            }
        ]
    }








|







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
                "v": 2,
                "alt": "json",
                "max-results": 50,
            }
        ],
        3: [ "https://www.googleapis.com/youtube/v3/",
            {
                "key": conf.get("youtube_apikey", ""),
                "maxResults": 50,
                "part": "id,snippet",
                "fields": "pageInfo,nextPageToken,items(id,snippet(title,thumbnails/default/url,channelTitle))",
            }
        ]
    }