ساده ترین ستون رادیویی اینترنتی "Kodi" یا نجات آجر "رزبری".

ساده ترین ستون رادیویی اینترنتی "Kodi" یا نجات آجر "رزبری".

پیش نیازهای اساسی:

  1. یک برد قدیمی و استفاده نشده نسل اول Raspberry Pi وجود دارد.
  2. تخته به عنوان وزنه مرده روی کابینت قرار می گیرد و استفاده نمی شود - تخته "آجر".

چه چیزی را می خواهید دریافت کنید:

  1. در یک نقطه خاص از زمان (مثلاً بر اساس خلق و خو)
    تخته دیگر "آجر" نیست و یک کارت حافظه جادویی در آن قرار می گیرد.
  2. یک کابل اترنت و یک دوشاخه از یک بلندگو یا هدفون معمولی خانگی به برد وصل شده است.
  3. پس از انرژی دادن به "آجر" سابق - آواز می خواند

ایده اصلی:

  1. حداقل تعداد حرکات برای هر تنظیمات، در ایده آل ترین حالت، ما فقط کابل "اترنت"، برق و بلندگوها را وصل می کنیم و هیچ کار دیگری از کلمه انجام نمی دهیم. "اصلا"؛
  2. به عنوان مثال، ما از "آجر" قبلی خارج از جعبه پشتیبانی می کنیم، به عنوان مثال، 20 ایستگاه رادیویی اینترنتی، سوئیچینگ آنها در یک دایره را می توان با فشار دادن چرخ ماوس یا روی یک پین خاص GPIO (دو سیم وصل کنید آنها را ببند (رویای من از کودکی))؛
  3. کنترل از طریق یک کانال رادیویی انجام می شود و این کانال رادیویی می تواند یک ماوس رادیویی معمولی باشد.
  4. یک سیستم آماده بگیرید، کیت توزیع را در "پروژه Yocto" مونتاژ کنید
    آن ها طبق معمول، ما هیچ کاری با شما انجام نمی دهیم، زیرا همه چیز قبلا انجام شده است.
    (فقط کافی است یک ناظر خارجی را در طرف دیگر قرار دهید "تلویزیون");

شرح

ساده ترین ستون رادیو اینترنتی "KODI"
مونتاژ طراحی شده برای بردهای قدیمی Raspberry Pi 1
(گرد و غبار را در جایی روی کمد جمع می کنند، اما چه کسانی آماده هستند بیشتر کار کنند)

لیست m3u8 از 12 ایستگاه رادیویی اینترنتی به طور پیش فرض استفاده می شود.

فرض بر این است که برد بدون خروجی HDMI کار می کند و برای خاموش کردن آن کافیست آداپتور برق را از پریز جدا کنید. و به عنوان یک کنترل از راه دور بی‌سیم فوق‌العاده مدرن، می‌توانید از ماوس رادیویی فوق‌العاده خود استفاده کنید (خوب، یا یک ماوس خاکستری معمولی را با دم متصل کنید).

هنگامی که روشن است، رابط شبکه به طور پیش فرض از طریق پروتکل DHCP پیکربندی می شود و آخرین ایستگاه رادیویی حفظ شده از لیست پخش می شود، حجم پخش توسط یک ماوس معمولی کنترل می شود:
(در نهایت موش خود را به عنوان "رئیس مدیریت" منصوب کنید و به او تبریک بگویید، او شایسته آن است)

  колесико вперед  - увеличение громкости звука
  колесико назад   - уменьшение громкости звука
  длительное нажатие (3сек и более) на правую кнопку мыши
                   - выбор следующий радиостанции
  длительное нажатие (3сек и более) на левую кнопку мыши
                   - выбор предыдущей радиостанции

برای اضافه کردن لیست خود از ایستگاه های رادیویی اینترنتی
همیشه می توانید یک کابل HDMI را از تلویزیون خود وصل کنید
و از رابط کاربری گرافیکی Kodi 17.6 استفاده کنید
(برد را خاموش کنید، HDMI را وصل کنید و آداپتور برق را روشن کنید)

منوی اصلی Kodi => "افزونه ها" => "افزونه های من"
          => "Clients PVR" => "PVR IPTV Simple Client"

اجرای اولیه

(ممکن)
در ابتدا، زمانی که تصمیم گرفتم یک "ستون رادیوی اینترنتی" بسازم، موارد زیر را برنامه ریزی کردم:

  • توزیع کنسول مینیمالیستی در پروژه Yocto.
  • جریان صوتی از طریق GStreamer پخش می شود.
  • رابط شبکه از طریق DHCP پیکربندی شده است.

و این راه حل دارای چندین مزیت است:

  1. به اندازه کافی سریع (خروج به حالت کار از منبع تغذیه 30-40 ثانیه).
  2. به اندازه کافی قابل اعتماد (برنامه های کمتر، نقاط شکست کمتر)؛
  3. توزیع کنسول در حالت فقط خواندنی بسیار ساده تر است
    آن ها برنامه ها چیزی در فایل سیستم روت نمی نویسند
    (یک سیستم فایل در رسانه SDHC به نظر من اولین نامزد برای شکست است).

توجه:

    В Yocto перевести корневую файловую систему (rootfs) 
    в режим только чтение можно сделать достаточно просто, 
    изменив один параметр во время сборки 

    Из коробки Yocto предлагает два варианта:
    1) Работа файловой системы в обычном режиме чтение/запись 
    (так работают все дистрибутивы общего назначения, например Ubuntu)
    2) Работа файловой системы в режиме только чтение
    (так работают специализированные дистрибутивы, например в маршрутизаторах)

    В режиме только чтение все каталоги, в которые обычно 
    записываются данные приложений и сервисов во время работы монтируются 
    в оперативную память (например каталог /var/log и т.п.)
    Данные актуальны только для текущего сеанса работы и после сброса питания
    данные теряются.

    Если в Yocto Project вы укажете при сборке использовать "read only", 
    то после сборки ваш дистрибутив будет настроен только на чтение, 
    но вы всегда можете добавить возможность динамического перевода 
    из "read only"  в "read/write", но это уже совсем другая история ...
    

و یک ایراد اساسی:

"باید انجام شود" یعنی. من باید N تعداد شب را بگذرانم
(معمولا بعد از کار، و این ناکارآمدترین زمان است، در این زمان مغز دیگر فکر نمی کند، معمولا می خوابد)

و با این حال، مقاله قبلی خود را در Habré در مورد مرکز چند رسانه ای نوشتم Kodi و Yocto پروژه
و فرصت ادامه در همان مسیر، بر انگیزه اکتشافی من غلبه کرد. بیشتر در این مورد در فصل بعدی.

تبدیل Kodi به یک بلندگوی رادیویی اینترنتی

برای اجرای عملکردی که نیاز دارم، یک روش دیگر را به دستور ساخت توزیع که در قسمت قبل توضیح داده شد اضافه می کنم مقاله فایل berserk-image.bb را ببینید

GUI_SETTINGS = "home/root/.kodi/userdata/guisettings.xml"

# конфигурация запуска последнего выбранного ТВ канала (1-фон 2-передний план)
F1_LINE = "<startlast default="true">0</startlast>"
R1_LINE = "<startlast>1</startlast>"
# конфигурация вывода звука, всегда подключен только аналоговый аудио выход
F2_LINE = "<audiodevice default="true">PI:HDMI</audiodevice>"
R2_LINE = "<audiodevice>PI:Analogue</audiodevice>"
# так как HDMI по умолчанию не используется отключаю автоматическое обновление
# а то может получиться что питание уехало, а данные остались не записанными
F3_LINE = "<addonupdates default="true">0</addonupdates>"
R3_LINE = "<addonupdates>2</addonupdates>"


# метод отвечает за добавление конфигурации:
# которая превращает "Умный телевизор" в "простую Интернет Радио колонку"
add_radio_guisettings() {
    sed -i "s|${F1_LINE}|${R1_LINE}|" ${IMAGE_ROOTFS}/${GUI_SETTINGS}
    sed -i "s|${F2_LINE}|${R2_LINE}|" ${IMAGE_ROOTFS}/${GUI_SETTINGS}
    sed -i "s|${F3_LINE}|${R3_LINE}|" ${IMAGE_ROOTFS}/${GUI_SETTINGS}
}


FIND_STR = "touch ./tmp/.FIRST_RUN."
SCRIPT_FIRST_RUN = "etc/init.d/first-run.sh"
# так как HDMI выход может не использоваться, 
# то необходимо отключить "стартовое приветствие"
off_kodi_welcome() {
    sed -i "s|${FIND_STR}|#&|" ${IMAGE_ROOTFS}/${SCRIPT_FIRST_RUN}
}

روش‌ها برای اصلاح سیستم فایل ریشه قبل از تشکیل تصویر توزیع در قالب یک فایل خام منفرد در نظر گرفته شده است که با دستور روی کارت حافظه نوشته می‌شود. dd

این کار به این صورت انجام می شود:
ROOTFS_POSTPROCESS_COMMAND += "add_radio_guisettings; off_kodi_welcome;"

به طور خلاصه، در فایل پیکربندی اصلی Kodi 17.6، "سه نقطه" تغییر می کند

  • راه اندازی پیکربندی آخرین کانال تلویزیونی انتخاب شده؛
  • پیکربندی خروجی صدا، فقط خروجی صوتی آنالوگ همیشه متصل است.
  • غیرفعال کردن به روز رسانی خودکار؛
  • توجه:
        Единственное с чем у меня возникли сложности, 
        это то, что пришлось еще подтащить файл базы данных 
        в формате sqlite => TV29.db, в котором указывается 
        текущий проигрываемый ТВ канал 
        (так как по умолчанию никакой из каналов не выбран), 
        а через xml конфигурацию в Kodi этого не сделать.
        

توالی دقیق تری از اقدامات برای هر مورد:

1) روی نماد "چرخ دنده" در گوشه سمت چپ بالای صفحه کلیک کنید
و "PVR and TV settings" را انتخاب کنید (تصویر تلویزیون با دو بوق)
بیشتر در سمت چپ منو، مورد "پخش" و در بخش مرکزی "عمومی" را انتخاب کنید.
"ادامه از آخرین کانال در هنگام راه اندازی" را در لیست کشویی انتخاب کنید
تنظیم "پیش زمینه" را انتخاب کنید

یا واضح تر:

      "Настройки PVR и ТВ" 
       => "Воспроизведение" 
       => "Продолжить с последнего канала при запуске" => "Передний план"

2) روی نماد "چرخ دنده" در گوشه سمت چپ بالای صفحه کلیک کنید و مورد را انتخاب کنید:

       "Системные настройки"  
       => "Дополнения" => "Обновления" => "Никогда не проверять обновления"

3) روی نماد "چرخ دنده" در گوشه سمت چپ بالای صفحه کلیک کنید و مورد را انتخاب کنید:

       "Системные настройки" 
       => "Аудио" => "Устройство вывода звука" => "PI: Analogue"

چقدر من دو سال است که تلویزیون را اشتباه نگاه می کنم.

باید به شما اعتراف کنم که در این دو سال یاد نگرفتم چگونه تلویزیون را به درستی تماشا کنم.

من معمولا در آشپزخانه تلویزیون تماشا می کنم. یک برد Raspberry Pi 2B به تلویزیون وصل شده است و کانکتورهای اترنت و HDMI به برد متصل هستند. تغذیه برد از طریق یک کابل USB معمولی که به پورت USB تلویزیون وصل می شود، تامین می شود. در واقع، روشن کردن تلویزیون با استفاده از ریموت کنترل استوک نیز برق برد Raspberry Pi را تامین می کند و خاموش کردن تلویزیون از ریموت کنترل نیز بلافاصله برق برد Raspberry Pi را دوباره تنظیم می کند.

بله، من به خوبی می دانم که نمی توان این کار را انجام داد، زیرا سیستم فایل ریشه مرکز چند رسانه ای Kodi (ext3) در حالت عادی خواندن / نوشتن من کار می کند. اما من آدم تنبلی هستم و برای شروع، تصمیم گرفتم بررسی کنم چقدر طول می کشد تا سیستم خاموش شود، تا زمانی که اصلاً بارگذاری متوقف شود، اما متاسفانه دو سال است که نمی توانم این کار را انجام دهم (شاید من فقط خوش شانس بودم، نمی دانم).

و به نظر من، اگر این حالت برای تلویزیون من مناسب است، برای یک "بلندگوی رادیویی اینترنتی ساده" نیز مناسب است، و از آنجایی که من به اجبار به روز رسانی خودکار افزونه های Kodi را خاموش کردم، احتمال خرابی سیستم فایل تبدیل می شود. حتی کمتر. تا اینجا من مشکلی در آن نمی بینم.

توجه:

    Но вы всегда при желании можете с помощью одной yocto команды 
    IMAGE_FEATURES += "read-only-rootfs"

    и определенной магии перевести ваш дистрибутив в режим "read only"
    

کیت توزیع "بلندگوهای رادیویی اینترنتی" که در مقاله توضیح داده شده است، یک کیت خانگی است و آنچه برای یک کیت توزیع خانگی مهم است یک رابط کاربری گرافیکی زیبا است. به نظر من، آموزش رانندگی به یک کاربر معمولی در هر دستور جادویی نامفهوم در کنسول بسیار دشوار یا تقریبا غیرممکن است و او حتی کلمه ای مانند آن را نمی داند. و اینجا رابط کاربری گرافیکی است، لطفا.

و این شاید استدلال اصلی من به نفع توزیع غیر کنسولی باشد. رابط کاربری گرافیکی لامپ گرم Kodi، واقعاً مورد نیاز نیست، اما وجود دارد.
(همچنین کاملاً فراموش کردم ذکر کنم که Kodi را می توان از راه دور کنترل کرد ، به عنوان مثال از طریق تلفن هوشمند با نصب برنامه Yatse ، و شاید برای کسی این یک مزیت باشد)

پیکربندی Kodi، برای کنترل ماوس

و حالا موشک

<keymap>
    <global>
        <mouse>
          <wheelup>VolumeUp</wheelup>
          <wheeldown>VolumeDown</wheeldown>
          <middleclick>ChannelDown</middleclick>
          <longclick id="0">ChannelDown</longclick>
          <longclick id="1">ChannelUp</longclick>
          <!-- конфигурационный rocket -->
        </mouse>
    </global>
</keymap>

پیکربندی رویدادهای جهانی را برای عناصر زیر لغو می کند:

  • چرخ ماوس به جلو حرکت کند
  • چرخ ماوس به عقب اسکرول کنید
  • فشار دادن دکمه وسط ماوس
  • پردازش یک کلیک طولانی ماوس (3 ثانیه یا بیشتر)،
    0 شناسه دکمه سمت راست، 1 شناسه دکمه سمت چپ

اطلاعات بیشتر در مورد پیکربندی رویدادهای ماوس:

kodi.wiki/view/Alternative_keymaps_for_mouse
kodi.wiki/view/Action_IDs
kodi.wiki/view/Window_IDs

اگر سیستم کابلی به شما نیامد چه باید بکنید

برخی از صاحبان خوشحال بردهای قدیمی Raspberry Pi 1 ممکن است فریاد بزنند: "اما من در خانه درگاه اترنت رایگان ندارم (یا هرگز نداشتم)" (شاید برد برای تحقیق خریداری شده باشد و روی کمد دراز کشیده باشد)

و از آنجایی که هیچ وای فای داخلی روی برد وجود ندارد، بدون اتصال اترنت، چندان کاربردی نیست.

البته امکان استفاده از برد Raspberry Pi 1 بدون اترنت وجود دارد اما نیاز به تلاش شما دارد. معمولاً انجام چنین کارهایی فقط به عنوان بخشی از مطالعه یک چیز جدید جالب است. این یک کار سفارشی نیست

بنابراین، بیایید یک مورد استفاده فرضی برای یک برد بدون اترنت در نظر بگیریم:

شما می توانید یک USB خارجی - آداپتور Wifi را با توجه به این موضوع وصل کنید
که آداپتور باید به خوبی تحت لینوکس کار کند

توجه:

    К сожалению часть WiFi адаптеров работать не будет, 
    это не особенность представленного в данной статье дистрибутива, 
    а скорее проблема конкретных драйверов WiFi адаптеров в ядре Linux. 
    Можно констатировать тот факт, что в настоящий момент вы не можете просто 
    пойти в магазин и купить любой WiFi адаптер. Скорее вы должны подобрать WiFi 
    адаптер из списка менее проблематичных и хорошо работающих под Linux.

    я проверял только следующии модели:
    - WiFi адаптер на чипсете Atheros D-Link DWA-126 802.11n (AR9271)
    - WiFi адаптер NetGear WNDA3200
    - WiFi адаптер NetGear WNA1100
    - WiFi адаптер TP-Link TL-WN722N (AR9271)
    - WiFi адаптер TL-WN322G v3
    - WiFi адаптер TL-WN422G
    - Wifi адаптер Asus USB-N53 chipset Ralink RT3572 
    

اگر از قبل یک آداپتور Wifi USB دارید، می توانید بررسی کنید که آیا در لینوکس به خوبی کار می کند مانند این:

  • چند توزیع محبوب لینوکس را نصب کنید
    هدف کلی، مانند "دسکتاپ اوبونتو"
  • سیستم را بوت کنید
  • آداپتور USB Wifi خود را وصل کنید
  • مدیر شبکه را راه اندازی کنید و سعی کنید به هات اسپات وای فای خود متصل شوید
  • اگر همه چیز به خوبی کار می کند و اتصال اینترنت شما پایدار است، آداپتور شما به خوبی پشتیبانی می شود و می توانید کار خود را برای اتصال این آداپتور در یک توزیع تخصصی و احتمالاً با سایر نسخه های هسته ادامه دهید.
    (اگر نه، پس نه، افسوس - بهتر است حتی سعی نکنید)

پشتیبانی از آداپتور Wifi خارجی در "Raspberry PI"

برای اینکه آداپتور WiFi به درستی در لینوکس کار کند: به دو چیز نیاز داریم:
1) پشتیبانی از هسته لینوکس برای آداپتور Wifi خاص
2) وجود یک ماژول هسته برای یک آداپتور Wifi خاص در سیستم

به عنوان مثال آداپتور TP-Link TL-WN722N را در نظر بگیرید. این یک آنتن ساده دارد.
بیایید چیپست را پیدا کنیم که برد روی آن کار می کند - من آن را "AR9271" دارم، توجه داشته باشید:

    что самое интересное, это то, что для одной и той же модели
    одного и того же производителя, чипсет Wifi может отличаться.
    Я например сталкивался с тем, что для TL-WN722N версии 2, 
    используется уже другой чипсет Realtek RTL8188, а он уже 
    плохо работал под Linux (на тот момент), увы такие вот дела, 
    т.е. иногда нужно еще приглядываться к маленьким цифрам 
    версии на обратной (темной) стороне адаптера.    
    

حال بیایید نام پارامتر را در پیکربندی هسته مسئول درایور چیپست AR9271 پیدا کنیم، بهتر است به دنبال ترکیبی از کلمات "AR9271 cateee.net" باشید.
     جایی که "cateee.net" یک سایت جالب است که پیکربندی های ماژول هسته لینوکس را توصیف می کند

ما بلافاصله نام پیکربندی هسته را پیدا می کنیم - CONFIG_ATH9K_HTC
و نام ماژول هسته که نیاز داریم ath9k_htc

و سپس فقط نام ماژول مورد نظر را در فایل قسمت پیکربندی مشخص کنید
هسته لینوکس => recipes-kernel/linux/files/rbpi.cfg، خط را اضافه کنید:
CONFIG_ATH9K_HTC=m

بنابراین، در آینده، می توانید هر تجهیزات اضافی را به سیستم خود متصل کنید (خوب، البته اگر قبلاً در هسته لینوکس پشتیبانی می شود)

اگر یک habra geek - سازنده هستید چه کاری باید انجام دهید

و شما جالب ترین چیزهایی مانند اینجا یا دانشجو هستید و رویای ایجاد چیزی مشابه را دارید.

سپس می توانید نوعی صفحه نمایش لمسی برای RPI در aliexpress بگیرید، یک باتری مناسب را در آنجا سفارش دهید، همه آن را به برد Raspberry Pi 1,2،3 یا 3 متصل کنید (بهتر است به XNUMX، زیرا Wifi داخلی دارد). یک رابط تم طراحی گرافیکی در Kodi انتخاب کنید که برای صفحه لمسی و voila طراحی شده است => می توانید یک پخش کننده صوتی ساده دریافت کنید. البته، بسیار حجیم خواهد بود، اما مال شما خواهد بود.

  Примечание:
  A для того, чтобы собрать Мультимедиа центр Kodi для самой бюджетной платы 
  Raspberry Pi Zero Wifi в yocto вам достаточно изменить две строки:

  конфигурационный файл => build/conf/local.conf
      MACHINE = 'raspberrypi0-wifi'

  рецепт сборки Kodi  => recipes-mediacentre/kodi/kodi_17.bbappend
      EXTRA_OECONF_append = "${@bb.utils.contains('MACHINE', 
                            'raspberrypi0-wifi', '${BS_RPI}',  '', d)}"

  اگر پاسخگویی رابط کاربری گرافیکی Kodi 17.6 به دلیل وجود یک هسته پردازشگر در Zero برای شما مرموز به نظر می رسد، می توانید با گوش های خود ظاهری ایجاد کنید و یک نسخه قدیمی تر، اما بسیار سریع بسازید، به عنوان مثال Kodi 15.2، "دوستانه تر" است. در این رابطه (گاهی اوقات میراث همه چیز را تعیین می کند)

متأسفانه، من تابلو ندارم، بنابراین نمی توانم آن را بررسی کنم، اما طبق احساسات من باید کار کند.

دستورالعمل مختصر مونتاژ

    1) Установите зависимости Yocto Project (например в Ubuntu): 
    sudo apt-get install -y --no-install-suggests --no-install-recommends 
        gawk wget git-core diffstat unzip texinfo gcc-multilib build-essential 
        chrpath socat cpio python python3 python3-pip python3-pexpect 
        xz-utils debianutils iputils-ping python3-git python3-jinja2 
        libegl1-mesa libsdl1.2-dev xterm

    2) Скачайте и установите Repo:
        mkdir ~/bin
        curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
        chmod a+x ~/bin/repo

    3) Загрузите проект с github:
        PATH=${PATH}:~/bin
        mkdir radio
        cd radio
        repo init -u https://github.com/berserktv/bs-manifest 
                  -m raspberry/rocko/radio-rpi-0.2.8.xml
        repo sync

    4) Соберите проект:
        ./shell.sh
        bitbake berserk-image
        
    можно тоже самое собрать для плат Raspberry Pi 3B Plus, 3B и 2B:
    repo init -u https://github.com/berserktv/bs-manifest 
              -m raspberry/rocko/radio-0.2.8.xml
    

دستورالعمل مونتاژ دقیق تر
و ضبط روی کارت microSDHC را ببینید در مقاله قبلی

Postscript

البته، ایده ستون رادیو اینترنتی معمولی است، برای همه شناخته شده است و در هابره مقالات زیادی در این زمینه پیدا خواهید کرد، به عنوان مثال اینجا

و همچنین ممکن است فکر کنید که من فقط الزامات یک راه حل آماده را تنظیم کردم. من می توانم به این موضوع پاسخ دهم و صادقانه صادقانه بگویم نه.

داستان آقای اروی

    Хотите верьте, хотите нет, а дело было так:

    Наш рабочий офис граничит с фирмой по производству разного звукового
    оборудования, и однажды директор этой фирмы, назовем его мистер "Эрви"
    подошел к нашему заместителю директора филиала мистеру "Арсению"
    и спросил у него, насколько сложно повесить на плату Raspberry Pi 
    проигрывание звукового потока т.е. плата подключается к сети 
    и колонкам, и "слышен характерный звук".

    После этого мистер Арсений подошел к заместителю моего 
    начальника - мистеру "Борису" и переадресовал вопрос ему, 
    ну а я, как сторонний наблюдатель случайно эту идею запомнил
    и назвал ее "Задача трех начальников".

    В общем хотели как лучше, 
    а получилось, цитата - "Но мистер Эрви, как всегда, помог."

    Через некоторое время я поинтересовался у мистера "Бориса" 
    его мнением по поводу написания небольшой заметки на эту тему 
    на "Хабре", на что "Борис" ответил, что изменение 
    "трех пунктов меню" в Kodi, особо не привносит никакой 
    новой информации и не заслуживает отдельного упоминания. 
    Конечно я с ним полностью согласен и поэтому, я не расскажу ему, 
    что что-то написал по этому поводу.

    Статья написана исключительно для платы "Raspberry Pi 1" 
    взятой у мистера "Бориса" на время эксперимента, 
    совпадения со всеми другими платами "Raspberry Pi 1" случайны.
    

مجالس خوب و متفاوت تر برای شما و بگذارید حتی آجر سابق امسال برای شما بخواند.

منبع: www.habr.com

اضافه کردن نظر