First commit.
This commit is contained in:
commit
5992d41d52
91
README.md
Normal file
91
README.md
Normal file
@ -0,0 +1,91 @@
|
||||
# media-center-setup
|
||||
|
||||
This is my setup for my media center. Its main purpose is for me to be able to replicate it, in case of need. However, with a few modifications, it can be a great way for you to have a Raspberry Pi with the following features:
|
||||
- an mpd server to stream local music, with an equalizer and snapcast support to stream.
|
||||
- an Airplay capable machine (for Apple Music for example).
|
||||
- a place to stream videos through Samba and NFS.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* An SD card, with the latest 64-bit Raspberry Pi OS installed on it. Use [Raspberry Pi imager](https://www.raspberrypi.com/software/) for that purpose.
|
||||
* A Raspberry Pi, where the firmware has been updated with the possibility of USB booting.
|
||||
|
||||
## First configuration
|
||||
|
||||
* Insert the SD card in the Pi, start up, and SSH to it.
|
||||
* Be sure to be in the wanted distribution target. For instance, I regularly need to be in _testing_, for mpd to be up to date. This can be done my modifying _/etc/apt/sources.list_. I usually type in the explicit name of testing (currently _bookworm_) to be sure where I'm at.
|
||||
* Run `sudo apt update && sudo apt dist-upgrade && sudo apt upgrade` to update the system to the last version we need.
|
||||
* Run `sudo apt install mpd mpc libasound2-plugin-equal shairport-sync snapserver samba nfs-kernel-server vim` to install the needed packages.
|
||||
|
||||
## Copying the SD card data to the USB media
|
||||
|
||||
Please note that this step can be done after the other configurations are performed, as most of the things will be copied.
|
||||
|
||||
* Transfer the utilities folder to your Pi.
|
||||
* Inside, there is a folder called usb-boot. It comes from RonR, a user from the Raspberry Pi forums. You can also [download it here](https://forums.raspberrypi.com/viewtopic.php?t=196778). The version included in this repository is from Feb 28, 2022.
|
||||
* Run `usb-boot.sh`.
|
||||
* You can now remove your SD card and boot the system from the USB media device.
|
||||
|
||||
## Configuring the audio
|
||||
|
||||
* I use a HifiBerry DAC. For that purpose, I need to do the following:
|
||||
* update `/boot/config.txt`
|
||||
* remove `dtparam=audio=on`
|
||||
* add `dtoverlay=hifiberry-dacplusadc` (find what you need on the [documentation](https://www.hifiberry.com/docs/software/configuring-linux-3-18-x/))
|
||||
* while you're on this file, you can also disable things you probably don't need like bluetooth (`dtoverlay=disable-bt`) or WiFi (`dtoverlay=disable-wifi`).
|
||||
* Copy the `/etc/asound.conf` file.
|
||||
* You can use the `equalizer.sh` script to configure the equalizer. A value I like is `74-66-64-66-72-72-66-64-66-74`.
|
||||
|
||||
### Troubleshooting audio
|
||||
|
||||
If you're having trouble with the Raspberry Pi audio, try one of the following things:
|
||||
* `sudo raspi-config` => `1 System Options` => `S2 Audio`.
|
||||
* list your devices with `aplay -l`.
|
||||
* try `speaker-test -c 2`, it should output sound.
|
||||
* you can also try `speaker-test -D hw:<card number>,<device number>`.
|
||||
|
||||
## Configuring mpd
|
||||
|
||||
My music is copied with rsync through my NAS. For that purpose I use a set of scripts, that you would have to modify to suit your needs.
|
||||
|
||||
* Fist, we create a folder where we'll mount to NAS: `mkdir /media/mantis-music`.
|
||||
* Then a folder where to copy the music: `mkdir /home/pi/music`.
|
||||
* Use to script to copy locally the music: `utilities/update-music.sh`.
|
||||
* Copy the mpd configuration file in `/etc/mpd.conf`.
|
||||
* Run `mpc update`, then `mpc ls` to try to play something with `mpc play`.
|
||||
* Test right away, it should output sound if you've rightly configured your audio.
|
||||
|
||||
## Configuring the sharing
|
||||
|
||||
### Samba
|
||||
|
||||
I use Samba to be able to organize my videos through my Windows machine.
|
||||
|
||||
* Edit `/etc/samba/smb.conf` and add the following:
|
||||
```
|
||||
[VideoPi]
|
||||
comment = Videos on Pi
|
||||
path = /home/pi/video
|
||||
read only = no
|
||||
browsable = yes
|
||||
```
|
||||
* Then set up a password for the user pi: `sudo smbpasswd -a pi`
|
||||
* You should be able to connect to it.
|
||||
|
||||
### NFS
|
||||
|
||||
NFS is used to communicate with my Linux machine reading videos through Kodi.
|
||||
|
||||
* Edit `/etc/exports` and add the following line to make it visible to the home network: `/home/pi/video 192.168.1.0/24(rw,all_squash,insecure)`.
|
||||
* You should be able to connect to it.
|
||||
|
||||
## Utilities
|
||||
|
||||
* `equalizer.sh` allows you to modify the equalizer values of Alsamixer.
|
||||
* `update-music.sh` calls the following two scripts to update your music:
|
||||
* `update-music/mount-nfs.sh` mounts the NAS volume.
|
||||
* `update-music/rsync-sync.sh` performs an rsync to copy the music.
|
||||
* `temperature.sh` displays the current Raspberry Pi temperature.
|
||||
* `add-cover.sh` extracts covers from the music files to make a `cover.jpg` file. Useful for old mpd that is not able to read directly into the file.
|
||||
* `video-tools/video-infos.sh` extract infos from the videos to verify if they're readable as needed.
|
||||
* `usb-boot` a set of script [documented here](https://forums.raspberrypi.com/viewtopic.php?t=196778).
|
19
etc/asound.conf
Normal file
19
etc/asound.conf
Normal file
@ -0,0 +1,19 @@
|
||||
pcm.!default {
|
||||
type plug
|
||||
slave.pcm plugequal;
|
||||
}
|
||||
ctl.!default {
|
||||
type hw card 0
|
||||
}
|
||||
ctl.equal {
|
||||
type equal;
|
||||
}
|
||||
pcm.plugequal {
|
||||
type equal;
|
||||
slave.pcm "plughw:0,0";
|
||||
}
|
||||
pcm.equal {
|
||||
type plug;
|
||||
slave.pcm plugequal;
|
||||
}
|
||||
|
11
etc/exports
Normal file
11
etc/exports
Normal file
@ -0,0 +1,11 @@
|
||||
# /etc/exports: the access control list for filesystems which may be exported
|
||||
# to NFS clients. See exports(5).
|
||||
#
|
||||
# Example for NFSv2 and NFSv3:
|
||||
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
|
||||
#
|
||||
# Example for NFSv4:
|
||||
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
|
||||
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
|
||||
#
|
||||
/home/pi/video 192.168.1.0/24(rw,all_squash,insecure)
|
439
etc/mpd.conf
Normal file
439
etc/mpd.conf
Normal file
@ -0,0 +1,439 @@
|
||||
# An example configuration file for MPD.
|
||||
# Read the user manual for documentation: http://www.musicpd.org/doc/user/
|
||||
# or /usr/share/doc/mpd/html/user.html
|
||||
|
||||
|
||||
# Files and directories #######################################################
|
||||
#
|
||||
# This setting controls the top directory which MPD will search to discover the
|
||||
# available audio files and add them to the daemon's online database. This
|
||||
# setting defaults to the XDG directory, otherwise the music directory will be
|
||||
# be disabled and audio files will only be accepted over ipc socket (using
|
||||
# file:// protocol) or streaming files over an accepted protocol.
|
||||
#
|
||||
music_directory "/home/pi/music/punk"
|
||||
#
|
||||
# This setting sets the MPD internal playlist directory. The purpose of this
|
||||
# directory is storage for playlists created by MPD. The server will use
|
||||
# playlist files not created by the server but only if they are in the MPD
|
||||
# format. This setting defaults to playlist saving being disabled.
|
||||
#
|
||||
playlist_directory "/var/lib/mpd/playlists"
|
||||
#
|
||||
# This setting sets the location of the MPD database. This file is used to
|
||||
# load the database at server start up and store the database while the
|
||||
# server is not up. This setting defaults to disabled which will allow
|
||||
# MPD to accept files over ipc socket (using file:// protocol) or streaming
|
||||
# files over an accepted protocol.
|
||||
#
|
||||
db_file "/var/lib/mpd/tag_cache"
|
||||
|
||||
# These settings are the locations for the daemon log files for the daemon.
|
||||
#
|
||||
# The special value "syslog" makes MPD use the local syslog daemon. This
|
||||
# setting defaults to logging to syslog.
|
||||
#
|
||||
# If you use systemd, do not configure a log_file. With systemd, MPD
|
||||
# defaults to the systemd journal, which is fine.
|
||||
#
|
||||
#log_file "/var/log/mpd/mpd.log"
|
||||
|
||||
# This setting sets the location of the file which stores the process ID
|
||||
# for use of mpd --kill and some init scripts. This setting is disabled by
|
||||
# default and the pid file will not be stored.
|
||||
#
|
||||
# If you use systemd, do not configure a pid_file.
|
||||
#
|
||||
#pid_file "/run/mpd/pid"
|
||||
|
||||
# This setting sets the location of the file which contains information about
|
||||
# most variables to get MPD back into the same general shape it was in before
|
||||
# it was brought down. This setting is disabled by default and the server
|
||||
# state will be reset on server start up.
|
||||
#
|
||||
state_file "/var/lib/mpd/state"
|
||||
#
|
||||
# The location of the sticker database. This is a database which
|
||||
# manages dynamic information attached to songs.
|
||||
#
|
||||
sticker_file "/var/lib/mpd/sticker.sql"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
# General music daemon options ################################################
|
||||
#
|
||||
# This setting specifies the user that MPD will run as. MPD should never run as
|
||||
# root and you may use this setting to make MPD change its user ID after
|
||||
# initialization. This setting is disabled by default and MPD is run as the
|
||||
# current user.
|
||||
#
|
||||
user "mpd"
|
||||
#
|
||||
# This setting specifies the group that MPD will run as. If not specified
|
||||
# primary group of user specified with "user" setting will be used (if set).
|
||||
# This is useful if MPD needs to be a member of group such as "audio" to
|
||||
# have permission to use sound card.
|
||||
#
|
||||
#group "nogroup"
|
||||
#
|
||||
# This setting sets the address for the daemon to listen on. Careful attention
|
||||
# should be paid if this is assigned to anything other than the default, any.
|
||||
# This setting can deny access to control of the daemon. Not effective if
|
||||
# systemd socket activation is in use.
|
||||
#
|
||||
# For network
|
||||
bind_to_address "localhost"
|
||||
#
|
||||
# And for Unix Socket
|
||||
#bind_to_address "/run/mpd/socket"
|
||||
#
|
||||
# This setting is the TCP port that is desired for the daemon to get assigned
|
||||
# to.
|
||||
#
|
||||
port "6600"
|
||||
#
|
||||
# Suppress all messages below the given threshold. Use "verbose" for
|
||||
# troubleshooting. Available setting arguments are "notice", "info", "verbose",
|
||||
# "warning" and "error".
|
||||
#
|
||||
log_level "info"
|
||||
#
|
||||
# Setting "restore_paused" to "yes" puts MPD into pause mode instead
|
||||
# of starting playback after startup.
|
||||
#
|
||||
#restore_paused "no"
|
||||
#
|
||||
# This setting enables MPD to create playlists in a format usable by other
|
||||
# music players.
|
||||
#
|
||||
#save_absolute_paths_in_playlists "no"
|
||||
#
|
||||
# This setting defines a list of tag types that will be extracted during the
|
||||
# audio file discovery process. The complete list of possible values can be
|
||||
# found in the user manual.
|
||||
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
|
||||
#
|
||||
# This example just enables the "comment" tag without disabling all
|
||||
# the other supported tags:
|
||||
#metadata_to_use "+comment"
|
||||
#
|
||||
# This setting enables automatic update of MPD's database when files in
|
||||
# music_directory are changed.
|
||||
#
|
||||
auto_update "yes"
|
||||
#
|
||||
# Limit the depth of the directories being watched, 0 means only watch
|
||||
# the music directory itself. There is no limit by default.
|
||||
#
|
||||
#auto_update_depth "3"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
# Symbolic link behavior ######################################################
|
||||
#
|
||||
# If this setting is set to "yes", MPD will discover audio files by following
|
||||
# symbolic links outside of the configured music_directory.
|
||||
#
|
||||
#follow_outside_symlinks "yes"
|
||||
#
|
||||
# If this setting is set to "yes", MPD will discover audio files by following
|
||||
# symbolic links inside of the configured music_directory.
|
||||
#
|
||||
#follow_inside_symlinks "yes"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
# Zeroconf / Avahi Service Discovery ##########################################
|
||||
#
|
||||
# If this setting is set to "yes", service information will be published with
|
||||
# Zeroconf / Avahi.
|
||||
#
|
||||
#zeroconf_enabled "yes"
|
||||
#
|
||||
# The argument to this setting will be the Zeroconf / Avahi unique name for
|
||||
# this MPD server on the network. %h will be replaced with the hostname.
|
||||
#
|
||||
#zeroconf_name "Music Player @ %h"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
# Permissions #################################################################
|
||||
#
|
||||
# If this setting is set, MPD will require password authorization. The password
|
||||
# setting can be specified multiple times for different password profiles.
|
||||
#
|
||||
#password "password@read,add,control,admin"
|
||||
#
|
||||
# This setting specifies the permissions a user has who has not yet logged in.
|
||||
#
|
||||
#default_permissions "read,add,control,admin"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
# Database #######################################################################
|
||||
#
|
||||
# An example of a database section instead of the old 'db_file' setting.
|
||||
# It enables mounting other storages into the music directory.
|
||||
#
|
||||
#database {
|
||||
# plugin "simple"
|
||||
# path "/var/lib/mpd/tag_cache"
|
||||
# cache_directory "/var/lib/mpd/cache"
|
||||
#}
|
||||
#
|
||||
# An example of database config for a satellite setup
|
||||
#
|
||||
#music_directory "nfs://fileserver.local/srv/mp3"
|
||||
#database {
|
||||
# plugin "proxy"
|
||||
# host "other.mpd.host"
|
||||
# port "6600"
|
||||
#}
|
||||
|
||||
# Input #######################################################################
|
||||
#
|
||||
input {
|
||||
plugin "curl"
|
||||
# proxy "proxy.isp.com:8080"
|
||||
# proxy_user "user"
|
||||
# proxy_password "password"
|
||||
}
|
||||
|
||||
# Decoder #####################################################################
|
||||
#
|
||||
|
||||
decoder {
|
||||
plugin "hybrid_dsd"
|
||||
enabled "no"
|
||||
# gapless "no"
|
||||
}
|
||||
decoder {
|
||||
plugin "wildmidi"
|
||||
enabled "no"
|
||||
#config_file "/etc/timidity/timidity.cfg"
|
||||
}
|
||||
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
# Audio Output ################################################################
|
||||
#
|
||||
# MPD supports various audio output types, as well as playing through multiple
|
||||
# audio outputs at the same time, through multiple audio_output settings
|
||||
# blocks. Setting this block is optional, though the server will only attempt
|
||||
# autodetection for one sound card.
|
||||
#
|
||||
# An example of an ALSA output:
|
||||
#
|
||||
#audio_output {
|
||||
# type "alsa"
|
||||
# name "ALSA Base"
|
||||
## device "hw:0,0" # optional
|
||||
# mixer_type "software" # optional
|
||||
## mixer_device "default" # optional
|
||||
## mixer_control "PCM" # optional
|
||||
## mixer_index "0" # optional
|
||||
#}
|
||||
|
||||
audio_output {
|
||||
type "alsa"
|
||||
name "Alsa Equal"
|
||||
device "plug:plugequal"
|
||||
mixer_type "software"
|
||||
}
|
||||
|
||||
audio_output {
|
||||
type "fifo"
|
||||
name "Snapcast"
|
||||
path "/tmp/snapfifo"
|
||||
format "48000:16:2"
|
||||
mixer_type "software"
|
||||
}
|
||||
|
||||
#
|
||||
# An example of an OSS output:
|
||||
#
|
||||
#audio_output {
|
||||
# type "oss"
|
||||
# name "My OSS Device"
|
||||
## device "/dev/dsp" # optional
|
||||
## mixer_type "hardware" # optional
|
||||
## mixer_device "/dev/mixer" # optional
|
||||
## mixer_control "PCM" # optional
|
||||
#}
|
||||
#
|
||||
# An example of a shout output (for streaming to Icecast):
|
||||
#
|
||||
#audio_output {
|
||||
# type "shout"
|
||||
# encoder "vorbis" # optional
|
||||
# name "My Shout Stream"
|
||||
# host "localhost"
|
||||
# port "8000"
|
||||
# mount "/mpd.ogg"
|
||||
# password "hackme"
|
||||
# quality "5.0"
|
||||
# bitrate "128"
|
||||
# format "44100:16:1"
|
||||
## protocol "icecast2" # optional
|
||||
## user "source" # optional
|
||||
## description "My Stream Description" # optional
|
||||
## url "http://example.com" # optional
|
||||
## genre "jazz" # optional
|
||||
## public "no" # optional
|
||||
## timeout "2" # optional
|
||||
## mixer_type "software" # optional
|
||||
#}
|
||||
#
|
||||
# An example of a recorder output:
|
||||
#
|
||||
#audio_output {
|
||||
# type "recorder"
|
||||
# name "My recorder"
|
||||
# encoder "vorbis" # optional, vorbis or lame
|
||||
# path "/var/lib/mpd/recorder/mpd.ogg"
|
||||
## quality "5.0" # do not define if bitrate is defined
|
||||
# bitrate "128" # do not define if quality is defined
|
||||
# format "44100:16:1"
|
||||
#}
|
||||
#
|
||||
# An example of a httpd output (built-in HTTP streaming server):
|
||||
#
|
||||
#audio_output {
|
||||
# type "httpd"
|
||||
# name "My HTTP Stream"
|
||||
# encoder "vorbis" # optional, vorbis or lame
|
||||
# port "8000"
|
||||
# bind_to_address "0.0.0.0" # optional, IPv4 or IPv6
|
||||
## quality "5.0" # do not define if bitrate is defined
|
||||
# bitrate "128" # do not define if quality is defined
|
||||
# format "44100:16:1"
|
||||
# max_clients "0" # optional 0=no limit
|
||||
#}
|
||||
#
|
||||
# An example of a pulseaudio output (streaming to a remote pulseaudio server)
|
||||
# Please see README.Debian if you want mpd to play through the pulseaudio
|
||||
# daemon started as part of your graphical desktop session!
|
||||
#
|
||||
#audio_output {
|
||||
# type "pulse"
|
||||
# name "My Pulse Output"
|
||||
## server "remote_server" # optional
|
||||
## sink "remote_server_sink" # optional
|
||||
## media_role "media_role" #optional
|
||||
#}
|
||||
#
|
||||
# An example of a winmm output (Windows multimedia API).
|
||||
#
|
||||
#audio_output {
|
||||
# type "winmm"
|
||||
# name "My WinMM output"
|
||||
## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
|
||||
# or
|
||||
## device "0" # optional
|
||||
## mixer_type "hardware" # optional
|
||||
#}
|
||||
#
|
||||
# An example of an openal output.
|
||||
#
|
||||
#audio_output {
|
||||
# type "openal"
|
||||
# name "My OpenAL output"
|
||||
## device "Digital Audio (S/PDIF) (High Definition Audio Device)" # optional
|
||||
#}
|
||||
#
|
||||
# An example of an sndio output.
|
||||
#
|
||||
#audio_output {
|
||||
# type "sndio"
|
||||
# name "sndio output"
|
||||
# mixer_type "hardware"
|
||||
#}
|
||||
#
|
||||
# An example of an OS X output:
|
||||
#
|
||||
#audio_output {
|
||||
# type "osx"
|
||||
# name "My OS X Device"
|
||||
## device "Built-in Output" # optional
|
||||
## channel_map "-1,-1,0,1" # optional
|
||||
#}
|
||||
#
|
||||
## Example "pipe" output:
|
||||
#
|
||||
#audio_output {
|
||||
# type "pipe"
|
||||
# name "my pipe"
|
||||
# command "aplay -f cd 2>/dev/null"
|
||||
## Or if you're want to use AudioCompress
|
||||
# command "AudioCompress -m | aplay -f cd 2>/dev/null"
|
||||
## Or to send raw PCM stream through PCM:
|
||||
# command "nc example.org 8765"
|
||||
# format "44100:16:2"
|
||||
#}
|
||||
#
|
||||
## An example of a null output (for no audio output):
|
||||
#
|
||||
#audio_output {
|
||||
# type "null"
|
||||
# name "My Null Output"
|
||||
# mixer_type "none" # optional
|
||||
#}
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
# Normalization automatic volume adjustments ##################################
|
||||
#
|
||||
# This setting specifies the type of ReplayGain to use. This setting can have
|
||||
# the argument "off", "album", "track" or "auto". "auto" is a special mode that
|
||||
# chooses between "track" and "album" depending on the current state of
|
||||
# random playback. If random playback is enabled then "track" mode is used.
|
||||
# See <https://wiki.hydrogenaud.io/index.php?title=Replaygain> for
|
||||
# more details about ReplayGain.
|
||||
# This setting is off by default.
|
||||
#
|
||||
#replaygain "album"
|
||||
#
|
||||
# This setting sets the pre-amp used for files that have ReplayGain tags. By
|
||||
# default this setting is disabled.
|
||||
#
|
||||
#replaygain_preamp "0"
|
||||
#
|
||||
# This setting sets the pre-amp used for files that do NOT have ReplayGain tags.
|
||||
# By default this setting is disabled.
|
||||
#
|
||||
#replaygain_missing_preamp "0"
|
||||
#
|
||||
# This setting enables or disables ReplayGain limiting.
|
||||
# MPD calculates actual amplification based on the ReplayGain tags
|
||||
# and replaygain_preamp / replaygain_missing_preamp setting.
|
||||
# If replaygain_limit is enabled MPD will never amplify audio signal
|
||||
# above its original level. If replaygain_limit is disabled such amplification
|
||||
# might occur. By default this setting is enabled.
|
||||
#
|
||||
#replaygain_limit "yes"
|
||||
#
|
||||
# This setting enables on-the-fly normalization volume adjustment. This will
|
||||
# result in the volume of all playing audio to be adjusted so the output has
|
||||
# equal "loudness". This setting is disabled by default.
|
||||
#
|
||||
volume_normalization "yes"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
# Character Encoding ##########################################################
|
||||
#
|
||||
# If file or directory names do not display correctly for your locale then you
|
||||
# may need to modify this setting.
|
||||
#
|
||||
filesystem_charset "UTF-8"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
max_output_buffer_size "32768"
|
241
etc/samba/smb.conf
Normal file
241
etc/samba/smb.conf
Normal file
@ -0,0 +1,241 @@
|
||||
#
|
||||
# Sample configuration file for the Samba suite for Debian GNU/Linux.
|
||||
#
|
||||
#
|
||||
# This is the main Samba configuration file. You should read the
|
||||
# smb.conf(5) manual page in order to understand the options listed
|
||||
# here. Samba has a huge number of configurable options most of which
|
||||
# are not shown in this example
|
||||
#
|
||||
# Some options that are often worth tuning have been included as
|
||||
# commented-out examples in this file.
|
||||
# - When such options are commented with ";", the proposed setting
|
||||
# differs from the default Samba behaviour
|
||||
# - When commented with "#", the proposed setting is the default
|
||||
# behaviour of Samba but the option is considered important
|
||||
# enough to be mentioned here
|
||||
#
|
||||
# NOTE: Whenever you modify this file you should run the command
|
||||
# "testparm" to check that you have not made any basic syntactic
|
||||
# errors.
|
||||
|
||||
#======================= Global Settings =======================
|
||||
|
||||
[global]
|
||||
|
||||
## Browsing/Identification ###
|
||||
|
||||
# Change this to the workgroup/NT-domain name your Samba server will part of
|
||||
workgroup = WORKGROUP
|
||||
|
||||
#### Networking ####
|
||||
|
||||
# The specific set of interfaces / networks to bind to
|
||||
# This can be either the interface name or an IP address/netmask;
|
||||
# interface names are normally preferred
|
||||
; interfaces = 127.0.0.0/8 eth0
|
||||
|
||||
# Only bind to the named interfaces and/or networks; you must use the
|
||||
# 'interfaces' option above to use this.
|
||||
# It is recommended that you enable this feature if your Samba machine is
|
||||
# not protected by a firewall or is a firewall itself. However, this
|
||||
# option cannot handle dynamic or non-broadcast interfaces correctly.
|
||||
; bind interfaces only = yes
|
||||
|
||||
|
||||
|
||||
#### Debugging/Accounting ####
|
||||
|
||||
# This tells Samba to use a separate log file for each machine
|
||||
# that connects
|
||||
log file = /var/log/samba/log.%m
|
||||
|
||||
# Cap the size of the individual log files (in KiB).
|
||||
max log size = 1000
|
||||
|
||||
# We want Samba to only log to /var/log/samba/log.{smbd,nmbd}.
|
||||
# Append syslog@1 if you want important messages to be sent to syslog too.
|
||||
logging = file
|
||||
|
||||
# Do something sensible when Samba crashes: mail the admin a backtrace
|
||||
panic action = /usr/share/samba/panic-action %d
|
||||
|
||||
|
||||
####### Authentication #######
|
||||
|
||||
# Server role. Defines in which mode Samba will operate. Possible
|
||||
# values are "standalone server", "member server", "classic primary
|
||||
# domain controller", "classic backup domain controller", "active
|
||||
# directory domain controller".
|
||||
#
|
||||
# Most people will want "standalone server" or "member server".
|
||||
# Running as "active directory domain controller" will require first
|
||||
# running "samba-tool domain provision" to wipe databases and create a
|
||||
# new domain.
|
||||
server role = standalone server
|
||||
|
||||
obey pam restrictions = yes
|
||||
|
||||
# This boolean parameter controls whether Samba attempts to sync the Unix
|
||||
# password with the SMB password when the encrypted SMB password in the
|
||||
# passdb is changed.
|
||||
unix password sync = yes
|
||||
|
||||
# For Unix password sync to work on a Debian GNU/Linux system, the following
|
||||
# parameters must be set (thanks to Ian Kahan <<kahan@informatik.tu-muenchen.de> for
|
||||
# sending the correct chat script for the passwd program in Debian Sarge).
|
||||
passwd program = /usr/bin/passwd %u
|
||||
passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
|
||||
|
||||
# This boolean controls whether PAM will be used for password changes
|
||||
# when requested by an SMB client instead of the program listed in
|
||||
# 'passwd program'. The default is 'no'.
|
||||
pam password change = yes
|
||||
|
||||
# This option controls how unsuccessful authentication attempts are mapped
|
||||
# to anonymous connections
|
||||
map to guest = bad user
|
||||
|
||||
########## Domains ###########
|
||||
|
||||
#
|
||||
# The following settings only takes effect if 'server role = classic
|
||||
# primary domain controller', 'server role = classic backup domain controller'
|
||||
# or 'domain logons' is set
|
||||
#
|
||||
|
||||
# It specifies the location of the user's
|
||||
# profile directory from the client point of view) The following
|
||||
# required a [profiles] share to be setup on the samba server (see
|
||||
# below)
|
||||
; logon path = \\%N\profiles\%U
|
||||
# Another common choice is storing the profile in the user's home directory
|
||||
# (this is Samba's default)
|
||||
# logon path = \\%N\%U\profile
|
||||
|
||||
# The following setting only takes effect if 'domain logons' is set
|
||||
# It specifies the location of a user's home directory (from the client
|
||||
# point of view)
|
||||
; logon drive = H:
|
||||
# logon home = \\%N\%U
|
||||
|
||||
# The following setting only takes effect if 'domain logons' is set
|
||||
# It specifies the script to run during logon. The script must be stored
|
||||
# in the [netlogon] share
|
||||
# NOTE: Must be store in 'DOS' file format convention
|
||||
; logon script = logon.cmd
|
||||
|
||||
# This allows Unix users to be created on the domain controller via the SAMR
|
||||
# RPC pipe. The example command creates a user account with a disabled Unix
|
||||
# password; please adapt to your needs
|
||||
; add user script = /usr/sbin/useradd --create-home %u
|
||||
|
||||
# This allows machine accounts to be created on the domain controller via the
|
||||
# SAMR RPC pipe.
|
||||
# The following assumes a "machines" group exists on the system
|
||||
; add machine script = /usr/sbin/useradd -g machines -c "%u machine account" -d /var/lib/samba -s /bin/false %u
|
||||
|
||||
# This allows Unix groups to be created on the domain controller via the SAMR
|
||||
# RPC pipe.
|
||||
; add group script = /usr/sbin/addgroup --force-badname %g
|
||||
|
||||
############ Misc ############
|
||||
|
||||
# Using the following line enables you to customise your configuration
|
||||
# on a per machine basis. The %m gets replaced with the netbios name
|
||||
# of the machine that is connecting
|
||||
; include = /home/samba/etc/smb.conf.%m
|
||||
|
||||
# Some defaults for winbind (make sure you're not using the ranges
|
||||
# for something else.)
|
||||
; idmap config * : backend = tdb
|
||||
; idmap config * : range = 3000-7999
|
||||
; idmap config YOURDOMAINHERE : backend = tdb
|
||||
; idmap config YOURDOMAINHERE : range = 100000-999999
|
||||
; template shell = /bin/bash
|
||||
|
||||
# Setup usershare options to enable non-root users to share folders
|
||||
# with the net usershare command.
|
||||
|
||||
# Maximum number of usershare. 0 means that usershare is disabled.
|
||||
# usershare max shares = 100
|
||||
|
||||
# Allow users who've been granted usershare privileges to create
|
||||
# public shares, not just authenticated ones
|
||||
usershare allow guests = yes
|
||||
|
||||
#======================= Share Definitions =======================
|
||||
|
||||
[homes]
|
||||
comment = Home Directories
|
||||
browseable = no
|
||||
|
||||
# By default, the home directories are exported read-only. Change the
|
||||
# next parameter to 'no' if you want to be able to write to them.
|
||||
read only = yes
|
||||
|
||||
# File creation mask is set to 0700 for security reasons. If you want to
|
||||
# create files with group=rw permissions, set next parameter to 0775.
|
||||
create mask = 0700
|
||||
|
||||
# Directory creation mask is set to 0700 for security reasons. If you want to
|
||||
# create dirs. with group=rw permissions, set next parameter to 0775.
|
||||
directory mask = 0700
|
||||
|
||||
# By default, \\server\username shares can be connected to by anyone
|
||||
# with access to the samba server.
|
||||
# The following parameter makes sure that only "username" can connect
|
||||
# to \\server\username
|
||||
# This might need tweaking when using external authentication schemes
|
||||
valid users = %S
|
||||
|
||||
# Un-comment the following and create the netlogon directory for Domain Logons
|
||||
# (you need to configure Samba to act as a domain controller too.)
|
||||
;[netlogon]
|
||||
; comment = Network Logon Service
|
||||
; path = /home/samba/netlogon
|
||||
; guest ok = yes
|
||||
; read only = yes
|
||||
|
||||
# Un-comment the following and create the profiles directory to store
|
||||
# users profiles (see the "logon path" option above)
|
||||
# (you need to configure Samba to act as a domain controller too.)
|
||||
# The path below should be writable by all users so that their
|
||||
# profile directory may be created the first time they log on
|
||||
;[profiles]
|
||||
; comment = Users profiles
|
||||
; path = /home/samba/profiles
|
||||
; guest ok = no
|
||||
; browseable = no
|
||||
; create mask = 0600
|
||||
; directory mask = 0700
|
||||
|
||||
[printers]
|
||||
comment = All Printers
|
||||
browseable = no
|
||||
path = /var/tmp
|
||||
printable = yes
|
||||
guest ok = no
|
||||
read only = yes
|
||||
create mask = 0700
|
||||
|
||||
# Windows clients look for this share name as a source of downloadable
|
||||
# printer drivers
|
||||
[print$]
|
||||
comment = Printer Drivers
|
||||
path = /var/lib/samba/printers
|
||||
browseable = yes
|
||||
read only = yes
|
||||
guest ok = no
|
||||
# Uncomment to allow remote administration of Windows print drivers.
|
||||
# You may need to replace 'lpadmin' with the name of the group your
|
||||
# admin users are members of.
|
||||
# Please note that you also need to set appropriate Unix permissions
|
||||
# to the drivers directory for these users to have write rights in it
|
||||
; write list = root, @lpadmin
|
||||
|
||||
[VideoPi]
|
||||
comment = Videos on Pi
|
||||
path = /home/pi/video
|
||||
read only = no
|
||||
browsable = yes
|
53
utilities/cover.sh
Normal file
53
utilities/cover.sh
Normal file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# Recursively gets into each folder, checks if there is an audio file
|
||||
# and extracts the cover with ffmpeg
|
||||
|
||||
### Helpers
|
||||
|
||||
# Check all extensions available
|
||||
# find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
export MUSIC_PATH="."
|
||||
|
||||
### Functions
|
||||
|
||||
file_exists()
|
||||
{
|
||||
files=( ./*$1 )
|
||||
if [ ${#files[@]} -gt 0 ]; then
|
||||
echo ${files[0]}
|
||||
fi
|
||||
}
|
||||
|
||||
check_extensions()
|
||||
{
|
||||
EXTENSIONS_SUPPORTED=("MP3" "Mp3" "mp3" "m4a")
|
||||
|
||||
for i in "${EXTENSIONS_SUPPORTED[@]}"
|
||||
do
|
||||
file_exists "$i"
|
||||
done
|
||||
}
|
||||
|
||||
extract_cover()
|
||||
{
|
||||
if [ ! -f "cover.jpg" ]
|
||||
then
|
||||
shopt -s nullglob # erase not matching result
|
||||
val=$(check_extensions)
|
||||
#echo "file found" $val "in" $(pwd)
|
||||
ffmpeg -i "$val" cover.jpg
|
||||
else
|
||||
echo "cover.jpg exists in " $(pwd)
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
### Program
|
||||
|
||||
find $MUSIC_PATH -type f -name ".*" -delete # remove hidden files
|
||||
export -f extract_cover check_extensions file_exists
|
||||
find $MUSIC_PATH \( -iname \*.MP3 -o -iname \*.Mp3 -o -iname \*.mp3 -o -iname \*.m4a \) -type f -execdir bash -c 'extract_cover "$0"' {} \;
|
1
utilities/equalizer.sh
Normal file
1
utilities/equalizer.sh
Normal file
@ -0,0 +1 @@
|
||||
sudo -H -u mpd alsamixer -D equal
|
1
utilities/temperature.sh
Normal file
1
utilities/temperature.sh
Normal file
@ -0,0 +1 @@
|
||||
vcgencmd measure_temp
|
4
utilities/update-music.sh
Normal file
4
utilities/update-music.sh
Normal file
@ -0,0 +1,4 @@
|
||||
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source ${__dir}/update-music/mount-nfs.sh
|
||||
sleep 5
|
||||
source ${__dir}/update-music/rsync-sync.sh
|
1
utilities/update-music/mount-nfs.sh
Normal file
1
utilities/update-music/mount-nfs.sh
Normal file
@ -0,0 +1 @@
|
||||
sudo mount -t nfs4 -o proto=tcp,port=2049 192.168.1.30:/volume1/music /media/mantis-music
|
1
utilities/update-music/rsync-sync.sh
Normal file
1
utilities/update-music/rsync-sync.sh
Normal file
@ -0,0 +1 @@
|
||||
sudo rsync -av --exclude="@eaDir" --delete /media/mantis-music/punk /home/pi/music
|
230
utilities/usb-boot/mbr2gpt.sh
Normal file
230
utilities/usb-boot/mbr2gpt.sh
Normal file
@ -0,0 +1,230 @@
|
||||
#!/bin/bash
|
||||
|
||||
trap '{ stty sane; echo ""; errexit "Aborted"; }' SIGINT SIGTERM
|
||||
|
||||
MNTPATH="/tmp/boot-gpt-mnt"
|
||||
|
||||
mntpart()
|
||||
{
|
||||
MNTED=TRUE
|
||||
if [ ! -d "${MNTPATH}/" ]; then
|
||||
mkdir "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to make partition mount point"
|
||||
fi
|
||||
fi
|
||||
mount "$1" "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to mount $2 partition"
|
||||
fi
|
||||
}
|
||||
|
||||
umntpart()
|
||||
{
|
||||
umount "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to unmount partition"
|
||||
fi
|
||||
rm -r "${MNTPATH}/"
|
||||
MNTED=FALSE
|
||||
}
|
||||
|
||||
errexit()
|
||||
{
|
||||
echo ""
|
||||
echo "$1"
|
||||
echo ""
|
||||
if [ "${MNTED}" = "TRUE" ]; then
|
||||
umount "${MNTPATH}/" &> /dev/null
|
||||
rm -rf "${MNTPATH}/" &> /dev/null
|
||||
fi
|
||||
echo "Usage: $0 /dev/sdX"
|
||||
echo ""
|
||||
exit 1
|
||||
}
|
||||
|
||||
MNTED=FALSE
|
||||
if [ $(id -u) -ne 0 ]; then
|
||||
errexit "$0 must be run as root user"
|
||||
fi
|
||||
PGMNAME="$(basename $0)"
|
||||
for PID in $(pidof -x -o %PPID "${PGMNAME}"); do
|
||||
if [ ${PID} -ne $$ ]; then
|
||||
errexit "${PGMNAME} is already running"
|
||||
fi
|
||||
done
|
||||
gdisk -l /dev/null &> /dev/null
|
||||
if [ $? -eq 127 ]; then
|
||||
echo ""
|
||||
echo "gdisk not installed"
|
||||
echo ""
|
||||
echo -n "Ok to install gdisk (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
break
|
||||
else
|
||||
errexit "Aborted"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo "Installing gdisk"
|
||||
echo ""
|
||||
apt-get update
|
||||
apt-get install gdisk
|
||||
fi
|
||||
DEVICE="$1"
|
||||
if [ "${DEVICE}" = "" ]; then
|
||||
errexit "No device specified"
|
||||
fi
|
||||
if [[ ! "${DEVICE}" =~ ^/dev/sd[a-z]$ || ! -b "${DEVICE}" ]]; then
|
||||
errexit "Invalid DEVICE: ${DEVICE}"
|
||||
fi
|
||||
if [ $(mount | grep -c "^${DEVICE}") -ne 0 ]; then
|
||||
errexit "${DEVICE} is in use (mounted)"
|
||||
fi
|
||||
PTTYPE="$(blkid ${DEVICE} | sed -n 's|^.*PTTYPE="\(\S\+\)".*|\1|p')"
|
||||
PTTYPE="$(tr [a-z] [A-Z] <<< "${PTTYPE}")"
|
||||
if [ "${PTTYPE}" = "DOS" ]; then
|
||||
PTTYPE="MBR"
|
||||
fi
|
||||
if [[ "${PTTYPE}" != "MBR" && "${PTTYPE}" != "GPT" ]]; then
|
||||
errexit "Unsupported partition table type"
|
||||
fi
|
||||
echo ""
|
||||
echo "Partition table type is ${PTTYPE}"
|
||||
if [ "${PTTYPE}" = "MBR" ]; then
|
||||
echo ""
|
||||
echo -n "Ok to convert ${DEVICE} to GPT (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
break
|
||||
else
|
||||
errexit "Aborted"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
sgdisk -z "${DEVICE}" &> /dev/null
|
||||
fi
|
||||
INFO="$(gdisk -l ${DEVICE})"
|
||||
LAST=$(sed -n 's|^.*last usable sector is \(\S\+\).*|\1|p' <<< "${INFO}")
|
||||
START=$(sed -n 's|^\s\+2\s\+\(\S\+\).*|\1|p' <<< "${INFO}")
|
||||
END=$(sed -n 's|^\s\+2\s\+\S\+\s\+\(\S\+\).*|\1|p' <<< "${INFO}")
|
||||
SHRINK=FALSE
|
||||
EXPAND=FALSE
|
||||
SDBOOT=FALSE
|
||||
if [ $(grep -c "Warning! Secondary partition table overlaps the last partition" <<< "${INFO}") -ne 0 ]; then
|
||||
echo ""
|
||||
echo "ROOT partition overlaps the Secondary partition table area"
|
||||
echo ""
|
||||
echo -n "Ok to resize ROOT partition (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
break
|
||||
else
|
||||
errexit "Aborted"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
SHRINK=TRUE
|
||||
elif [ ${END} -lt ${LAST} ]; then
|
||||
echo ""
|
||||
echo -n "Expand ROOT partition to use all available space (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
EXPAND=TRUE
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [ -b /dev/mmcblk0 ]; then
|
||||
echo ""
|
||||
echo -n "Set SD card to boot ${DEVICE} (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
SDBOOT=TRUE
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [[ "${PTTYPE}" = "MBR" || "${SHRINK}" = "TRUE" || "${EXPAND}" = "TRUE" || "${SDBOOT}" = "TRUE" ]]; then
|
||||
echo ""
|
||||
echo -n "Ok to process ${DEVICE} (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
break
|
||||
else
|
||||
errexit "Aborted"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ "${SHRINK}" = "TRUE" ]; then
|
||||
echo ""
|
||||
resize2fs -f -M "${DEVICE}2"
|
||||
fi
|
||||
if [[ "${SHRINK}" = "TRUE" || "${EXPAND}" = "TRUE" ]]; then
|
||||
gdisk "${DEVICE}" <<EOF > /dev/null
|
||||
d
|
||||
2
|
||||
n
|
||||
2
|
||||
${START}
|
||||
|
||||
|
||||
w
|
||||
y
|
||||
EOF
|
||||
echo ""
|
||||
resize2fs -f "${DEVICE}2"
|
||||
fi
|
||||
gdisk "${DEVICE}" <<EOF > /dev/null
|
||||
r
|
||||
h
|
||||
1
|
||||
n
|
||||
0c
|
||||
n
|
||||
n
|
||||
w
|
||||
y
|
||||
EOF
|
||||
PARTUUID_1="$(blkid ${DEVICE}1 | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
PARTUUID_2="$(blkid ${DEVICE}2 | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
mntpart "${DEVICE}1" "BOOT"
|
||||
sed -i '/^[[:space:]]*#/!s|\(^.*rootwait\).*$|\1|' "${MNTPATH}/cmdline.txt"
|
||||
sed -i "/^[[:space:]]*#/!s|^\(.*root=\)\S\+\(\s\+.*\)$|\1PARTUUID=${PARTUUID_2}\2|" "${MNTPATH}/cmdline.txt"
|
||||
umntpart
|
||||
mntpart "${DEVICE}2" "ROOT"
|
||||
sed -i "/^[[:space:]]*#/!s|^\S\+\(\s\+/boot\s\+.*\)$|PARTUUID=${PARTUUID_1}\1|" "${MNTPATH}/etc/fstab"
|
||||
sed -i "/^[[:space:]]*#/!s|^\S\+\(\s\+/\s\+.*\)$|PARTUUID=${PARTUUID_2}\1|" "${MNTPATH}/etc/fstab"
|
||||
umntpart
|
||||
if [ "${SDBOOT}" = "TRUE" ]; then
|
||||
mntpart "/dev/mmcblk0p1" "BOOT"
|
||||
sed -i "/^[[:space:]]*#/!s|^\(.*root=\)\S\+\(\s\+.*\)$|\1PARTUUID=${PARTUUID_2}\2|" "${MNTPATH}/cmdline.txt"
|
||||
umntpart
|
||||
mntpart "${DEVICE}2" "ROOT"
|
||||
sed -i "/^[[:space:]]*#/!s|^\S\+\(\s\+/boot\s\+.*\)$|/dev/mmcblk0p1\1|" "${MNTPATH}/etc/fstab"
|
||||
umntpart
|
||||
fi
|
||||
if [[ "${SHRINK}" = "FALSE" && "${EXPAND}" = "FALSE" ]]; then
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
echo "Nothing to do!"
|
||||
echo ""
|
||||
fi
|
145
utilities/usb-boot/sdc-boot.sh
Normal file
145
utilities/usb-boot/sdc-boot.sh
Normal file
@ -0,0 +1,145 @@
|
||||
#!/bin/bash
|
||||
|
||||
trap '{ stty sane; echo ""; errexit "Aborted"; }' SIGINT SIGTERM
|
||||
|
||||
MNTPATH="/tmp/sdc-boot-mnt"
|
||||
|
||||
mntpart()
|
||||
{
|
||||
MNTED=TRUE
|
||||
if [ ! -d "${MNTPATH}/" ]; then
|
||||
mkdir "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to make partition mount point"
|
||||
fi
|
||||
fi
|
||||
mount "$1" "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to mount $2 partition"
|
||||
fi
|
||||
}
|
||||
|
||||
umntpart()
|
||||
{
|
||||
umount "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to unmount partition"
|
||||
fi
|
||||
rm -r "${MNTPATH}/"
|
||||
MNTED=FALSE
|
||||
}
|
||||
|
||||
errexit()
|
||||
{
|
||||
echo ""
|
||||
echo "$1"
|
||||
echo ""
|
||||
if [ "${MNTED}" = "TRUE" ]; then
|
||||
umount "${MNTPATH}/" &> /dev/null
|
||||
rm -rf "${MNTPATH}/" &> /dev/null
|
||||
fi
|
||||
echo "Usage: $0 [ /dev/sdX2 | /dev/mmcblk0p2 | hhhhhhhh-02 | hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh ]"
|
||||
echo ""
|
||||
exit 1
|
||||
}
|
||||
|
||||
MNTED=FALSE
|
||||
if [ $(id -u) -ne 0 ]; then
|
||||
errexit "$0 must be run as root user"
|
||||
fi
|
||||
PGMNAME="$(basename $0)"
|
||||
for PID in $(pidof -x -o %PPID "${PGMNAME}"); do
|
||||
if [ ${PID} -ne $$ ]; then
|
||||
errexit "${PGMNAME} is already running"
|
||||
fi
|
||||
done
|
||||
if [ ! -b /dev/mmcblk0 ]; then
|
||||
errexit "No SD card present"
|
||||
fi
|
||||
DEVICE="$1"
|
||||
if [ "${DEVICE}" = "" ]; then
|
||||
mntpart "/dev/mmcblk0p1" "SD card BOOT"
|
||||
DEVICE="$(sed -n '/^[[:space:]]*#/!s|^.*root=\(\S\+\)\s\+.*$|\1|p' "${MNTPATH}/cmdline.txt")"
|
||||
umntpart
|
||||
if [[ "${DEVICE}" =~ ^PARTUUID=.*$ ]]; then
|
||||
DEVICE="$(blkid -l -t "${DEVICE}" | sed -n 's|^\(/dev/.*\):.*|\1|p')"
|
||||
fi
|
||||
PARTUUID="$(blkid "${DEVICE}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
else
|
||||
DEVICE="$(tr [A-Z] [a-z] <<< "${DEVICE}")"
|
||||
if [[ "${DEVICE}" =~ ^[[:xdigit:]]{8}-02$ || "${DEVICE}" =~ ^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$ ]]; then
|
||||
PARTUUID="${DEVICE}"
|
||||
BLKID="$(blkid -l -t PARTUUID="${PARTUUID}")"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Invalid DEVICE: ${DEVICE}"
|
||||
fi
|
||||
DEVICE="$(sed -n 's|^\(/dev/.*\):.*$|\1|p' <<< "${BLKID}")"
|
||||
fi
|
||||
if [[ ("${DEVICE}" != "/dev/mmcblk0p2" && ! "${DEVICE}" =~ ^/dev/sd[a-z]2$) || ! -b "${DEVICE}" ]]; then
|
||||
errexit "Invalid DEVICE: ${DEVICE}"
|
||||
fi
|
||||
PARTUUID="$(blkid "${DEVICE}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
echo ""
|
||||
echo -n "Set SD card to boot to ${DEVICE} [PARTUUID=${PARTUUID}] (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
break
|
||||
else
|
||||
errexit "Aborted"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
mntpart "/dev/mmcblk0p1" "SD card BOOT"
|
||||
if [ "${DEVICE}" = "/dev/mmcblk0p2" ]; then
|
||||
sed -i "/^[[:space:]]*#/!s|^\(.*root=\)\S\+\(\s\+.*\)$|\1/dev/mmcblk0p2\2|" "${MNTPATH}/cmdline.txt"
|
||||
else
|
||||
sed -i "/^[[:space:]]*#/!s|^\(.*root=\)\S\+\(\s\+.*\)$|\1PARTUUID=${PARTUUID}\2|" "${MNTPATH}/cmdline.txt"
|
||||
fi
|
||||
umntpart
|
||||
if [ "${DEVICE}" != "/dev/mmcblk0p2" ]; then
|
||||
mntpart "${DEVICE}" "device ROOT"
|
||||
sed -i "/^[[:space:]]*#/!s|^\S\+\(\s\+/boot\s\+.*\)$|/dev/mmcblk0p1\1|" "${MNTPATH}/etc/fstab"
|
||||
umntpart
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
echo "SD card is set to boot to ${DEVICE} [PARTUUID=${PARTUUID}]"
|
||||
if [[ "${DEVICE}" != "/dev/mmcblk0p2" && "$(blkid /dev/mmcblk0p2 | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" = "${PARTUUID}" ]]; then
|
||||
echo ""
|
||||
echo "WARNING : SD card (/dev/mmcblk0p2) and USB device (${DEVICE}) have the same PARTUUID (${PARTUUID}) : SD card will boot instead of USB device"
|
||||
fi
|
||||
DEV_LIST=()
|
||||
DEV_LIST+=/dev/mmcblk0p2
|
||||
DEV_LIST+=($(ls -l /dev/sd?2 2> /dev/null | sed -n 's|^.*\(/dev/.*\)|\1|p'))
|
||||
if [ ${#DEV_LIST[@]} -gt 1 ]; then
|
||||
for i in ${!DEV_LIST[@]}; do
|
||||
if [ ${i} -lt $((${#DEV_LIST[@]} - 1)) ]; then
|
||||
j=$((i + 1))
|
||||
while [ ${j} -lt ${#DEV_LIST[@]} ]; do
|
||||
if [ "$(blkid "${DEV_LIST[i]}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" = "$(blkid "${DEV_LIST[j]}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" ];then
|
||||
if [[ "${DEV_LIST[i]}" != "/dev/mmcblk0p2" && ("${DEV_LIST[i]}" = "${DEVICE}" || "${DEV_LIST[j]}" = "${DEVICE}") ]]; then
|
||||
echo ""
|
||||
echo "WARNING : ${DEV_LIST[i]} and ${DEV_LIST[j]} have the same PARTUUID : $(blkid "${DEV_LIST[i]}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
fi
|
||||
fi
|
||||
((j += 1))
|
||||
done
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo ""
|
||||
if [ "$1" != "" ]; then
|
||||
echo -n "Reboot now (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ ${answer} = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ ${answer} = [yY] ]]; then
|
||||
shutdown -r now
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
fi
|
193
utilities/usb-boot/set-partuuid.sh
Normal file
193
utilities/usb-boot/set-partuuid.sh
Normal file
@ -0,0 +1,193 @@
|
||||
#!/bin/bash
|
||||
|
||||
trap '{ stty sane; echo ""; errexit "Aborted"; }' SIGINT SIGTERM
|
||||
|
||||
MNTPATH="/tmp/set-partuuid-mnt"
|
||||
|
||||
mntpart()
|
||||
{
|
||||
MNTED=TRUE
|
||||
if [ ! -d "${MNTPATH}/" ]; then
|
||||
mkdir "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to make partition mount point"
|
||||
fi
|
||||
fi
|
||||
mount "$1" "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to mount $2 partition"
|
||||
fi
|
||||
}
|
||||
|
||||
umntpart()
|
||||
{
|
||||
umount "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to unmount partition"
|
||||
fi
|
||||
rm -r "${MNTPATH}/"
|
||||
MNTED=FALSE
|
||||
}
|
||||
|
||||
errexit()
|
||||
{
|
||||
echo ""
|
||||
echo "$1"
|
||||
echo ""
|
||||
if [ "${MNTED}" = "TRUE" ]; then
|
||||
umount "${MNTPATH}/" &> /dev/null
|
||||
rm -rf "${MNTPATH}/" &> /dev/null
|
||||
fi
|
||||
echo "Usage: $0 device [ hhhhhhhh-02 | hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh | random ]"
|
||||
echo ""
|
||||
exit 1
|
||||
}
|
||||
|
||||
MNTED=FALSE
|
||||
if [ $(id -u) -ne 0 ]; then
|
||||
errexit "$0 must be run as root user"
|
||||
fi
|
||||
PGMNAME="$(basename $0)"
|
||||
for PID in $(pidof -x -o %PPID "${PGMNAME}"); do
|
||||
if [ ${PID} -ne $$ ]; then
|
||||
errexit "${PGMNAME} is already running"
|
||||
fi
|
||||
done
|
||||
gdisk -l "${DEVICE}" &> /dev/null
|
||||
if [ $? -eq 127 ]; then
|
||||
echo ""
|
||||
echo "gdisk not installed"
|
||||
echo ""
|
||||
echo -n "Ok to install gdisk (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
break
|
||||
else
|
||||
errexit "Aborted"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo "Installing gdisk"
|
||||
echo ""
|
||||
apt-get update
|
||||
apt-get install gdisk
|
||||
fi
|
||||
DEVICE="$1"
|
||||
PARTUUID="$2"
|
||||
if [[ (! "${DEVICE}" =~ ^/dev/sd[a-z]2$ && ! "${DEVICE}" = "/dev/mmcblk0p2") || ! -b "${DEVICE}" ]]; then
|
||||
errexit "Invalid DEVICE: ${DEVICE}"
|
||||
fi
|
||||
ORIGUUID="$(blkid "${DEVICE}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
if [ "${PARTUUID}" = "" ]; then
|
||||
PARTUUID="${ORIGUUID}"
|
||||
else
|
||||
DEVICE_P="$(sed 's/[0-9]\+$//' <<< "${DEVICE}")"
|
||||
DEVICE_D="${DEVICE_P}"
|
||||
if [ "${DEVICE_D}" = "/dev/mmcblk0p" ]; then
|
||||
DEVICE_D="${DEVICE_D:0:(${#DEVICE_D} - 1)}"
|
||||
fi
|
||||
SDCBOOT=FALSE
|
||||
if [ -b /dev/mmcblk0p1 ]; then
|
||||
mntpart "/dev/mmcblk0p1" "BOOT"
|
||||
CLRPART="$(sed -n '/^[[:space:]]*#/!s|^.*root=\(\S\+\)\s.*|\1|p' "${MNTPATH}/cmdline.txt")"
|
||||
if [ "${CLRPART}" != "/dev/mmcblk0p2" ]; then
|
||||
CLRUUID="$(sed -n 's|PARTUUID=\(\S\+\)|\1|p' <<< "${CLRPART}")"
|
||||
if [[ "${CLRUUID}" != "$(blkid /dev/mmcblk0p2 | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" && "${CLRUUID}" = "${ORIGUUID}" ]]; then
|
||||
SDCBOOT=TRUE
|
||||
fi
|
||||
fi
|
||||
umntpart
|
||||
fi
|
||||
PTTYPE="$(blkid "${DEVICE_D}" | sed -n 's|^.*PTTYPE="\(\S\+\)".*|\1|p')"
|
||||
if [[ "${PTTYPE}" != "dos" && "${PTTYPE}" != "gpt" ]]; then
|
||||
errexit "Unsupported partition table type: ${PTTYPE}"
|
||||
fi
|
||||
PARTUUID="$(tr [A-Z] [a-z] <<< "${PARTUUID}")"
|
||||
if [[ "${PARTUUID}" != "random" && (("${PTTYPE}" = "dos" && ! "${PARTUUID}" =~ ^[[:xdigit:]]{8}-02$) || \
|
||||
("${PTTYPE}" = "gpt" && ! "${PARTUUID}" =~ ^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$)) ]]; then
|
||||
errexit "Invalid PARTUUID: ${PARTUUID}"
|
||||
fi
|
||||
echo ""
|
||||
echo -n "Set PARTUUID on ${DEVICE} to ${PARTUUID} (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
break
|
||||
else
|
||||
errexit "Aborted"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
ORIGUUID_1="$(blkid "${DEVICE_P}1" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
ORIGUUID_2="$(blkid "${DEVICE_P}2" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
if [ "${PTTYPE}" = "dos" ]; then
|
||||
if [ "${PARTUUID}" = "random" ]; then
|
||||
PTUUID="$(hexdump -n 4 -e '"%08X"' /dev/random | tr [A-Z] [a-z])"
|
||||
else
|
||||
PTUUID="${PARTUUID:0:(${#PARTUUID} - 3)}"
|
||||
fi
|
||||
fdisk "${DEVICE_D}" <<EOF &> /dev/null
|
||||
x
|
||||
i
|
||||
0x${PTUUID}
|
||||
r
|
||||
w
|
||||
EOF
|
||||
else
|
||||
if [ "${PARTUUID}" = "random" ]; then
|
||||
sgdisk -u 2:'R' "${DEVICE_D}" > /dev/null
|
||||
else
|
||||
sgdisk -u 2:${PARTUUID} "${DEVICE_D}" > /dev/null
|
||||
fi
|
||||
fi
|
||||
partprobe
|
||||
PARTUUID="$(blkid "${DEVICE}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
PARTUUID_1="$(blkid "${DEVICE_P}1" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
PARTUUID_2="$(blkid "${DEVICE_P}2" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
mntpart "${DEVICE_P}2" "ROOT"
|
||||
sed -i "/^[[:space:]]*#/!s|^\(PARTUUID=\)${ORIGUUID_1}\(\s\+/boot\s\+.*\)$|\1${PARTUUID_1}\2|" "${MNTPATH}/etc/fstab"
|
||||
sed -i "/^[[:space:]]*#/!s|^\(PARTUUID=\)${ORIGUUID_2}\(\s\+/\s\+.*\)$|\1${PARTUUID_2}\2|" "${MNTPATH}/etc/fstab"
|
||||
umntpart
|
||||
mntpart "${DEVICE_P}1" "BOOT"
|
||||
sed -i "/^[[:space:]]*#/!s|^\(.*root=PARTUUID=\)${ORIGUUID_2}\(\s\+.*\)$|\1${PARTUUID_2}\2|" "${MNTPATH}/cmdline.txt"
|
||||
umntpart
|
||||
if [ "${SDCBOOT}" = "TRUE" ]; then
|
||||
mntpart "/dev/mmcblk0p1" "SD card BOOT"
|
||||
sed -i "/^[[:space:]]*#/!s|^\(.*root=PARTUUID=\)${ORIGUUID_2}\(\s\+.*\)$|\1${PARTUUID_2}\2|" "${MNTPATH}/cmdline.txt"
|
||||
umntpart
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
echo "PARTUUID on ${DEVICE} is set to ${PARTUUID}"
|
||||
if [ -b /dev/mmcblk0 ]; then
|
||||
if [[ "${DEVICE}" != "/dev/mmcblk0p2" && "$(blkid /dev/mmcblk0p2 | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" = "${PARTUUID}" ]]; then
|
||||
echo ""
|
||||
echo "WARNING : SD card (/dev/mmcblk0p2) and USB device (${DEVICE}) have the same PARTUUID (${PARTUUID}) : SD card will boot instead of USB device"
|
||||
fi
|
||||
fi
|
||||
DEV_LIST=()
|
||||
if [ -b /dev/mmcblk0 ]; then
|
||||
DEV_LIST+=/dev/mmcblk0p2
|
||||
fi
|
||||
DEV_LIST+=($(ls -l /dev/sd?2 2> /dev/null | sed -n 's|^.*\(/dev/.*\)|\1|p'))
|
||||
if [ ${#DEV_LIST[@]} -gt 1 ]; then
|
||||
for i in ${!DEV_LIST[@]}; do
|
||||
if [ ${i} -lt $((${#DEV_LIST[@]} - 1)) ]; then
|
||||
j=$((i + 1))
|
||||
while [ ${j} -lt ${#DEV_LIST[@]} ]; do
|
||||
if [ "$(blkid "${DEV_LIST[i]}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" = "$(blkid "${DEV_LIST[j]}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" ];then
|
||||
if [[ "${DEV_LIST[i]}" != "/dev/mmcblk0p2" && ("${DEV_LIST[i]}" = "${DEVICE}" || "${DEV_LIST[j]}" = "${DEVICE}") ]]; then
|
||||
echo ""
|
||||
echo "WARNING : ${DEV_LIST[i]} and ${DEV_LIST[j]} have the same PARTUUID : $(blkid "${DEV_LIST[i]}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
fi
|
||||
fi
|
||||
((j += 1))
|
||||
done
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo ""
|
403
utilities/usb-boot/usb-boot.sh
Normal file
403
utilities/usb-boot/usb-boot.sh
Normal file
@ -0,0 +1,403 @@
|
||||
#!/bin/bash
|
||||
|
||||
trap '{ stty sane; echo ""; errexit "Aborted"; }' SIGINT SIGTERM
|
||||
|
||||
MNTPATH="/tmp/usb-boot-mnt"
|
||||
|
||||
mntdev()
|
||||
{
|
||||
MNTED=TRUE
|
||||
if [ ! -d "${MNTPATH}/" ]; then
|
||||
mkdir "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to make ROOT partition mount point"
|
||||
fi
|
||||
fi
|
||||
mount "${1}2" "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to mount ROOT partition"
|
||||
fi
|
||||
if [ ! -d "${MNTPATH}/boot/" ]; then
|
||||
mkdir -p "${MNTPATH}/boot/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to make BOOT partition mount point"
|
||||
fi
|
||||
fi
|
||||
mount "${1}1" "${MNTPATH}/boot/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to mount BOOT partition"
|
||||
fi
|
||||
}
|
||||
|
||||
umntdev()
|
||||
{
|
||||
umount "${MNTPATH}/boot/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to unmount BOOT partition"
|
||||
fi
|
||||
umount "${MNTPATH}/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to unmount ROOT partition"
|
||||
fi
|
||||
rm -r "${MNTPATH}/"
|
||||
MNTED=FALSE
|
||||
}
|
||||
|
||||
errexit()
|
||||
{
|
||||
echo ""
|
||||
echo "$1"
|
||||
echo ""
|
||||
if [ "${MNTED}" = "TRUE" ]; then
|
||||
umount "${MNTPATH}/boot/" &> /dev/null
|
||||
umount "${MNTPATH}/" &> /dev/null
|
||||
rm -rf "${MNTPATH}/" &> /dev/null
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
MNTED=FALSE
|
||||
if [ $(id -u) -ne 0 ]; then
|
||||
errexit "Must be run as root user: sudo $0"
|
||||
fi
|
||||
PGMNAME="$(basename $0)"
|
||||
for PID in $(pidof -x -o %PPID "${PGMNAME}"); do
|
||||
if [ ${PID} -ne $$ ]; then
|
||||
errexit "${PGMNAME} is already running"
|
||||
fi
|
||||
done
|
||||
gdisk -l "${DEVICE}" &> /dev/null
|
||||
if [ $? -eq 127 ]; then
|
||||
echo ""
|
||||
echo "gdisk not installed"
|
||||
echo ""
|
||||
echo -n "Ok to install gdisk (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
break
|
||||
else
|
||||
errexit "Aborted"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo "Installing gdisk"
|
||||
echo ""
|
||||
apt-get update
|
||||
apt-get install gdisk
|
||||
fi
|
||||
rsync --version &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "rsync not installed (run: apt-get install rsync)"
|
||||
fi
|
||||
ROOT_PART="$(mount | sed -n 's|^\(/dev/.*\) on / .*|\1|p')"
|
||||
ROOT_DEV="$(sed 's/[0-9]\+$//' <<< "${ROOT_PART}")"
|
||||
if [ "${ROOT_DEV}" = "/dev/mmcblk0p" ]; then
|
||||
ROOT_DEV="${ROOT_DEV:0:(${#ROOT_DEV} - 1)}"
|
||||
fi
|
||||
ROOT_TYPE="$(blkid "${ROOT_PART}" | sed -n 's|^.*TYPE="\(\S\+\)".*|\1|p')"
|
||||
if [ -b /dev/mmcblk0 ]; then
|
||||
USESDC=TRUE
|
||||
else
|
||||
USESDC=FALSE
|
||||
fi
|
||||
if [[ $(cat /proc/cpuinfo | grep -c "^Revision\s\+:\s\+[abcd]0311[1245]$") -ne 0 || $(cat /proc/cpuinfo | grep -c "^Revision\s\+:\s\+c03130$") -ne 0 ]]; then
|
||||
RPI_4=TRUE
|
||||
else
|
||||
RPI_4=FALSE
|
||||
fi
|
||||
if [ "$(vcgencmd otp_dump | grep 17:)" = "17:3020000a" ]; then
|
||||
RPI_3=TRUE
|
||||
else
|
||||
RPI_3=FALSE
|
||||
fi
|
||||
if [[ "${USESDC}" = "TRUE" && "${RPI_4}" = "TRUE" ]]; then
|
||||
whiptail --backtitle "USB Boot" --title "USB Boot Method" --yesno --defaultno "\nUse SD card to boot the USB device?" 12 40
|
||||
YESNO=$?
|
||||
if [ ${YESNO} -eq 255 ]; then
|
||||
errexit "Aborted"
|
||||
elif [ ${YESNO} -ne 0 ]; then
|
||||
USESDC=FALSE
|
||||
fi
|
||||
elif [[ "${USESDC}" = "TRUE" && "${RPI_3}" = "TRUE" ]]; then
|
||||
whiptail --backtitle "USB Boot" --title "USB Boot Method" --yesno "\nUse SD card to boot the USB device (recommended)?" 12 54
|
||||
YESNO=$?
|
||||
if [ ${YESNO} -eq 255 ]; then
|
||||
errexit "Aborted"
|
||||
elif [ ${YESNO} -ne 0 ]; then
|
||||
USESDC=FALSE
|
||||
fi
|
||||
elif [[ "${USESDC}" = "FALSE" && "${RPI_4}" = "FALSE" && "${RPI_3}" = "FALSE" ]]; then
|
||||
errexit "Not a Raspberry Pi 4, Raspberry Pi 3B+, or Raspberry Pi 3B with OTP set, and no SD card present or used"
|
||||
fi
|
||||
USBDEVS=($(ls -l /dev/sd? 2> /dev/null | sed -n 's|^.*\(/dev/.*\)|\1|p'))
|
||||
if [ ${#USBDEVS[@]} -eq 0 ]; then
|
||||
errexit "No available USB mass storage devices found"
|
||||
fi
|
||||
for i in ${!USBDEVS[@]}; do
|
||||
USBDEVS[i]="${USBDEVS[i]} ${USBDEVS[i]} OFF"
|
||||
done
|
||||
USB_DEST="$(whiptail --backtitle "USB Boot" --title "USB Mass Storage Devices" --notags --radiolist \
|
||||
"\nSelect the USB mass storage device to boot" 13 47 ${#USBDEVS[@]} ${USBDEVS[@]} 3>&1 1>&2 2>&3)"
|
||||
if [[ $? -ne 0 || "${USB_DEST}" = "" ]]; then
|
||||
errexit "Aborted"
|
||||
fi
|
||||
USB_BOOT="${USB_DEST}1"
|
||||
USB_ROOT="${USB_DEST}2"
|
||||
if [ "${ROOT_DEV}" != "${USB_DEST}" ]; then
|
||||
whiptail --backtitle "USB Boot" --title "Replicate BOOT/ROOT Contents" --yesno "\nReplicate BOOT/ROOT contents from ${ROOT_DEV} to ${USB_DEST}?" 12 64
|
||||
YESNO=$?
|
||||
if [ ${YESNO} -eq 255 ]; then
|
||||
errexit "Aborted"
|
||||
elif [ ${YESNO} -eq 0 ]; then
|
||||
if [ $(mount | grep -c "^${USB_DEST}") -ne 0 ]; then
|
||||
errexit "USB mass storage device in use (mounted)"
|
||||
fi
|
||||
DEVSIZE=$(blockdev --getsz "${USB_DEST}")
|
||||
PTTYPES=()
|
||||
if [ ${DEVSIZE} -le 4294966784 ]; then
|
||||
PTTYPES[0]="dos MBR ON"
|
||||
PTTYPES[1]="gpt GPT OFF"
|
||||
else
|
||||
PTTYPES[0]="gpt GPT ON"
|
||||
fi
|
||||
PTTYPE="$(whiptail --backtitle "USB Boot" --title "Partition Table Type" --notags --radiolist \
|
||||
"\nSelect the partition table type to use (MBR = 2TB Maximum)" 10 62 ${#PTTYPES[@]} ${PTTYPES[@]} 3>&1 1>&2 2>&3)"
|
||||
if [[ $? -ne 0 || "${PTTYPE}" = "" ]]; then
|
||||
errexit "Aborted"
|
||||
fi
|
||||
whiptail --backtitle "USB Boot" --title "WARNING" --yesno "\nWARNING\n\nAll existing data on USB device ${USB_DEST} will be destroyed!\n\nDo you wish to continue?" 14 64
|
||||
YESNO=$?
|
||||
if [ ${YESNO} -ne 0 ]; then
|
||||
errexit "Aborted"
|
||||
fi
|
||||
echo ""
|
||||
echo "Replicating BOOT/ROOT contents from ${ROOT_DEV} to ${USB_DEST} (this will take a while)"
|
||||
if [ "${PTTYPE}" = "dos" ]; then
|
||||
DEVSIZE=$(blockdev --getsz "${USB_DEST}")
|
||||
dd bs=512 seek=0 count=34 if=/dev/zero of="${USB_DEST}" &> /dev/null
|
||||
dd bs=512 seek=$((${DEVSIZE} - 33)) count=33 if=/dev/zero of="${USB_DEST}" &> /dev/null
|
||||
partprobe
|
||||
echo "label: dos" | sfdisk "${USB_DEST}" > /dev/null
|
||||
sfdisk "${USB_DEST}" <<EOF &> /dev/null
|
||||
,256MiB,c
|
||||
,+,83
|
||||
EOF
|
||||
else
|
||||
sgdisk -Z "${USB_DEST}" &> /dev/null
|
||||
sgdisk -n 1:0:+256M "${USB_DEST}" &> /dev/null
|
||||
sgdisk -t 1:0700 "${USB_DEST}" > /dev/null
|
||||
sgdisk -n 2:0:0 "${USB_DEST}" &> /dev/null
|
||||
sgdisk -t 2:8300 "${USB_DEST}" > /dev/null
|
||||
fi
|
||||
partprobe
|
||||
mkfs.vfat -F 32 "${USB_BOOT}" > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to create BOOT filesystem"
|
||||
fi
|
||||
dosfsck "${USB_BOOT}" > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "BOOT filesystem appears corrupted"
|
||||
fi
|
||||
if [ "${ROOT_TYPE}" = "f2fs" ]; then
|
||||
mkfs.f2fs -f "${USB_ROOT}" > /dev/null
|
||||
else
|
||||
mkfs.ext4 -F -q -b 4096 "${USB_ROOT}" > /dev/null
|
||||
fi
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to create ROOT filesystem"
|
||||
fi
|
||||
mntdev "${USB_DEST}"
|
||||
rsync -aDH --partial --numeric-ids --delete --force --exclude "${MNTPATH}" --exclude '/dev' --exclude '/lost+found' --exclude '/media' --exclude '/mnt' \
|
||||
--exclude '/proc' --exclude '/run' --exclude '/sys' --exclude '/tmp' --exclude '/etc/udev/rules.d/70-persistent-net.rules' \
|
||||
--exclude '/var/lib/asterisk/astdb.sqlite3-journal' "${OPTIONS[@]}" / "${MNTPATH}/"
|
||||
if [[ $? -ne 0 && $? -ne 24 ]]; then
|
||||
errexit "Unable to replicate BOOT/ROOT contents from ${ROOT_DEV} to ${USB_DEST}"
|
||||
fi
|
||||
mkdir -p "${MNTPATH}/dev/" "${MNTPATH}/lost+found/" "${MNTPATH}/media/" "${MNTPATH}/mnt/" "${MNTPATH}/proc/" "${MNTPATH}/run/" "${MNTPATH}/sys/" "${MNTPATH}/tmp/"
|
||||
if [ $? -ne 0 ]; then
|
||||
errexit "Unable to create directories"
|
||||
fi
|
||||
chmod a+rwxt "${MNTPATH}/tmp/"
|
||||
umntdev
|
||||
echo ""
|
||||
echo "BOOT/ROOT contents replicated from ${ROOT_DEV} to ${USB_DEST}"
|
||||
fi
|
||||
fi
|
||||
if [ "${USESDC}" = "TRUE" ]; then
|
||||
if [ -b /dev/mmcblk0 ]; then
|
||||
while [ "$(blkid /dev/mmcblk0p2 | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" = "$(blkid ${USB_DEST}2 | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" ]
|
||||
do
|
||||
echo ""
|
||||
echo "WARNING : SD card (/dev/mmcblk0) and USB device (${USB_DEST}) have the same PARTUUID : SD card will boot instead of USB device"
|
||||
echo ""
|
||||
echo -n "Ok to change PARTUUID on USB device (${USB_DEST}) (y/n)? "
|
||||
while read -r -n 1 -s answer; do
|
||||
if [[ "${answer}" = [yYnN] ]]; then
|
||||
echo "${answer}"
|
||||
if [[ "${answer}" = [yY] ]]; then
|
||||
break
|
||||
else
|
||||
break 2
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo "Changing PARTUUID on USB device (${USB_DEST})"
|
||||
PTTYPE="$(blkid "${USB_DEST}" | sed -n 's|^.*PTTYPE="\(\S\+\)".*|\1|p')"
|
||||
if [ "${PTTYPE}" = "dos" ]; then
|
||||
PTUUID="$(hexdump -n 4 -e '"%08X"' /dev/random | tr [A-Z] [a-z])"
|
||||
fdisk "${USB_DEST}" <<EOF &> /dev/null
|
||||
x
|
||||
i
|
||||
0x${PTUUID}
|
||||
r
|
||||
w
|
||||
EOF
|
||||
else
|
||||
sgdisk -u 2:'R' "${USB_DEST}" &> /dev/null
|
||||
fi
|
||||
done
|
||||
else
|
||||
errexit "No SD card present"
|
||||
fi
|
||||
fi
|
||||
if [ "${PTTYPE}" = "gpt" ]; then
|
||||
gdisk "${USB_DEST}" <<EOF > /dev/null
|
||||
r
|
||||
h
|
||||
1
|
||||
n
|
||||
0c
|
||||
n
|
||||
n
|
||||
w
|
||||
y
|
||||
EOF
|
||||
fi
|
||||
partprobe &> /dev/null
|
||||
PARTUUID_1="$(blkid "${USB_BOOT}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
PARTUUID_2="$(blkid "${USB_ROOT}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
if [ "${USESDC}" = "TRUE" ]; then
|
||||
if [ -b /dev/mmcblk0 ]; then
|
||||
mntdev "/dev/mmcblk0p"
|
||||
sed -i "/^[[:space:]]*#/!s|^\(.*root=\)\S\+\(\s\+.*\)$|\1PARTUUID=${PARTUUID_2}\2|" "${MNTPATH}/boot/cmdline.txt"
|
||||
umntdev
|
||||
else
|
||||
errexit "No SD card present"
|
||||
fi
|
||||
fi
|
||||
mntdev "${USB_DEST}"
|
||||
sed -i "/^[[:space:]]*#/!s|^\(.*root=\)\S\+\(\s\+.*\)$|\1PARTUUID=${PARTUUID_2}\2|" "${MNTPATH}/boot/cmdline.txt"
|
||||
sed -i "/^[[:space:]]*#/!s|^\S\+\(\s\+/\s\+.*\)$|PARTUUID=${PARTUUID_2}\1|" "${MNTPATH}/etc/fstab"
|
||||
if [ "${USESDC}" = "TRUE" ]; then
|
||||
sed -i "/^[[:space:]]*#/!s|^\S\+\(\s\+/boot\s\+.*\)$|/dev/mmcblk0p1\1|" "${MNTPATH}/etc/fstab"
|
||||
if [ $(grep -v '^[[:space:]]*#' "${MNTPATH}/etc/rc.local" | grep -c 'usb-boot-init') -eq 0 ]; then
|
||||
cat <<\EOF1 > "${MNTPATH}/etc/usb-boot-init"
|
||||
#!/bin/bash
|
||||
|
||||
MNTBOOT="/tmp/usb-boot-init-mnt"
|
||||
|
||||
REBOOT=FALSE
|
||||
ROOT_PART="$(mount | sed -n 's|^\(/dev/.*\) on / .*|\1|p')"
|
||||
ROOT_DEV="$(sed 's/[0-9]\+$//' <<< "${ROOT_PART}")"
|
||||
if [ "${ROOT_DEV}" = "/dev/mmcblk0p" ]; then
|
||||
ROOT_DEV="${ROOT_DEV:0:(${#ROOT_DEV} - 1)}"
|
||||
fi
|
||||
mkdir -p "${MNTBOOT}/"
|
||||
mount "${ROOT_DEV}1" "${MNTBOOT}/"
|
||||
if [ "$(find "${MNTBOOT}/" -type f -iregex "${MNTBOOT}/ssh\(\.txt\)?" -print -delete)" != "" ]; then
|
||||
update-rc.d ssh enable && invoke-rc.d ssh start
|
||||
fi
|
||||
if [ -f "${MNTBOOT}/wpa_supplicant.conf" ]; then
|
||||
mv "${MNTBOOT}/wpa_supplicant.conf" /etc/wpa_supplicant/wpa_supplicant.conf
|
||||
REBOOT=TRUE
|
||||
fi
|
||||
if [[ $(grep -v '^[[:space:]]*#' "${MNTBOOT}/cmdline.txt" | grep -c 'init=/usr/lib/raspi-config/init_resize.sh') -ne 0 ]]; then
|
||||
sed -i '/^[[:space:]]*#/!s| init=/usr/lib/raspi-config/init_resize\.sh||' "${MNTBOOT}/cmdline.txt"
|
||||
START2=$(sfdisk -d "${ROOT_DEV}" | sed -n "s|^${ROOT_PART}\s\+:.*start=\s*\([0-9]\+\).*$|\1|p")
|
||||
PTTYPE="$(blkid "${ROOT_DEV}" | sed -n 's|^.*PTTYPE="\(\S\+\)".*|\1|p')"
|
||||
if [ "${PTTYPE}" = "dos" ]; then
|
||||
sfdisk --delete "${ROOT_DEV}" 2 > /dev/null
|
||||
echo "${START2},+" | sfdisk --no-reread --no-tell-kernel -N2 "${ROOT_DEV}" &> /dev/null
|
||||
else
|
||||
PARTUUID="$(blkid "${ROOT_PART}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
sgdisk -d 2 "${ROOT_DEV}" > /dev/null
|
||||
sgdisk -n 2:${START2}:0 "${ROOT_DEV}" > /dev/null
|
||||
sgdisk -u 2:${PARTUUID} "${ROOT_DEV}" > /dev/null
|
||||
fi
|
||||
cat <<\EOF2 > /etc/init.d/resize_root_fs
|
||||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: resize_root_fs
|
||||
# Required-Start:
|
||||
# Required-Stop:
|
||||
# Default-Start: 3
|
||||
# Default-Stop:
|
||||
# Short-Description: Resize root filesystem
|
||||
# Description:
|
||||
### END INIT INFO
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
resize2fs "$(mount | sed -n 's|^\(/dev/.*\) on / .*|\1|p')"
|
||||
update-rc.d resize_root_fs remove
|
||||
rm /etc/init.d/resize_root_fs
|
||||
;;
|
||||
*)
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
EOF2
|
||||
chmod +x /etc/init.d/resize_root_fs
|
||||
update-rc.d resize_root_fs defaults
|
||||
REBOOT=TRUE
|
||||
fi
|
||||
umount "${MNTBOOT}/"
|
||||
rm -r "${MNTBOOT}/"
|
||||
sed -i '/usb-boot-init/d' /etc/rc.local
|
||||
rm /etc/usb-boot-init
|
||||
if [ "${REBOOT}" = "TRUE" ]; then
|
||||
shutdown --no-wall -r now
|
||||
fi
|
||||
EOF1
|
||||
chmod +x "${MNTPATH}/etc/usb-boot-init"
|
||||
sed -i 's|^exit 0$|/etc/usb-boot-init\nexit 0|' "${MNTPATH}/etc/rc.local"
|
||||
fi
|
||||
else
|
||||
sed -i "/^[[:space:]]*#/!s|^\S\+\(\s\+/boot\s\+.*\)$|PARTUUID=${PARTUUID_1}\1|" "${MNTPATH}/etc/fstab"
|
||||
fi
|
||||
umntdev
|
||||
DEV_LIST=()
|
||||
if [ -b /dev/mmcblk0 ]; then
|
||||
DEV_LIST+=/dev/mmcblk0p2
|
||||
fi
|
||||
DEV_LIST+=($(ls -l /dev/sd?2 2> /dev/null | sed -n 's|^.*\(/dev/.*\)|\1|p'))
|
||||
if [ ${#DEV_LIST[@]} -gt 1 ]; then
|
||||
for i in ${!DEV_LIST[@]}; do
|
||||
if [ ${i} -lt $((${#DEV_LIST[@]} - 1)) ]; then
|
||||
j=$((i + 1))
|
||||
while [ ${j} -lt ${#DEV_LIST[@]} ]; do
|
||||
if [ "$(blkid "${DEV_LIST[i]}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" = "$(blkid "${DEV_LIST[j]}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')" ]; then
|
||||
if [[ "${DEV_LIST[i]}" != "/dev/mmcblk0p2" || "${DEV_LIST[j]}" != "${USB_DEST}2" ]]; then
|
||||
echo ""
|
||||
echo "WARNING : ${DEV_LIST[i]} and ${DEV_LIST[j]} have the same PARTUUID : $(blkid "${DEV_LIST[i]}" | sed -n 's|^.*PARTUUID="\(\S\+\)".*|\1|p')"
|
||||
fi
|
||||
fi
|
||||
((j += 1))
|
||||
done
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [ "${USESDC}" = "TRUE" ]; then
|
||||
echo ""
|
||||
echo "SD card must remain in place to boot the USB device"
|
||||
else
|
||||
if [ -b /dev/mmcblk0 ]; then
|
||||
echo ""
|
||||
echo "SD card must be removed to boot the USB device"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
89
utilities/usb-boot/usb-boot.txt
Normal file
89
utilities/usb-boot/usb-boot.txt
Normal file
@ -0,0 +1,89 @@
|
||||
Running Raspbian on USB Devices : Made Easy
|
||||
|
||||
A recurring topic of discussion is how to configure and reliably run Raspbian on a USB flash drive, USB hard drive, or USB SSD instead of an SD card.
|
||||
|
||||
A Raspberry Pi 3B+ has a native USB boot mode (this mode has to be manually enabled by setting an OTP bit on a Raspberry Pi 3B). This native USB boot mode has serious compatibility issues. A bootcode.bin file is available for older Raspberry Pi models. Unfortunately, both of these approaches have serious limitations and once working, can easily be broken by simply plugging in an additional USB storage device.
|
||||
|
||||
The easiest and most reliable way to run Raspbian on a USB device with any Raspberry Pi is to leave an SD card containing Raspbian in place, but use it only for starting Raspbian that is residing on a USB device. While setting up such a configuration is not rocket science, it can be confusing to a newcomer or someone unfamiliar with Linux internals. In an effort to simplify the task, I've created the attached script named 'usb-boot' to automate the process.
|
||||
|
||||
If usb-boot is running on a Raspberry Pi 4, usb-boot first prompts: 'Use SD card to boot the USB device?'
|
||||
|
||||
If 'No' is selected, the SD card will not be altered and the direct USB boot capability of the Raspberry Pi 4 will be used.
|
||||
|
||||
[NOTE: The direct USB boot capability of the Raspberry Pi 4 requires an updated bootloader and firmware: https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711_bootloader_config.md]
|
||||
|
||||
If usb-boot is running on a Raspberry Pi 3B+ or a Raspberry Pi 3B with its OTP bit set, usb-boot first prompts: 'Use SD card to boot the USB device (recommended)?'
|
||||
|
||||
If 'No' is selected, the SD card will not be altered, but booting the USB device may be limited and/or unreliable as described above.
|
||||
|
||||
usb-boot then presents a list of available USB mass storage devices and prompts: 'Select the USB mass storage device to boot'
|
||||
|
||||
Use the arrow keys on your keyboard to navigate to the desired device and press the spacebar to select it. Then use the tab key to navigate to the 'Ok' or 'Cancel' button and press the return key.
|
||||
|
||||
usb-boot will then prompt: 'Replicate BOOT/ROOT contents from /dev/mmcblk0 to /dev/sdX?'
|
||||
|
||||
/dev/mmcblk0 is the SD card and /dev/sdX is the USB device.
|
||||
|
||||
Select 'No' if the USB device already has Raspbian on it and you wish to use it (nothing will be copied).
|
||||
|
||||
Select 'Yes' if you want to copy the Raspbian on your SD card to the USB device (everything will be copied).
|
||||
|
||||
If you select 'Yes', usb-boot will then prompt: 'Select the partition table type to use (MBR = 2TB Maximum)'
|
||||
|
||||
usb-boot will then prompt: 'All existing data on USB device /dev/sdX will be destroyed!' and ask: 'Do you wish to continue?'
|
||||
|
||||
If you select 'Yes', the copy will begin. The time required for this process will depend on the amount of data on your SD card and the speed of your storage devices.
|
||||
|
||||
usb-boot will then complete the configuration process and warn you of any potential conflicts it detects.
|
||||
|
||||
When usb-boot has finished, you should be able to reboot and be running Raspbian on the USB device (first power off and remove the SD card if not using the SD card to boot the USB device).
|
||||
|
||||
=====
|
||||
|
||||
sdc-boot provides a convenient way to select which attached device will be booted.
|
||||
|
||||
Usage syntax is:
|
||||
|
||||
sdc-boot [ /dev/sdX2 | /dev/mmcblk0p2 | hhhhhhhh-02 | hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh ]
|
||||
|
||||
/dev/sdX2 is a USB device
|
||||
|
||||
/dev/mmcblk0p2 is the SD card
|
||||
|
||||
hhhhhhhh-02 | hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh is a device identified by its PARTUUID
|
||||
|
||||
If no device is specified, the currently selected boot device will be displayed.
|
||||
|
||||
=====
|
||||
|
||||
GPT partition tables are necessary for devices whose size is over 2TB.
|
||||
|
||||
mbr2gpt converts an MBR partition table on a USB device to a GPT partition table, as well as optionally expanding the ROOT partition and enabling booting via an SD card.
|
||||
mbr2gpt converts any size USB device, including SD cards placed in a USB adapter.
|
||||
|
||||
!!! DO NOT PROCEED UNLESS YOU HAVE A RELIABLE BACKUP OF THE DEVICE BEING CONVERTED !!!
|
||||
!!! INITIAL TESTING SHOULD BE PERFORMED ON A USB DEVICE CONTAINING EXPENDABLE DATA !!!
|
||||
|
||||
Usage syntax is:
|
||||
|
||||
mbr2gpt /dev/sdX
|
||||
|
||||
mbr2gpt will prompt for permission to perform the following optional functions:
|
||||
|
||||
1. Convert the USB device to use GPT partition tables
|
||||
|
||||
2. Expand the ROOT partition to use all available space
|
||||
|
||||
3. Set the SD card to boot the USB device
|
||||
|
||||
=====
|
||||
|
||||
set-partuuid displays or changes the ROOT partition PARTUUID on a device.
|
||||
|
||||
Usage syntax is:
|
||||
|
||||
set-partuuid device [ hhhhhhhh-02 | hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh | random ]
|
||||
|
||||
device may be /dev/sdX2 or /dev/mmcblk0p2
|
||||
|
||||
If no partuuid is specified, the current ROOT partition PARTUUID will be displayed.
|
24
utilities/video-tools/README.md
Normal file
24
utilities/video-tools/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# video-tools
|
||||
|
||||
## Use case
|
||||
|
||||
I bought a machine named Vero 4K+, running OSMC, to read my video library on my TV. It works fine on a number of files, but was struggling on some others, when no hardware decoder was available. I [found this thread](https://discourse.osmc.tv/t/ff-h264-dropping-and-skipping-frames/37459) that gave a first hint. I then decided to made a quick list of all the format of my files, to be able to test them. That's how this little project started.
|
||||
|
||||
## Set up
|
||||
|
||||
* On a Linux machine with apt: `sudo apt install -y ffmpeg mediainfo`
|
||||
* If you are on Windows, you can:
|
||||
* [install WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10)
|
||||
* install Debian/Ubuntu/etc and type in: `sudo apt install -y ffmpeg mediainfo`
|
||||
* If you access a networked drive, you can access it like this for example:
|
||||
* `sudo mkdir /mnt/multimedia`
|
||||
* `sudo mount -t drvfs //Vero/Multimedia /mnt/multimedia/` (this need to be done at each reboot)
|
||||
|
||||
## video-info.sh
|
||||
|
||||
video-info.sh generates a CSV file with the format, the format profile, and the codec ID. It can be opened with any spreadsheet software. If it is interrupted, an option to resume the operation is available, so that it is possible to launch it multiple time on huge video libraries.
|
||||
|
||||
It generates a file named `videoinfo-date_hour.csv` in the folder where the videos are located.
|
||||
|
||||
By default, only the following file extensions are taken into account: `mkv`, `mp4`, and `avi`. You can easily add more by modifying the script. You can know which file extensions you have in your library by typing the following command (more [on this thread](https://stackoverflow.com/questions/1842254/how-can-i-find-all-of-the-distinct-file-extensions-in-a-folder-hierarchy)):
|
||||
`find . -type f | sed -e 's/.*\.//' | sed -e 's/.*\///' | sort | uniq -c | sort -rn`
|
90
utilities/video-tools/video-info.sh
Normal file
90
utilities/video-tools/video-info.sh
Normal file
@ -0,0 +1,90 @@
|
||||
#/bin/sh
|
||||
CURRENT_DATE=$(date +%Y%m%d_%H%M)
|
||||
bold=$(tput bold)
|
||||
normal=$(tput sgr0)
|
||||
|
||||
# $1 = video file / $2 = result file
|
||||
get_mediainfo() {
|
||||
echo "Processing $1..."
|
||||
echo -ne "\"$1\", " >> $2
|
||||
mediainfo --Inform="Video;%Format%, %Format_Profile%, %CodecID%" "$1" >> $2
|
||||
}
|
||||
|
||||
gather_data() {
|
||||
echo "Gathering data..."
|
||||
|
||||
RESULT_PATH=./$1/videoinfo-"$CURRENT_DATE".csv
|
||||
PWD=$(pwd $1)/$1
|
||||
|
||||
echo $PWD >> $RESULT_PATH
|
||||
echo 'File, Format, Format Profile, Codec ID' >> $RESULT_PATH
|
||||
find $1 -type f \( -iname \*.mkv -o -iname \*.mp4 -o -iname \*.avi \) | while read file;
|
||||
do
|
||||
get_mediainfo "$(pwd $1)/$file" $RESULT_PATH
|
||||
done
|
||||
echo "Finished! Result file is located at ${bold}$RESULT_PATH${normal}."
|
||||
}
|
||||
|
||||
gather_data_continue() {
|
||||
echo "Continue gather data of file ${bold}$1${normal}..."
|
||||
|
||||
LAST_LINE=$(tail -n 1 $1)
|
||||
LAST_FILE=$(echo $LAST_LINE | sed 's/^\"\(.*\)\".*$/\1/g')
|
||||
|
||||
# where to find the files...
|
||||
FIRST_LINE=$(head -n 1 $1)
|
||||
|
||||
# remove last line of file (might print error on WSL, hence the error suppression)
|
||||
sed -i '$d' $1 2>/dev/null
|
||||
|
||||
FOUND_FILE=0
|
||||
find $FIRST_LINE -type f \( -iname \*.mkv -o -iname \*.mp4 -o -iname \*.avi \) | while read file;
|
||||
do
|
||||
if [[ $file == $LAST_FILE ]]; then
|
||||
FOUND_FILE=1
|
||||
fi
|
||||
|
||||
if [[ $FOUND_FILE == 1 ]]; then
|
||||
get_mediainfo "$file" $1
|
||||
fi
|
||||
done
|
||||
echo "Finished! Result file is located at ${bold}$1${normal}."
|
||||
}
|
||||
|
||||
continue_gather() {
|
||||
if [[ $1 == "" ]]; then
|
||||
echo "No input file. Please input CSV file generated by this program."
|
||||
elif [[ -f $1 ]]; then
|
||||
gather_data_continue $1
|
||||
else
|
||||
echo "File ${bold}$1${normal} does not exist."
|
||||
fi
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
echo -e "This tool generates a CSV file with useful video data\n"
|
||||
echo -e "Options:"
|
||||
echo -e "./video-info.sh [-h, --help]"
|
||||
echo -e "\t\tDisplays help and usage.\n"
|
||||
echo -e "./video-info.sh DIRECTORY"
|
||||
echo -e "\t\tCreates a CSV file from all the video files from the directory.\n"
|
||||
echo -e "./video-info.sh [-c, --continue] FILE"
|
||||
echo -e "\t\tIf the gathering was interrupted, use this option to resume."
|
||||
echo -e "\t\tTakes the incompletely generated CSV file as a parameter.\n"
|
||||
echo -e "There are some limitations as this is a pretty simple tool. If you have any issue,"
|
||||
echo -e "please read the README file."
|
||||
}
|
||||
|
||||
if [[ $1 == "" ]] || [[ $1 == "--help" ]] || [[ $1 == "-h" ]]; then
|
||||
show_usage
|
||||
exit
|
||||
elif [[ $1 == "-c" ]] || [[ $1 == "--continue" ]]; then
|
||||
continue_gather $2
|
||||
else
|
||||
if [[ -d $1 ]]; then
|
||||
gather_data $1
|
||||
else
|
||||
echo -e "Directory ${bold}$1${normal} does not exist. Showing usage...\n"
|
||||
show_usage
|
||||
fi
|
||||
fi
|
Loading…
Reference in New Issue
Block a user