Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(sipi): upload video support (DEV-771 / DEV-207) (#1952)
* feat(sipi): upload video

* feat(sipi): upload video

* feat(sipi): upload video

* build: bump sipi version to one with updated knora.json

* build: bump testcontainers

* feat(sipi): add shell lua

* feat(sipi): store video

* feat(sipi): support video upload

* test: activate for video representation

* test: add debugging output

* build: fix missing env file

* refactor(sipi): restore store.lua script

* feat(sipi): support video upload

* refactor(sipi): restore store.lua script

* refactor(sipi): clean up video upload

* build: add new sipi version with knora.info video support

* chore(sipi): hardcoded fps in case of video

* chore(deps): update sipi

* feat(upload): more on video upload

* refactor: remove properties from moving image file value

* test: regenerate ontology test responses

* deps(sipi): bump to newer version with video information support

* refactor(gh-ci): format indent

* chore(deps): bump sipi to latest (incl. bc and imagemagick)

* refactor(sipi): clean up and improve upload script

* refactor(sipi): clean up and improve upload script

* refactor(sipi): clean up and improve upload script

* refactor(sipi): clean up and improve store script

* feat(sipi): add script to export frames from video

* test(ontologie): regenerate test data

* refactor(sipi): remove commented code

* refactor(sipi): add missing newline

* refactor(sipi): update comment

Co-authored-by: Balduin Landolt <33053745+BalduinLandolt@users.noreply.github.com>

* refactor(sipi): remove author from shell script

* refactor(sipi): remove unused script

* refactor(sipi): fix typo in coment

Co-authored-by: irinaschubert <irina.schubert@dasch.swiss>

* refactor(sipi): update comment

Co-authored-by: irinaschubert <irina.schubert@dasch.swiss>

* refactor(sipi): delete empty and unused function

Co-authored-by: Kilchenmann <github@milchkannen.ch>
Co-authored-by: Balduin Landolt <33053745+BalduinLandolt@users.noreply.github.com>
Co-authored-by: irinaschubert <irina.schubert@dasch.swiss>
  • Loading branch information
4 people committed Apr 5, 2022
1 parent 603efef commit 47f2e28
Show file tree
Hide file tree
Showing 36 changed files with 5,364 additions and 5,054 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -37,6 +37,8 @@ dependencies.txt
/client-test-data.zip
/sipi/images/082E/*
/sipi/images/originals/082E/*
sipi/images/1111/*
sipi/images/originals/1111/*
/dependencies.bzl

.idea/
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Expand Up @@ -31,7 +31,7 @@ services:
environment:
- TZ=Europe/Zurich
- SIPI_EXTERNAL_PROTOCOL=http
- SIPI_EXTERNAL_HOSTNAME=localhost
- SIPI_EXTERNAL_HOSTNAME=0.0.0.0
- SIPI_EXTERNAL_PORT=1024
- SIPI_WEBAPI_HOSTNAME=api
- SIPI_WEBAPI_PORT=3333
Expand Down
22 changes: 2 additions & 20 deletions knora-ontologies/knora-base.ttl
Expand Up @@ -1195,11 +1195,9 @@

:fps rdf:type owl:DatatypeProperty ;

rdfs:label "Frames per second"@en ;

rdfs:subPropertyOf :valueHas ;

:subjectClassConstraint :MovingImageFileValue ;
:subjectClassConstraint :FileValue ;

:objectDatatypeConstraint xsd:decimal .

Expand Down Expand Up @@ -2183,23 +2181,7 @@

:MovingImageFileValue rdf:type owl:Class ;

rdfs:subClassOf :FileValue ,
[ rdf:type owl:Restriction ;
owl:onProperty :dimX ;
owl:cardinality "1"^^xsd:nonNegativeInteger
] ,
[ rdf:type owl:Restriction ;
owl:onProperty :dimY ;
owl:cardinality "1"^^xsd:nonNegativeInteger
] ,
[ rdf:type owl:Restriction ;
owl:onProperty :fps ;
owl:maxCardinality "1"^^xsd:nonNegativeInteger
] ,
[ rdf:type owl:Restriction ;
owl:onProperty :duration ;
owl:maxCardinality "1"^^xsd:nonNegativeInteger
] ;
rdfs:subClassOf :FileValue ;

rdfs:comment "Represents a moving image file"@en .

Expand Down
6 changes: 4 additions & 2 deletions project/Dependencies.scala
Expand Up @@ -9,14 +9,16 @@ import sbt.Keys._
import sbt.{Def, _}

object Dependencies {

val fusekiImage = "daschswiss/apache-jena-fuseki:2.0.8" // should be the same version as in docker-compose.yml
val sipiImage = "daschswiss/sipi:3.5.0" // base image the knora-sipi image is created from

// versions
val akkaHttpVersion = "10.2.9"
val akkaVersion = "2.6.19"
val fusekiImage = "daschswiss/apache-jena-fuseki:2.0.8" // should be the same version as in docker-compose.yml
val jenaVersion = "4.4.0"
val metricsVersion = "4.0.1"
val scalaVersion = "2.13.8"
val sipiImage = "daschswiss/sipi:3.3.4" // base image the knora-sipi image is created from
val ZioHttpVersion = "2.0.0-RC3"
val ZioPreludeVersion = "1.0.0-RC10"
val ZioVersion = "2.0.0-RC2"
Expand Down
213 changes: 213 additions & 0 deletions sipi/scripts/export-moving-image-frames.sh
@@ -0,0 +1,213 @@
#!/bin/bash

# Export moving image frames for preview in search results and on timeline
# Extract frames from mp4 file:
# - 6 frames per minute
# Create matrix file:
# - max. 36 frames per matrix
# - one matrix file corresponds to 6 minutes of film

set -e

dir=$(pwd)
sep='---------------------------------'

die() {
echo >&2 "$@"
exit 1
}

usage() {
echo "usage: <command> -i [infile]>"
echo ${sep}
echo "-i = Inpute file"
echo ${sep}
}

collect() {
film=''
cnt=1
#for b in `ls`; do
for file in *.$1; do
[ -f ${file} ]
name=$2

for c in ${cnt}; do
if [ "$film" == '' ] || [ "$film" == 'VIDEO_TS.VOB' ]; then
film=${file}
else
case $3 in
true) film="$film|$file" ;;
esac
fi
done
cnt=$(expr ${cnt} + 1)
done
needle="|"
num=$(grep -o "$needle" <<<"$film" | wc -l)
num=$(expr ${num} + 1)
}

# default variables
infile=''

# alternative to get some information from the source file:
# ffprobe -v quiet -print_format json -show_format -show_streams ~/sourceFile.mov

while getopts ':i:' OPTION; do
case ${OPTION} in
i) infile=$OPTARG ;;
*) echo 'Unknown Parameter' ;;
esac
done

if [ $# -eq 0 ]; then
usage
die ''
fi

# check the arguments: if they exist
num='^[0-9]+$'

# infile must be given and has to be an mp4
if [ -z "${infile}" ]; then
# must be given
usage
die "ERROR: The Input File (-i) is missing"
fi

# check if mime-type is video/mp4
if file -b --mime-type "${infile}" -ne "video/mp4"; then
die "ERROR: The Input File (-i) is not mp4"
fi

# get the right output path, the file and the filename (without extension)
dir=$(dirname "${infile}")
file=$(basename "${infile}")
name="${file%.*}"

cd $dir

# --
# store the frames and matrix files in specific folder;
# folder has same unique name as the uploaded file
mkdir -p $name
cd $name

# --
# read frame rate from input file
framerate=$(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate ../"${file}")

IFS="/" read -a array <<<"$framerate"
numerator="${array[0]}"
denumerator="${array[1]}"

fps=$(expr "scale=2;${numerator}/${denumerator}" | bc)

# --
# read aspect ratio from input file
aspect=$(ffprobe -v error -select_streams v:0 -show_entries stream=display_aspect_ratio -of csv=s=x:p=0 ../"${file}")

echo $aspect

# --
# split the aspect ratio to have two separated numbers for calculating
IFS=':' read -a array <<<"$aspect"
aspectW="${array[0]}"
aspectH="${array[1]}"

# --
# framesize
framewidth='256'
frameheight=$(($framewidth * $aspectH / $aspectW))
framesize=${framewidth}'x'${frameheight}

echo ${sep}
echo 'Start with frame export'
# check the outpath for the frames;
# if exists, delete it and create new
if [ -d "frames" ]; then
rm -rf ./frames
fi
mkdir -p 'frames'

framestart=$(echo ${start} + 1 | bc)
ffmpeg -i ../${file} -an -ss 1 -f image2 -s ${framesize} -vf framestep=${fps} frames/${name}'_f_%d.jpg' 2>&1

echo 'Done'
echo ${sep}

# --
# create the matrix files
echo 'Start with creating matrix file'

# change directory frames
cd frames
# Get the number of files: one image = one second of movie
# numfiles is equivalent to the movie duration (in seconds)
numfiles=(*)
numfiles=${#numfiles[@]}

image="${name}_f_1.jpg"

# check if file exists
if [[ ! -f "${image}" ]]; then
die "File not found: ${image}"
fi

# grab the identify string, make sure it succeeded
# IMG_CHARS=$(identify "${image}" 2> /dev/null) || die "${image} is not a proper image"
# grab width and height
IMG_CHARS=$(echo "${IMG_CHARS}" | sed -n 's/\(^.*\)\ \([0-9]*\)x\([0-9]*\)\ \(.*$\)/\2 \3/p')

width=$(echo "${IMG_CHARS}" | awk '{print $1}')
height=$(echo "${IMG_CHARS}" | awk '{print $2}')

ar=$(echo "scale=6; ${width}/${height}" | bc)
# width for the whole output matrix image: 960px
# so one preview image has a width of 160px
# calculate the height for the output matrix image form the aspect ratio
matrix_width="960"
matrix_height=$(echo "scale=0; ${matrix_width}/${ar}" | bc)
matrix_size="${matrix_width}x${matrix_height}"

frame_width="160"
frame_height=$(echo "scale=0; ${matrix_height}/6" | bc)
frame_size="${frame_width}x${frame_height}"
# get every 10th image; start with the image number 5;
# how many matrixes will it produce?
calcmatrix=$(echo "scale=4; (${numfiles}/10)/36" | bc)
echo '#Matrix (calculated) '${calcmatrix}
nummatrix=$(echo ${calcmatrix} | awk '{printf("%.0f\n",$1 + 0.75)}')
echo '#Matrix (rounded) '${nummatrix}

t=0
while [ ${t} -lt ${nummatrix} ]; do
echo 'Matrix nr '${t}
firstframe=$(echo "scale=0; ${t}*360+5" | bc)
MATRIX=$(find *_${firstframe}.jpg)' '

c=1
while [ ${c} -lt 36 ]; do
sec=$(echo "scale=0; ${firstframe}+(${c}*10)" | bc)
if [ $sec -lt $numfiles ]; then
img="${name}_f_${sec}.jpg"
if [ -f ${img} ]; then
MATRIX+=${img}' '
fi
fi

let c=c+1
done

# here we make the montage of every matrix file
# montage -size 320x180 DB_4_5.jpg DB_4_15.jpg DB_4_25.jpg DB_4_35.jpg -geometry +0+0 montage1.jpg
# $(echo "montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../matrix/${name}'_m_'${t}'.jpg'")
montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../${name}'_m_'${t}'.jpg' 2>&1

let t=t+1

done

echo 'Done'
echo ${sep}
12 changes: 8 additions & 4 deletions sipi/scripts/file_info.lua
Expand Up @@ -144,10 +144,14 @@ function make_audio_file_info(extension)
end

function make_video_file_info(extension)
return {
media_type = VIDEO,
extension = extension
}
if not table.contains(video_extensions, extension) then
return nil
else
return {
media_type = VIDEO,
extension = extension
}
end
end

function make_text_file_info(extension)
Expand Down

0 comments on commit 47f2e28

Please sign in to comment.