示例#1
0
文件: Mixins.cs 项目: Seneral/CSTube
        /// <summary>
        /// Descrambles data of all streams at the given args position and breaks them down into individual parameters.
        /// Result is written into back into the given position as a parsed ObscuredContainer and returned
        /// </summary>
        public static ObscuredContainer ApplyDescrambler(ObscuredContainer args, string streamKey)
        {
            // Break up individual streams
            string[]          streamBundle    = args.GetValue <string>(streamKey).Split(',');
            ObscuredContainer streamContainer = new ObscuredContainer();

            foreach (string streamRaw in streamBundle)
            {             // Descramble and index stream data
                NameValueCollection streamParsed = HttpUtility.ParseQueryString(streamRaw);
                ObscuredContainer   streamInfo   = ObscuredContainer.FromDictionary(
                    streamParsed.AllKeys.ToDictionary(k => k, k => (object)HttpUtility.UrlDecode(streamParsed[k]))
                    );
                streamContainer.Add(streamRaw, streamInfo);
            }
            // Write descrambled data back into args
            args[streamKey] = streamContainer;

            /*YouTube.Log(string.Format (
             *      "Applying Descrambler: \n \t " +
             *              string.Join(" \n \t ", streamContainer.Select(s =>
             *                              string.Join("; ", ((ObscuredContainer)s.Value).Select(p => (string)p.Key + "=" + (string)p.Value))
             *              ))
             * ));*/

            return(streamContainer);
        }
示例#2
0
文件: Extract.cs 项目: Seneral/CSTube
        /// <summary>
        /// Read the YouTube player configuration (args and assets) from the JSON data embedded into the HTML page.
        /// It serves as the primary source of obtaining the stream manifest data.
        /// </summary>
        internal static YTPlayerConfig getYTPlayerConfig(string watchHTML)
        {
            string configRaw = Helpers.DoRegex(extractYTPlayerConfig, watchHTML, 1);

            if (string.IsNullOrEmpty(configRaw))
            {
                CSTube.Log("ERROR: Video is unavailable!");
                return(new YTPlayerConfig());
            }

            // Get config as JSON structure
            JObject obj         = Helpers.TryParseJObject(configRaw);
            JToken  argsToken   = obj.GetValue("args");
            JToken  assetsToken = obj.GetValue("assets");

            // Create config and read it from JSON
            YTPlayerConfig config = new YTPlayerConfig();

            config.args   = ObscuredContainer.FromJSONRecursive(argsToken, 10);
            config.assets = ObscuredContainer.FromJSONRecursive(assetsToken, 10);

            if (config.args == null || config.assets == null)
            {
                CSTube.Log("ERROR: Player Config JSON is invalid!");
            }
            return(config);
        }
示例#3
0
文件: Mixins.cs 项目: Seneral/CSTube
        /// <summary>
        /// Apply the decrypted signature to the stream manifest.
        /// </summary>
        public static void ApplySignature(ObscuredContainer streamContainer, string js)
        {
            int numSignaturesFound = 0;

            foreach (KeyValuePair <string, object> s in streamContainer)
            {             // Iterate over each stream and sign the URLs
                ObscuredContainer stream = (ObscuredContainer)s.Value;
                string            URL    = stream.GetValue <string>("url");
                if (URL.Contains("signature="))
                {                 // Sometimes, signature is provided directly by YT, so we can skip the whole signature descrambling
                    numSignaturesFound++;
                    continue;
                }

                // Get, decode and save signature
                string cipheredSignature = stream.GetValue <string>("s");
                string signature         = Cipher.DecodeSignature(js, cipheredSignature);
                stream["url"] = URL + "&signature=" + signature;

                /*YouTube.Log(string.Format (
                 *      "Descrambled Signature for ITag={0} \n \t s={1} \n \t signature={2}",
                 *      stream.GetValue<string>("itag"),
                 *      cipheredSignature,
                 *      signature));*/
            }

            if (numSignaturesFound > 0)
            {
                CSTube.Log(string.Format("{0} out of {1} URLs already contained a signature!", numSignaturesFound, streamContainer.Count));
            }
        }
示例#4
0
文件: Helpers.cs 项目: Seneral/CSTube
        /// <summary>
        /// Retrieves an arbitrary attribute from the specified container.
        /// Path separated by / and \ expects respective container tree.
        /// Returns default(T) on failure to retrive the attribute.
        /// </summary>
        public static T TryGetAttribute <T>(ObscuredContainer container, string path)
        {
            string[]          cmd = path.Split('\\', '/');
            ObscuredContainer cur = container;

            for (int i = 0; i < cmd.Length - 1; i++)
            {             // Follows the specified path down the container hierarchy if possible
                cur = cur.GetValue <ObscuredContainer>(cmd[i]);
                if (cur == null)
                {
                    return(default(T));
                }
            }
            return(cur.GetValue <T>(cmd[cmd.Length - 1]));
        }
示例#5
0
        /// <summary>
        /// Read value of the give attribute collection into the class members
        /// Affects type, URL, ITag and Abr
        /// </summary>
        private void ReadAttributeValues(ObscuredContainer attributes)
        {
            if (attributes.ContainsKey("type"))
            {
                type = attributes.GetValue <string>("type");
            }

            if (attributes.ContainsKey("url"))
            {
                URL = attributes.GetValue <string>("url");
            }

            if (attributes.ContainsKey("itag"))
            {
                ITag = attributes.GetValue <string>("itag");
            }

            if (attributes.ContainsKey("abr"))
            {
                Abr = attributes.GetValue <string>("abr");
            }
        }
示例#6
0
        /// <summary>
        /// Decodes stream data found in the specified raw container into an object.
        /// </summary>
        internal Stream(ObscuredContainer streamContainer)
        {
            stream = streamContainer;

            // Read parameters ITag, abr and URL
            ReadAttributeValues(stream);

            // Read format information from tables using ITag
            format = ITags.getFormatProfile(ITag);

            // 'video/webm; codecs="vp8, vorbis"' -> 'video/webm', 'vp8, vorbis'
            Tuple <string, string> typeSplit = Extract.splitType(type);

            // Seperate type info
            mimeType = typeSplit.Item1;
            string[] typeInfo = mimeType.Split('/');
            type    = typeInfo[0];
            subType = typeInfo[1];

            // Read and parse codecs (seperate audio and video)
            codecs     = typeSplit.Item2.Split(',').Select(c => c.Trim()).ToList();
            videoCodec = isProgressive ? codecs[0] : (type == "video" ? codecs[0] : null);
            audioCodec = isProgressive ? codecs[1] : (type == "audio" ? codecs[0] : null);
        }
示例#7
0
        /// <summary>
        /// Tries to convert the tree of the given JToken into a ObscuredContainer tree.
        /// Values are either own ObscuredContainer or string values, building a tree.
        ///
        /// Usually assumes jToken to be a JObject or JArray.
        /// If it is not, tries to find an encapsulated structure (quoted & escaped) and parses that.
        /// If no such structure was found, returns null!
        /// </summary>
        public static ObscuredContainer FromJSONRecursive(JToken jToken, int depth)
        {
            if (jToken == null || depth <= 0 || !jToken.HasValues)
            {
                return(null);
            }
            if (string.IsNullOrWhiteSpace(jToken.ToString(Newtonsoft.Json.Formatting.None)))
            {
                return(null);
            }

            if (jToken.Type == JTokenType.Object)
            {             // Convert the JObject
                JObject           jObject         = (JObject)jToken;
                ObscuredContainer objectContainer = new ObscuredContainer();
                foreach (KeyValuePair <string, JToken> cToken in jObject)
                {                 // Interpret the child tokens either as string values or as own ObscureContainers
                    string value       = cToken.Value.ToString(Newtonsoft.Json.Formatting.None).Trim(' ', '\"');
                    object childObject = FromJSONRecursive(cToken.Value, depth - 1);
                    objectContainer.Add(cToken.Key, childObject ?? value);
                }
                return(objectContainer);
            }

            if (jToken.Type == JTokenType.Array)
            {             // Convert all JObject children of the JArray
                JArray jArray = (JArray)jToken;
                if (jArray.First.Type != JTokenType.Object)
                {
                    return(null);
                }

                int counter = 0;
                ObscuredContainer arrayContainer = new ObscuredContainer();
                foreach (JObject cArrObject in jArray.Children <JObject>())
                {                 // Parse array children as own containers and encapsulate them
                    ObscuredContainer cArrContainer = FromJSONRecursive(cArrObject, depth - 1);
                    arrayContainer.Add((counter++).ToString(), cArrContainer);
                }
                return(arrayContainer);
            }


            // Token is NOT a JObject -> Try to find an encapsulated structure in the token
            string rawValue  = jToken.ToString(Newtonsoft.Json.Formatting.None).Trim(' ', '\"');
            string safeValue = Regex.Unescape(rawValue);

            // Check if it is an embedded JObject
            if (safeValue.StartsWith("{") && safeValue.EndsWith("}") && safeValue.Length > 2)
            {
                return(FromJSONRecursive(Helpers.TryParseJObject(safeValue), depth - 1));
            }

            // Check if it is an embedded JArray
            if (safeValue.StartsWith("[") && safeValue.EndsWith("]"))
            {
                return(FromJSONRecursive(Helpers.TryParseJArray(safeValue), depth - 1));
            }

            return(null);
        }
示例#8
0
 /// <summary>
 /// Construct a Caption and reads meta-data (no HTTP fetch)
 /// </summary>
 public Caption(ObscuredContainer captionTrack)
 {
     url  = captionTrack.GetValue <string>("baseUrl");
     name = captionTrack.GetValue <ObscuredContainer>("name").GetValue <string>("simpleText");
     code = captionTrack.GetValue <string>("languageCode");
 }
示例#9
0
        /// <summary>
        /// Fetches the information about this video (including available streams and captions).
        /// Conains two synchronous HTTP fetches and several descrambling and signing algorithms.
        /// </summary>
        public async Task FetchInformation()
        {
            CSTube.Log("Fetching information of video " + videoID);

            // Page HTML
            watchHTML = await Helpers.ReadHTML(watchURL);

            if (string.IsNullOrEmpty(watchHTML))
            {
                return;
            }

            // PlayerConfig JSON
            playerConfig = Extract.getYTPlayerConfig(watchHTML);

            if (playerConfig.args == null || playerConfig.assets == null)
            {
                return;
            }

            // JavaScript Source
            string jsURL = Extract.getJSURL(playerConfig);

            jsSRC = await Helpers.ReadHTML(jsURL);

            // VideoInfo Raw Data
            //string videoInfoURL = Extract.getVideoInfoURL(videoID, watchURL, watchHTML);
            //videoInfoRAW = Helpers.ReadHTML(videoInfoURL);

            CSTube.Log("Finished downloading information! Continuing to parse!");

            // Parse Raw video info data
            //System.Collections.Specialized.NameValueCollection p = HttpUtility.ParseQueryString(videoInfoRAW);
            //videoInfo = ObscuredContainer.FromDictionary(p.AllKeys.ToDictionary(k => k, k => (object)p[k]));


            // Get all stream formats this video has (progressive and adaptive)
            List <string> streamMaps = new List <string>();

            streamMaps.Add("url_encoded_fmt_stream_map");
            if (playerConfig.args.ContainsKey("adaptive_fmts"))
            {
                streamMaps.Add("adaptive_fmts");
            }
            foreach (string streamFormat in streamMaps)
            {
                // Descramble stream data in player args
                ObscuredContainer streamBundle = Mixins.ApplyDescrambler(playerConfig.args, streamFormat);

                // If required, apply signature to the stream URLs
                Mixins.ApplySignature(streamBundle, jsSRC);

                // Write stream data into Stream objects
                foreach (object streamData in streamBundle.Values)
                {
                    formatStreams.Add(new Stream((ObscuredContainer)streamData));
                }
            }


            // Try to read out captionTracks if existant
            ObscuredContainer captionTrackBundle =
                Helpers.TryGetAttribute <ObscuredContainer>(playerConfig.args,
                                                            "player_response/captions/playerCaptionsTracklistRenderer/captionTracks");

            if (captionTrackBundle != null)
            {             // Write caption tracks into Caption objects
                foreach (object captionTrack in captionTrackBundle.Values)
                {
                    captionTracks.Add(new Caption((ObscuredContainer)captionTrack));
                }
            }

            videoDataAvailable = true;

            // Log success!
            CSTube.Log(string.Format(
                           "Finished parsing video data! Found {0} video streams and {1} caption tracks for video '{2}'!",
                           formatStreams.Count, captionTracks.Count, title
                           ));
            CSTube.Log(string.Format("Video Streams: \n \t " +
                                     string.Join(" \n \t ", formatStreams.Select(s => s.ToString()))
                                     ));
            if (captionTracks.Count > 0)
            {
                CSTube.Log(string.Format("Caption Tracks: \n \t " +
                                         string.Join(" \n \t ", captionTracks.Select(s => s.ToString()))
                                         ));
            }
        }