dimanche 22 septembre 2019

Retrogaming: sauve ta recalbox dans le "Claude" !

Bon je n'ai pas fait de tuto step by step pour GPi Case finalement... je vous conseil d'aller sur la page du GPi Case de RecalBox: https://forum.recalbox.com/topic/18158/gpi-case-recalbox-6-1-beta-4-pre-release-disponible

Mais aujourd'hui je vais donc vous expliquer comment synchroniser vos sauvegardes dans le cloud (désolé pour le jeu de mot pourri, désolé pour les Claude !), et donc dans mon cas avec Dropbox.

Voici comment j'ai procédé... c'est pas si simple :-(

1) Je ne suis pas parti de zéro.... Il va falloir se connecter en ssh à la recalbox et j'ai suivi ce tuto mais que la step 1 pour l'installation de rcloud sur recalbox de mon GPi Case.: https://forum.recalbox.com/topic/13840/tutorial-how-to-sync-saves-roms-to-the-cloud.

Donc je vous remet la step 1 ici.
Attention, il ne faut pas oublier de commencer à faire cette commande sinon on ne peut pas mettre à jour l'OS qui est verrouillé par défaut même si on a compte root !
mount -o remount, rw /
Je le laisse donc en anglais pour cette partie:

open ssh and run those commands:
cd ~
mkdir -p rpi-sync    
wget -O rclone-install.sh https://raw.github.com/pageauc/rclone4pi/master/rclone-install.sh   
wget -O rclone-sync.sh https://raw.github.com/pageauc/rclone4pi/master/rclone-sync.sh   
wget -O rpi-sync/Readme.md https://raw.github.com/pageauc/rclone4pi/master/Readme.md
now we have all the necessary files, but we have to modify rclone-install.sh
run: nano ./rclone-install.sh
Delete all the sudo on the file, at the end you should have something like that:
0_1524321467750_Screenshot from 2018-04-21 16-33-36.png
now let's finish with the commands
chmod +x rclone-install.sh
chmod +x rclone-sync.sh
./rclone-install.sh 
edit: 06/01/2020, sur la version GPI Case, pas de problème mais sur ma borne avec la 6.1.1 sur un Raspberry Pi 3B+, j'ai du lancer le script ainsi (la command chmod n'a pas d'effet je crois :-(
bash rclone-install.sh

2) A partir de la conf du compte de cloud (dropbox ou autre), c'est à vous d'improviser et de s'adapter ;-)
Pour mon cas, j'ai utilisé une dropbox, donc je vais vous expliquez comment j'ai fait (mais je ne peux pas vous faire pour les autres clouds mais cela sera très proche en terme de commande à utiliser) .

Dans le cas de Dropbox, j'ai créé une application "recalbox-lesv2" sur https://www.dropbox.com/developers/apps  de mon compte dropbox (je n'ai pas mis "recalbox" parce qu'existe déjà et donc à vous de trouver la votre en rajoutant un pseudo à la fin par exemple)

On créé un app (on pourra rester en dev, pas de soucis) :



On rajoute le lien "Redirect URIs" comme conseillé par rclone (j'ai vu cela dans certaines options mais c'est peut être inutile ?!):




Puis on devra généré un token pour plus tard, donc on peut garder la page ouverte dans le browser.




C'est rclone 1.6 que j'ai installé dans mon cas, donc dans "rclone config" j'ai fait une nouvelle conf avec le nom "remote", je n'est pas mis de client_id ou client_secret, j'ai dis non au "advanced settings" et puis non pour pourvoir rentrer le token à la main.


# rclone config
n) New remote
s) Set configuration password
q) Quit config
n/s/q> n
name> remote
Type of storage to configure.
Enter a string value. Press Enter for the default ("").
Choose a number from below, or type in your own value
 1 / 1Fichier
   \ "fichier"
 2 / Alias for an existing remote
   \ "alias"
 3 / Amazon Drive
   \ "amazon cloud drive"
 4 / Amazon S3 Compliant Storage Provider (AWS, Alibaba, Ceph, Digital Ocean, Dreamhost, IBM COS, Minio, etc)
   \ "s3"
 5 / Backblaze B2
   \ "b2"
 6 / Box
   \ "box"
 7 / Cache a remote
   \ "cache"
 8 / Dropbox
   \ "dropbox"
 9 / Encrypt/Decrypt a remote
   \ "crypt"
10 / FTP Connection
   \ "ftp"
11 / Google Cloud Storage (this is not Google Drive)
   \ "google cloud storage"
12 / Google Drive
   \ "drive"
13 / Google Photos
   \ "google photos"
14 / Hubic
   \ "hubic"
15 / JottaCloud
   \ "jottacloud"
16 / Koofr
   \ "koofr"
17 / Local Disk
   \ "local"
18 / Mega
   \ "mega"
19 / Microsoft Azure Blob Storage
   \ "azureblob"
20 / Microsoft OneDrive
   \ "onedrive"
21 / OpenDrive
   \ "opendrive"
22 / Openstack Swift (Rackspace Cloud Files, Memset Memstore, OVH)
   \ "swift"
23 / Pcloud
   \ "pcloud"
24 / Put.io
   \ "putio"
25 / QingCloud Object Storage
   \ "qingstor"
26 / SSH/SFTP Connection
   \ "sftp"
27 / Union merges the contents of several remotes
   \ "union"
28 / Webdav
   \ "webdav"
29 / Yandex Disk
   \ "yandex"
30 / http Connection
   \ "http"
31 / premiumize.me
   \ "premiumizeme"
Storage> 8
** See help for dropbox backend at: https://rclone.org/dropbox/ **

Dropbox App Client Id
Leave blank normally.
Enter a string value. Press Enter for the default ("").
client_id>
Dropbox App Client Secret
Leave blank normally.
Enter a string value. Press Enter for the default ("").
client_secret>
Edit advanced config? (y/n)
y) Yes
n) No
y/n> n
Remote config
Use auto config?
 * Say Y if not sure
 * Say N if you are working on a remote or headless machine
y) Yes
n) No
y/n> n
For this to work, you will need rclone available on a machine that has a web browser available.
Execute the following on your machine (same rclone version recommended) :
        rclone authorize "dropbox"
Then paste the result below:
result> {"access_token":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","token_type":"bearer","expiry":"0001-01-01T00:00:00Z"}
--------------------
[remote]
type = dropbox
token = {"access_token":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","token_type":"bearer","expiry":"0001-01-01T00:00:00Z"}
--------------------
y) Yes this is OK
e) Edit this remote
d) Delete this remote
y/e/d> Y
Current remotes:

Name                 Type
====                 ====
remote               dropbox

e) Edit existing remote
n) New remote
d) Delete remote
r) Rename remote
c) Copy remote
s) Set configuration password
q) Quit config
e/n/d/r/c/s/q> q


Attention dans result il faut mettre sous ce format JSON:
{"access_token":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","token_type":"bearer","expiry":"0001-01-01T00:00:00Z"}

Le token d'accès doit être généré manuellement à partir de la page app de "recalbox-lesv2" (je ne peux pas vous montrer quand c'est généré parce qu'il disparaît ensuite... mais vous allez avoir une chaine en base64 à recopier dans le la variable "access_token" dans le JSON ci-dessus.




3) pour tester, j'ai lancer la commande:
rclone lsd remote:
ou
rclone ls remote:

Si c'est OK, c'est vide....que dale !!!


Mais si votre token est faut, il vous le dira aussi :-(

4) pour tester la copy, j'ai créé un repertoire /saves dans ma dropbox sur mon PC donc dans /Applications/recalbox-lesv2/
et j'ai lancé la commande suivante :
rclone copy /recalbox/share/saves/ remote:saves

Magic !!! sur mon PC, j'avais mon répertoire "saves" complet de ma recalbox:



5) Lancer le backup des sauvegardes quand on éteint le GPi Case (si wifi OK !)

Si pas déjà fait, ou si reboot, il faudra relancer cette commande:
mount -o remount, rw /
Il faut aller modifier le script python pour le safe shutdown du gpi case de retroflag  maintenant :
cd /recalbox/system/hardware/case/installers/gpi/assets/ 

Il faut editer le fichier python suivant mais avant c'est bien de faire un petit dos2unix parce que des "^M" traine dans le fichier ;-) :
dos2unix -u recalbox_SafeShutdown_gpi.py 
nano recalbox_SafeShutdown_gpi.py 
Le contenu du fichier est ainsi (dans recalbox 6.1 Beta 4 pour GPi Case) et il faut ajouter/modifier le code en surligner en jaune :


import RPi.GPIO as GPIO
import socket
from multiprocessing import Process

# Initialize pins
powerPin = 26
powerenPin = 27

# Initialize GPIO settings
def init():
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(powerPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(powerenPin, GPIO.OUT)
        GPIO.output(powerenPin, GPIO.HIGH)
        GPIO.setwarnings(False)

# Check if internet is available
def is_internet_available():
        try:
                socket.create_connection(("www.google.com", 80))
                return True
        except OSError:
                return False
# Lookup emulationstation def lookupEmulationStation(): import psutil # Run through all process for p in psutil.process_iter(): if "emulationstation" in p.cmdline(): print("Emultion-station found") return p print("Emultion-station NOT found") return None # Kill process recursively def killEmulationStationTree(): import psutil try: # Get emulationstation process parent = lookupEmulationStation() if parent is None: return allProcess = { parent } # Get all childrens recursively children = parent.children(recursive=True) print("Got children: {}".format(children)) # Tell EmulationStation to quit demo/mode and/or to exit gracefully try: with open("/tmp/emulationstation.quitnow", "w") as sf: sf.write("exit") sf.flush() sf.close() except IOError: pass # Can't wait or retry # Quit emulationstation gracefully parent.terminate() print("Emulationstation terminated") # Kill all children for child in children: allProcess.add(child) child.kill() print("Children killed") # Wait for everyone _, stillAlive = psutil.wait_procs(allProcess, 20) print("Stillalive: {}".format(stillAlive)) # if emulation is still alive, give it a last chance to quit if parent in stillAlive: psutil.wait_procs(stillAlive, 20) print("Last chance end") except psutil.NoSuchProcess: print("exception in killEmulationStationTree") pass # Waits for user to hold button up to 1 second before issuing poweroff command def poweroff(): while True: GPIO.wait_for_edge(powerPin, GPIO.FALLING) # Stop all killEmulationStationTree()

                import os
                # Backup saves in the cloud if wifi available
                if is_internet_available():
                        os.system("/usr/bin/rclone copy /recalbox/share/saves/ remote:saves")
# Fast stop import os os.system("reboot -f") if __name__ == "__main__": #initialize GPIO settings init() #create a multiprocessing.Process instance for each function to enable parallelism powerProcess = Process(target = poweroff) powerProcess.start() powerProcess.join() GPIO.cleanup()

Vous sauvegarder le code ainsi par CTRL+X + Y (cours rapide de sauvegarde dans nano ;-) 

Puis pour vérifier la syntaxe, vous pouvez le recompiler :
python -m py_compile recalbox_SafeShutdown_gpi.py 
Ensuite pour finir, il faut rebooter pour que cela soit pris en compte pour les prochains power off.

6) Mais dans l'autre sens ??? et dans le cas où on serait en Raspberry Pi 3 B+ sans bouton d'arrêt ??? (edit: 09/01/2020). Il va falloir aussi être capable de lancer la restauration des sauvegardes qui sont dans le cloud quand on rallume (si wifi OK et accès internet bien sur !) et la sauvegarde quand on éteint sans le bouton (edit: 09/01/2020).

Si pas déjà fait, ou si reboot, il faudra relancer cette commande:
mount -o remount, rw /
Et ensuite on va créer un fichier pour lancer la récupération des sauvegardes du cloud.
cd /etc/init.d/
nano ./S99sync
Voici le contenu du fichier que j'ai créé:
Edit 09/02/2020: attention, la partie en rouge que j'ai rajouté n'est que pour la version RPI 2/3 sans bouton de stop. Pour le GPI Case on utilisera plutôt le script précédemment présenté pour gérer le backup lors du shutdown : "recalbox_SafeShutdown_gpi.py")

Edit 23/02/2020: mise à jour du script suivant pour mieux gérer en cas de non connexion à internet (j'essai 3 fois et j'arrête pour ne pas bloquer l'arrêt du système)


#!/bin/bash
case "$1" in
start)
COUNTER=0
# check that is online to run the first sync command
while [ $COUNTER -lt 3 ]
do
        if [[ "$(ping -c 1 8.8.8.8 | grep ' 0% packet loss' )" == "" ]]; then
                echo "Internet isn't present"
        else
                echo "Internet is present"
                # Run the command that will execute only once at the start
                /usr/bin/rclone copy remote:saves /recalbox/share/saves/ &
                wait $!
                break
        fi
        COUNTER= [$COUNTER +1]
done
;;

stop)
COUNTER=0
# check that is online to run the first sync command while [ $COUNTER -lt 3 ] do if [[ "$(ping -c 1 8.8.8.8 | grep ' 0% packet loss' )" == "" ]]; then echo "Internet isn't present" else echo "Internet is present" # Run the command that will execute only once at the start /usr/bin/rclone copy /recalbox/share/saves/ remote:saves & wait $! break fi
        COUNTER= [$COUNTER +1]
done
;; restart|reload) ;; *)
# by default for standard "start" without parameter
COUNTER=0
# check that is online to run the first sync command
while [ $COUNTER -lt 3 ]
do
        if [[ "$(ping -c 1 8.8.8.8 | grep ' 0% packet loss' )" == "" ]]; then
                echo "Internet isn't present"
        else
                echo "Internet is present"
                # Run the command that will execute only once at the start
                /usr/bin/rclone copy remote:saves /recalbox/share/saves/ &
                wait $!
                break
        fi
        COUNTER= [$COUNTER +1]
done

esac

exit $?


Puis il va falloir le rendre executable:
chmod +x S99sync
Et ensuite pour que cela marche au boot, il faut copier la conf de rclone dans le repertoire racine ainsi:
mkdir /.config
cd /.config
mkdir rclone
cp -i /recalbox/share/system/.config/rclone/rclone.conf /.config/rclone
Et on peut rebooter mais via le bouton du GPi Case de préférence ;-).

Conclusion:
Ainsi vous avez vos sauvegardes dans le cloud, recalbox va démarrer plus ou moins vite maintenant parce qu'il devra checker le réseau avant et il faudra backuper des fichiers ou les restaurer. Cela reste raisonable, j'ai vu au maximum 10 à 15 secondes de plus à l'arrêt et au démarrage.

Et le jour où j'aurai mon panel arcade sous recalbox, je pourrais commencer une partie sur le panel et finir sur le GPi Case et vice-versa, magique ! non ?!

Enjoy ! ;-) (dans mon cas j'ai mis 3 soirées et un samedi matin pour finir, je suis lent et j'ai du comprendre des choses et debugguer les différents codes pour finaliser, j'espère que pour vous cela durera au max une soirée ;-)

P.S: j'ai essayé de le faire cet article surtout pour le refaire par la suite sur ma recalbox de mon futur panel ;-), mais il faudra peut être aussi pouvoir déclencher pas que sur le poweroff/poweron mais voir aussi avec ma domotique et Alexa ;-)

4 commentaires :

  1. Bon je viens de revalider mon tuto ;-) Je viens de le refaire sur la version 6.1 release de recalbox dispo ici : https://archive.recalbox.com/

    RépondreSupprimer
  2. Encore... je viens de revalider mon tuto sur un RPI 3B+ en recalbox 6.1.1 ;-) J'ai fait un edit en rouge parce que j'ai trouvé un problème avec chmod dans le répertoire du user root. Mais rien de grave, je valide ;-)

    RépondreSupprimer
  3. Oups attention, la restauration des sauvegardes du cloud vers recalbox fonctionne bien avec le Raspberry Pi 3 B+ mais pas dans l'autre sens, j'ai un problème pour faire le backup, je debug cela demain, désolé je suis fatigué ce soir ;-(

    RépondreSupprimer
  4. Oups, je suis stupide... j'ai modifié le safe shutdown du GPI case dans le cas de la version RPI 3B+ ... à moi de trouver une solution pour ce case... je ferais un update de l'article pour ;-)

    RépondreSupprimer