Skip to content

Commit 47f2e28

Browse files
subotickilchenmannBalduinLandoltirinaschubert
authored
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>
1 parent 603efef commit 47f2e28

36 files changed

Lines changed: 5364 additions & 5054 deletions

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ dependencies.txt
3737
/client-test-data.zip
3838
/sipi/images/082E/*
3939
/sipi/images/originals/082E/*
40+
sipi/images/1111/*
41+
sipi/images/originals/1111/*
4042
/dependencies.bzl
4143

4244
.idea/

‎docker-compose.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ services:
3131
environment:
3232
- TZ=Europe/Zurich
3333
- SIPI_EXTERNAL_PROTOCOL=http
34-
- SIPI_EXTERNAL_HOSTNAME=localhost
34+
- SIPI_EXTERNAL_HOSTNAME=0.0.0.0
3535
- SIPI_EXTERNAL_PORT=1024
3636
- SIPI_WEBAPI_HOSTNAME=api
3737
- SIPI_WEBAPI_PORT=3333

‎knora-ontologies/knora-base.ttl‎

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,11 +1195,9 @@
11951195

11961196
:fps rdf:type owl:DatatypeProperty ;
11971197

1198-
rdfs:label "Frames per second"@en ;
1199-
12001198
rdfs:subPropertyOf :valueHas ;
12011199

1202-
:subjectClassConstraint :MovingImageFileValue ;
1200+
:subjectClassConstraint :FileValue ;
12031201

12041202
:objectDatatypeConstraint xsd:decimal .
12051203

@@ -2183,23 +2181,7 @@
21832181

21842182
:MovingImageFileValue rdf:type owl:Class ;
21852183

2186-
rdfs:subClassOf :FileValue ,
2187-
[ rdf:type owl:Restriction ;
2188-
owl:onProperty :dimX ;
2189-
owl:cardinality "1"^^xsd:nonNegativeInteger
2190-
] ,
2191-
[ rdf:type owl:Restriction ;
2192-
owl:onProperty :dimY ;
2193-
owl:cardinality "1"^^xsd:nonNegativeInteger
2194-
] ,
2195-
[ rdf:type owl:Restriction ;
2196-
owl:onProperty :fps ;
2197-
owl:maxCardinality "1"^^xsd:nonNegativeInteger
2198-
] ,
2199-
[ rdf:type owl:Restriction ;
2200-
owl:onProperty :duration ;
2201-
owl:maxCardinality "1"^^xsd:nonNegativeInteger
2202-
] ;
2184+
rdfs:subClassOf :FileValue ;
22032185

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

‎project/Dependencies.scala‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ import sbt.Keys._
99
import sbt.{Def, _}
1010

1111
object Dependencies {
12+
13+
val fusekiImage = "daschswiss/apache-jena-fuseki:2.0.8" // should be the same version as in docker-compose.yml
14+
val sipiImage = "daschswiss/sipi:3.5.0" // base image the knora-sipi image is created from
15+
1216
// versions
1317
val akkaHttpVersion = "10.2.9"
1418
val akkaVersion = "2.6.19"
15-
val fusekiImage = "daschswiss/apache-jena-fuseki:2.0.8" // should be the same version as in docker-compose.yml
1619
val jenaVersion = "4.4.0"
1720
val metricsVersion = "4.0.1"
1821
val scalaVersion = "2.13.8"
19-
val sipiImage = "daschswiss/sipi:3.3.4" // base image the knora-sipi image is created from
2022
val ZioHttpVersion = "2.0.0-RC3"
2123
val ZioPreludeVersion = "1.0.0-RC10"
2224
val ZioVersion = "2.0.0-RC2"
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
#!/bin/bash
2+
3+
# Export moving image frames for preview in search results and on timeline
4+
# Extract frames from mp4 file:
5+
# - 6 frames per minute
6+
# Create matrix file:
7+
# - max. 36 frames per matrix
8+
# - one matrix file corresponds to 6 minutes of film
9+
10+
set -e
11+
12+
dir=$(pwd)
13+
sep='---------------------------------'
14+
15+
die() {
16+
echo >&2 "$@"
17+
exit 1
18+
}
19+
20+
usage() {
21+
echo "usage: <command> -i [infile]>"
22+
echo ${sep}
23+
echo "-i = Inpute file"
24+
echo ${sep}
25+
}
26+
27+
collect() {
28+
film=''
29+
cnt=1
30+
#for b in `ls`; do
31+
for file in *.$1; do
32+
[ -f ${file} ]
33+
name=$2
34+
35+
for c in ${cnt}; do
36+
if [ "$film" == '' ] || [ "$film" == 'VIDEO_TS.VOB' ]; then
37+
film=${file}
38+
else
39+
case $3 in
40+
true) film="$film|$file" ;;
41+
esac
42+
fi
43+
done
44+
cnt=$(expr ${cnt} + 1)
45+
done
46+
needle="|"
47+
num=$(grep -o "$needle" <<<"$film" | wc -l)
48+
num=$(expr ${num} + 1)
49+
}
50+
51+
# default variables
52+
infile=''
53+
54+
# alternative to get some information from the source file:
55+
# ffprobe -v quiet -print_format json -show_format -show_streams ~/sourceFile.mov
56+
57+
while getopts ':i:' OPTION; do
58+
case ${OPTION} in
59+
i) infile=$OPTARG ;;
60+
*) echo 'Unknown Parameter' ;;
61+
esac
62+
done
63+
64+
if [ $# -eq 0 ]; then
65+
usage
66+
die ''
67+
fi
68+
69+
# check the arguments: if they exist
70+
num='^[0-9]+$'
71+
72+
# infile must be given and has to be an mp4
73+
if [ -z "${infile}" ]; then
74+
# must be given
75+
usage
76+
die "ERROR: The Input File (-i) is missing"
77+
fi
78+
79+
# check if mime-type is video/mp4
80+
if file -b --mime-type "${infile}" -ne "video/mp4"; then
81+
die "ERROR: The Input File (-i) is not mp4"
82+
fi
83+
84+
# get the right output path, the file and the filename (without extension)
85+
dir=$(dirname "${infile}")
86+
file=$(basename "${infile}")
87+
name="${file%.*}"
88+
89+
cd $dir
90+
91+
# --
92+
# store the frames and matrix files in specific folder;
93+
# folder has same unique name as the uploaded file
94+
mkdir -p $name
95+
cd $name
96+
97+
# --
98+
# read frame rate from input file
99+
framerate=$(ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate ../"${file}")
100+
101+
IFS="/" read -a array <<<"$framerate"
102+
numerator="${array[0]}"
103+
denumerator="${array[1]}"
104+
105+
fps=$(expr "scale=2;${numerator}/${denumerator}" | bc)
106+
107+
# --
108+
# read aspect ratio from input file
109+
aspect=$(ffprobe -v error -select_streams v:0 -show_entries stream=display_aspect_ratio -of csv=s=x:p=0 ../"${file}")
110+
111+
echo $aspect
112+
113+
# --
114+
# split the aspect ratio to have two separated numbers for calculating
115+
IFS=':' read -a array <<<"$aspect"
116+
aspectW="${array[0]}"
117+
aspectH="${array[1]}"
118+
119+
# --
120+
# framesize
121+
framewidth='256'
122+
frameheight=$(($framewidth * $aspectH / $aspectW))
123+
framesize=${framewidth}'x'${frameheight}
124+
125+
echo ${sep}
126+
echo 'Start with frame export'
127+
# check the outpath for the frames;
128+
# if exists, delete it and create new
129+
if [ -d "frames" ]; then
130+
rm -rf ./frames
131+
fi
132+
mkdir -p 'frames'
133+
134+
framestart=$(echo ${start} + 1 | bc)
135+
ffmpeg -i ../${file} -an -ss 1 -f image2 -s ${framesize} -vf framestep=${fps} frames/${name}'_f_%d.jpg' 2>&1
136+
137+
echo 'Done'
138+
echo ${sep}
139+
140+
# --
141+
# create the matrix files
142+
echo 'Start with creating matrix file'
143+
144+
# change directory frames
145+
cd frames
146+
# Get the number of files: one image = one second of movie
147+
# numfiles is equivalent to the movie duration (in seconds)
148+
numfiles=(*)
149+
numfiles=${#numfiles[@]}
150+
151+
image="${name}_f_1.jpg"
152+
153+
# check if file exists
154+
if [[ ! -f "${image}" ]]; then
155+
die "File not found: ${image}"
156+
fi
157+
158+
# grab the identify string, make sure it succeeded
159+
# IMG_CHARS=$(identify "${image}" 2> /dev/null) || die "${image} is not a proper image"
160+
# grab width and height
161+
IMG_CHARS=$(echo "${IMG_CHARS}" | sed -n 's/\(^.*\)\ \([0-9]*\)x\([0-9]*\)\ \(.*$\)/\2 \3/p')
162+
163+
width=$(echo "${IMG_CHARS}" | awk '{print $1}')
164+
height=$(echo "${IMG_CHARS}" | awk '{print $2}')
165+
166+
ar=$(echo "scale=6; ${width}/${height}" | bc)
167+
# width for the whole output matrix image: 960px
168+
# so one preview image has a width of 160px
169+
# calculate the height for the output matrix image form the aspect ratio
170+
matrix_width="960"
171+
matrix_height=$(echo "scale=0; ${matrix_width}/${ar}" | bc)
172+
matrix_size="${matrix_width}x${matrix_height}"
173+
174+
frame_width="160"
175+
frame_height=$(echo "scale=0; ${matrix_height}/6" | bc)
176+
frame_size="${frame_width}x${frame_height}"
177+
# get every 10th image; start with the image number 5;
178+
# how many matrixes will it produce?
179+
calcmatrix=$(echo "scale=4; (${numfiles}/10)/36" | bc)
180+
echo '#Matrix (calculated) '${calcmatrix}
181+
nummatrix=$(echo ${calcmatrix} | awk '{printf("%.0f\n",$1 + 0.75)}')
182+
echo '#Matrix (rounded) '${nummatrix}
183+
184+
t=0
185+
while [ ${t} -lt ${nummatrix} ]; do
186+
echo 'Matrix nr '${t}
187+
firstframe=$(echo "scale=0; ${t}*360+5" | bc)
188+
MATRIX=$(find *_${firstframe}.jpg)' '
189+
190+
c=1
191+
while [ ${c} -lt 36 ]; do
192+
sec=$(echo "scale=0; ${firstframe}+(${c}*10)" | bc)
193+
if [ $sec -lt $numfiles ]; then
194+
img="${name}_f_${sec}.jpg"
195+
if [ -f ${img} ]; then
196+
MATRIX+=${img}' '
197+
fi
198+
fi
199+
200+
let c=c+1
201+
done
202+
203+
# here we make the montage of every matrix file
204+
# montage -size 320x180 DB_4_5.jpg DB_4_15.jpg DB_4_25.jpg DB_4_35.jpg -geometry +0+0 montage1.jpg
205+
# $(echo "montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../matrix/${name}'_m_'${t}'.jpg'")
206+
montage -size ${matrix_size} ${MATRIX} -tile 6x6 -geometry +0+0 -resize ${frame_size} ../${name}'_m_'${t}'.jpg' 2>&1
207+
208+
let t=t+1
209+
210+
done
211+
212+
echo 'Done'
213+
echo ${sep}

‎sipi/scripts/file_info.lua‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,14 @@ function make_audio_file_info(extension)
144144
end
145145

146146
function make_video_file_info(extension)
147-
return {
148-
media_type = VIDEO,
149-
extension = extension
150-
}
147+
if not table.contains(video_extensions, extension) then
148+
return nil
149+
else
150+
return {
151+
media_type = VIDEO,
152+
extension = extension
153+
}
154+
end
151155
end
152156

153157
function make_text_file_info(extension)

0 commit comments

Comments
 (0)