02
avril
'23

Projets en cours, conférences et Mastodon

Publié le 02 avril 2023

C'est l'heure de faire un point sur les projets en cours, les trucs à venir et la bascule Twitter vers Mastodon. C'est aussi l'occasion pour moi de rédiger le premier billet de blog de 2023, parce que bon tout le monde sait que je n'en écris que 3 ou 4 par an. Allez hop, on commence par les projets en cours.

Les projets avancent (doucement)

Depuis janvier 2023, j'ai décidé de me focaliser sur seulement deux projets à la fois histoire de ne pas trop me disperser. Le premier projet concerne l'ajout du support des micro-contrôleurs de la série STM32WLxx dans la bibliothèque libopencm3 (y compris de leur transceiver sub-GHz intégré), et ce dernier avance bien. Après avoir perdu un temps considérable à déboguer la communication entre le transceiver et le CPU principal (via l'achat d'une carte de développement dédiée permettant cela), j'ai enfin réussi à implémenter les primitives permettant l'envoi et la réception de trames LoRa et entamé le développement d'un driver dédié aux communications sans-fil pour ce type de micro-contrôleurs. Le développement de ce dernier est toujours en cours, mais cela devrait aboutier assez rapidement à quelque-chose d'utilisable (bien qu'améliorable). La partie la plus longue consistera à bien intégrer tout ce code selon les règles de coding du projet libopencm3, et soumettre mes modifications aux développeurs afin que tout ce boulot d'intégration puisse servir à d'autres. Et puis intégrer tout ça au projet 3615 LoRa, qui permettra à deux Minitels de communiquer en sans-fil avec une portée de plusieurs kilomètres !

Le second projet, plus conséquent, concerne le portage de GRBL (un logiciel de commande de machine numérique) sur la Cricut Maker, une découpeuse vinyle closed-source. Le gros de la rétro-ingénierie a été faite, et le portage de GRBL permet désormais de communiquer via du GCODE grâce au port USB de la Cricut Maker. Reste le pilotage des moteurs qui de prime abord se passait bien, jusqu'à ce que vienne le moment d'intégrer ces derniers avec le code de GRBL. En effet, GRBL a été initialement développé pour piloter des moteurs pas-à-pas (steppers), et non des moteurs à courant continu couplés à des encodeurs rotatifs comme c'est le cas sur cette découpeuse. Mon intuition me disait que les piloter comme des steppers serait un échec, et les derniers essais en stream ont bien démontré que c'était le cas. Bref, il va falloir que je m'arme de courage et que j'arrive à implémenter un pilotage efficace de ces moteurs, malgré le fait que ce soit la première fois que je m'attaque à ce type de code. Mais il paraît que c'est formateur, donc je vais m'accrocher 😅.

Je pense que j'ai réussi à m'y tenir, à ces deux projets, durant les différents streams depuis janvier. Bon ok, une fois je n'avais pas le courage ou l'esprit à coder, et on a fait autre chose (hack hardware). Mais ce n'est arrivé qu'une seule fois !

Trucs à venir

Depuis le COVID et le confinement, je m'étais sérieusement calmé sur ma participation à des conférences en présentiel et je n'avais pas soumis de sujet de talk (ou très peu) durant les dernières années. Il semblerait que j'ai repris goût à donner des talks, et que plusieurs des talks que j'ai soumis (pour certains en collaboration avec Romain Cayre) aient été acceptés à différentes conférences. J'ai ainsi fait un tour en mars à Lausanne, à l'occasion de la conférence Insomni'hack, et serai normalement présent dans quelques semaines à la Toulouse Hacking Convention ainsi qu'à SSTIC en juin. Ça sera aussi l'occasion de rencontrer des gens autour d'un verre (de bière, de coca ou de Club Mate), de discuter cybersécurité et hacking (ou autre), et de voyager un peu dans des contrées pas si lointaines 😆...

En tout cas, si vous avez prévu de participer à l'un de ces évènements, n'hésitez pas à me pinger sur Mastodon !

Twitter vs. Mastodon, quelques mois après

En parlant de Mastodon, je profite de ce billet pour faire un premier bilan de ma bascule du site de l'oiseau bleu à celui du pachyderme fédéré. Pour rappel, j'ai conservé mon compte Twitter mais ne poste plus dessus et ai créé un compte Mastodon sur lequel je suis désormais actif. Je publie de temps en temps, principalement sur des sujets liés à la cybersécurité (on ne se refait pas), mes streams et les projets en cours, ainsi que sur la publication des vidéos de replay de ces streams sur ma chaîne Peertube.

Premier constat après quelques mois d'utilisation normale: j'apprécie grandement l'absence de publicité et de tweets "intrusifs" ou sponsorisés. J'essaie de suivre des comptes assez divers, histoire de ne pas m'enfermer dans une bulle, et je suis assez agréablement surpris de voir que pas mal de gens de la communauté cybersécurité ont créé des comptes Mastodon et publient/réagissent régulièrement. Du coup, la veille technique est facilitée et me permet de me tenir à jour en ne consultant que Mastodon, ce qui est très appréciable. Certes, j'ai du dire adieu à mes 5000+ followers, mais ça fait énormément de bien au mental d'utiliser une plateforme plus saine et peuplée de personnes différentes qui ne sont pas sur Twitter. Ce qui implique donc de croiser de nouvelles têtes, d'avoir des échanges différents de ceux que je pouvais avoir précédemment, bref de toucher d'autres profils, de nouvelles personnes et de pouvoir échanger avec ces dernières.

Toutefois, il y a quelques éléments qui sont décevants avec Mastodon (enfin, surtout les utilisateurs) et qui valent le coup d'être notés. Le premier, c'est de voir que certaines personnes qui possèdent un compte Twitter et Mastodon et qui postaient sur les deux plateformes au moment des frasques d'Elon ne publient plus que sur Twitter et gardent leur compte Mastodon en backup. C'était attendu, Mastodon n'étant pas un clone de Twitter, et Twitter continuant de fonctionner "comme prévu" malgré les décisions random prises par son dirigeant. Mais ce n'est pas grave, c'est juste frustrant de ne pouvoir réagir à leurs messages sur le site de l'oiseau, car je me suis promis de ne plus y participer. Le second aspect concerne les réactions aux toots: il est difficile de savoir comment ces derniers sont perçus car la seule métrique que l'on possède en tant qu'auteur d'un toot est le nombre de likes ou de retoots qu'a reçu un toot particulier. J'ai donc pris l'habitude de re-tooter et de liker des toots pour notifier à leurs auteurs que je les ai appréciés, en espérant que cela leur permettra d'avoir une idée plus précise de l'impact que leur toot peut avoir. Car oui, quand on partage quelque-chose dans le vide pendant quelques mois, ça donne vite l'impression que c'est inutile voire invisible. Soutenons nos pouèteurs favoris 💪 !

Au global, je ne regrette pas ce passage à Mastodon, loin de là ! J'adore certaines des fonctionnalités offertes par cette plateforme, et pour rien au monde je ne voudrais revenir à Twitter. Il m'arrive quand même de suivre de temps à autre mon fil Twitter, mais j'y retrouve la grande majorité des évènements ou informations que j'ai déjà vu sur Mastodon ... la publicité et les tweets inutiles en plus. Et je ne parlerai pas des dernières décisions du sieur Musk quant à l'avenir de la plateforme à l'oiseau bleu.

Allez, je vais continuer à me concentrer sur mes projets en cours, mes streams hebdomadaires et j'espère pouvoir poster un nouveau billet dans quelques mois !

17
févr.
'12

Jiwa v3, retour à la case départ

Publié le 17 février 2012

Je suis retourné sur Jiwa aujourd'hui. Je m'attendais à trouver ma plateforme de musique habituelle, mais je me suis rendu compte que cela faisait un bail que je n'y avais pas été. Le site a complètement changé de look et a été repris par *Allomusic* qui en a profité pour le PURifier à l'Hadopi. Vu que je m'étais bien intéressé à cette plateforme il y a de cela quelques années, je me suis demandé si les choses avaient changé. Et puis parce que cette satanée limitation de 30 secondes d'écoute m'a été insupportable.

Jiwa version 3

Comme en témoigne le code source de Jiwa, le site est passé en vesion 3:

<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# jiwavtrois: http://ogp.me/ns/fb/jiwavtrois#">

Jiwa avait fait de nombreux efforts il y a quelques années pour empêcher le téléchargement des fichiers MP3 et leur sauvegarde. J'avais d'ailleurs à l'époque rédigé quelques articles et développé une extension Firefox dédiée. Il semble donc raisonnable de penser que les protections mises en place dans le code sont identiques ou meilleures que les dernières implémentées.

J'ai donc repris mes anciens Proof of Concept, et je me suis attelé à la tâche. J'ai tout simplement réessayé mes outils, sans succès. J'ai donc sorti l'artillerie lourde (paros + firefox) et commencé à regarder les requêtes et là, ce fut la surprise. Une grosse suprise. Non seulement quelques détails ont changé, certes très minimes, mais j'ai pu retrouver des requêtes similaires à celles employées quelques années plus tôt sur Jiwa ... Comme si on avait fait un bond en arrière de quelques années.

C'est reparti comme en 40 !

Autant dans la version précédente de Jiwa, on pouvait toujours écouter sans limite, autant avec la version actuelle Jiwa ne sert que d'appât: les pages redirigent pour l'écoute vers le site d'Allomusic, qui bride énormément les contenus et force un poil la main pour amener le visiteur à prendre un compte Premium.

Il faut dire qu'à l'époque où je m'étais interessé à Jiwa, l'écoute en ligne était toujours possible, ce qui par ailleurs me satisfaisait pleinement. Mais dans la configuration actuelle, je trouve ça un brin décevant de se servir de l'ancienne image de Jiwa pour essayer de remonter l'image d'un autre service.

On s'éloigne quand même du modèle original de Jiwa, et même de celui de Deezer: il ne s'agit plus de streaming gratuit en ligne reposant sur de la pub mais d'une version lite qui vise à convertir les utilisateurs en comptes Premium. Certainement une preuve que le modèle économique qui se veut reposer sur la pub n'est pas viable, comme a pu l'expérimenter Deezer. Sans parler des tarifs faramineux demandés par les majors comme Universal Music.

Alors bon, je ne suis pas un fervent adepte de ce système, bon nombre de lecteurs le savent, et j'ai adapté mes précédents scripts pour prendre en charge la version actuelle de Jiwa. Plusieurs restrictions ont été mises en œuvres pour éviter le téléchargement:

Le détail amusant c'est que même si l'écoute est limitée à 30 secondes, le fichier MP3 complet est téléchargé. Le bridage par user-agent est même complètement inefficace, je soupçonne même le script distant de bugger quasi aléatoirement ...

iJaw nouvelle génération

Pour ceux qui seraient intéressés, je diffuse ci-après le code source de mon Proof of Concept amélioré. Ce bout de code python est un poil sale, mais je ne compte pas le maintenir. C'est une preuve de concept, juste présente pour démontrer ce que j'affirme dans ce billet.

"""
Crapy Jiwa download tool
"""

import re
import sys
import json
import time
from hashlib import md5
from random import choice
import urllib,httplib,urllib2

def filter(s):
        return s.replace('\n',' ')

def randomUA():
        rstr = ''.join([choice('0123456789') for i in range(32)])
        return 'Mozilla Firefox (%s)'%rstr

def getSongDetails(sid):
        r = urllib2.Request('http://www.jiwa.fr/song/%d/play/'%sid)
        resp = urllib2.urlopen(r).read()
        return json.loads(resp)

def lookupSongs(keywords):
        try:
                r = urllib2.Request('http://www.jiwa.fr/search?q=%s'%urllib.quote(keywords))
                resp = urllib2.urlopen(r).read()
                matches = re.findall('/track/.*-([0-9]+)\.html',resp)
                res = []
                for match in list(set(matches))[:5]:
                        res.append(getSongDetails(int(match)))
                return res
        except urllib2.Error,e:
                return None


def dlTrack(sid,filename):
        errors = 0
        while errors<3:
                token = getToken(sid)
                _t = token.split('=')
                url = 'http://m.jiwa.fm/play.php'
                l1 = 'gwqd29ydg7sqys_qsh0'
                l3 = _t[0]
                l4 = _t[1]
                l5 = _t[2]
                l6 = _t[3]
                l7 = md5(l1).hexdigest()
                l8 = md5(l3+l7+str(sid)).hexdigest()
                url = "%s?r=%s&s=%s&t=%s&m=%s&from=1" % (url,l4,str(sid),l8,l6)

                r = httplib.HTTPConnection('m.jiwa.fm:80')
                r.putrequest('GET',url)
                r.putheader('Host','m.jiwa.fm:80')
                r.putheader('User-Agent',randomUA())
                r.endheaders()
                token = r.getresponse().read()
                if token=='-1':
                        errors += 1
                        time.sleep(1)
                else:
                        f = open(filename,'wb')
                        f.write(token)
                        f.close()
                        return True
        return False

def getToken(sid):
        params = *
        's':str(sid),
        *
        p = urllib.urlencode(params)

        r = httplib.HTTPConnection('m.jiwa.fm:80')
        r.putrequest('POST','http://m.jiwa.fm/token.php?r=1')
        r.putheader('Host','m.jiwa.fm:80')
        r.putheader('User-Agent',randomUA())
        r.putheader('Content-Type','application/x-www-form-urlencoded')
        r.putheader('Content-Length',str(len(p)))
        r.endheaders()
        r.send(p)

        token = r.getresponse().read()
        return token


if __name__ == '__main__':

        print '[>] iJaw - A Jiwa search & download tool'
        print '[>] author: virtualabs (http://www.virtualabs.fr)'
        print '[>]'

        if len(sys.argv)>2:
                operator = sys.argv[1]
                if operator == 'search':
                        kw = ' '.join(sys.argv[2:])
                        print '[+] Looking for songs ...'
                        tracks = lookupSongs(kw)
                        if tracks:
                                if len(tracks)>0:
                                        print ''
                                        print 'Song ID  - Song Name [Artist, Album]'
                                        print ''
                                        for t in tracks:
                                                print '%s - %s [%s, %s]' % (t['songId'],filter(t['songName']),filter(t['artistName']),filter(t['albumName']))
                                        print ''
                                        print '%d songs found' % len(tracks)
                                else:
                                        print '[!] No songs found.'
                        else:
                                print '[!] No songs found.'
                elif operator == 'get':
                        tracks = [t for t in sys.argv[2:]]
                        for sid in tracks:
                                print '[+] getting track info ...'
                                _track = getSongDetails(int(sid))
                                filename = '%s_%s_%s.mp3' % (_track['artistName'].replace(' ','_'),_track['albumName'].replace(' ','_'),_track['songName'].replace(' ','_'))
                                print '[+] downloading track ...'
                                if dlTrack(sid,filter(filename)):
                                        print '[i] saved to %s.' % filter(filename)
                                else:
                                        print '[!] Unable to download song.'
                elif operator == 'dl':
                        kw = ' '.join(sys.argv[2:])
                        tracks = lookupSongs(kw)
                        if tracks:
                                for t in tracks:
                                        _track = getSongDetails(t['songId'])
                                        filename = '%s_%s_%s.mp3' % (_track['artistName'].replace(' ','_'),_track['albumName'].replace(' ','_'),_track['songName'].replace(' ','_'))
                                        print '[+] downloading song "%s [%s]" ...' % (_track['songName'],_track['artistName'])
                                        if dlTrack(int(t['songId']),filter(filename)):
                                                print '[i] saved to %s.' % filter(filename)
                                        else:
                                                print '[!] Failed to download song: %s' % _track['songName']
                                print '[+] Done.'
                        else:
                                print '[!] No songs found'
        else:
                print '[i] usage: %s [search|get|dl] [second arg]'
                print '[i]'
                print '[i] + you can look for songs:'
                print '[i]'
                print '[i]  $ ijaw.py search Madonna like a virgin'
                print '[i]  $ 12345 - Like a virgin [Madonna, ...]'
                print '[i]'
                print '[i] + and download a given song with its ID:'
                print '[i]  $ ijaw.py get 12345'
                print '[i]'
                print '[i] + or download all tracks found:'
                print '[i]'
                print '[i]  $ ijaw.py dl Madonna like a virgin'
                print '[i]'
                print '[i] + Have phun !'

Conclusion

Il est étonnant que le passage de Jiwa sous la houlette d'Allomusic n'ait pas introduit un nouveau système plus fiable de lecture de musique en ligne, et il est franchement dommage qu'une plateforme labellisée PUR force autant la main de l'utilisateur vers les comptes payants, en tentant de promouvoir l'écoute gratuite mais très limitée. OK, le modèle économique basé sur les pubs n'est pas forcément viable, mais on est pas QUE des vaches à lait. A bon entendeur ...



Les contenus disponibles sur ce blog sont publiés sous licence Creative Commons BY-NC-SA.
Vous pouvez réutiliser tout ou partie de ces contenus à condition de citer l'auteur et l'origine, vous ne pouvez en faire une utilisation commerciale, et enfin vous devez partager tout travail ou œuvre dérivée sous les mêmes conditions — c'est-à-dire avec la même licence d'utilisation Creative Commons.