1
1
Fork
You've already forked personal
0

VLC playlist extraction #1

Closed
opened 2025年04月06日 14:06:56 +02:00 by andraxin · 1 comment

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)
  • 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
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>
Owner
Copy link

Thanks! Finally rewrote my version to be less bad. I didn't use all your changes but your one big query per playlist was very helpful to speed it up.

Thanks! Finally rewrote my version to be less bad. I didn't use all your changes but your one big query per playlist was very helpful to speed it up.
Sign in to join this conversation.
No Branch/Tag specified
main
ubuntu-xps
No results found.
Labels
Clear labels
No items
No labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sphi/personal#1
Reference in a new issue
sphi/personal
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?