Arti Zirk - Syncly MusicSync

From EIK wiki

Esitlus

Esitluse slaidid on siin: http://slides.com/artizirk/musicsync

Syncly MusicSync

Syncly MusicSync on minu Java[1] kodu töö. Tegu on üsnagi KISS teenusega mis võimaldab kasutajal sünkroniseerida YouTube keskonnas olevaid playliste helifailidena enda telefoni.

Idee

Õues ringi jalutades või rattaga sõites meeldib mulle kõrvaklapid pähe pannes muusikat kuulata. Tavaliselt on minu muusika allikaks YouTube üles laetud lood mis on mul playlisti lisatud ning mida ma siis aegaajalt ühe väikese scriptiga enda arvuti ja sealt edasi telefoni tõmban. youtube-dl on üks väike pythonis kirjutatud programm mis oskab seda väga hästi teha.

#!/bin/bash
youtube-dl -i -x --download-archive .archive.txt -f bestaudio "http://www.youtube.com/playlist?list=PLB5VrND_o3PgZNzNdohFDWE5BTFIPDImQ"
adb push -p * /sdcard/Music/

Mu sõber Mark tegi selle automaatse sünkroniseerimis osa isegi ära enamvähem ning lükkas koodi githubi üles nightsnack[2] nimega aga see ei lahenda ära lugude telefoni saamis probleemi.

Sügisel algas Java progemis kursus mille raames otsutasin Javas kirjutatud Androidi rakenduse teha mis oskaks minu lugusi automaatselt telefoni tõmmata.

Planeering

Kõigepealt alustasin Androidi platvormi dokumentatsiooni lugemisega ja üritasin enamvähem paika panna mida ma androidi rakenduses kuvada tahaksin ning tegin endale enamvähem selgeks kuidas Androidi poolt ära teostada.

Selle lõpptulemuseks on midagi sellist

MusicSync home.pngMusicSync settings.png

Edasi panin paika punktid mida mul oleks vaja et backend server teeks:

  • Playlistide kuvamine / lisamine / eemaldamine
  • Playlistides olevate lugude kuvamine
  • Teadete genereerimine playlisti lugude lisamise/eemaldamise korral

Tehnoloogiate valik

Backend serveri kirjutamiseks tuli teha valik kuidas ma täpselt selle ära lahendan kasutades mis asju.

Playlistide ja seal olevate lugude salvestamiseks otsustasin kasutada MongoDB andmebaasi mis on üks NoSQL andmebaas JSON sarnaste dokumentide salvestamiseks. Valisin MongoDB just selle pärast, et olen seda varasemalt kasutanud, arenduse käigus andmestruktuuri muutmine on üli kerge ning kuna SQL pole mulle eriti kunagi meeldinud.

YouTubest lugude allalaadimiseks hetkel ainuke laialt toetatud varjant on kasutada youtube-dl pythoni programmi. On olemas ka erinevaid veebilehti mis oskavad YouTubest alla laadida helifaile aga ka need suuretõenäosusega kasutavad midagi sarnast tagaplaanil. Kuna youtube-dl on kirjutatud Pythonis siis on selle kasutamine Pythoni scriptides kergem ja annab rohkem võimalusi selle käitumise muutmiseks kui mõne muu lahenduse kasutamine.

Kuna mu arti.ee serverist pole eriti palju kettapinda, et sinna ka muusika ära mahutada siis otsustasin kasutada Amazon S3 pilveservereid kuhu Youtubest allalaetud muusika paigutada ning sealt neid vajadusel telefoni tõmmata.

Genereeritud teadete edastamiseks sobib mulle väga hästi nginx-push-stream-module mis võimaldab kergesti genereerida Server Sent Events striimi mida on võimalik töödelda väga kergelt kõigi programeerimis keeltega ja veebilehitsejatel on sse tarbimise jaoks sisseehitatud tugi javascriptis.

HTTP API ehitamiseks, mille kaudu teha päringuid serveri pihta, otsustasin teha kasutades Pythoni teeki nimega Falcon. Falcon on minimaalne Pythoni WSGI liidest täitev teek HTTP RESTful api'de tegemiseks. Siin on näide sellst kui ilus lõpptulemus jääb kasutades Falconit https://gist.github.com/artizirk/ae9cf28d1f45ef0aa965

Backendi implementeerimine

Alustasin Youtube playlisti videote lisamise ja eemaldamise eventide genereerimisega. Selleks otsustasin ära kasutada youtube-dl sisse ehitatud funktsionaalsust mis annab mulle tagasi nimekirja lugudest mis on playlistis

Hetkel siis on mul üks playlist-sync.py fail kus on while True loop mis iga andmebaasi lisatud playlisti kohta käivitab get_song_updates meetodi. get_song_updates meetod omakorda tõmbab alla, kasutades youtube-dl teeki, ette antud youtube playlisti html lehe ning ekstraktib sealt kõigi lugude id'd ning ka lugude nimed. Peale seda kui mul on käes list loo id-ga, küsin ma andmebaasist kas need lood on juba lisatud, kui pole, lisan selle ning genereerin evendi. Selleks et teada saada kas mõni lugu on kustutatud playlistis käin ma kõik andmebaasi lisatud antud playlisti lood läbi ning testin kas nad on eelnevalt alla tõmmatud lugude listis, kui pole siis genereerin evendi et lugu on kustutatud youtube playlistist.

youtube-downloader.py skript kuulab eelnevalt genereeritud evente ning lugude lisamise korral tõmbab nad alla youtube-dl teegi abil ja laeb nad Amazon S3'e üles kasutades boto3 teeki.

api.py hoolitseb HTTP API eest. Hetkel implementeerib http api neli endpointi kuhu pihta on võimalik päringuid teha

  • /playlists/ - kollektsioon playlistidest
  • /playlists/{playlist_id} - üks kindel playlist
  • /playlists/{playlist_id}/songs/ - kollektsioon lugudest ühes playlistis
  • /playlists/{playlist_id}/songs/{song_id} - üks kindel lugu

Iga see url on implementeeritud erineva klasssi poolt mis omakorda pärinevad BaseResponse klassist ning kõik nad tagastavad andmeid json vormingus

paar näidet hetkel implementeeritud api kohta

% # Küsime serveri lisatud playlistid
% http GET http://localhost:8000/playlists
HTTP/1.0 200 OK
Date: Tue, 08 Dec 2015 13:16:33 GMT
Server: WSGIServer/0.2 CPython/3.5.0
content-length: 202
content-type: application/json; charset=utf-8

[
    {
        "_id": "PLGE39Wpa-qf1xjp4gmJ_1PBzH7a_-GdOe",
        "title": "Nice Music [WIP]",
        "type": "YoutubePlaylist"
    },
    {
        "_id": "PLB5VrND_o3PgZNzNdohFDWE5BTFIPDImQ",
        "title": "Music I like",
        "type": "YoutubePlaylist"
    }
]
% # Küsime mis lood on ühes playlistis
% http GET http://localhost:8000/playlists/PLB5VrND_o3PgZNzNdohFDWE5BTFIPDImQ/songs
HTTP/1.0 200 OK
Date: Tue, 08 Dec 2015 13:17:01 GMT
Server: WSGIServer/0.2 CPython/3.5.0
content-length: 546
content-type: application/json; charset=utf-8

[
    {
        "_id": "qFDP9egTwfM",
        "deleted": false,
        "playlist": "PLB5VrND_o3PgZNzNdohFDWE5BTFIPDImQ",
        "title": "Netsky - Rio (Official Video) ft. Digital Farm Animals",
        "type": "Youtube",
        "url": "http://s3.storage.ms.wut.ee/qFDP9egTwfM"
    },
    {
        "_id": "FGBhQbmPwH8",
        "deleted": false,
        "playlist": "PLB5VrND_o3PgZNzNdohFDWE5BTFIPDImQ",
        "title": "Daft Punk - One More Time",
        "type": "Youtube",
        "url": "http://s3.storage.ms.wut.ee/FGBhQbmPwH8"
    }
]


Kokkuvõttes on ülesehitus midagi sellist:

MusicSync chart.png

Androidi klient

Nüüd kus backend osa on valmis on mul võimalik Androidi rakendus ka ära lõpetada. Kui kunagi peaksin selle valmis saama siis viskan siia ka kirjelduse selle kohta ja uuendan esitlust.

Raha

Kuna serverid maksavad, peale minu on veel inimesi kes on näidanud huvi selle teenuse kasutamise vastu ja ma ise enda taskust ka kõike kinni ei taha maksta siis otsustasin selle tasuliseks teenuseks teha teistele.

Hetkel on plaan küsida selle teenuse eest 1€ kuus mis tähendab, et mul oleks vaja vähemalt 10 inimest kes püsivalt maksaks et ma oma Amazon S3 ja DigitalOceanis oleva serveri tasudega nulli saaks.

Amazon S3 maksab hetkel 0.03€/GB ja 0.1€ eest saab seal hoida ~3GB jagu andmeid, failide tõmbamine maksab 0.09€/GB nii et kogu muusikakogu 3GB alla laadimine maksab 0.27€ mis omakorda tähendab et DigitalOceani serveri maksmiseks jääb 0.63€. DigitalOceani server maksab mulle hetkel 6€/kuus mis siis lõpuks teebki, et vähemalt 10 pidevalt maksvat kasutajat vaja et asi nulli tuleks rahaliselt. Iga järgmine kasutaja hakkab sisse tooma mulle umbes 0.6€ kuus.

See kõik on muidugi eeldusel, et kasutajad keskmiselt väga palju muusikat üles ei lae.

Legaalne pool

Youtube Terms of Service ei luba tegelikuses nende lehelt videosi alla laadida ning neid vaadata/kuulata mujal kui youtube enda lehel ja youtube rakenduses. https://www.youtube.com/terms lehel olev tekst teeb selle üsna selgeks nii, et üsna riskantne värk oleks mul seda musicsync asja suureks ajada. Paar kümmend inimest peaks okei olema ja mitte väga palju tähelepanu äratama.

Viited