From 9369e20df4ad0080c47c823ea33d48d20c3f7f73 Mon Sep 17 00:00:00 2001 From: Bruno Tavares Date: Tue, 29 Jan 2019 01:01:00 +0000 Subject: [PATCH] Documentation for media center setup This commit adds documentation on how to use LibreElec to turn a RPi into a media center + a download center and even a PIHole DNS filter. At least a PI3 for this amount of things running on it. --- docs/mediacenter.md | 251 ++++++++++++++++++++++++++++++++++++++++++++ docs/secrets.md | 8 ++ secrets/env.sh | Bin 3387 -> 3975 bytes 3 files changed, 259 insertions(+) create mode 100644 docs/mediacenter.md create mode 100644 docs/secrets.md diff --git a/docs/mediacenter.md b/docs/mediacenter.md new file mode 100644 index 0000000..432dce1 --- /dev/null +++ b/docs/mediacenter.md @@ -0,0 +1,251 @@ +# Media Center Setup + +Given the amount of things running, a Pi3 is ideal. It **will** struggle to run it all, specially as the USB and network use a single bus. +Using torrents (which demands a lot of network) writing to the USB disk (which shares the network bus) **will** make the Pi freeze sometimes. + +Just remove the power cable, and restart as necessary. +Maybe eperiment: celeron ultratop small form PC, Rock64 or Odroid would run them better + +Tested on Libreelec 8 and 9. + +## Windows Setup + +Windows DOES come with Zeroconf/Avahi/Bonjour/mDNS implementatio on recent versions, but I've noticed they don't quite match some avahi versions and fail to find some devices from time to time. Also, some apps would work with the mDNS hostname, while others would ignore it. + +It was more reliable to use iTunes Bonjour implementation. Here is the trick: + +- Search for iTunes installer (.msi version, not the app store version) + - Go to iTunes page on apple.com + - Select Download + - Avoid the recomendation to use the appstore + - Scroll a bit down + - Select: Other versions - Windows + - The button now should point to the installer +- Unzip the installer +- Install only the Bonjour64 + +## Kodi setup + +- Flash Install LibreElec + - Simplest flashing tool: Balena Etcher +- Boot the Pi + +Kodi should work out of the box with the remote control of a modern TV (couple of years old). +If it doesn't work on first boot, it is very likely be caused by bad cabling. It is possible that a cable supports 1080 transmission, but fails to transmit CEC information. Good HDMI cables often come written CEC-compliant or Ethernet or something on these lines on the cable itself, on small white letters. + +A good chance of working is HDAMI 1.4 cables. + +On the startup, turn on Samba Sharing and SSH services. +Configure an static IP if you meant to use it as a PiHole instance as well. + +It might be necessery a couple for restarts until the NTP sets the time, and Addons are updated. +Check if the wired or network is connecting on startup. It might need to toggle the autoconnect toggle. + +### Addons + +You might be interested in adding a Subtitle provider. There is Legendas.TV and Subscene providers, and a AutoSub service available. + +Install Docker, which will be our main tool to run services on Kodi. + +### Network + +To make use of Zerotier, and have a p2p VPN access to the device, it is important to ask the network manager to not manage the zt interface +(as of I'm writing). + +```sh +cp /etc/connman/main.conf /storage/.config/connman_main.conf +## Edit the file to ignore zt interfaces and reboot +## https://github.com/LibreELEC/LibreELEC.tv/commit/7cee2a095cb6c9126971afc58c145aad473fe7d7 +## This will not be necessary in future releases +``` + +If you intend to use PiHole, you need to manually setup the ip, make it static. +You may do this over ssh using `connmanctl`. + +### Transmission + +Edit settings to enable other devices to connect: + +```sh +docker stop transmission + +## Enable RPC password +## add a username +## add a plaintext password (will be hashed automatically later) +## Add RPC host list and enable: +### Eg: 127.0.0.1,192.168.0.* +``` + +### Sonarr/Radarr + +- Add Transmission as client +- Enable rename +- :warning: Don't add series with Monitor all by default, it is very heavy to update a lot of seasons at once with a Pi +- Connect: Kodi +- Add indexers using Jackett + +## File Sharing + +Kodi comes with SMB service available, but [due to limitations on VLC (libdsm)](https://github.com/videolabs/libdsm/issues/110), both Desktop and Android, it is not possible to use use newer versions of SMB protocol. + +This means we need to enable the CIFS/SMB 1.0 protocol on Kodi, even tho that is not that much secure. +What we do for convenience. + +Head to Settings, and change the Minimum and Maxium to SMB 1. + +That also means that we need to enable SMB/CIFS 1.0 protocol on Windows 10. +Since April 2018, Windows have disabled SMB1 as it is not secure. But you have still the option to enable the client and server discovery. [Better documented here](https://support.microsoft.com/pt-br/help/2696547/how-to-detect-enable-and-disable-smbv1-smbv2-and-smbv3-in-windows-and) + +After enabling (there might be some restarts needed), you should be able to acess the files on the network folder. + +## PiHole + +PiHole is capable to run from Docker. After giving the node a static ip in the network, start it and configure the DHCP server on the router. + +## Scripted setup + +```sh +DOWNLOADS=/storage//Downloads +MOVIES=/storage//Series +SERIES=/storage//Movies +SYNCTHING=/storage//Syncthing +USERNAME= +PASSWORD= +CONFIG_FOLDER=/storage +TZ=America/Sao_Paulo + +pihole() { + #IP= + #IPv6= + WEBPASSWORD="$PASSWORD" + + IP_LOOKUP="$(ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}')" + IPv6_LOOKUP="$(ip -6 route get 2001:4860:4860::8888 | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}')" + IP="${IP:-$IP_LOOKUP}" # use $IP, if set, otherwise IP_LOOKUP + IPv6="${IPv6:-$IPv6_LOOKUP}" # use $IPv6, if set, otherwise IP_LOOKUP + + echo "### Make sure your IPs are correct, hard code ServerIP ENV VARs if necessary\nIP: ${IP}\nIPv6: ${IPv6}" + docker run -d \ + --name pihole-ng \ + -p 53:53/tcp -p 53:53/udp \ + -p 67:67/udp \ + -p 80:80 \ + -p 443:443 \ + -v "${CONFIG_FOLDER}/pihole/pihole:/etc/pihole/" \ + -v "${CONFIG_FOLDER}/pihole/dnsmasq.d/:/etc/dnsmasq.d/" \ + -e ServerIP="${IP}" \ + -e ServerIPv6="${IPv6}" \ + -e IPv6=true \ + -e WEBPASSWORD \ + -e TZ \ + -e DNS1=1.0.0.1 \ + -e DNS2=8.8.8.8 \ + --restart=unless-stopped \ + --cap-add=NET_ADMIN \ + --dns=127.0.0.1 --dns=1.0.0.1 \ + pihole/pihole:latest + + echo -n "Your password for https://${IP}/admin/ is " + docker logs pihole-ng 2>/dev/null | grep 'password' +} + +syncthing() { + docker run --name=syncthing \ + -e GUI_USERNAME="$USERNAME" \ + -e GUI_PASSWORD_PLAIN="$PASSWORD" \ + -e UID=0 -e GID=0 \ + -v "${CONFIG_FOLDER}/syncthing/:/syncthing/config" \ + -v "${SYNCTHING}/:/syncthing/data" \ + --network=host \ + --restart=unless-stopped -d \ + funkyfuture/rpi-syncthing +} + +transmission() { + docker run --name=transmission \ + -v /storage/transmission:/config \ + -v "${DOWNLOADS}/:/downloads" \ + -v "${DOWNLOADS}/watch:/watch" \ + -e PGID=0 -e PUID=0 \ + -e TZ \ + -p 9091:9091 \ + --net=host \ + --restart=unless-stopped -d \ + lsioarmhf/transmission +} + +sonarr() { + docker run \ + --name sonarr \ + -p 8989:8989 \ + -e PUID=0 -e PGID=0 \ + -e TZ \ + -v "${CONFIG_FOLDER}/sonarr:/config" \ + -v "${SERIES}/:/tv" \ + -v "${DOWNLOADS}:/downloads" \ + --net=host \ + --restart=unless-stopped -d \ + lsioarmhf/sonarr +} + +jackett() { + docker run --name=jackett \ + -v "${CONFIG_FOLDER}/jackett:/config" \ + -v "${DOWNLOADS}/:/downloads" \ + -e PGID=0 -e PUID=0 \ + -e TZ \ + -p 9117:9117 \ + --net=host \ + --restart=unless-stopped -d \ + lsioarmhf/jackett +} + +radarr() { + docker run --name=radarr \ + -v "${CONFIG_FOLDER}/radarr:/config" \ + -v "${DOWNLOADS}/:/downloads" \ + -v "${MOVIES}/:/movies" \ + -e PGID=0 -e PUID=0 \ + -e TZ \ + -p 7878:7878 \ + --net=host \ + --restart=unless-stopped -d \ + lsioarmhf/radarr +} + +bazarr() { + docker run --name=bazarr \ + -v "${CONFIG_FOLDER}/bazarr:/config" \ + -v "${MOVIES}/:/movies" \ + -v "${SERIES}/:/tv" \ + -e PGID=0 -e PUID=0 \ + -e TZ \ + -p 6767:6767 \ + --net=host \ + --restart=unless-stopped -d \ + lsioarmhf/bazarr +} + +zerotier() { + docker run --name zerotier \ + --device=/dev/net/tun \ + --net=host \ + --cap-add=NET_ADMIN \ + --cap-add=SYS_ADMIN \ + -v "${CONFIG_FOLDER}/zerotier-one:/var/lib/zerotier-one" \ + --restart=unless-stopped -d \ + bltavares/zerotier +} + +zerotier-join() { + docker exec zerotier zerotier-cli join $1 +} + +## libreelec: defualt hostname +# kodi: http://libreelec.local:8080/ +# transmission: http://libreelec.local:9091/ +# sonarr: http://libreelec.local:8989/ +# jackett: http://libreelec.local:9117/ +# radarr: http://libreelec.local:7878/ +# bazarr: http://libreelec.local:6767/ +``` \ No newline at end of file diff --git a/docs/secrets.md b/docs/secrets.md new file mode 100644 index 0000000..56e75be --- /dev/null +++ b/docs/secrets.md @@ -0,0 +1,8 @@ +# Secrets + +Secrets are stored using `git-crypt`. + +```sh +# unlock +git crypt unlock <(cat | base64 -d) +``` \ No newline at end of file diff --git a/secrets/env.sh b/secrets/env.sh index 1a490d3d2189fb6e5e79f8af15a8897f7a5100ee..a1560b202b4f40acc1780d4fa6fcf074f34ee37f 100644 GIT binary patch literal 3975 zcmV;24|wnZM@dveQdv+`0D(pFM%Bg40b$jfs{{W8cR$9h>F1#p^4l8kIm!RW|G#n8 z9dN>6F81A(2Mf_`_a_hEyD6eOVAf_E&(CvWlTM)W(%Ql$P4^T@vp?A(OM{k_z6y_- zg0GGO2*gSCwiq}EavE(e?kVLbJCn6h%X}D0?AK)iLvD`AXIx>ei(eccj{;$fOkLn< zdM8S9(ZNN^%|*A1=4KI$Q}SW%tlh4ghuc;1OFiS_z}kG9e=TevV(suFD@)F8PD;}B z!ApV-NQ#dz8fr8OxSgw>XIC%=mRFFm1z_wz7A?K1DqvGN9`tot1p;tOgBdNhr%)Yz zoYJ;TmkKhWW?~}tk-9;|@&%EV_;P|wMX6;(*+`SNNg0CW;vv;RODVhAq`=FL!)Ssn zyQ&hL`FW_nTAFFskEpsZr!ecL?QVcZ!G*;Bh(-5P6|0>nK!EiQqOh&f`sNF}_c7Q# zuMBbdppB$!oW3riE zJ-bjdn=$FtqPBbRk_FB(U|QyKvR zGp(Bdf9Sr`uGjqLYJNNTrN#;C`Yc%N-tY{T1dhQu1Gjm#d`tM}hm-|Lk)Lu@?+)nQ z`a&%l`30GPTrfftk!ysqH(1pd$Zt_0k+qvIG<>bP+XAF=xjiS!-+0cu^&_7QaJ5ZD zd7IIdboVYRSMeqp`P^a3NjOJL+F>eNi46)A6-*&$^VQ7kkwO8WbETm9M z^^ta~Obp8%#%|;N>V3)5lq&&AjH|KDES(@MJ<9K413>U*knEG+czP-$S9RBEQ;CTX z@SwK+hJDR^$Ivxb(ayu&m~JP^vHar-n=YCq_97U??K&+K*mNeSibK-?=#4iMmNg7ora6ma{4kCB|XxhQG+5) ze=5quwHtx$r#x!pa+*rERjKkS>!AFUz5G|4I#>DL%94s`>&NwoKvfWbc7@AKSU^k0 zi_4^(a}^YUYpXhfHj1cl0dqC$9PiMx`{Kc1Ila-^ibdo8joltP@;UKm*4EEP2chT9 zR)tV&QQMtDJOoS|?`03rslRnVR}w2q)5B{r(c-F3Hn{}Ez(&NmtNIJF(PTekTA1)B z^|OUwod$6@lV*F3!^OW`)7Q?Q9>4p0t+PMeCxWNkP>>*1%#YsRwr^ z&&g;%N!QWm^3_jHhpKEg=PWGX;j*73T-FoG87`?&0&0EYru!?>u2TNK7{MVdA9O@o zavO@-o6!`1vO%kah_{`j=*4wk$HDXg?afEUU-GkF=;nJ>GX?r$RAM|u`;NxW6%4A$ zs;AC}81wJ{diGHLhLQir6)~)Rug=as@2dEVL&IPayRd~mz2`PFYp=zH|5e$L6ylj! z(^Hb#E%UuBjW;@1(ukP=O+42xpQLjHAiiSL{pp0|ha1=eB!DkD*P@7i3Y+Tg3=(C2 zF(Ck|Sg6 zHQX<+>2PS`mb7hd0!$@+s}#AeYwZQoj`iPPmqnCMvxFh4J|U$?{cc3 zbhZECmQnAGds=eN4vW+uHXUj^BGePC0$OkG+V1b?R&X?V6jOAKCA(DfJA|Kb##`F7 zv^(-!P#04n`%x%dZ&`8o1{T7+IpKFj)`A!u_UlAa=-N;#^H*H6aK|Jh2*H-ow6cEb z{9SyG!_Ku}J+8+leoA&Y;3ERz#lPZp{!t&~Kk$KY#rL0iHyATqm-`u&F6 z)2!yWt8}ZyZFX%^%F4-+BI`Hzov8r+9KPsyYO2F8w|#2g%_-L*6)wrc;r&$WFVDnz zA%^c-GwE~1y>xNc6fB(sGozGA=iAG)p1~n{GrY4B$*=u*}(2#H^$EQJslY$S4qMmx|M>!w=k>>?R;>wgF0^%O|}~e@noQi zX&uHw4$gj;JF9Rm@m7B70zB13$MF$jSTkCKq3DnazT&ORD|x%>=<1d%F+?M4qelDV zYA#ygUjO7va4i>5L)^7KJ^siKQAL|+UW$B>Zz6>aT-vQx1=ZL^y)xU{s?Irn*teRc zHA{~~733G_#uD{=$I_eHF9LA=>qGN#nHV?z9~ZIP!UO82MKG~!-Oa}8vfMsl!WjFr zWnDXrBcysaYp{ak2llNsJzgG$KNlROODfCmTL-=AZxhe2?}mPtp^JO?VLvg|#gVLO z?(IEsyQ@qq6ME`rARB3xQyQXFiEN1<_o_$+Eji+UsqxmZC06K4ohm{gXQ1tCKETh8 zky_tX!vHL?zLYA*aJw@~LaCsTJ6f>Fw!aKHBLKGDH?R6f=hr7O&7|?ltMCl;;eao6 z5h_6P^v`aFhUjV+jG-F4?WLcR1v??2zH8B@jy$MQk0-vi_Kg?U<)5f3$2QKqN%}G& zSv~;VnYl{k=Ae7z@JNI8m)Sq7wve`MR8ryr`Wz!;J_`B$h(N_R4X|fku9FK4RrsY{ zG^w5e0CN(?@pAKUH&&t|;kagY>QPP^YJ8iZ6de;SYS@_xrWVV8#7a!mn;z^ZZP``1 zPZ69#y-AD-KXsPZ+Mp{V9XI3Aa(1mNhO%Z(G)%>jZ2Y=S9O@2qCaJR#{K0kqimbKa z-8L;^rw5M=b~8V{r6i8XIBq7fPj2@bmIbkBmJ#@jraHyXFLhZ0yWu}M)PUnhUe1(> zTXWM}n5&{{!aNfqz8_Q7-o$!;k$#5Oo?(E0I(bWSY^wEM)t)c#@oZ{IXN@0|b@g`I zpFfbMOf(ujBRA2+1>`;VbGX>N_sS`k^<>QCcrYb|P#7Z|rmz$oRP<$6%Btoi1(Vqf zX0KV7CP=lw-vxban+PMX{b@}9i`gBd=HhFWuZ|L@C?=kkP7kOf(M0L@ z%3F4L#5mzgpuHfP9bUgO<96B$mO(M|g`i)#F5du%Xg~x!Dvh+jPli8Tzbyt}@Pxgh zsIYxQn!N?nj=>;1r1?7)RhZ15^C^PByKUs%jz4qDPwRp;vrwRvnn8fx9e ztYU$|jD(z6OJ2Zpu0#|SN`P8Wuz|$>@j`=Bp?f5tLbEuRBb{+>(q|@{damJ)q+4t} z3TT*n)MU-#iRCm-0##g8WMcv{(gm67z!^;^=Z4GsYozTD9Q|Q=?y6Pto>es^_!dox zHxUc%Fd8iH+;i;_ zK5TZs_^IPJHWm~Pkj_Yo13awTZt|OX?bGZL=>M9#={G5iMn3ec(cDqTDDOt7V1`7c z@)Z9`o9!Ralw8>GzraMGOUJa$(!(4**U^woT5Ot%W96&iF}yed4p1AD*T#;BA+)2i zTsW^_5$U`2G492+N2ySO0Keg2XV}TyHf2WZ@x5(e?t0N~S@t#W^LN(Dk58}}a0gc` z)#I2)=Rkc7qV+^pP#5dXB3hj)Cl{Kwqo#_CBjMl2nh-z48k4;)ok|kSS{a5Es3;ap)eNOrZV`CW^JfN;j+ zxJ`nes22lIKcp!B1+%*BBTK7J+)jJ!%P6u=ux-f9xiI1b(F3RR)fuja?Oy6sSHNoD zR-zZ=VbsLPCT}OX%^XDL7$gA{=QW^Mcf)XXRnxof&uvh-pbEzARuOsJnI`A)~*(i8vy literal 3387 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 RGWW&6RwAZxAE9PYXn-HYq9*_V