try levenshtein sort, but it is too slow
1 files changed, 96 insertions(+), 13 deletions(-)

M site/gms.scm
M site/gms.scm +96 -13
@@ 27,7 27,8 @@ exec -a "$0" guile -L $(realpath $(dirna
         (ice-9 rdelim)
         (ice-9 string-fun)
         (ice-9 ftw)
-        (ice-9 format))
+        (ice-9 format)
+        (srfi srfi-1))
 
 (define-syntax-rule (read-first-line command)
   (let* ((port (open-input-pipe command))

          
@@ 35,14 36,62 @@ exec -a "$0" guile -L $(realpath $(dirna
     (close-pipe port)
     res))
 
+(define-syntax-rule (read-all-lines command)
+  (let* ((port (open-input-pipe command))
+         (res '()))
+    (while (not (eof-object? (peek-char port)))
+      (set! res (cons (read-line port) res)))
+    (close-pipe port)
+    (reverse (cdr res))))
+
 (define (read-file-as-string filename)
   (let* ((port (open-input-file filename))
          (content (if (eof-object? (peek-char port)) "" (read-delimited "" port))))
     (close port)
     content))
 
+(define (filename-extension filename)
+  (string-append "." (car (take-right (string-split filename #\.) 1))))
+
+(define (basename->title basename)
+  (string-join
+   (string-tokenize
+    basename (char-set-complement (list->char-set
+                                   (apply append
+                                          (map char-set->list
+                                               (list char-set:symbol char-set:punctuation char-set:iso-control char-set:blank))))))
+   " "))
+
+(define levenshtein
+  (let ((cache '()))
+    (λ (s t)
+      (define key `(,s ,t))
+      (or (and=> (assoc key cache) cdr)
+          (let ((res
+                 (let lev ((s (string->list s))
+                           (sl (string-length s))
+                           (t (string->list t))
+                           (tl (string-length t)))
+                   (cond ((zero? sl) tl)
+                         ((zero? tl) sl)
+                         (else
+	                      (min (+ (lev (cdr s) (- sl 1) t tl) 1)
+                               (+ (lev s sl (cdr t) (- tl 1)) 1)
+                               (+ (lev (cdr s) (- sl 1) (cdr t) (- tl 1))
+		                          (if (char=? (car s) (car t)) 0 1))))))))
+            (set! cache (alist-cons key res cache))
+            res)))))
+
+(define (levenshtein-sort stream-files streamname)
+  (display 'stream-files) 
+  (write stream-files)
+    (newline)
+
 (define (convert-video filename)
-  (define basename (basename filename))
+  "Convert a video file to a freenet stream"
+  (define name (basename filename))
+  (define basename-without-extension (basename filename (filename-extension filename)))
+  (define streamname (format #f "~a-stream.m3u" basename-without-extension))
   (define start 0)
   (define len 5)
   (define stop (+ start len))

          
@@ 50,20 99,49 @@ exec -a "$0" guile -L $(realpath $(dirna
     (set! start (+ start len))
     (set! len (+ len 10))
     (set! stop (+ start len)))
-  (define (ffmpeg index start stop) 
-    (close-pipe (open-input-pipe 
-                 (format #f "ffmpeg -ss ~d -to ~d -accurate_seek -i ~a -y -g 360 -q:a 3 -q:v 3 -filter:v scale=640:-1 ~a-~3'0d.ogv"
-                         start stop filename index filename))))
-  (map (λ(x) (ffmpeg x start stop)(step)) (iota 27))
-  (close-pipe (open-input-pipe (format #f "mplayer ~a -ss 5 -nosound -vf scale -zoom -xy 600 -vo jpeg:outdir=. -frames 1" filename)))
-  (close-pipe (open-input-pipe (format #f "ls ~a-*ogv > ~a-stream.m3u" filename filename))))
+  (define duration-seconds
+    (inexact->exact
+     (string->number (read-first-line (format #f "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ~a" filename)))))
+  (define (ffmpeg index start stop)
+    (when (and #false (< start duration-seconds)) ;; skip early when the video is finished.
+      (close-pipe (open-input-pipe 
+                   (format #f "ffmpeg -ss ~d -to ~d -accurate_seek -i ~a -y -g 360 -q:a 3 -q:v 3 -filter:v scale=640:-1 ~a-~3'0d.ogv"
+                           start stop filename basename-without-extension index)))))
+  ;; convert the video in segments
+  (map (λ(x) (ffmpeg x start stop)(step)) (iota 999))
+  (close-pipe (open-input-pipe (format #f "mplayer ~a -ss 5 -nosound -vf scale -zoom -xy 600 -vo jpeg:outdir=. -frames 1 && cp 00000001.jpg ~a.jpg" filename basename-without-extension)))
+  ;; move the file to the current directory if needed
+  (when (not (equal? name filename))
+    (close-pipe (open-input-pipe (format #f "mv ~a ~a" filename name))))
+  ;; create stream playlist that continues with random other playlists after finishing. This might benefit from heuristics like sorting later streams by similarity to the original stream
+  (close-pipe (open-input-pipe (format #f "ls ~a-*ogv > ~a" basename-without-extension streamname)))
+  (let*  ((stream-files (read-all-lines "ls *-stream.m3u"))
+          (sorted (levenshtein-sort stream-files streamname)))
+    (map (λ (filename)(format #f "echo '~a' >> ~a" filename streamname)) sorted)
+    (list (cons 'filename filename)
+          (cons 'basename name)
+          (cons 'streamname streamname)
+          (cons 'title (basename->title basename-without-extension)))))
 
-(define (main args)
-  (define next-video (read-first-line "ls ../media/*.* | shuf | head -n 1"))
+(define (add-video next-video)
+  (define next-video-metadata (convert-video next-video))
+  (define entry-template (read-file-as-string "video.html"))
+  (define next-entry
+    (string-replace-substring
+     (string-replace-substring
+      (string-replace-substring
+       (string-replace-substring
+        entry-template "{{{TITLE}}}" (assoc-ref next-video-metadata 'title))
+       "{{{M3ULINK}}}" (assoc-ref next-video-metadata 'streamname))
+      "{{{FILELINK}}}" (assoc-ref next-video-metadata 'basename))
+     "{{{FILENAME}}}" (assoc-ref next-video-metadata 'basename)))
+  (let* ((port (open-output-file (string-append "../entries/" (assoc-ref next-video-metadata 'basename)))))
+    (display next-entry port)
+    (close port))
   (define template (read-file-as-string "template.html"))
-  (define entry-filenames (map (λ (x) (string-append "../entries/" x)) (reverse (sort (scandir "../entries/" (λ (x) (string-suffix? ".html" x))) string-ci<=)))) ;; TODO fill entries with video files
+  (define entry-filenames (map (λ (x) (string-append "../entries/" x)) (read-all-lines "ls --sort=time ../entries/")))
   (define entries (map read-file-as-string entry-filenames))
-  (define replaced (string-replace-substring template "{{{STREAMS}}}" (string-join entries "\n")))
+  (define replaced (string-replace-substring template "{{{STREAMS}}}" (string-join entries "\n\n")))
   (let* ((port (open-output-file "index.html")))
     (display replaced port)
     (close port))

          
@@ 71,3 149,8 @@ exec -a "$0" guile -L $(realpath $(dirna
   (display next-video)
   (display replaced)
   (newline))
+
+(define (main args)
+  (define next-video (read-first-line "ls ../media/*.* | shuf | head -n 1"))
+  (when (not (eof-object? next-video))
+    (add-video next-video)))