8
8
import logging
9
9
import re
10
10
11
+ from streamlink .exceptions import NoStreamsError , PluginError
11
12
from streamlink .plugin import Plugin , pluginmatcher
12
13
from streamlink .plugin .api import validate
14
+ from streamlink .stream .dash import DASHStream
13
15
from streamlink .stream .hls import HLSStream
16
+ from streamlink .stream .http import HTTPStream
14
17
15
18
16
19
log = logging .getLogger (__name__ )
17
20
18
21
19
- @pluginmatcher (re .compile (
20
- r"https?://(?:www\.)?ccma\.cat/tv3/directe/(?P<ident>.+?)/" ,
21
- ))
22
+ @pluginmatcher (
23
+ name = "live" ,
24
+ pattern = re .compile (r"https://(?:www)?\.ccma\.cat/3cat/directes/(?P<ident>[^/?]+)" ),
25
+ )
26
+ @pluginmatcher (
27
+ name = "vod" ,
28
+ pattern = re .compile (r"https://(?:www)?\.ccma\.cat/3cat/[^/]+/video/(?P<ident>\d+)" ),
29
+ )
22
30
class TV3Cat (Plugin ):
23
- _URL_STREAM_INFO = "https://dinamics.ccma.cat/pvideo/media.jsp"
31
+ _URL_API_GEO = "https://dinamics.ccma.cat/geo.json"
32
+ _URL_API_MEDIA = "https://api-media.ccma.cat/pvideo/media.jsp"
24
33
25
- _MAP_CHANNELS = {
26
- "tv3" : "tvi" ,
34
+ _MAP_CHANNEL_IDENTS = {
35
+ "catalunya-radio" : "cr" ,
36
+ "catalunya-informacio" : "ci" ,
37
+ "catalunya-musica" : "cm" ,
38
+ "icat" : "ic" ,
27
39
}
28
40
29
- def _get_streams (self ):
30
- ident = self .match .group ("ident" )
41
+ def _call_api_media (self , fmt , schema , params ):
42
+ geo = self .session .http .get (
43
+ self ._URL_API_GEO ,
44
+ schema = validate .Schema (
45
+ validate .parse_json (),
46
+ {"geo" : str },
47
+ validate .get ("geo" ),
48
+ ),
49
+ )
50
+ if not geo :
51
+ raise PluginError ("Missing 'geo' value" )
31
52
32
- schema_media = {
33
- "geo" : str ,
34
- "url" : validate .url (path = validate .endswith (".m3u8" )),
35
- }
53
+ log .debug (f"{ geo = } " )
54
+ schema = validate .all (
55
+ {
56
+ "geo" : str ,
57
+ "format" : fmt ,
58
+ "url" : schema ,
59
+ },
60
+ validate .union_get ("geo" , "url" ),
61
+ )
36
62
37
- stream_infos = self .session .http .get (
38
- self ._URL_STREAM_INFO ,
63
+ ident = self .match ["ident" ]
64
+ streams = self .session .http .get (
65
+ self ._URL_API_MEDIA ,
39
66
params = {
40
67
"media" : "video" ,
41
68
"versio" : "vast" ,
42
- "idint" : self ._MAP_CHANNELS .get (ident , ident ),
43
- "profile" : "pc" ,
44
- "desplacament" : "0" ,
45
- "broadcast" : "false" ,
69
+ "idint" : self ._MAP_CHANNEL_IDENTS .get (ident , ident ),
70
+ "profile" : "pc_3cat" ,
71
+ ** (params or {}),
46
72
},
47
73
schema = validate .Schema (
48
74
validate .parse_json (),
49
75
{
50
76
"media" : validate .any (
51
- [schema_media ],
77
+ [schema ],
52
78
validate .all (
53
- schema_media ,
79
+ schema ,
54
80
validate .transform (lambda item : [item ]),
55
81
),
56
82
),
@@ -59,12 +85,41 @@ def _get_streams(self):
59
85
),
60
86
)
61
87
62
- for stream in stream_infos :
63
- log .info (f"Accessing stream from region { stream ['geo' ]} " )
64
- try :
65
- return HLSStream .parse_variant_playlist (self .session , stream ["url" ], name_fmt = "{pixels}_{bitrate}" )
66
- except OSError :
67
- pass
88
+ log .debug (f"{ streams = } " )
89
+ for _geo , data in streams :
90
+ if _geo == geo :
91
+ return data
92
+
93
+ log .error ("The content is geo-blocked" )
94
+ raise NoStreamsError
95
+
96
+ def _get_live (self ):
97
+ schema = validate .url (path = validate .endswith (".m3u8" ))
98
+ url = self ._call_api_media ("HLS" , schema , {"desplacament" : 0 })
99
+ return HLSStream .parse_variant_playlist (self .session , url )
100
+
101
+ def _get_vod (self ):
102
+ schema = [
103
+ validate .all (
104
+ {
105
+ "label" : str ,
106
+ "file" : validate .url (),
107
+ },
108
+ validate .union_get ("label" , "file" ),
109
+ ),
110
+ ]
111
+ urls = self ._call_api_media ("MP4" , schema , {"format" : "dm" })
112
+ for label , url in urls :
113
+ if label == "DASH" :
114
+ yield from DASHStream .parse_manifest (self .session , url ).items ()
115
+ else :
116
+ yield label , HTTPStream (self .session , url )
117
+
118
+ def _get_streams (self ):
119
+ if self .matches ["live" ]:
120
+ return self ._get_live ()
121
+ else :
122
+ return self ._get_vod ()
68
123
69
124
70
125
__plugin__ = TV3Cat
0 commit comments