06 юли 2009

Дългият път на съзиданието

Категория: Dev,НонсенсLucho @ 18:24

Често обичам да не документирам, да не описвам нещата които правя… понякога съжалявам за това. И не толкова че видите ли след 5 години ще съм забравил за какво е бил даден проект, а по-скоро защото не записвам яките моменти, които съм преживял докато съм го разработвал.

Ето, сега ще се поправя и ще опиша стъпките, идеите, мислите, абе всичко през което минавам докато дивелъпвам проекта си по Python. В този смисъл постът, който четете в момента ще се допълва ежедневно с новости, за това стей тюнд.

Проекта представлява конзолен mp3 плейър за Линукс (уж Python е портабъл… но аз не го вярвам това, за сега :P ), като интерфейсът ще е тип „стара дос програма“ – графичен интерфейс с ascii символи. Надявам се, освен стандартните за всеки mp3 плейър нещица, да успея и да го накарам да работи с онлайн радиа (streaming). Естествено в момента си поставям цели, за които не съм напълно убеден дали ще са реализуеми… но какво пък – ще опитам! Друг фичър, който много искам да направя е да минава в бекграунд режим, за което имам някакви идеи, но пак неизвестните са доста.

Естествено вече съм попрегледал какви благинки предлага Python и какви външни компоненти (ех, колко SE  звучи това) бих могъл да ползвам. За нещастие, част от нещата изглежда са за Python 2.5, но докато неразцъкам и това твърдение е в сферата на unknown-ото.

Day 0:

И така „дей 0″ е по-скоро „вечер 0″, бутнах се под Убунтуто си и си сложих Eric (IDE за Python). Доста бутони, доста функции, дано да се работи приятно с това нещо. Тръгнах да създавам нов проект и се сетих – нямам име за него. Е… след 15 секундно размишление се спрях на „Pyp3″ („пай-пи-три“). И така вече съм готов да започвам най-нелепата част… подкарване на някой от основните модули които ще ползвам. За съжаление не намерих нищо,  което да пуска звук и да е за Python 3, така че ще се пише на Python 2.5. Това всъщност не е толкова зле, защото:

А) Рано или късно ще портнат библиотеките до 3

Б) Python 2.5 е стандартна версия за всяко Убунту и други дистрибуции

Та инсталирах audiere и numpy (дипенденси) от репобраузъра и успешно пуснах една mp3-ка (чак да не повярва човек :D ). Следваща стъпка е да разцъкам curses – нещото, което осигурява ascii интерфейса.

След един час четене не умопомрачително дългия менюъл на curse и малко конзолно тестче стигнах до следните два важни извода:

1. Curse няма готови диалогчета (за open file например) – селско (все пак има надежда, че някъде някой е написал нещо по въпроса)

2. Терминалите не са това което бяха. На 22 инчов монитор в текстов режим имам 48 реда по 136 колони, в графичен много повече… къде изчезнаха добрите стари 80×25. Може би са изчезнали заедно с DOS, кой знае :D. На малкия ми латоп (резолюция 1024х600), в текстов режим конзолата е баш 80х25 реда. Т.е. нищо не изглежда както трябва – неприятно.

Друг проблем е че трябва да поддържам доста диалогови прозорци и мишка. Решението може би е тази надстройка на curses – Urwid, която ще спести излишното копане за диалози.

Интерфейса, който за сега ми се върти в главата включва тикер за текуща песен, скролче за звука, бутони add, rem, jmp, que, ext, bkg (добавяне/махане на песен, jump по име – включва диалог, que – включва диалог, exit и преминаване в background режим), текуща плейлиста (в общи линии списък със заглавия).

Day 1:

Продължих да разцъквам Urwid и като цяло съм доста доволен. Направих прототип на плейлиста, но изникна нов проблем. В плейлистата човек може да има хиляди песни и е нормално да има скролбар, само че скролбара е фиксиран от броя редове в терминала (това значи, че е ограничен по брой позиции, на които да се мести), което неминуемо води до проблем че при преместване на скролбара с една позиция, в листата може да бъдат скипнати повече записи отколкото може да покаже. Ще се получи „прескачане“, което ще е по-скоро дразнещо от колкото полезно. Поради този факт (и това че скролбар не е имплментиран в Urwid официално :-P) обмислям вариант да мина без скролбар, а по-скоро с филтър, който да филтрира песните по част от име и да сложа поле, което да показва на кой номер песен (страница) е плейлистата в момента. Това би помогнало и за реализацията на Jump функционалността, като просто се филтрира по ключова дума и се натисне някаква клавишна кобмбинация… нийййт. Вечерта завърши с Пранка :-)

Day 2 & 3:

За тези два дни горе-долу направих плейлистата и филтрирането и леко се отчайвам от скоростта с която напредвам, а ми предстой да свържа кода за directory browsing с плейлистата. Все пак все повече ми се избистрят идеите в главата, така че може да се каже че положението не е толкова критично. Дано дедлайна не ме притисне много…

Day 4:

Интегрирах кода за браузване из файловата система с плейлистата и добавих код за плейване на аудио файлове – даже пуснах няколко (big success)! Вече съм една идея по-спокоен, защото имам нещо работещо. И все пак ми остава queue функционалността на плейлистата, което няма да е толкова прозрчано и просто. Чудя се дали да не напиша малко тестове и паралелно с това да не рефакторна кода на няколко места, може би е добра идея (Както се вижда съм скептик относно предварителното писане на тестове… а по принцип съм скептик и относно писането на тестове ИЗОБЩО, но се старая да променя това!!!). Намерих още една библиотека, която би била полезна в проекта – Mutagen, служи за четене на тагове от разнообразни аудио формати и ще ми помогне с title/author в плейлистата.

Day 5 & 6:

Уж беше събота и неделя, а свърших доста малко неща. Все пак позавърших изгледа на плейъра и четенето/показването на ID3 таговете. Реших да отложа правенето на опашка, защото е мега досадно и не е толкова фън, колкото плейването на стрийминг и рънването в бекграунд. Плейването на стрийминг се оказа доста нетривиална задача, която ми отвори един куп проблеми, та и там нещатата не са розови. Трябва да почна да пиша тестове… ех тези ш****и тестове…

Day 7 & 8:

Оказа се, че Audiere е тотално скаран с всичките ми идеи да го подкарам с http стрийм… то не бяха наименовани пайпове, сокети, даже се мъчих да му вкарам numpy-ски масив от float32, но след дълги среднощни борби реших да се преориентирам към GStreamer, който е бая тежко, по-точно обемно нещо, и много исках да не опирам до него. За това пък плейва без грижи само като му се даде URI. Отначало исках да ползвам Audiere за плейване на локални файлове и Gstreamer за уеб стрийминг, но освен че е доста грозно решение, срещнах трудности в освобождаването на заетия аудио ресурс когато искам да подменя on-the-fly библиотеката. По-точно Audiere освобождаваше веднага всичко, но на GStreamer му отнемаше поне 5-6 секунди и switch-ването помежду им не ставаше гладко. В резултат на това се наложи да си напиша адаптор към GStreamer от стария код за плейване на фиайлове, който ползваше Audiere. Беше кървава баня, но се справих, даже промените по интерфейса бяха почти незначителни. За съжаление GStreamer е слабо документиран също, макар че има туториали, но референса (вкл. и този, който е в интерпретатора) е много постен. Голяма мъка беше, но сега поне мога да плейвам радио :-)

Day 9:

Гледах Ледена Епоха 3 (3D), но не беше нещо особено. Поради този факт не свърших много – оправих цветовата схема на плейъра, проведох задълбочен рисърч за пускане на application в background, който не доведе до никакви реални резултати. Впоследствие открих workaround – просто трябва да се пусне плейъра във втори терминал (ctrl+alt+f[1..6]) и да се switch-ва в него като трябва да се контролира pyp3. Останаха ми още два малки таска и после тестове.

Day 10:

Добавих възможност за конфигуриране на outlook-а на плейъра, като се опишат цветовете на отделните части от ascii интерфейса в конфигурационен файл. Готино е когато нещата са максимално customisable. Сложих и event handler, който да следи по кутлурен начин дали дадена песен е свършила. Това ми отне сумати време, защото няма кой знае колко полезно инфо в нета… или трябва да падне голямо четене, докато се разбере по-точно. Много мразя, когато се занимавам с непопулярни неща, защото са слабо документирани и само ми късат нервите! Освен това в крайна сметка при четене от online stream не пристига event за край на stream-а ако се прекъсне връзката, което е хипер малумно, защото на това се надявах най-вече :(. Оправих разни бъгове и с това приключвам официално работа по Pyp3 (е имам да пиша тестове, но за тях няма да блогвам).

Final Conclusion:

Използвани компоненти: GStreamer, Urwid, Mutagen, Audiere (GStreamer-а го замени впоследствие)

Функционалност:

- плейване на файлове и стрийминг

- филтриране, jump, add/del, select, directory browsing dialog (само интегриране)

- auto save/load на плейлистата и цветовата гама на плейъра

- play/pause/stop/next/prev, fastforward/slowbackward ( :-D ), ID3 tags, текущо време на плейване, state на плейъра, volume

- autoplay next track, autoreconnect to stream on lost connection

След една камара време и безсъние резултата не е чак толкова грандиозен, но това се дължи основно на факта, че падна голяма борба заради слабите документации и защото трябваше да switch-на от Audiere към GStreamer. Съответно голяма част от времето отиде за направата на, иначе, не особено сложни неща.

Неща за които не ми стигна времето:

- Queue – малко ме е яд за това, защото има полза от опашка

- Rating – не ползвам такива неща… но може да има смисал

Все пак от много време не бях писал нещо, което да ми е на сърце и да ме ентусиазира, така че, въпреки всички трудности съм happy :-)

pyp3