most of program1 is done, shape of program2, actual app to be started
This commit is contained in:
@@ -56,9 +56,9 @@ flowchart TB
|
|||||||
dll L_dll_raw_0@== "onAirDropDownload.sh" ==> raw
|
dll L_dll_raw_0@== "onAirDropDownload.sh" ==> raw
|
||||||
raw L_raw_pgrm1_0@=== pgrm1
|
raw L_raw_pgrm1_0@=== pgrm1
|
||||||
pgrm1 L_pgrm1_videos_0@== ffmpeg ==> videos
|
pgrm1 L_pgrm1_videos_0@== ffmpeg ==> videos
|
||||||
pgrm1 -- register videos --> bdd
|
pgrm1 -- 1) register videos --> bdd
|
||||||
app["DanceVideos App"] -- classify, register playlists --> bdd
|
app["DanceVideos App"] -- 2) classify, register playlists --> bdd
|
||||||
pgrm2 -- convert playlists to directories of symlink --> playlists
|
pgrm2 -- 3) convert playlists to directories of symlink --> playlists
|
||||||
pgrm2 --- bdd
|
pgrm2 --- bdd
|
||||||
videos L_videos_playlists_0@-.- playlists
|
videos L_videos_playlists_0@-.- playlists
|
||||||
raw@{ shape: docs}
|
raw@{ shape: docs}
|
||||||
|
|||||||
25
model/register_video.sh
Normal file
25
model/register_video.sh
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# register_video \
|
||||||
|
# file_name raw_file duration mp4_file \
|
||||||
|
# rotated_file thumbnail_file record_datetime \
|
||||||
|
# day_of_week record_time \
|
||||||
|
# lat long address
|
||||||
|
register_video() {
|
||||||
|
local file_name=$1
|
||||||
|
local raw_file=$2
|
||||||
|
local duration=$3
|
||||||
|
local mp4_file=$4
|
||||||
|
local rotated_file=$5
|
||||||
|
local thumbnail_file=$6
|
||||||
|
local record_datetime=${7:-$(date +%s)}
|
||||||
|
local day_of_week=${8:-$(date +'%A')}
|
||||||
|
local lat=${9:-0.000000}
|
||||||
|
local long=${10:-0.000000}
|
||||||
|
local address=${11:-Unknown}
|
||||||
|
if [ -z "$raw_file" ] || [ -z "$mp4_file" ]; then
|
||||||
|
echo "Error: raw_file and mp4_file are required"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sqlite3 $DANCE_VIDEOS_DB "INSERT OR REPLACE INTO videos (file_name, raw_file, duration, mp4_file, rotated_file, thumbnail_file, record_datetime, day_of_week, lat, long, address) VALUES('$file_name','$raw_file', '$duration', '$mp4_file', '$rotated_file', '$thumbnail_file', $record_datetime, '$day_of_week', $lat, $long, '$address')"
|
||||||
|
}
|
||||||
|
export -f register_video
|
||||||
14
model/videos.sql
Normal file
14
model/videos.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS videos (
|
||||||
|
file_name VARCHAR(255) PRIMARY KEY,
|
||||||
|
raw_file VARCHAR(255) UNIQUE,
|
||||||
|
duration DECIMAL(10,2),
|
||||||
|
mp4_file VARCHAR(255),
|
||||||
|
rotated_file VARCHAR(255),
|
||||||
|
thumbnail_file VARCHAR(255),
|
||||||
|
record_datetime TIMESTAMP,
|
||||||
|
day_of_week VARCHAR(50),
|
||||||
|
record_time TIME GENERATED ALWAYS AS (TIME(record_datetime)) VIRTUAL,
|
||||||
|
lat DECIMAL(10,6),
|
||||||
|
long DECIMAL(11,7),
|
||||||
|
address VARCHAR(255)
|
||||||
|
);
|
||||||
98
program1/metadata.sh
Normal file
98
program1/metadata.sh
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
# === CONFIGURATION ===
|
||||||
|
|
||||||
|
# === Utilitaires ===
|
||||||
|
esc() { printf '%s' "$1" | sed 's/"/""/g'; }
|
||||||
|
export -f esc
|
||||||
|
|
||||||
|
safe_weekday() {
|
||||||
|
local iso="$1"
|
||||||
|
python3 - <<PY
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
s = "$iso"
|
||||||
|
try:
|
||||||
|
dt = datetime.fromisoformat(s)
|
||||||
|
print(dt.strftime('%A'))
|
||||||
|
except Exception:
|
||||||
|
print("Unknown")
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
export -f safe_weekday
|
||||||
|
|
||||||
|
get_creation_time() {
|
||||||
|
local f="$1"
|
||||||
|
local ct=""
|
||||||
|
for tag in CreateDate MediaCreateDate TrackCreateDate; do
|
||||||
|
ct=$(exiftool -"$tag" -d "%Y-%m-%dT%H:%M:%S" -s3 "$f" 2>/dev/null || true)
|
||||||
|
[ -n "$ct" ] && break
|
||||||
|
done
|
||||||
|
if [ -z "$ct" ]; then
|
||||||
|
ct="$(date -r "$f" +"%Y-%m-%dT%H:%M:%S")"
|
||||||
|
fi
|
||||||
|
echo "$ct"
|
||||||
|
}
|
||||||
|
export -f get_creation_time
|
||||||
|
|
||||||
|
get_duration() {
|
||||||
|
local f="$1"
|
||||||
|
ffprobe -v error -show_entries format=duration \
|
||||||
|
-of default=noprint_wrappers=1:nokey=1 "$f" 2>/dev/null || echo ""
|
||||||
|
}
|
||||||
|
export -f get_duration
|
||||||
|
|
||||||
|
get_gps() {
|
||||||
|
local f="$1"
|
||||||
|
local lat lon
|
||||||
|
lat="$(exiftool -GPSLatitude -n -s3 "$f" 2>/dev/null || true)"
|
||||||
|
lon="$(exiftool -GPSLongitude -n -s3 "$f" 2>/dev/null || true)"
|
||||||
|
echo "$lat|$lon"
|
||||||
|
}
|
||||||
|
export -f get_gps
|
||||||
|
|
||||||
|
reverse_geocode() {
|
||||||
|
local lat="$1" lon="$2"
|
||||||
|
local json="" postal city district
|
||||||
|
|
||||||
|
if [ -n "$lat" ] && [ -n "$lon" ]; then
|
||||||
|
json="$(curl -s "https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lon}&zoom=16&addressdetails=1")"
|
||||||
|
|
||||||
|
# Extraction sécurisée avec jq
|
||||||
|
postal=$(echo "$json" | jq -r '.address.postcode // ""')
|
||||||
|
city=$(echo "$json" | jq -r '.address.city // .address.town // .address.village // ""')
|
||||||
|
district=$(echo "$json" | jq -r '.address.neighbourhood // .address.suburb // .address.residential // .address.village // .address.municipality // ""')
|
||||||
|
|
||||||
|
# Concaténation formatée, ignorer champs vides
|
||||||
|
formatted=""
|
||||||
|
[ -n "$postal" ] && formatted="$formatted$postal"
|
||||||
|
[ -n "$city" ] && formatted="${formatted:+$formatted, }
|
||||||
|
$city"
|
||||||
|
[ -n "$district" ] && formatted="${formatted:+$formatted, }
|
||||||
|
$district"
|
||||||
|
|
||||||
|
echo "$formatted" | tr '\n' ' ' | tr -s ' ' ' ' | sed 's/ *$//'
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f reverse_geocode
|
||||||
|
|
||||||
|
process_video() {
|
||||||
|
local f="$1"
|
||||||
|
|
||||||
|
local ct weekday duration lat lon address
|
||||||
|
ct="$(get_creation_time "$f")"
|
||||||
|
weekday="$(safe_weekday "$ct")"
|
||||||
|
duration="$(get_duration "$f")"
|
||||||
|
IFS="|" read -r lat lon <<<"$(get_gps "$f")"
|
||||||
|
address="$(reverse_geocode "$lat" "$lon")"
|
||||||
|
|
||||||
|
echo "$ct|$weekday|$duration|$lat|$lon|$address"
|
||||||
|
# --- CSV row ---
|
||||||
|
# echo "\"$(basename "$f")\",\"$(esc "$f")\",\"$(esc "$ct")\",\"$(esc "$weekday")\",\"$(esc "$duration")\",\"$(esc "$lat")\",\"$(esc "$lon")\",\"$(esc "$formatted")\""
|
||||||
|
}
|
||||||
|
export -f process_video
|
||||||
|
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
IFS=$'\n\t'
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
SCRIPTS_DIR=$(dirname `realpath ${BASH_SOURCE[0]}`)
|
||||||
|
|
||||||
# === CONFIGURATION ===
|
# === CONFIGURATION ===
|
||||||
export DOSSIER_SOURCE="${HOME}/Downloads"
|
export DOSSIER_SOURCE="${HOME}/Downloads"
|
||||||
export DOSSIER_DESTINATION_RAW="${HOME}/Documents/.DanceVideos/raw"
|
export DOSSIER_DESTINATION_RAW="${HOME}/Documents/.DanceVideos/raw"
|
||||||
@@ -42,19 +44,84 @@ import_downloaded_file
|
|||||||
|
|
||||||
export DOSSIER_DESTINATION_MP4="$(dirname $DOSSIER_DESTINATION_RAW)/videos"
|
export DOSSIER_DESTINATION_MP4="$(dirname $DOSSIER_DESTINATION_RAW)/videos"
|
||||||
|
|
||||||
|
## DB utilities
|
||||||
|
export DANCE_VIDEOS_DB="$(dirname $DOSSIER_DESTINATION_RAW)/db.sqlite"
|
||||||
|
export MODEL_DIR=$(realpath $SCRIPTS_DIR/../model)
|
||||||
|
|
||||||
|
cat $MODEL_DIR/videos.sql | sqlite3 $DANCE_VIDEOS_DB
|
||||||
|
|
||||||
|
source $MODEL_DIR/register_video.sh
|
||||||
|
# register_video \
|
||||||
|
# file_name raw_file duration mp4_file \
|
||||||
|
# rotated_file thumbnail_file record_datetime day_of_week \
|
||||||
|
# lat long address
|
||||||
|
|
||||||
|
source $SCRIPTS_DIR/metadata.sh
|
||||||
|
source $SCRIPTS_DIR/rotation.sh
|
||||||
|
|
||||||
|
mp4_dir() {
|
||||||
|
local raw="$1" weekday="$2" address="${3:-wherever}"
|
||||||
|
address=$(sed 's/[^a-zA-Z0-9]/_/g' <<< $(sanitize_name "$address") | tr -s '_' '_')
|
||||||
|
local dir=$DOSSIER_DESTINATION_MP4
|
||||||
|
dir=$dir/$(date -jf "%Y-%m-%dT%H:%M:%S" "$(get_creation_time $raw)" +%Y%m%d_%H%M%S)
|
||||||
|
dir=${dir}_${weekday}_${address}
|
||||||
|
mkdir -p $dir
|
||||||
|
echo $dir
|
||||||
|
}
|
||||||
|
export -f mp4_dir
|
||||||
|
|
||||||
|
write_thumbnail() {
|
||||||
|
set -x
|
||||||
|
local raw="$1"
|
||||||
|
local thumbnail="$2"
|
||||||
|
ffmpeg -ss 00:00:03 -i $raw -vframes 1 $thumbnail 2>/dev/null
|
||||||
|
set +x
|
||||||
|
}
|
||||||
|
export -f write_thumbnail
|
||||||
|
|
||||||
|
write_mp4() {
|
||||||
|
local src="$1"
|
||||||
|
local dst="$2"
|
||||||
|
[ -e "$dst" ] || reencode_with_rotation $src $dst
|
||||||
|
}
|
||||||
|
export -f write_mp4
|
||||||
|
|
||||||
|
process_raw_file() {
|
||||||
|
local raw="$1"
|
||||||
|
|
||||||
|
local ct weekday duration lat lon address
|
||||||
|
IFS="|" read -r ct weekday duration lat lon address <<<"$(process_video "$raw")"
|
||||||
|
|
||||||
|
local dir=$(mp4_dir $raw $weekday "$address")
|
||||||
|
|
||||||
|
local thumbnail=${dir}/thumbnail.jpg
|
||||||
|
$(write_thumbnail $raw $thumbnail)
|
||||||
|
|
||||||
|
local mp4=${dir}/video.mp4
|
||||||
|
$(write_mp4 $raw $mp4)
|
||||||
|
|
||||||
|
register_video $(basename $raw) "$raw" "$duration" "$mp4" \
|
||||||
|
"$mp4" "$thumbnail" "'$ct'" "$weekday" \
|
||||||
|
"$lat" "$lon" "$address"
|
||||||
|
}
|
||||||
|
export -f process_raw_file
|
||||||
|
|
||||||
iphone_video() {
|
iphone_video() {
|
||||||
local raw="$1"
|
local raw="$1"
|
||||||
echo iphone $raw
|
echo iphone $raw
|
||||||
|
process_raw_file $raw
|
||||||
}
|
}
|
||||||
export -f iphone_video
|
export -f iphone_video
|
||||||
screen_video() {
|
screen_video() {
|
||||||
local raw="$1"
|
local raw="$1"
|
||||||
echo screen $raw
|
echo screen $raw
|
||||||
|
process_raw_file $raw
|
||||||
}
|
}
|
||||||
export -f screen_video
|
export -f screen_video
|
||||||
whatsapp_video() {
|
whatsapp_video() {
|
||||||
local raw="$1"
|
local raw="$1"
|
||||||
echo whatsapp $raw
|
echo whatsapp $raw
|
||||||
|
process_raw_file $raw
|
||||||
}
|
}
|
||||||
export -f whatsapp_video
|
export -f whatsapp_video
|
||||||
|
|
||||||
|
|||||||
46
program1/rotation.sh
Normal file
46
program1/rotation.sh
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
get_rotation_filter() {
|
||||||
|
local f="$1"
|
||||||
|
local rotation
|
||||||
|
rotation=$(exiftool -Rotation -n "$f" | awk '{print $3}')
|
||||||
|
# echo $rotation $f >> "rotations.txt"
|
||||||
|
case "$rotation" in
|
||||||
|
# 90) echo "transpose=1" ;;
|
||||||
|
# 270) echo "transpose=2" ;;
|
||||||
|
# 180) echo "hflip,vflip" ;;
|
||||||
|
# *) echo "" ;;
|
||||||
|
*) echo "transpose=1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
export -f get_rotation_filter
|
||||||
|
|
||||||
|
reencode_with_rotation() {
|
||||||
|
local src="$1"
|
||||||
|
local dst="$2"
|
||||||
|
local filter
|
||||||
|
filter="$(get_rotation_filter "$src")"
|
||||||
|
|
||||||
|
if [ -n "$filter" ]; then
|
||||||
|
echo " Correction d’orientation (rotation=${filter})"
|
||||||
|
if ffmpeg -encoders 2>/dev/null | grep -q 'h264_videotoolbox'; then
|
||||||
|
ffmpeg -nostdin -i "$src" -vf "$filter" \
|
||||||
|
-c:v h264_videotoolbox -b:v 5M -c:a aac -map_metadata -1 -y "$dst"
|
||||||
|
else
|
||||||
|
ffmpeg -nostdin -i "$src" -vf "$filter" \
|
||||||
|
-c:v libx264 -preset fast -crf 18 -c:a aac -map_metadata -1 -y "$dst"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Pas de rotation → simple copie (ou réencodage minimal)
|
||||||
|
if ffmpeg -y -i "$src" -c copy -map 0 -movflags +faststart "$dst" < /dev/null 2>/dev/null; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
if ffmpeg -encoders 2>/dev/null | grep -q 'h264_videotoolbox'; then
|
||||||
|
ffmpeg -nostdin -i "$src" \
|
||||||
|
-c:v h264_videotoolbox -b:v 5M -c:a aac -map_metadata -1 -y "$dst"
|
||||||
|
else
|
||||||
|
ffmpeg -nostdin -i "$src" \
|
||||||
|
-c:v libx264 -preset fast -crf 18 -c:a aac -map_metadata -1 -y "$dst"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f reencode_with_rotation
|
||||||
14
program2/program2.sh
Executable file
14
program2/program2.sh
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
set -euo pipefail
|
||||||
|
SCRIPTS_DIR=$(dirname `realpath ${BASH_SOURCE[0]}`)
|
||||||
|
|
||||||
|
export DANCE_VIDEOS_DB="${HOME}/Documents/.DanceVideos/db.sqlite"
|
||||||
|
export DOSSIER_PLAYLIST="$(dirname $DANCE_VIDEOS_DB)/playlists"
|
||||||
|
|
||||||
|
rm -rf $DOSSIER_PLAYLIST
|
||||||
|
|
||||||
|
export PLAYLIST_ALL=$DOSSIER_PLAYLIST/all
|
||||||
|
mkdir -p $PLAYLIST_ALL
|
||||||
|
|
||||||
|
for v in $(sqlite3 $DANCE_VIDEOS_DB "select rotated_file from videos"); do
|
||||||
|
ln -s $v $PLAYLIST_ALL/$(basename $(dirname $v)).mp4
|
||||||
|
done
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
set -eux
|
set -eux
|
||||||
|
SCRIPTS_DIR=$(dirname `realpath ${BASH_SOURCE[0]}`)
|
||||||
|
|
||||||
LOCK_FILE="/tmp/.lock.onSD_DANSE"
|
LOCK_FILE="/tmp/.lock.onSD_DANSE"
|
||||||
playsound="afplay /System/Library/Sounds/Morse.aiff"
|
playsound="afplay /System/Library/Sounds/Morse.aiff"
|
||||||
@@ -21,6 +22,8 @@ fi
|
|||||||
SOURCE_DIR=${HOME}/Documents/.DanceVideos/playlists/ # final / is important to delete
|
SOURCE_DIR=${HOME}/Documents/.DanceVideos/playlists/ # final / is important to delete
|
||||||
mkdir -p ${SOURCE_DIR}
|
mkdir -p ${SOURCE_DIR}
|
||||||
|
|
||||||
|
$SCRIPTS_DIR/../program2/program2.sh
|
||||||
|
|
||||||
DESTINATION_DIR=TEST
|
DESTINATION_DIR=TEST
|
||||||
mkdir -p /Volumes/SD_DANSE/${DESTINATION_DIR}
|
mkdir -p /Volumes/SD_DANSE/${DESTINATION_DIR}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user