السلام عليكم و رحمة الله تعالى و بركاته...
قبل أيام وجدت عددا من فيديوهات ناذرة على موقع اليوتيوب كنت أبحث عنها. و لتحميل نسخ منها على حاسوبي احتجت لتحميل و تجربة بعض البرامج المخصصة لهذا الغرض و كما تعلمون فموقع اليوتيوب لا يتيح عملية التحميل بل المشاهدة فقط. المهم بعد تجربة عدد من برامج التحميل فكرت في البحث عن كيفية تحميل الملفات و بعد اكتشافها تبادرت إلى ذهني فكرة عمل موضوع لأتشارك معكم نتيجة بحثي الصغير.
قبل المضي في الموضوع لا بد أن أنوه أنه و في أي لحظة, قد يصبح محتوى هذا الموضوع متجاوز و غير عملي لأن طريقة تحديد و إيجاد رابط ملف الفيديو يتم استخلاصه من الكود المصدر لصفحة اليوتيوب التي يظهر بداخلها الفيديو و أي تغيير أو تعديل في بنية كود صفحات الموقع تحيل الموضوع و البرنامج المرفق و الكود و المجهود إلى سلة المهملات.
كيف أكتشف يدويا رابط ملف الفيديو بمساعدة فايرفوكس و فايربوغ؟
عندما تفتح صفحة لمشاهدة فيديو على اليوتيوب يقوم كود جافاسكريبت بتهيئة مربع عرض الفيديوهات ببرامترات العرض, و من بين هذه البرامترات هناك رابط ملف الفيديو الذي يقوم مربع العرض بتحميله و عرض محتواه بالتوازي.
للوصول إلى رابط ملف الفيديو المختبئ داخل الكود المصدر لصفحة موقع اليوتيوب, سنستعمل المتصفح فايرفوكس و ملحقه الشهير فايربوغ. نبدأ بإظهار نافذة الفايربوغ ثم نقوم بإدخال عنوان صفحة الفيديو التي نريد عرضها. بعد انتهاء عملية تحميل الصفحة, سنلاحظ أن فايرفوكس ما زال يقوم بتحميل بيانات من خادم اليوتيوب! هذه البيانات هي بيانات ملف الفيديو التي نريد الوصول إليها. نقوم باختيار tab المسمى Network ثم اسفله سنجد زر مسمى Flash نقوم بالنقر عليه, نلاحظ لائحة بها عنصرين. العنصر الأول هو ملف كنترول مربع عرض فيديوهات اليوتيوب و الثاني و الذي هو ظالتنا هو رابط ملف الفيديو. للتأكد يمكننا النقر عليه بالزر الأيمن للفأرة و اختيار Copy adress من القائمة التي ستظهر ثم إدخال الرابط في شريط العنوان لتتم عملية تحميل ملف الفيديو من الخادم على الحاسوب.
كيف أكتشف يديويا رابط ملف الفيديو داخل كود هتمل صفحة اليوتيوب؟
رابط ملف فيديو اليوتيوب موجود كبارامتر لتهيئة مربع عرض الفيديو كما أسلفنا ذكره سابقا لكن هناك رابط مشابه له سهل الإيجاد و لكن يجب تعديله ليصبح طلب تحميل ملف الفيديو و ليس صورة preview للفيديو. الرابط يكون بشكل كهذا :
http://v3.lscache5.c.youtube.com/generate_204?ip=0.0.0.0&...
سنحتاج لتعديله ليصبح بالشكل التالي الذي يتيح لنا تحميل ملف الفيديو :
http://v3.lscache5.c.youtube.com/videoplayback?ip=0.0.0.0&...
لإيجاد رابط ملف الفيديو, قم بالنقر على الزر الأيمن للفأرة في أي مكان على صفحة يوتيوب ثم قم باختيار Page source code من القائمة ثم قم بالبحث عن مثلا [generate_204?ip] عند إيجاد النص قم بنسخ فقط الرابط ثم بعد لصقه على شريط العنوان, قم بتغيير المقطع generate_204 إلى videoplayback ثم انقر على الزر Enter ليتم تحميل ملف الفيديو.
استعراض كود البرنامج المرفق :
لتسهيل استخدام ما اكتشفناه بسهولة, سنقوم بتحويله إلى برنامج صغير يقوم بتحميل ملف فيديو من خادم اليوتيوب عن طريق تحديد رابط الصفحة على مربع نص و الضغط على زر التحميل كما هو ظاهر في الصفحة أعلاه.
تعريفات :
لأننا سنقوم بتخصيص مسلك خاص لتنفيذ كود تحميل ملف الفيديو, سنحتاج لوسيلة للوصول إلى مربعات واجهة المستخدم لتغييرها لتتلائم و الوضع الحالي لعمل التطبيق. محاولة تغيير قيمة خاصية كائن تم إنشاؤه خارج المسلك ستؤدي إلى رفع استثناء و لهذا سنقوم بالمناداة على دوال تغيير قيم مثل هذه الكائنات عن طريق Delegate.
' إظهار مربع الحوار لاختيار و تأكيد اسم ملف الفيديو من داخل مسلك التحميل
Private Delegate Function GetDestinationFileNameDelegate(ByVal name As String) As String
' الدالة التي تتم المناداة عليها عندما ينتهي مسلك التحميل من عملية التحميل
Private Delegate Sub DownloadOperationEndedDelegate(ByVal msg As String)
' تغيير قيمة خاصية من مسلك آخر غير المسلك الرئيسي
Private Delegate Sub SetPropertyValueDelegate(ByVal obj As Object, ByVal prop As String, ByVal value As Object)توابث :
سنحتاج لبعض الثوابت لتسهيل عملية كتابة الكود. من بين هذ التوابث نص التعبير المنتظم الذي يمكننا من الوصول إلى رابط ملف الفيديو على الخادم و عنوان الفيديو.
' نص عنوان النافذة الرئيسية
Private Const FormTitle As String = "منزل ملفات فيديو يوتيوب"
' تعبير منتظم للوصول إلى رابط ملف الفيديو من داخل الكود المصدر لصفحة اليوتيوب
Private Const VideoFileRegEx As String = "img\.src\s=\s\'(.*?)\'\;"
' تعبير منتظم للحصول على اسم ملف الفيديو من داخل الكود المصدر لصفحة اليوتيوب
Private Const VideoTitleRegEx As String = "\'VIDEO_TITLE\'(.*?)\'(.*?)\'\,"
' حجم البيانات التي تتم قراءتها من خادم اليوتيوب
Private Const BufferSize As Integer = 1024متغيرات عامة :
المتغير الأول المعرف على مستوى الفورم يمكننا من التحكم في المسلك الخاص بالتحميل. المتغير الثاني نهيئه على false و عند تغيير قيمته إلى true نكون قد أنهينا عمل المسلك الخاص بتنفيذ عملية التحميل.
' كائن المسلك الخاص بعملية التحميل
Private DownloadThread As Threading.Thread
' انهاء عمل مسلك التحميل إذا true
Private StopDownload As Boolean = Falseتحميل محتوى صفحة اليوتيوب :
للوصول إلى رابط تحميل ملف الفيديو, نحتاج إلى تحميل كود المصدر لصفحة اليوتيوب التي يتم بداخلها عرض الفيديو.
' تحميل الكود المصدر لصفحة اليوتيوب
' url : رابط صفحة اليوتيوب المراد تحميل ملف الفيديو المعروض عليها.
Private Function RetrievePageContent(ByVal url As String) As String
Dim page As String = ""
' ارسال استعلام طلب تحميل الصفحة إلى الخادم
Dim req As HttpWebRequest = HttpWebRequest.Create(url)
' الحصول على الجواب من الخادم
Dim res As HttpWebResponse = req.GetResponse()
' الوصول إلى دفق البيانات الآتية من الخادم
Dim strm As New StreamReader(res.GetResponseStream())
SetPropertyValue(DownloadProgressBar, "Style", ProgressBarStyle.Marquee)
' قراءة البيانات حتى نهايتها أو طلب ذلك من المستخدم عن طريق الزر رجوع
While Not strm.EndOfStream AndAlso Not StopDownload
' قراءة محتوى الصفحة سطر بسطر
page &= strm.ReadLine() & vbCrLf
End While
' غلق دفق البيانات
strm.Close()
' غلق جواب الخادم
res.Close()
' إرجاع كود هتمل الصفحة
Return page
End Functionدالة DownloadVideoFromYoutube :
هذه الدالة تقوم بتحميل محتوى صفحة اليوتيوب ثم عن طريق التعابير المنتظمة, تقوم بالوصول إلى رابط تحميل ملف الفيديو و عنوانه و أخيرا تقوم بتحميل ملف الفيديو.
' الدالة التي يتم تنفيذ كودها في مسلك خاص لتحميل ملف الفيديو من اليوتيوب
Private Sub DownloadVideoFromYoutube()
SetPropertyValue(Me, "Text", "الوصول إلى عنوان الملف...")
' تحميل كود مصدر الصفحة التي تظهر الفيديو
Dim page As String = RetrievePageContent(URLTextBox.Text)
SetPropertyValue(Me, "Text", "تحديد اسم الملف...")
' تحديد السطر الموجود فيه رابط ملف الفيديو من صفحة هتمل
Dim mth As Match = Regex.Match(page, VideoFileRegEx)
If mth.Groups.Count < 2 Then
Me.Invoke(New DownloadOperationEndedDelegate(AddressOf DownloadOperationEnded), New Object() {"خطأ أثناء تحديد اسم ملف الفيديو..."})
Return
End If
' تحديد رابط ملف الفيديو و تعديله لطلب تحميل ملف الفيديو
Dim hostFile As String = mth.Groups(1).Value
hostFile = Regex.Replace(hostFile, "youtube.com(.*?)\?", "youtube.com/videoplayback?")
' تحديد اسم ملف الفيديو من
Dim localFile As String = Regex.Split(page, VideoTitleRegEx)(2)
' طلب تأكيد اسم ملف الفيديو من المستخدم
If Not StopDownload Then localFile = Me.Invoke(New GetDestinationFileNameDelegate(AddressOf GetDestinationFileName), New Object() {localFile})
' الغاء العملية إذا اختار المستخدم ذلك
If localFile = "" Then
If Not StopDownload Then Me.Invoke(New DownloadOperationEndedDelegate(AddressOf DownloadOperationEnded), New Object() {""})
Return
End If
SetPropertyValue(Me, "Text", "جاري تحميل ملف الفيديو...")
' ارسال طلب التحميل للخادم
Dim req As HttpWebRequest = HttpWebRequest.Create(hostFile)
' الوصول إلى جواب الخادم
Dim res As HttpWebResponse = req.GetResponse()
' الحصول على دفق بيانات ملف الفيديو الآتية من الخادم
Dim hStrm As Stream = res.GetResponseStream()
' تهيئة مربع تطور عملية التحميل
SetPropertyValue(DownloadProgressBar, "Style", ProgressBarStyle.Continuous)
SetPropertyValue(DownloadProgressBar, "Minimum", 0)
SetPropertyValue(DownloadProgressBar, "Maximum", CType(res.ContentLength, Integer))
SetPropertyValue(DownloadProgressBar, "Value", 0)
' حجم البيانات الاجمالي الذي تم تحميله
Dim n As Integer = 0
' حجم البيانات الحالي الذي تمت قراءته في آخر قراءة
Dim i As Integer = 0
' المخزن المؤقت للبيانات المحملة من الخادم قبل كتابتها في ملف الفيديو على الحاسوب
Dim buf(BufferSize) As Byte
' فتح دفق بيانات إلى نسخة ملف البيانات على الحاسوب
Dim lStrm As Stream = File.OpenWrite(localFile)
' تحميل البيانات و نسخها على الملف حتى نهاية دفق الخادم أو طلب الانهاء من المستخدم
While Not StopDownload
' قراءة البيانات المحملة من الخادم
i = hStrm.Read(buf, 0, BufferSize)
' كتابتها على النسخة على الحاسوب
lStrm.Write(buf, 0, i)
n += i
SetPropertyValue(DownloadProgressBar, "Value", n)
' نهاية عملية النسخ إذا لم تتم عملية القراءة
If i <= 0 Then Exit While
End While
' غلق نسخة الملف
lStrm.Close()
' غلق دفق البيانات من الخادم
hStrm.Close()
' غلق جواب الخادم
res.Close()
' إعلام المستخدم بنهاية عملية التحميل
Media.SystemSounds.Exclamation.Play()
SetPropertyValue(Me, "Text", "انتهى تحميل ملف الفيديو...")
Me.Invoke(New DownloadOperationEndedDelegate(AddressOf DownloadOperationEnded), New Object() {"تم تحميل ملف الفيديو."})
End Subالدالة أسفله تتم المناداة عليها عندما ينقر المستخدم على زر التحميل.
' عند اختيار المستخدم زر التحميل
Private Sub DownloadButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DownloadButton.Click
' منع المستخدم من اختيار زر التحميل وقت عملية التحميل
DownloadButton.Enabled = False
' إنشاء كائن مسير المسلك الخاص بتحميل ملف الفيديو
DownloadThread = New Threading.Thread(AddressOf DownloadVideoFromYoutube)
' تنفيذ كود مسلك التحميل
DownloadThread.Start()
End Subبعد تحميل كود المصدر لصفحة اليوتيوب و الوصول إلى رابط و عنوان ملف الفيديو, نحتاج من المستخدم إلى تأكيد عنوان و مسار نسخة ملف الفيديو على الحاسوب و لأن العملية تتم على مسلك آخر غير المسلك الرئيسي للتطبيق نحتاج لتنفيذ كود اختيار اسم و مسار الملف على دالة تتم المناداة عليها عن طريق Delegate GetDestinationFileNameDelegate :
' تتم المناداة على هذه الدالة للحصول على اسم نسخة ملف الفيديو عن طريق مربع الحوار 'افتح' من داخل
' مسلك آخر غير المسلك الرئيسي للتطبيق. المناداة على دوال إظهار مربعات الحوار مباشرة من مسلك التحميل
' سيؤدي إلى رفع استثناء أو تجميد التطبيق
Private Function GetDestinationFileName(ByVal name As String) As String
Dim sfd As New SaveFileDialog()
With sfd
.FileName = name
.Filter = "Flash video files (*.flv)|*.flv"
.DefaultExt = "flv"
If .ShowDialog() = Windows.Forms.DialogResult.OK Then Return .FileName
End With
' إرجاع نص فارغ في حالة اختيار المستخدم إلغاء عملية التحميل
Return ""
End Functionعند انتهاء الدالة DownloadVideoFromYoutube تقوم هذه الأخيرة بالمناداة على الدالة DownloadOperationEnded عن طريق Delegate DownloadOperationEndedDelegate ليتم تنفيذ الكود على المسلك الرئيسي للتطبيق حتى تقوم الدالة بإعلام المستخدم بانتهاء عملية التحميل و تهيئة واجهة المستخدم لوضع انتظار عملية تحميل أخرى.
' تتم المناداة على هذه الدالة لإعلام التطبيق بانتهاء مسلك التحميل مع تحديد رسالة بنتيجة
' العملية التي ستظهر للمستخدم
Private Sub DownloadOperationEnded(ByVal msg As String)
' إظهار الرسالة
If msg <> "" Then MsgBox(msg, MsgBoxStyle.Information, "نهاية عملية التحميل")
' تهيئة وضعية انتظار عملية تحميل أخرى
StopDownload = False
DownloadButton.Enabled = True
Me.Text = FormTitle
With DownloadProgressBar
.Style = ProgressBarStyle.Continuous
.Minimum = 0
.Maximum = 0
.Value = 0
End With
' انهاء مسلك التحميل
DownloadThread.Join(5000)
DownloadThread.Abort()
DownloadThread = Nothing
End Subلنتمكن من تغيير قيم مربعات التحكم من المسلك الخاص بتحميل ملف الفيديو, سنستفيد من خدمات الدالة SetPropertyValue التي تقوم بتنفيذ كودها في المسلك الرئيسي للتطبيق لتقوم بعملها.
' تتم المناداة على الدالة لتغيير محتوى خاصية كائن تم انشاؤه خارج مسلك التحميل
' محاولة تغيير قيمة خاصية كائن لا ينتمي إلى مسلك التحميل تؤدي إلى رفع اسثناء
' obj : الكائن المراد تغيير قيمة خاصيته
' prop : اسم الخاصية المراد تغيير قيمتها
' value : القيمة الجديدة للخاصية
Private Sub SetPropertyValue(ByVal obj As Object, ByVal prop As String, ByVal value As Object)
' إذا, حاليا, الكود سيتم تنفيذه خارج المسلك الذي تم انشاء النموذج فيه...
If Me.InvokeRequired Then
' تنفيذ كود الدالة داخل المسلك الذي انشئ فيه النموذج
Me.Invoke(New SetPropertyValueDelegate(AddressOf SetPropertyValue), New Object() {obj, prop, value})
' خروج
Return
End If
' الوصول إلى نوع الكائن
Dim t As Type = obj.GetType()
' الوصول إلى الخاصية
Dim p As Reflection.PropertyInfo = t.GetProperty(prop, Reflection.BindingFlags.Instance Or Reflection.BindingFlags.Public)
' تغيير قيمتها
p.SetValue(obj, value, Nothing)
End Subو أخيرا, عندما يتم غلق التطبيق, نقوم بطلب التأكيد من المستخدم ثم نقوم بإنهاء عملية التحميل الجارية و ننهي التطبيق.
' عندما يغلق المستخدم نموذج التطبيق
Private Sub UtubDownloaderForm_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
' طلب التأكيد
Dim r As MsgBoxResult = MsgBoxResult.Yes
If DownloadProgressBar.Value <> 0 Then r = MsgBox("هل تريد فعلا انهاء عملية التحميل الجارية؟", MsgBoxStyle.Question Or MsgBoxStyle.YesNo, "خروج")
If r = MsgBoxResult.No Then
' إلغاء عملية التطبيق
e.Cancel = True
Return
End If
' إنهاء عملية التحميل الحالية
StopDownload = True
' إنهاء مسلك التحميل
If Not DownloadThread Is Nothing Then
DownloadThread.Join(5000)
DownloadThread.Abort()
End If
End Subأعرف أنني قصرت في الشرح و لكن بالمقابل, أنا جاهز للإجابة على أي سؤال في الموضوع...
ستجدون في الملف المرفق مع هذا الموضوع مشروع صغير يمكن من تحميل ملفات الفيديو من اليوتيوب, به الكود الذي تم استعراضه في هذا الموضوع.
تحياتي...
