diff --git a/.gitignore b/.gitignore index c8dc066..a5d5c0a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .profile +www/LOVELand/jukebox/prefs +www/LOVELand/jukebox/albumart zen/tools/scraping/transiscope/transiscope.json diff --git a/doc/images/1.point_zen.jpg b/doc/images/1.point_zen.jpg new file mode 100644 index 0000000..016af3a Binary files /dev/null and b/doc/images/1.point_zen.jpg differ diff --git a/doc/images/Astroport_Subscribe_Notify.png b/doc/images/Astroport_Subscribe_Notify.png new file mode 100644 index 0000000..cf0d217 Binary files /dev/null and b/doc/images/Astroport_Subscribe_Notify.png differ diff --git a/doc/images/Astroport_Zen_Economy_USE.png b/doc/images/Astroport_Zen_Economy_USE.png new file mode 100644 index 0000000..0128c0d Binary files /dev/null and b/doc/images/Astroport_Zen_Economy_USE.png differ diff --git a/doc/images/Swarm0.jpg b/doc/images/Swarm0.jpg new file mode 100644 index 0000000..c358b9b Binary files /dev/null and b/doc/images/Swarm0.jpg differ diff --git a/doc/images/ZenTAG_ipfs_ipns_swarm_grep_awk_DB.png b/doc/images/ZenTAG_ipfs_ipns_swarm_grep_awk_DB.png new file mode 100644 index 0000000..61c1e4e Binary files /dev/null and b/doc/images/ZenTAG_ipfs_ipns_swarm_grep_awk_DB.png differ diff --git a/doc/images/ZenTag_Existence.png b/doc/images/ZenTag_Existence.png new file mode 100644 index 0000000..aedab47 Binary files /dev/null and b/doc/images/ZenTag_Existence.png differ diff --git a/doc/images/code_weaving_bakery.jpg b/doc/images/code_weaving_bakery.jpg new file mode 100644 index 0000000..90cf4a8 Binary files /dev/null and b/doc/images/code_weaving_bakery.jpg differ diff --git a/doc/images/cropped-zenroom_only-logo-150x150.png b/doc/images/cropped-zenroom_only-logo-150x150.png new file mode 100644 index 0000000..fb0109e Binary files /dev/null and b/doc/images/cropped-zenroom_only-logo-150x150.png differ diff --git a/doc/images/decode-os_only-logo-150x150.png b/doc/images/decode-os_only-logo-150x150.png new file mode 100644 index 0000000..c0287de Binary files /dev/null and b/doc/images/decode-os_only-logo-150x150.png differ diff --git a/doc/images/devuan-emblem-150x150.png b/doc/images/devuan-emblem-150x150.png new file mode 100644 index 0000000..027738a Binary files /dev/null and b/doc/images/devuan-emblem-150x150.png differ diff --git a/doc/images/freecoin_logo_square-150x150.png b/doc/images/freecoin_logo_square-150x150.png new file mode 100644 index 0000000..3770371 Binary files /dev/null and b/doc/images/freecoin_logo_square-150x150.png differ diff --git a/doc/images/swlogo.png b/doc/images/swlogo.png new file mode 100644 index 0000000..9cc0eda Binary files /dev/null and b/doc/images/swlogo.png differ diff --git a/doc/images/zenbridge_only-logo-150x150.png b/doc/images/zenbridge_only-logo-150x150.png new file mode 100644 index 0000000..007270d Binary files /dev/null and b/doc/images/zenbridge_only-logo-150x150.png differ diff --git a/www/LOVELand/jukebox/prefs/collection.sq3 b/www/LOVELand/jukebox/prefs/collection.sq3 deleted file mode 100755 index c9b0766..0000000 Binary files a/www/LOVELand/jukebox/prefs/collection.sq3 and /dev/null differ diff --git a/www/LOVELand/jukebox/prefs/prefs.var b/www/LOVELand/jukebox/prefs/prefs.var deleted file mode 100755 index de47dbd..0000000 --- a/www/LOVELand/jukebox/prefs/prefs.var +++ /dev/null @@ -1 +0,0 @@ -a:135:{s:24:"music_directory_albumart";s:0:"";s:10:"mysql_host";s:9:"localhost";s:14:"mysql_database";s:7:"romprdb";s:10:"mysql_user";s:5:"rompr";s:14:"mysql_password";s:11:"romprdbpass";s:10:"mysql_port";s:4:"3306";s:10:"proxy_host";s:0:"";s:10:"proxy_user";s:0:"";s:14:"proxy_password";s:0:"";s:14:"sortbycomposer";b:0;s:13:"composergenre";b:0;s:17:"composergenrename";a:1:{i:0;s:9:"Classical";}s:16:"preferlocalfiles";b:0;s:25:"mopidy_collection_folders";a:3:{i:0;s:17:"Spotify Playlists";i:1;s:11:"Local media";i:2;s:16:"SoundCloud/Liked";}s:19:"lastfm_country_code";s:2:"FR";s:15:"country_userset";b:1;s:13:"debug_enabled";i:0;s:14:"custom_logfile";s:0:"";s:16:"cleanalbumimages";b:1;s:17:"do_not_show_prefs";b:0;s:24:"load_plugins_at_loadtime";b:0;s:21:"beets_server_location";s:0:"";s:10:"multihosts";O:8:"stdClass":1:{s:7:"Default";O:8:"stdClass":6:{s:4:"host";s:9:"localhost";s:4:"port";s:4:"6600";s:8:"password";s:0:"";s:6:"socket";s:0:"";s:12:"mopidy_slave";b:0;s:11:"radioparams";O:8:"stdClass":4:{s:9:"radiomode";s:0:"";s:10:"radioparam";s:0:"";s:11:"radiomaster";i:1589517825735;s:12:"radioconsume";i:0;}}}s:8:"dev_mode";b:0;s:9:"live_mode";b:0;s:23:"collection_load_timeout";i:3600000;s:20:"smartradio_chunksize";i:5;s:19:"linkchecker_nextrun";d:1589827498644;s:21:"linkchecker_isrunning";b:0;s:21:"linkchecker_frequency";i:604800000;s:20:"linkchecker_polltime";i:5000;s:19:"audiobook_directory";s:0:"";s:17:"collection_player";s:3:"mpd";s:15:"snapcast_server";s:0:"";s:13:"snapcast_port";s:4:"1705";s:15:"displaycomposer";b:1;s:14:"artistsatstart";a:2:{i:0;s:15:"Various Artists";i:1;s:11:"Soundtracks";}s:14:"nosortprefixes";a:1:{i:0;s:3:"The";}s:16:"sortcollectionby";s:6:"artist";s:17:"showartistbanners";b:1;s:14:"google_api_key";s:0:"";s:23:"google_search_engine_id";s:0:"";s:22:"sync_lastfm_playcounts";b:0;s:20:"sync_lastfm_at_start";b:0;s:20:"last_lastfm_synctime";i:1589222665;s:25:"lfm_importer_start_offset";i:0;s:24:"lfm_importer_last_import";i:0;s:10:"sortbydate";b:0;s:11:"notvabydate";b:0;s:11:"currenthost";s:7:"Default";s:14:"player_backend";s:3:"mpd";s:15:"collectionrange";i:0;s:11:"lastfm_user";s:0:"";s:18:"lastfm_session_key";s:0:"";s:11:"autotagname";s:0:"";s:10:"tradsearch";b:0;s:17:"lastfm_scrobbling";b:0;s:18:"lastfm_autocorrect";b:0;s:13:"sourceshidden";b:0;s:14:"playlisthidden";b:0;s:10:"infosource";s:6:"lastfm";s:23:"playlistcontrolsvisible";b:0;s:19:"sourceswidthpercent";i:25;s:20:"playlistwidthpercent";i:25;s:11:"downloadart";b:1;s:9:"clickmode";s:6:"double";s:7:"chooser";s:9:"albumlist";s:14:"hide_albumlist";b:0;s:13:"hide_filelist";b:0;s:14:"hide_radiolist";b:0;s:17:"hide_podcastslist";b:0;s:18:"hide_playlistslist";b:0;s:18:"hide_audiobooklist";b:0;s:13:"hide_searcher";b:0;s:11:"hidebrowser";b:0;s:17:"shownupdatewindow";s:0:"";s:15:"scrolltocurrent";b:0;s:14:"alarm_ramptime";i:30;s:16:"alarm_snoozetime";i:8;s:10:"lastfmlang";s:7:"default";s:9:"user_lang";s:2:"en";s:8:"synctags";b:0;s:8:"synclove";b:0;s:13:"synclovevalue";s:1:"5";s:5:"theme";s:10:"fruit-soda";s:9:"icontheme";s:11:"Modern-Dark";s:9:"coversize";s:12:"40-Large.css";s:8:"fontsize";s:13:"04-Grande.css";s:10:"fontfamily";s:10:"Nunito.css";s:25:"collectioncontrolsvisible";b:0;s:16:"displayresultsas";s:10:"collection";s:18:"crossfade_duration";i:5;s:15:"newradiocountry";s:12:"countries/GB";s:24:"search_limit_limitsearch";b:0;s:15:"scrobblepercent";i:50;s:15:"updateeverytime";b:0;s:16:"fullbiobydefault";b:1;s:21:"mopidy_search_domains";a:2:{i:0;s:5:"local";i:1;s:7:"spotify";}s:20:"mopidy_radio_domains";a:2:{i:0;s:5:"local";i:1;s:7:"spotify";}s:14:"outputsvisible";b:0;s:16:"wheelscrollspeed";s:3:"150";s:20:"searchcollectiononly";b:0;s:20:"displayremainingtime";b:1;s:12:"cdplayermode";b:0;s:21:"auto_discovembobulate";b:0;s:13:"ratman_sortby";s:6:"Rating";s:18:"ratman_showletters";b:0;s:9:"sleeptime";i:30;s:7:"sleepon";b:0;s:20:"advanced_search_open";b:0;s:14:"sortwishlistby";s:6:"artist";s:18:"player_in_titlebar";b:0;s:21:"communityradiocountry";s:14:"united kingdom";s:22:"communityradiolanguage";s:0:"";s:17:"communityradiotag";s:0:"";s:20:"communityradiolistby";s:7:"country";s:21:"communityradioorderby";s:4:"name";s:10:"browser_id";N;s:13:"playlistswipe";b:1;s:22:"podcastcontrolsvisible";b:1;s:28:"default_podcast_display_mode";i:0;s:28:"default_podcast_refresh_mode";i:4;s:25:"default_podcast_sort_mode";i:0;s:30:"podcast_mark_new_as_unlistened";b:0;s:24:"use_albumart_in_playlist";b:1;s:19:"podcast_sort_levels";i:4;s:14:"podcast_sort_0";s:5:"Title";s:14:"podcast_sort_1";s:6:"Artist";s:14:"podcast_sort_2";s:8:"Category";s:14:"podcast_sort_3";s:3:"new";s:10:"bgimgparms";O:8:"stdClass":1:{s:5:"dummy";s:4:"baby";}s:6:"alarms";a:0:{}s:15:"collection_type";s:6:"sqlite";s:10:"test_width";i:1600;s:11:"test_height";i:761;} \ No newline at end of file diff --git a/zen/ssb_INIT.sh b/zen/ssb_INIT.sh index 559e038..bb9fe14 100755 --- a/zen/ssb_INIT.sh +++ b/zen/ssb_INIT.sh @@ -32,17 +32,17 @@ echo ' ' ######################################################################## -# ENVIRONEMENT DETECTION + IPFS ~/.zen/ipfs/.$ipfsnodeid/G1SSB/_info +# ENVIRONEMENT DETECTION + IPFS ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_info ######################################################################## -ipfsnodeid=$(ipfs id -f='\n') -[[ $ipfsnodeid == "" ]] && echo "ERROR missing IPFS Node id !! IPFS is not installed !?" && exit 1 +IPFSNODEID=$(ipfs id -f='\n') +[[ $IPFSNODEID == "" ]] && echo "ERROR missing IPFS Node id !! IPFS is not installed !?" && exit 1 ######################################################################## -ssbpub=$(sbotc whoami 2>/dev/null | jq -r .id) -[[ $ssbpub == "" ]] && echo "ERROR sbotc NOT running !! Please check it..." && exit 1 +WHOAMI=$(sbotc whoami 2>/dev/null | jq -r .id) +[[ $WHOAMI == "" ]] && echo "ERROR sbotc NOT running !! Please check it..." && exit 1 ######################################################################## [[ ! -f ~/.ssb/secret.dunikey ]] && $MY_PATH/tools/secret2dunikey.sh -g1pub=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) -[[ $g1pub == "" ]] && echo "ERROR g1pub empty !! Please check it..." && exit 1 +G1PUB=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) +[[ $G1PUB == "" ]] && echo "ERROR G1PUB empty !! Please check it..." && exit 1 ######################################################################## # GET NODE disk performance. TODO, publish and use as IPFS repartition @@ -54,9 +54,10 @@ sizeAvail=$(df -h ~/ | tail -n 1 | awk '{print $4}') # IPFS LOCAL REPOSITORY for Node Identity G1 + SSB -mkdir -p ~/.zen/ipfs/.$ipfsnodeid/G1SSB +mkdir -p ~/.zen/ipfs/.$IPFSNODEID/G1SSB -# SSB PUBLISH ipfs informations +# SSB PUBLISH EXTRA ipfs informations !! +# This SSB messages are read by ./zen/ssb_IPFS_swarm.sh to build your IPFS #Swarm0 sbotc publish '{"type":"ipfsnodeid","text":"'"$(ipfs id -f='\n')"'"}' sbotc publish '{"type":"ipfstryme","text":"'"$(ipfs id | jq -r .Addresses[] | tail -n 1)"'"}' @@ -65,26 +66,36 @@ sbotc publish '{"type":"ipfstryme","text":"'"$(ipfs id | jq -r .Addresses[] | ta CESIUM="https://g1.data.le-sou.org" # PREPARE title -[[ -f ~/.zen/ipfs/.$ipfsnodeid/G1SSB/_uidna ]] && uidna=$(cat ~/.zen/ipfs/.$ipfsnodeid/G1SSB/_uidna) -[[ ! $uidna ]] && title=$(curl -s ${CESIUM}/user/profile/${g1pub} | jq -r '._source.title') || title="Station $uidna" -[[ $title == null ]] && title="Station $USER@$(cat /etc/hostname)" -city=$(curl -s ${CESIUM}/user/profile/${g1pub} | jq -r '._source.city') -[[ $city != null ]] && title="$title in $city" +# Made from Cesium+ profile tittle and city OR user@hostname +title=$(curl -s ${CESIUM}/user/profile/${G1PUB} | jq -r '._source.title') +[[ -f ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna ]] && uidna=$(cat ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna) -# REFRESH cesium_geoPoint in SSB feed -geopointlat=$(curl -s ${CESIUM}/user/profile/${g1pub} | jq '._source.geoPoint.lat') +# Put in .$IPFSNODEID INDEX: _g1.uidna & _g1.cesium_name (used by Minetest flavour and others) +[[ $uidna ]] && echo "$uidna" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.uidna +[[ $title ]] && echo "$title" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.cesium_name + +[[ $uidna ]] && [[ "$title" == "null" ]] && title="Station $uidna" +[[ "$title" == "null" ]] && title="Station $USER@$(cat /etc/hostname)" + +city=$(curl -s ${CESIUM}/user/profile/${G1PUB} | jq -r '._source.city') +[[ "$city" != "null" ]] && title="$title in $city" + + +# ADD "cesium_geoPoint.lat" AND "cesium_geoPoint.lon" messages in SSB feed +# This way any SSB account is connected to its Cesium+ Geolocalisation. +geopointlat=$(curl -s ${CESIUM}/user/profile/${G1PUB} | jq '._source.geoPoint.lat') [[ $geopointlat != null ]] && sbotc publish '{"type":"cesium_geoPoint.lat","text":"'"$geopointlat"'"}' -geopointlon=$(curl -s ${CESIUM}/user/profile/${g1pub} | jq '._source.geoPoint.lon') +geopointlon=$(curl -s ${CESIUM}/user/profile/${G1PUB} | jq '._source.geoPoint.lon') [[ $geopointlon != null ]] && sbotc publish '{"type":"cesium_geoPoint.lon","text":"'"$geopointlon"'"}' # REFRESH Cesium+ Avatar image -curl -s ${CESIUM}/user/profile/${g1pub} | jq -r '._source.avatar._content' | base64 -d > "/tmp/_g1.avatar.png" +curl -s ${CESIUM}/user/profile/${G1PUB} | jq -r '._source.avatar._content' | base64 -d > "/tmp/_g1.avatar.png" ## PUBLISH ABOUT MESSAGE ############################################## -# IF AVATAR not png take IMAGE of G1 wallet QRCode -qrencode -s 5 -o ~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.qrcode.png "$g1pub" -imagefile=~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.qrcode.png +# PICTURE: IF AVATAR not png take IMAGE of G1 wallet QRCode +qrencode -s 5 -o ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.qrcode.png "$G1PUB" +imagefile=~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.qrcode.png qrname=${imagefile##*/} qrid="$(sbotc blobs.add < $imagefile)" qrtype="$(file -b --mime-type $imagefile)" @@ -97,10 +108,10 @@ else echo "AVATAR FOUND" imagefile=/tmp/_g1.avatar.png # PUBLISH AVATAR TO IPFS - cp /tmp/_g1.avatar.png ~/.zen/ipfs/.$ipfsnodeid/G1SSB/ + cp /tmp/_g1.avatar.png ~/.zen/ipfs/.$IPFSNODEID/G1SSB/ fi -# Prepare QRCode File for SSB +# Prepare QRCode File for SSB (add to blobs) name=${imagefile##*/} id="$(sbotc blobs.add < $imagefile)" type="$(file -b --mime-type $imagefile)" @@ -111,7 +122,7 @@ echo " /\ |_ _ _|_ /--\|_)(_)|_||_ : PUBLISH to SSB feed... -$ssbpub +$WHOAMI $title $imagefile $id : $type : $size bits @@ -128,7 +139,7 @@ fi export LC_ALL=C.UTF-8 #attipix export LANG=C.UTF-8 #attipix DUNITERNODE=$($MY_PATH/tools/duniter_getnode.sh) -[[ $DUNITERNODE ]] && g1balance=$(silkaj -p $DUNITERNODE balance $g1pub 2>&1) || g1balance=$(silkaj balance $g1pub 2>&1) +[[ $DUNITERNODE ]] && g1balance=$(silkaj -p $DUNITERNODE balance $G1PUB 2>&1) || g1balance=$(silkaj balance $G1PUB 2>&1) silkajQuantitativeAmountPattern='Total\sQuantitative\s+=\s+(.*)\s+Ğ1' if [[ $g1balance =~ $silkajQuantitativeAmountPattern ]] then @@ -138,9 +149,9 @@ else fi # OLD ssb-server publish -# sbot publish --type about --about $ssbpub --description "[Astroport Node](https://astroport.com) [$ipfsnodeid](http://localhost:8080/ipns/$ipfsnodeid) - Wallet $g1pub - $diskperf" --name "$title" --image "$id" +# sbot publish --type about --about $WHOAMI --description "[Astroport Node](https://astroport.com) [$IPFSNODEID](http://localhost:8080/ipns/$IPFSNODEID) - Wallet $G1PUB - $diskperf" --name "$title" --image "$id" # NEW sbotc publish to oasis -sbotc publish "{\"type\":\"about\",\"about\":\"$ssbpub\",\"description\":\"![QRCode]($qrid)\\n[Astroport #Swarm0 DEV Station](https://astroport.com)\\n - [Gchange](http://$nodename/gchange/#/app/wot/$g1pub) \\n - [Cesium - $myJune -](https://$nodename/cesium/#/app/wot/$g1pub/) \\n IPFS NODE ID : [$ipfsnodeid](http://localhost:8080/ipns/$ipfsnodeid) \\n DISK: $sizeAvail = $diskperf \\n - [LOVE Land entrance](http://$nodename/) \\n\",\"name\":\"$title\",\"image\":\"$id\"}" +sbotc publish "{\"type\":\"about\",\"about\":\"$WHOAMI\",\"description\":\"![QRCode]($qrid)\\n[Astroport #Swarm0 DEV Station](https://astroport.com)\\n - [Gchange](http://$nodename/gchange/#/app/wot/$G1PUB) \\n - [Cesium - $myJune -](https://$nodename/cesium/#/app/wot/$G1PUB/) \\n IPFS NODE ID : [$IPFSNODEID](http://localhost:8080/ipns/$IPFSNODEID) \\n DISK: $sizeAvail = $diskperf \\n - [LOVE Land entrance](http://$nodename/) \\n\",\"name\":\"$title\",\"image\":\"$id\"}" # SSB PUBLISH G1 wallet silkaj balance @@ -161,7 +172,7 @@ echo " $g1balance -~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.qrcode.png +~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.qrcode.png -- sbotc publish -- " @@ -171,8 +182,8 @@ if [[ ! $(file "/tmp/_g1.avatar.png" | grep 'PNG') ]]; then # sbotc publish '{"type":"post","text":"![QRCode]('"$qrid"')\n Scan QRCode with [Cesium](https://cesium.app).\n Thank you\n ONE :heart:","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":'"$size"',"type":"'"$type"'"}]}' else # Publish only if new avatar - if [[ $(diff /tmp/_g1.avatar.png ~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.avatar.png) ]]; then - sbotc publish '{"type":"post","text":"![Cesium Avatar]('"$id"')\n from my Wallet [Cesium](https://demo.cesium.app/#/app/wot/'"$g1pub"') '"$g1pub"'","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":'"$size"',"type":"'"$type"'"}]}' + if [[ $(diff /tmp/_g1.avatar.png ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.avatar.png) ]]; then + sbotc publish '{"type":"post","text":"![Cesium Avatar]('"$id"')\n from my Wallet [Cesium](https://demo.cesium.app/#/app/wot/'"$G1PUB"') '"$G1PUB"'","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":'"$size"',"type":"'"$type"'"}]}' fi fi @@ -184,13 +195,12 @@ ___ _ _ __ _|_| | __) (_|(_|(_| ~/.zen/ipfs -ipfs ls /ipns/$ipfsnodeid +ipfs ls /ipns/$IPFSNODEID " # COPY NODE G1SSB ID to IPFS -curl -s ${CESIUM}/user/profile/${g1pub} | jq -r '._source.title' > ~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.cesium_name -echo "$ssbpub" > ~/.zen/ipfs/.$ipfsnodeid/G1SSB/_ssb.whoami -echo "$g1pub" > ~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.pubkey +echo "$WHOAMI" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_ssb.whoami +echo "$G1PUB" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.pubkey # IPFS Node PUBLISH Adresses so Pub can become bootstrap for ${g1author} ipfs id | jq -r .Addresses[] > ~/.zen/ipfs/.${ipfsnodeid}/Addresses @@ -204,7 +214,7 @@ IWALLETS=$(ipfs add -rHq ~/.zen/ipfs | tail -n 1) NODEIPNS=$(ipfs name publish --allow-offline --quieter /ipfs/$IWALLETS) echo " -ipfs ls /ipns/$ipfsnodeid/.$ipfsnodeid/ +ipfs ls /ipns/$IPFSNODEID/.$IPFSNODEID/ _ _ _ _ _ (_)_ ____ _(_) |_ __ _| |_(_) ___ _ __ | | _ \ \ / / | __/ _| | __| |/ _ \| _ \