میخواستم وقتی رسپبریپای خریدم، از کارهای جالبی که باهاش میکنم توی کانال و بلاگم بنویسم! ولی خب متاسفانه خیلی فرصت نمیشه سراغش برم، این تعطیلات چند روزه باعث شد تا بعد مدتها سعی کنم کارهایی که میخواستم باهاش انجام بدم رو عملی کنم. مهمترین کارم این بود که بجای وصل کردن لپتاپ به تلویزیون از رسپبریپای برای دیدن سریال یا ویدئوهای یوتیوب استفاده کنم. یه روزی درگیر این بودم که یه فیلم FullHD رو روی رسپبریپای با زیرنویس فارسی اجرا کنم که آخرشم نشد. (البته میخواستم اینکار رو روی Raspbian انجام بدم، OMXPlayer فیلمو خیلی خوب پخش میکرد ولی با زیرنویس فارسی مشکل داشت)
به این فکرمیکردم که چیکار کنم، یادم افتاد تلویزیون یه بخشی داره که مستقیم به یوتیوب وصل میشه ولی خب اینترنت ایران اون رو باز نمیکنه. درواقع اگر من بتونم یه شبکهای داشته باشم که این قضیه دسترسی رو حل کنه! نه تنها میتونم با همون تلویزیون ویدئوهای یوتیوب رو ببینم، هرجایی که مشکلاتی از قبیل تحریم و دسترسی داشتم (مثل کنسول PS4 یا بیلد کردن پروژههای اندروید) با وصل شدن به این شبکه حل میشه!
مودمم که این قابلیت رو نداره، پس تنها راه همون رسپبریپای بود. با کمی سرچ کردن توی گوگل آموزش ساخت هاتاسپات با رسپبریپای رو پیدا کردم و بعد رفع یکی دو تا مشکل که در ادامه توضح دادم، تونستم کاری که میخوام رو بکنم. ادامه …
تقریبا یکسال پیش بود که پست کوتاهی در مورد “منابع الهام بخش برای ساخت UI اپ موبایل” نوشتم و تعدادی سایت رو معرفی کردم ولی یه مشکلی وجود داشت، اونم این بود که همهی UIها با زبان انگلیسی درست شده بودند و معمولا وقتی همین UIها رو با زبان فارسی درست میکنید، درصد زیادی از زیباییشون از دست میدن. برای حل این مشکل با بچههای هارمس تصمیم گرفتیم خودمون زمان بذاریم و از اپهای اندرویدی معروف ایرانی اسکرینشات تهیه کنیم تا در اختیار همهی برنامهنویسان و طراحان فارسیزبان بذاریم. خروجی این تصمیم، سایت UI Screenshots شده که در ادامه بیشتر در موردش توضیح دادم. ادامه …
چند وقت پیش یه AMA (فکرکنم معادل فارسیش میشه “از هرچیزی بپرس”) توی reddit بود که تیم توسعهدهندهی دگر سوالاتی که ازشون پرسیده شده رو جواب دادن. دیشب وقت کردم بخونمش و نکاتی که بنظرم جالب رو بود رو در ادامه نوشتم. البته اگر میخواید خودتون همهی سوال و جوابهارو بخونید، میتونید به لینک زیر برید.
اگر یادتون باشه آخرین نمونه پروژهای که توی گیتهاب گذاشتم (SingleAcitvityPattern) با زبان کاتلین نوشته شده بود. هدف اصلیم از این پروژه پیادهسازی الگوی Single Activity با استفاده از فرگمنتها و هدف فرعیم آشنایی بیشتر با کاتلین بود. قبلا در مورد هدف اصلی یه پست بلاگ نوشتم و الان میخوام در رابطه با هدف فرعی صحبت کنم. استفاده از کاتلین زمان بیشتری ازم گرفت ولی در عوض با نکات مختلفی آشنا شدم که در ادامه براتون تعریف میکنم.
ترکیب کاتلین و Dagger
دیروز چندساعتی درگیر این بودم که چطوری توی پروژه میتونم NavigationManager رو از طریق Dagger به فرگمنتها اینجکت کنم. با توجه به ایدهای که در مورد NavigationManager داشتم، میشه توی یه پروژه چندتا NavigationManager داشت که هرکدوم مدیریت تعدادی از فرگمنتهارو به عهده داشته باشه (اگر پست قبلی بلاگمو در مورد این پروژه خوندید و ایدهمو در رابطه با NavigationManager متوجه نشدید، دلیلش اینه توضیحش توی متن سخته! شاید بعدا یه ویدیو برای توضیحش درست کردم)، این ایده استفاده از Dagger برای اینجکت کردنو خیلی سخت کرده بود. البته توی این چند ساعت، درگیر دو تا مشکل دیگه هم داشتم.
اولین مشکل این بود که وقتی Dagger رو به پروژه اضافه کردم، موقع اجرا خطای NoClassDefFoundError میداد!!! آخرش بعد از کلی تلاش و سرچ کردن، فهمیدم مشکل از من نیست! مشکل از Dagger هست و نسخه جدید ۲٫۱۴٫۱ رو فقط به خاطر حل این مشکل ریلیز کردن.😑
مشکل دوم این بود که وقتی میخواستم بصورت همزمان دو انوتیشن @inject و یه qualifire به اسم @PerFragment رو برای یه فیلد استفاده کنم، برنامه کرش میکرد!! بعد از کلی دیباگ کردن و بررسی سورسهای جنریت شدهی Dagger، متوجه شدم qualifireیی که میذارم اصلا اثر نمیکنه، انگار وجود نداره!! در نهایت با سرچ کردن، فهمیدم اگر میخوام همچین کاری رو توی کاتلین بکنم، باید از یه انوتیشن به اسم @field استفاده کنم. وقتی کد رو به شکل زیر تغییر دادم، برنامه درست شد.
1 |
@Inject @field:PerFragment lateinit var navigationManager: NavigationManager |
این نکته رو از یه issue توی ریپوی Dagger یاد گرفتم. کلا داخل این issue در مورد Best Practiceهای استفاده از Dagger توی کاتلین صحبت میکنن. نکتههای جالبی داخلش هست.
https://github.com/google/dagger/issues/900
تفاوت onCreateView با onViewCreated
همون شبی که پروژه رو توی گیتهاب گذاشتم، شایان پوروطن لطف کرد و ۲ ۳ ساعتی در مورد جزییات پیادهسازی پروژه صحبت کردیم. جدای همه نکتههایی که ازش یاد گرفتم، یه نکته جالب و ساده هم یادم داد. قضیه این بود که من غر میزدم چرا نمیتونیم داخل متد onCreateView فرگمنت از قابلیت static layout imports کاتلین استفاده کنیم و مجبوریم بجاش کدمون رو توی متد onViewCreated بنویسم، اینجا بود که شایان بهم گفت خب درستش همینه و نه اونکاری که من تا الان میکردم😀
تا اون لحظه، هرچی سورس و مثال دیده بودم، همشون توی onCreateView کار findview رو انجام داده بودن و این باعث شده بود که فکرکنم راه درستش اینه!!! درصورتی که اشتباه بود. البته خیلی کم پیش میاد که این اشتباه باعث باگ بشه ولی بهرحال امکانش هست. دلیل اشتباه بودنش اینه که اگر layout پیچیده باشه، احتمال داره بعضی از viewها هنوز توی onCreateView ساخته نشده باشند!! برای اطمینان بیشتر باید هرکاری با view دارید، توی onViewCreated انجام بدید.
کشف ریپوهای کاتلینی-اندرویدی
چون هنوز کاتلین رو مسلط نیستم، تقریبا برای هرکاری باید توی گوگل بگردم. در حین همین گشتنها به یه اکانت organization جالب توی گیتهاب به اسم android رسیدم. دو نفر داخلش هستند، یکیش جیک وارتون هست. همین که جیک وارتون داخلشه، دیگه حجت رو بر من تموم کرد و همه ریپوهاشو بررسی کردم. دو تا ریپوی جالب پیدا کردم.
اولی android-ktx که extensionهای کاربردی اندروید هست.
https://github.com/android/android-ktx
دومی kotlin-guides که راهنمای استفاده از کاتلین در پروژههای اندروید هست.
اول بخاطر اینکه دو هفته هس که نمیرسم “لینکهای آخر هفته” رو توی کانال بذارم باید عذرخواهی کنم. دلیلش اینه بخاطر سربازی رفتن کارها فشرده شده و دیگه خیلی وقت نمیکنم که بلاگها و مقالههایی که بنظرم جالب هستند رو بخونم. چون اولویتم کیفیت به کمیت هس، ترجیحم اینه که کلا لینکی نذارم تا اینکه بخوام لینکهایی که هنوز خودم نخوندمو به بقیه معرفی کنم.
حالا برسیم به اصل موضوع که چطوری میشه ارائهی خوبی داشته باشیم. شرایط نسبت به سالهای گذشته تغییر کرده و رویدادهای زیادی توی کشور برگزار میشن، بطور مثال @irlogcat که برای اندروید هس هر ماه داره برگزار میشه. در نتیجه این فرصت برای همه هست که بتونن دانششون رو در اختیار بقیه بذارن، شاید همین الان شما هم داوطلب ارائه توی یکی از این رویدادها باشید یا حداقل تصمیم گرفته باشید در آینده اینکارو بکنید. اما یه نکتهی مهم اینه بخاطر وجود سایتهایی مثل Youtube و بالا رفتن سرعت اینترنت خیلی از کسایی که توی رویدادهای داخلی شرکت میکنند، ارائههایی که خارج از ایران برگزار میشن رو هم دیدن و انتظارشون بالاتر رفته. برای همین بهتره اگر میخوایم جایی ارائه بدیم، سعی کنیم با تمرین کیفیت ارائهمون رو بالاتر ببریم و به ارائههای خوب خارجی نزدیک کنیم.
یکی از نکات عجیبی که من توی توییتر میبینم، اینه که خیلیا به این قضیه افتخار میکنند که مثلا یکساعت قبل ارائه اسلایدهارو درست کردن و بعد رفتن ارائه دادن یا فقط یه روز برای آمادهسازی کل ارائه وقت گذاشتند. بنظر من این قضیه بیشتر از اینکه نشون بده یه نفر خیلی باحال و خفن هس، نشون میده برای اونا که میخوان ارائهاش رو ببینن اهمیت قائل نیس. در این رابطه مقالهی “How to Prepare a Talk” که در ادامه لینکشو میذارم یه نکته جالب گفته، اونم اینه که میگه برای آمادهسازی یه ارائه حداقل باید ۲۰ساعت وقت بذارید! بعد خودش مثال زده که اگر احساس میکنید این زمان خیلی زیاده، اینجوری فکرکنید که ۵۰۰نفر ارائهی ۳۰دقیقهای شمارو میبینند (حضوری،آنلاین یا بعدا ضبط شده)، اگر ۵۰۰*۳۰ رو حساب کنید میشه ۱۵۰۰۰دقیقه نفر-دقیقه که همون ۲۵۰نفر-ساعت هست. حالا واقعا ارزش نداره شما ۲۰ساعت وقت بذارید تا ۲۵۰ساعت بقیه الکی هدر نره؟!
همه جای مقاله “How to Prepare a Talk” که خوبه ولی یه بخش دیگشم برام خیلی جالب بود، همیشه فکرمیکردم من عجیبم که خوشم نمیاد روی کاغذ نکتهای بنویسم و همراه خودم برای ارائه ببرم! (چون اینو به عنوان توصیهی خوب برای ارائه زیاد شنیدم)، دلیلشم اینه که سعی میکنم اینقدر تمرین کنم تا با دیدن اسلایدها نکاتی که میخوام بگم یادم بیاد و اگرم نکتهای یادم رفته! کلا دیگه یادم بره، نه اینکه توی کاغذ یه چیزی ببینم که نوشته باشم بگم ولی یادم نیاد!!!!! درواقع دوس ندارم دیگه درگیر یه کاغذ بشم و میخوام تمرکزم همون ارائه دادن باشه. حالا وقتی این مقاله رو خوندم، فهمیدم من عجیب نیستم! واقعا این روشی هس که بعضیا استفاده میکنند. تا حالا به کسی اینکاری که خودم میکردمو توصیه نمیکردم، از این به بعد به بقیه پیشنهاد میدم چون ارائه دادن رو راحتتر میکنه.
حتما اگر ارائه دادن رو دوست دارید یا میخواید ارائه بدید این مقالهی طولانی رو بخونید. نکاتش خیلی بیشتر از این هست که من بتونم دونه دونه بنویسم و بهتره برید خودشو بخونید. هرچقدر ازش تعریف کنم، کم هس. تک تک نکتههایی که در مورد مراحل آمادهسازی یه ارائه میگه واقعا تاثیرگذار و کاربردیه. کاشکی به جای اون مطالبی که توی درس “درس شیوه ارائه مطالب علمی وفنی” داخل دانشگاه میگن (البته از همه دانشگاهها خبر ندارم،برای ما که مسخره بود)، از این جور مقالهها معرفی کنن. لینک مقاله “How to Prepare a Talk”:
توی پست قبلی در مورد الگوهای مختلف ساخت پروژههای اندروید صحبت کردم و خلاصهای از تحقیقاتمو نوشتم. خروجی اون تحقیقات یه پروژه تمرینی براساس الگوی “تک اکتیویتی، چند فرگمنت” شده که در ادامه بیشتر در موردش توضیح میدم و میتونید از طریق گیتهابم به سورسش دسترسی پیدا کنید.
توی این پروژه تمرکزم روی این قضیه بوده که چطوری جا به جایی بین صفحههای برنامه (همون فرگمنتها) رو مدیریت کنم. برای همین فعلا الگوهای معماری مثل MVP یا Dagger2 و RxJava رو قاطی پروژه نکردم. در ضمن از زبان Kotlin برای ساخت پروژه استفاده کردم تا با این زبان بیشتر آشنا بشم ولی بدیهی هس که هرکاری توی این پروژه کردم رو میشه خیلی راحت توی یه پروژهی جاوایی انجام داد.
چالشها
سعی کردم انواع چالشهایی که بخاطر استفاده از فرگمنتها توی پروژههای واقعی پیش میاد رو توی این پروژه پیاده کنم تا اگر پیادهسازیم مشکلی داره سریعتر متوجه بشم. چالشهایی که به ذهنم رسید این موارد بوده: (اگر چالش دیگهای به ذهنتون میرسه بهم بگید تا در موردش بیشتر صحبت کنیم)
۱- استفاده از BottomNavigationView: جدیدا در بیشتر برنامهها از Bottom Navigation استفاده میشه، به همین دلیل منم برای شروع این مدل نویگیشن رو انتخاب کردم. البته در آینده میشه مثال رو با navigation drawer هم پیاده کرد یا مدیریت backstack رو از حالتی که الان هست به حالتی شبیه اینستاگرام تغییر داد که هر تب برای خودش backstack جدا داشته باشه.
۲- مدیریت دکمهی Back: بصورت پیشفرض این دکمه رو اکتیویتی مدیریت میکنه ولی خیلی وقتا پیش میاد بعضی از فرگمنتها نیاز دارند که خودشون اینکارو به جای اکتیویتی میزبان مدیریت کنند.
۳- استفاده از Tab به همراه ViewPager در فرگمنت: گاهی پیش میاد یه فرگمنت داخلش تعدادی تب داره که هر تب خودش فرگمنت جدایی هس، توی این حالت مدیریت فرگمنتهای داخلی به عهدهی اداپتر ViewPager هست
۴- استفاده از فرگمنتی که داخلش تعدادی فرگمنت دیگه داره: در ظاهر میتونه شبیه حالت قبلی باشه ولی فرقش توی اینه مدیریت فرگمنتهای داخلی با فرگمنت والد هست.
۵- مدیریت ویوهای داخل اکتیویتی: گاهی توی اپلیکیشن نیاز هست وقتی کاربر روی آیتمی کلیک کرد، به جزییاتش بره. توی این حالت دیگه نیاز نیست Bottom Navigation رو ببینه یا شاید لازم باشه دکمهی FAB نشون داده بشه.
ایدهها
برای اینکه بتونم این چالشهای بالارو حل کنم، از ایدههای زیر استفاده کردم.
ایده اول – مدیریت جا به جایی صفحات یا همون navigation رو به یه کلاس به اسم NavigationManager سپردم. البته خیلی از مثالهای توی گیتهاب اینجوری بودند. اینکار باعث رعایت شدن اصل Single Responsibility که حرف S در اصول SOLID هست میشه. این کلاس برای مدیریت navigation نیاز به یدونه FragmentManager و شناسه لیوتی که قراره کانتینر فرگمنتها باشه داره. وقتی مدیریت navigation در یک کلاس جدا باشه، در آینده میشه مثلا بدون اینکه بقیه بخشهای برنامه رو خیلی تغییر داد، بجای backstack اندروید از یه backstack کاستوم استفاده کرد. درکل دست آدمو برای توسعههای بعدی باز میذاره.
ایده دوم- برای اینکه بتونم ساختار تو در تو بودن فرگمنتهارو در جاهایی که خود فرگمنت شامل تعدادی فرگمنت دیگه باشه رو مدیریت کنم، از پیادهسازی Dagger2 ایده گرفتم. ایده اینه هر کلاسی که اینترفیس HasNavigationManager رو پیادهسازی کرده باشه وظیفه داشته باشه فرگمنتهای داخلشو مدیریت کنه ( از ماژول اندروید Dagger2 و نحوهی کمک گرفتنش اینترفیسهای HasActivityInjector یا HasFragmentInjector الگو گرفتم). قضیه اینه هر فرگمنت وقتی داره لود میشه چک میکنه چه کسی مدیریتشو داره و ازش یه object از نوع NavigationManager میگیره تا کارهایی که نیاز داره رو انجام بده. اون کلاسی که اینترفیس HasNavigationManager رو پیاده میکنه، باید در پیادهسازی متد provideNavigationManager یه object از نوع NavigationManager برای فرگمنتهای که مدیریت میکنه فراهم کنه تا اونا به روشی که گفتم ازش استفاده کنند. اینکار باعث میشه چالش ۴ راحتتر حل بشه و مدیریت سلسله مراتبی باشه، در نتیجه اکتیویتی میزبان همهی این فرگمنتها خیلی شلوغ نشه!
ایده سوم- فرگمنتهایی که نیاز داشته باشند دکمهی back رو مدیریت کنند، میتونند متد onBackPressed رو که داخل BaseFragment هست، override کنند. اکتیوتی وقتی دکمهی بک زده میشه، چک میکنه فرگمنت جاری (من فرض کردم همیشه یه فرگمنت به عنوان فرگمنت جاری باشه! این فرض رو میشه در آینده عوض کرد. الان هر فرگمنتی که آخرین بار نشون داده شده باشه میشه فرگمنت جاری) میخواد بک رو هندل کنه یا نه؟! اگر کرد که اکتیویتی کاری نمیکنه و اگر نکرد، خود اکتیویتی کار همیشگیش رو انجام میده.
ایده چهارم- برای اینکه هر فرگمنت بتونه المانهای داخل اکتیویتی رو برای خودش مدیریت کنه، یه سری متد توی اینترفیس FragmentInteractionListener تعریف کردم. فرگمنتها برای ارتباط با اکتیویتی میزبان از اینترفیسهای خودشون مثل OnNotificationFragmentInteractionListener یا OnProfileFragmentInteractionListener استفاده میکنند که همشون از FragmentInteractionListener ارثبری میکنند، در نتیجه همه فرگمنتها میتونن متدهای داخل اینترفیس FragmentInteractionListener که اکتیویتی میزبانشون پیادهسازی کرده رو صدا بزنند.
در آخر تاکید کنم این پروژه تمرینی در حال توسعه هست و امکان داره جاهاییش اشکال داشته باشه یا حتی در آینده تغییر بکنه، در نتیجه با اینکه تلاشم اینه همهی BestPracticeهارو داخلش رعایت کنم ولی فعلا زوده که خیلی خیلی مطمئن از ساختارش توی پروژههاتون استفاده کنید.
میتونید سورس کامل پروژه رو توی گیتهابم و ریپوی SingleActivityPattern ببینید.
از وقتی اندروید رو شروع کردم، خیلی رابطهی خوبی با فرگمنت نداشتم و فقط جاهایی که مجبور بودم ازش استفاده کردم. تا الانم پیش نیومده پروژهای رو بطور کامل با فرگمنت درست کنم. چند وقته که خیلی وسوسه شدم به سمت استفادهی بیشتر از فرگمنت برم و دو سه روزی هست که دارم روی این موضوع تحقیق میکنم. سعی کردم مقالهها و سورسهای مختلفی که به این موضوع ربط دارند رو دونه دونه بخونم و روی فرقهاشون فکرکنم. البته در مورد فرگمنتها و مخصوصا FragmentManager هم بیشتر مطالعه کردم، تقریبا هیچ دیدی از نحوهی کار FragmentManager نداشتم ولی الان خیلی بهتر شدم😃. این پست بیشتر از اینکه قرار باشه الگوی خاصی رو تایید کنه یا قضیهای رو اثبات کنه، خلاصهای از تحقیقات شخصیم هس که در نهایت خروجیش یه پروژهی تمرینی با الگوی “تک اکتیویتی، چند فرگمنت” شده که چند روز آینده توی گیتهاب میذارم. ادامه …
اگر خیلی پروژهی اندروید روی لپتاپتون داشته باشید، کلی از هاردتون رو پر میکنند. البته بخشهای اصلی پروژه خیلی فضا نمیگیرند و اصولا ۸۰درصد از فضای اشغال شده بخاطر فولدرهای build هست. چند روز پیش دیدم که هاردم پر شده و هرچی گشتم چیزی پیدا نکردم که بشه پاک کرد تا هارد خالی بشه. واسه همین رفتم سراغ پروژههای اندرویدم تا فولدرهای build اون پروژههایی که دیگه خیلی ازشون استفاده نمیکنم رو پاک کنم. چندتایی رو پاک کردم اما دیدم خیلی طول میکشه😴 واسه همین تصمیم گرفتم به جای اینکه یه کار تکراری رو پشت هم انجام بدم، یه shell script بنویسم که همینکارو برام بکنه😎
بعدی کمی سرچ کردن، آخرش تونستم به نتیجهای که میخوام برسم. این اسکریپ که نوشتمو اگر توی فولدر پروژههاتون اجرا کنید، دونه دونه ازتون میپرسه که میخواید فولدرهای build کدوم پروژه رو پاک کنه! اگر y یا Y جواب بدید، همه فولدرهای build رو پاک میکنه😃 با این کار تونستم ۵گیگ هاردمو خالی کنم، بدون اینکه چیز مهمی رو پاک کنم!😃 لینک اسکریپت:
https://gist.github.com/abbas-oveissi/2d85b8178c11952ae8960392d834b03a
‼️نکتهی مهم: اگر میخواید اسکریپت رو تست کنید، حتما از پروژههاتون نسخهی پشتیبان داشته باشید. چون من تخصصی توی اینجور چیزا ندارم و تازه دارم یاد میگیرم😃یهو مشکلی برای پروژههاتون پیش نیاد!
هنوز یه هفته از زلزلهی قبلی نگذشته که دوباره یک ساعت پیش یه زلزلهی ۴.۲ ریشتری اومد. اومدن زلزله به آدم استرس و نگرانی میده، ولی بدتر از خود زلزله این صحبتهایی هست که بعضیا به استناد این کانال پیشبینی زلزله (@pishbiniezelzele21) میکنند. هفتهی پیش در مورد این کانال زیاد صحبت میشد اما اصلا فکرنمیکردم موضوع دنبالهداری باشه و مطمئن بودم که حتما توسط پلیس دستگیر میشه و کانال رو مسدود میکنند. حالا نه تنها کانالش مسدود نشده، حتی ۱۰۰هزار نفر به عضوهاش اضافه شدن!!!!!
ادعای پیشبینی زلزله
صاحب کانال آقای علی اصغر برهمند هست، ایشون ادعا میکنند اولین کانال پیش بینی علمی زلزله رو دارند که از طریق دریافت اطلاعات ماهوارهای ومحاسبات ریاضی وتغییرات تکتونیکی زمین! میتونند با دقت ۷۰ درصد زلزله رو پیشبینی کنند. البته من که کانالشون رو بررسی میکردم فقط مختص به ایران نیس و زلزلههای ایتالیا یا جاهای دیگه رو هم پیشبینی میکنند. توی کانالشون گفتند که اگر بهش کمک کنند، دقت پیشبینیشو به ۸۵ درصد میرسونه! ادامه …
سایت یوتیوب، یکی از منابع مهم یادگیری برنامهنویسی هس! بطور مثال توی پست “منابع آموزشی برای افزایش مهارت در برنامهنویسی اندروید” در مورد این صحبت کردم که میشه از طریق یوتیوب به ویدئوی کنفرانسهای اندروید دسترسی داشت. بخاطر اینکه بعضی از دوستان توی دیدن ویدئوها مشکل داشتند، یه پیام توی کانال تلگرام نوشتم و روشهایی که برای دانلود از یوتیوب استفاده میکنم رو معرفی کردم. بعد این پیام، تعدادی از دوستان هم روشهای دیگهای رو معرفی کردند که بعضیهاش خیلی خوب بودند. در ادامه میتونید این روشهارو ببینید. (ترتیبشون دلیل خاصی نداره و همه رو چک کنید تا روشی که باهاش راحتترید رو پیدا کنید) ادامه …