VLC can dump its media DB to "normal" storage,
which removes the requirement for root.
A more complex SQL expression allows reducing
the number of queries to one per playlist.
Since Android doesn't have Bash (by default),
the included script is adjusted for sh.
Other changes compared to update-playlists.sh:
- change name to better reflect functionality
- isolate on-device vs on-host logic to a single check/choice at start
- on-device: just run the script "as is"
- on-host: use optional first argument to select behavior
- process pre-fetched DB on host (needs
.and DB) - pull DB, process on host (needs
., or:to overwrite) - push script and run on-device (default)
- process pre-fetched DB on host (needs
- treat (optional) arguments as playlist names for extraction
- skip converting absolute to relative paths
- unnecessary on the same device
- simple to do with an editor, if desired
- skip trashing old playlists
- updating the DB would need
root
- updating the DB would need
extract-vlc-playlist.sh
#!/bin/sh
# ADAPTED from https://codeberg.org/sphi/personal/src/branch/main/android/update-playlists.sh
# Extract playlists from VLC on Android and dump them to transferable .m3u8 files
# under `vlc_playlists` in the current directory.
#
# This script can be run on Linux with an Android device connected over USB,
# or directly in Termux on Android.
#
# Before running, select `Settings -> Advanced -> Dump media database` in VLC!
# That avoids the need for a rooted setup.
set -euo pipefail
DB=vlc_media.db
PL=vlc_playlists
bail() {
echo $'\x1b[1;31m'"$*"$'\x1b[m'
exit 1
}
[ -v ANDROID_ROOT ] || { # not on Android
if [ -r $DB ] && [ . = "${1-}" ]
then
shift
echo Using pre-fetched "'$DB'"
else
command -V adb || bail Missing "'adb'"
case "${1-}" in
.|:) # pull the DB, run locally
adb pull /sdcard/$DB
shift
;;
*) # push the script, run on-device
adb push "0ドル" /sdcard/
adb shell sh "/sdcard/$(basename "0ドル")" "$@"
exit 0
esac
fi
}
command -V sqlite3 || bail Missing "'sqlite3'"
[ -r $DB ] || bail Missing "'$DB'" in "'$PWD'"
[ -d $PL ] || mkdir -vp $PL
QUERY="
SELECT m.duration/1000, a.name, m.title, f.path, m.filename
FROM Media m
INNER JOIN PlaylistMediaRelation pmr
ON pmr.media_id=m.id_media
INNER JOIN Playlist p
ON pmr.playlist_id=p.id_playlist
INNER JOIN Artist a
ON m.artist_id=a.id_artist
INNER JOIN Folder f
ON f.id_folder=m.folder_id
WHERE p.name="
query() {
echo "$@" | sqlite3 -tabs $DB
}
urldecode() {
# remove 'file://' prefix
local urlencoded_path="${1#file://}"
# convert URL-encoded hex characters to first shell-escaped, then binary variant
# e.g. a dot `.` encoded as %2E becomes \x2E becomes $'\x2e'
printf '%b' "${urlencoded_path//%/\\x}"
}
extract_playlist() {
local name="1ドル"
echo \#EXTM3U
echo \#PLAYLIST:$name
query "$QUERY'$name'" | {
IFS=$'\t'
while read -r seconds artist title path filename
do echo \#EXTINF:$seconds,$artist - $title
echo "$(urldecode "$path")$filename"
done
}
}
# dump all playlists, when none specified
(($#)) || set -- $(query SELECT name FROM Playlist)
for name
do extract_playlist "$name" > "$PL/$name.m3u8"
done
echo "done"
VLC can dump its media DB to "normal" storage,
which removes the requirement for `root`.
A more complex SQL expression allows reducing
the number of queries to one per playlist.
Since Android doesn't have Bash (by default),
the included script is adjusted for `sh`.
Other changes compared to [update-playlists.sh](https://codeberg.org/sphi/personal/src/branch/main/android/update-playlists.sh):
- change name to better reflect functionality
- isolate on-device vs on-host logic to a single check/choice at start
* on-device: just run the script "as is"
* on-host: use optional **first** argument to select behavior
- process pre-fetched DB on host (needs `.` **and** DB)
- pull DB, process on host (needs `.`, or `:` to overwrite)
- push script and run on-device (default)
- treat (optional) arguments as playlist *names* for extraction
- skip converting absolute to relative paths
* unnecessary on the same device
* simple to do with an editor, if desired
- skip trashing old playlists
* updating the DB would need `root`
<details><summary>extract-vlc-playlist.sh</summary>
```sh
#!/bin/sh
# ADAPTED from https://codeberg.org/sphi/personal/src/branch/main/android/update-playlists.sh
# Extract playlists from VLC on Android and dump them to transferable .m3u8 files
# under `vlc_playlists` in the current directory.
#
# This script can be run on Linux with an Android device connected over USB,
# or directly in Termux on Android.
#
# Before running, select `Settings -> Advanced -> Dump media database` in VLC!
# That avoids the need for a rooted setup.
set -euo pipefail
DB=vlc_media.db
PL=vlc_playlists
bail() {
echo $'\x1b[1;31m'"$*"$'\x1b[m'
exit 1
}
[ -v ANDROID_ROOT ] || { # not on Android
if [ -r $DB ] && [ . = "${1-}" ]
then
shift
echo Using pre-fetched "'$DB'"
else
command -V adb || bail Missing "'adb'"
case "${1-}" in
.|:) # pull the DB, run locally
adb pull /sdcard/$DB
shift
;;
*) # push the script, run on-device
adb push "0ドル" /sdcard/
adb shell sh "/sdcard/$(basename "0ドル")" "$@"
exit 0
esac
fi
}
command -V sqlite3 || bail Missing "'sqlite3'"
[ -r $DB ] || bail Missing "'$DB'" in "'$PWD'"
[ -d $PL ] || mkdir -vp $PL
QUERY="
SELECT m.duration/1000, a.name, m.title, f.path, m.filename
FROM Media m
INNER JOIN PlaylistMediaRelation pmr
ON pmr.media_id=m.id_media
INNER JOIN Playlist p
ON pmr.playlist_id=p.id_playlist
INNER JOIN Artist a
ON m.artist_id=a.id_artist
INNER JOIN Folder f
ON f.id_folder=m.folder_id
WHERE p.name="
query() {
echo "$@" | sqlite3 -tabs $DB
}
urldecode() {
# remove 'file://' prefix
local urlencoded_path="${1#file://}"
# convert URL-encoded hex characters to first shell-escaped, then binary variant
# e.g. a dot `.` encoded as %2E becomes \x2E becomes $'\x2e'
printf '%b' "${urlencoded_path//%/\\x}"
}
extract_playlist() {
local name="1ドル"
echo \#EXTM3U
echo \#PLAYLIST:$name
query "$QUERY'$name'" | {
IFS=$'\t'
while read -r seconds artist title path filename
do echo \#EXTINF:$seconds,$artist - $title
echo "$(urldecode "$path")$filename"
done
}
}
# dump all playlists, when none specified
(($#)) || set -- $(query SELECT name FROM Playlist)
for name
do extract_playlist "$name" > "$PL/$name.m3u8"
done
echo "done"
```
</details>