示例#1
0
        public R <PlayResource> GetResourceById(AudioResource resource)
        {
            var channel = resource.ResourceId;

            // request api token
            string jsonResponse;

            if (!WebWrapper.DownloadString(out jsonResponse, new Uri($"http://api.twitch.tv/api/channels/{channel}/access_token"), new Tuple <string, string>("Client-ID", twitchClientId)))
            {
                return(RResultCode.NoConnection.ToString());
            }

            var jsonDict = (Dictionary <string, object>)Util.Serializer.DeserializeObject(jsonResponse);

            // request m3u8 file
            var token = Uri.EscapeUriString(jsonDict["token"].ToString());
            var sig   = jsonDict["sig"];
            // guaranteed to be random, chosen by fair dice roll.
            var    random = 4;
            string m3u8;

            if (!WebWrapper.DownloadString(out m3u8, new Uri($"http://usher.twitch.tv/api/channel/hls/{channel}.m3u8?player=twitchweb&&token={token}&sig={sig}&allow_audio_only=true&allow_source=true&type=any&p={random}")))
            {
                return(RResultCode.NoConnection.ToString());
            }

            // parse m3u8 file
            var dataList = new List <StreamData>();

            using (var reader = new System.IO.StringReader(m3u8))
            {
                var header = reader.ReadLine();
                if (string.IsNullOrEmpty(header) || header != "#EXTM3U")
                {
                    return(RResultCode.TwitchMalformedM3u8File.ToString());
                }

                while (true)
                {
                    var blockInfo = reader.ReadLine();
                    if (string.IsNullOrEmpty(blockInfo))
                    {
                        break;
                    }

                    var match = M3U8ExtMatch.Match(blockInfo);
                    if (!match.Success)
                    {
                        continue;
                    }

                    switch (match.Groups[1].Value)
                    {
                    case "EXT-X-TWITCH-INFO": break;                             // Ignore twitch info line

                    case "EXT-X-MEDIA":
                        string streamInfo = reader.ReadLine();
                        Match  infoMatch;
                        if (string.IsNullOrEmpty(streamInfo) ||
                            !(infoMatch = M3U8ExtMatch.Match(streamInfo)).Success ||
                            infoMatch.Groups[1].Value != "EXT-X-STREAM-INF")
                        {
                            return(RResultCode.TwitchMalformedM3u8File.ToString());
                        }

                        var streamData = new StreamData();
                        // #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000,CODECS="mp4a.40.2",VIDEO="audio_only"
                        for (int i = 0; i < infoMatch.Groups[3].Captures.Count; i++)
                        {
                            string key   = infoMatch.Groups[4].Captures[i].Value.ToUpper(CultureInfo.InvariantCulture);
                            string value = infoMatch.Groups[5].Captures[i].Value;

                            switch (key)
                            {
                            case "BANDWIDTH": streamData.Bandwidth = int.Parse(value, CultureInfo.InvariantCulture); break;

                            case "CODECS": streamData.Codec = TextUtil.StripQuotes(value); break;

                            case "VIDEO":
                                StreamQuality quality;
                                if (Enum.TryParse(TextUtil.StripQuotes(value), out quality))
                                {
                                    streamData.QualityType = quality;
                                }
                                else
                                {
                                    streamData.QualityType = StreamQuality.unknown;
                                }
                                break;
                            }
                        }

                        streamData.Url = reader.ReadLine();
                        dataList.Add(streamData);
                        break;

                    default: break;
                    }
                }
            }

            // Validation Process

            if (dataList.Count <= 0)
            {
                return(RResultCode.TwitchNoStreamsExtracted.ToString());
            }

            int codec = SelectStream(dataList);

            if (codec < 0)
            {
                return("The stream has no audio_only version.");
            }

            return(new PlayResource(dataList[codec].Url, resource.ResourceTitle != null ? resource : resource.WithName($"Twitch channel: {channel}")));
        }
示例#2
0
        public async Task <PlayResource> GetResourceById(ResolveContext?_, AudioResource resource)
        {
            var channel = resource.ResourceId;

            // request api token
            var access = await WebWrapper
                         .Request($"https://api.twitch.tv/api/channels/{channel}/access_token")
                         .WithHeader("Client-ID", TwitchClientIdPrivate)
                         .AsJson <JsonAccessToken>();

            // request m3u8 file
            if (access is null || access.token is null || access.sig is null)
            {
                throw Error.LocalStr(strings.error_media_internal_invalid + " (tokenResult|sigResult)");
            }
            var token = Uri.EscapeUriString(access.token);
            var sig   = access.sig;
            // guaranteed to be random, chosen by fair dice roll.
            const int random = 4;
            var       m3u8   = await WebWrapper
                               .Request($"http://usher.twitch.tv/api/channel/hls/{channel}.m3u8?player=twitchweb&&token={token}&sig={sig}&allow_audio_only=true&allow_source=true&type=any&p={random}")
                               .AsString();

            // parse m3u8 file
            var dataList = new List <StreamData>();

            using (var reader = new System.IO.StringReader(m3u8))
            {
                var header = reader.ReadLine();
                if (string.IsNullOrEmpty(header) || header != "#EXTM3U")
                {
                    throw Error.LocalStr(strings.error_media_internal_missing + " (m3uHeader)");
                }

                while (true)
                {
                    var blockInfo = reader.ReadLine();
                    if (string.IsNullOrEmpty(blockInfo))
                    {
                        break;
                    }

                    var match = M3U8ExtMatch.Match(blockInfo);
                    if (!match.Success)
                    {
                        continue;
                    }

                    switch (match.Groups[1].Value)
                    {
                    case "EXT-X-TWITCH-INFO": break;                     // Ignore twitch info line

                    case "EXT-X-MEDIA":
                        string?streamInfo = reader.ReadLine();
                        Match  infoMatch;
                        if (string.IsNullOrEmpty(streamInfo) ||
                            !(infoMatch = M3U8ExtMatch.Match(streamInfo)).Success ||
                            infoMatch.Groups[1].Value != "EXT-X-STREAM-INF")
                        {
                            throw Error.LocalStr(strings.error_media_internal_missing + " (m3uStream)");
                        }

                        var streamData = new StreamData();
                        // #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000,CODECS="mp4a.40.2",VIDEO="audio_only"
                        for (int i = 0; i < infoMatch.Groups[3].Captures.Count; i++)
                        {
                            string key   = infoMatch.Groups[4].Captures[i].Value.ToUpperInvariant();
                            string value = infoMatch.Groups[5].Captures[i].Value;

                            switch (key)
                            {
                            case "BANDWIDTH": streamData.Bandwidth = int.Parse(value, CultureInfo.InvariantCulture); break;

                            case "CODECS": streamData.Codec = TextUtil.StripQuotes(value); break;

                            case "VIDEO":
                                streamData.QualityType = Enum.TryParse(TextUtil.StripQuotes(value), out StreamQuality quality)
                                                                 ? quality
                                                                 : StreamQuality.unknown; break;
                            }
                        }

                        streamData.Url = reader.ReadLine();
                        dataList.Add(streamData);
                        break;
                    }
                }
            }

            // Validation Process

            if (dataList.Count <= 0)
            {
                throw Error.LocalStr(strings.error_media_no_stream_extracted);
            }

            int codec = SelectStream(dataList);

            if (codec < 0)
            {
                throw Error.LocalStr(strings.error_media_no_stream_extracted);
            }
            var selectedStream = dataList[codec];

            if (selectedStream.Url == null)
            {
                throw Error.LocalStr(strings.error_media_no_stream_extracted);
            }

            if (resource.ResourceTitle == null)
            {
                resource.ResourceTitle = $"Twitch channel: {channel}";
            }
            return(new PlayResource(selectedStream.Url, resource));
        }
示例#3
0
        public R <PlayResource, LocalStr> GetResourceById(AudioResource resource)
        {
            var channel = resource.ResourceId;

            // request api token
            if (!WebWrapper.DownloadString(out string jsonResponse, new Uri($"https://api.twitch.tv/api/channels/{channel}/access_token"), ("Client-ID", TwitchClientId)))
            {
                return(new LocalStr(strings.error_net_no_connection));
            }

            var jObj = JObject.Parse(jsonResponse);

            // request m3u8 file
            var tokenResult = jObj.TryCast <string>("token");
            var sigResult   = jObj.TryCast <string>("sig");

            if (!tokenResult.Ok || !sigResult.Ok)
            {
                return(new LocalStr(strings.error_media_internal_invalid + " (tokenResult|sigResult)"));
            }
            var token = Uri.EscapeUriString(tokenResult.Value);
            var sig   = sigResult.Value;
            // guaranteed to be random, chosen by fair dice roll.
            const int random = 4;

            if (!WebWrapper.DownloadString(out string m3u8, new Uri($"http://usher.twitch.tv/api/channel/hls/{channel}.m3u8?player=twitchweb&&token={token}&sig={sig}&allow_audio_only=true&allow_source=true&type=any&p={random}")))
            {
                return(new LocalStr(strings.error_net_no_connection));
            }

            // parse m3u8 file
            var dataList = new List <StreamData>();

            using (var reader = new System.IO.StringReader(m3u8))
            {
                var header = reader.ReadLine();
                if (string.IsNullOrEmpty(header) || header != "#EXTM3U")
                {
                    return(new LocalStr(strings.error_media_internal_missing + " (m3uHeader)"));
                }

                while (true)
                {
                    var blockInfo = reader.ReadLine();
                    if (string.IsNullOrEmpty(blockInfo))
                    {
                        break;
                    }

                    var match = M3U8ExtMatch.Match(blockInfo);
                    if (!match.Success)
                    {
                        continue;
                    }

                    switch (match.Groups[1].Value)
                    {
                    case "EXT-X-TWITCH-INFO": break;                     // Ignore twitch info line

                    case "EXT-X-MEDIA":
                        string streamInfo = reader.ReadLine();
                        Match  infoMatch;
                        if (string.IsNullOrEmpty(streamInfo) ||
                            !(infoMatch = M3U8ExtMatch.Match(streamInfo)).Success ||
                            infoMatch.Groups[1].Value != "EXT-X-STREAM-INF")
                        {
                            return(new LocalStr(strings.error_media_internal_missing + " (m3uStream)"));
                        }

                        var streamData = new StreamData();
                        // #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000,CODECS="mp4a.40.2",VIDEO="audio_only"
                        for (int i = 0; i < infoMatch.Groups[3].Captures.Count; i++)
                        {
                            string key   = infoMatch.Groups[4].Captures[i].Value.ToUpper(CultureInfo.InvariantCulture);
                            string value = infoMatch.Groups[5].Captures[i].Value;

                            switch (key)
                            {
                            case "BANDWIDTH": streamData.Bandwidth = int.Parse(value, CultureInfo.InvariantCulture); break;

                            case "CODECS": streamData.Codec = TextUtil.StripQuotes(value); break;

                            case "VIDEO":
                                streamData.QualityType = Enum.TryParse(TextUtil.StripQuotes(value), out StreamQuality quality)
                                                                 ? quality
                                                                 : StreamQuality.unknown; break;
                            }
                        }

                        streamData.Url = reader.ReadLine();
                        dataList.Add(streamData);
                        break;
                    }
                }
            }

            // Validation Process

            if (dataList.Count <= 0)
            {
                return(new LocalStr(strings.error_media_no_stream_extracted));
            }

            int codec = SelectStream(dataList);

            if (codec < 0)
            {
                return(new LocalStr(strings.error_media_no_stream_extracted));
            }

            return(new PlayResource(dataList[codec].Url, resource.ResourceTitle != null ? resource : resource.WithName($"Twitch channel: {channel}")));
        }
示例#4
0
		public RResultCode GetResourceById(string id, string name, out AudioResource resource)
		{
			var channel = id;

			// request api token
			string jsonResponse;
			if (!WebWrapper.DownloadString(out jsonResponse, new Uri($"http://api.twitch.tv/api/channels/{channel}/access_token")))
			{
				resource = null;
				return RResultCode.NoConnection;
			}

			var jsonDict = (Dictionary<string, object>)jsonParser.DeserializeObject(jsonResponse);

			// request m3u8 file
			var token = Uri.EscapeUriString(jsonDict["token"].ToString());
			var sig = jsonDict["sig"];
			// guaranteed to be random, chosen by fair dice roll.
			var random = 4;
			string m3u8;
			if (!WebWrapper.DownloadString(out m3u8, new Uri($"http://usher.twitch.tv/api/channel/hls/{channel}.m3u8?player=twitchweb&&token={token}&sig={sig}&allow_audio_only=true&allow_source=true&type=any&p={random}")))
			{
				resource = null;
				return RResultCode.NoConnection;
			}

			// parse m3u8 file
			var dataList = new List<StreamData>();
			using (var reader = new System.IO.StringReader(m3u8))
			{
				var header = reader.ReadLine();
				if (string.IsNullOrEmpty(header) || header != "#EXTM3U")
				{
					resource = null;
					return RResultCode.TwitchMalformedM3u8File;
				}

				while (true)
				{
					var blockInfo = reader.ReadLine();
					if (string.IsNullOrEmpty(blockInfo))
						break;

					var match = m3u8ExtMatch.Match(blockInfo);
					if (!match.Success)
						continue;

					switch (match.Groups[1].Value)
					{
					case "EXT-X-TWITCH-INFO": break; // Ignore twitch info line
					case "EXT-X-MEDIA":
						string streamInfo = reader.ReadLine();
						Match infoMatch;
						if (string.IsNullOrEmpty(streamInfo) ||
							 !(infoMatch = m3u8ExtMatch.Match(streamInfo)).Success ||
							 infoMatch.Groups[1].Value != "EXT-X-STREAM-INF")
						{
							resource = null;
							return RResultCode.TwitchMalformedM3u8File;
						}

						var streamData = new StreamData();
						// #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=128000,CODECS="mp4a.40.2",VIDEO="audio_only"
						for (int i = 0; i < infoMatch.Groups[3].Captures.Count; i++)
						{
							string key = infoMatch.Groups[4].Captures[i].Value.ToUpper();
							string value = infoMatch.Groups[5].Captures[i].Value;

							switch (key)
							{
							case "BANDWIDTH": streamData.Bandwidth = int.Parse(value); break;
							case "CODECS": streamData.Codec = TextUtil.StripQuotes(value); break;
							case "VIDEO":
								StreamQuality quality;
								if (Enum.TryParse(TextUtil.StripQuotes(value), out quality))
									streamData.QualityType = quality;
								else
									streamData.QualityType = StreamQuality.unknown;
								break;
							}
						}

						streamData.Url = reader.ReadLine();
						dataList.Add(streamData);
						break;
					default: break;
					}
				}
			}

			resource = new TwitchResource(channel, name ?? $"Twitch channel: {channel}", dataList);
			return dataList.Count > 0 ? RResultCode.Success : RResultCode.TwitchNoStreamsExtracted;
		}