#lang racket (require web-racket racket/runtime-path racket/gui racket-sprintf "utils.rkt" "music-library.rkt" "translate.rkt" "playlist.rkt" "player.rkt" ) (provide (all-from-out web-racket) rktplayer% ) (define-runtime-path rktplayer-start "gui/rktplayer.html") (define player-menu (λ () (menu 'main-menu (menu-item 'm-file (tr "File") #:submenu (menu (menu-item 'm-select-library-dir (tr "Select Music Library Folder")) (menu-item 'm-set-lang (tr "Set language")) (menu-item 'm-quit (tr "Quit") #:separator #t))) ) )) (define rktplayer% (class ww-webview% (inherit-field settings) (super-new [html-file rktplayer-start] ) (define closed #f) (define el-seeker #f) (define el-library #f) (define el-playlist #f) (define el-at #f) (define el-length #f) (define music-library (send settings get 'music-library (find-system-path 'home-dir))) (define current-music-path #f) (define playlist #f) (define current-at-seconds 0) (define current-length-seconds 0) (define (update-time at-seconds length-seconds) (let ((as (inexact->exact (round at-seconds))) (ls (inexact->exact (round length-seconds)))) (when (or (not (= current-at-seconds as)) (not (= current-length-seconds ls))) (set! current-at-seconds as) (set! current-length-seconds ls) (let ((as-str (sprintf "%02d:%02d:%02d" (quotient as 3600) (quotient (remainder as 3600) 60) (remainder (remainder as 3600) 60))) (ls-str (sprintf "%02d:%02d:%02d" (quotient ls 3600) (quotient (remainder ls 3600) 60) (remainder (remainder ls 3600) 60))) ) (unless closed (send el-at set-inner-html! as-str) (send el-length set-inner-html! ls-str) (let ((seeker (exact->inexact (/ (* 100 as) ls)))) ;(displayln (format "seeker = ~a" seeker)) (send el-seeker set! (format "~a" seeker))) ) ) ) ) ) (define current-track-nr #f) (define (update-track-nr nr) (let ((id (λ () (string->symbol (format "track-~a" (+ current-track-nr 1)))))) (unless (eq? current-track-nr #f) (displayln (format "current track: ~a" (id))) (let ((el (send this element (id)))) (send el remove-class! "current"))) (set! current-track-nr nr) (unless (eq? current-track-nr #f) (displayln (format "current track: ~a" (id))) (let ((el (send this element (id)))) (send el add-class! "current"))) ) ) (define player (new player% [time-updater update-time] [track-nr-updater update-track-nr] [settings settings] )) (define inner-html-handlers (make-hash)) (define/override (html-loaded) (super html-loaded) (ww-connect 'play play) (ww-connect 'prev previous-track) (ww-connect 'next next-track) (ww-connect 'repeat repeat) (ww-connect 'volume volume) (set! el-seeker (send this element 'seek)) (displayln (format "el-seeker: ~a" (send el-seeker get))) (let ((seek-reactor (make-delayed-reactor 0.3 (λ (percentage) (send this seek-to percentage))))) (send el-seeker on-change! seek-reactor)) (set! el-library (send this element 'library)) (set! el-playlist (send this element 'tracks)) (set! el-at (send this element 'time)) (set! el-length (send this element 'totaltime)) (send this set-menu! (player-menu)) (send this connect-menu! 'm-quit (λ () (send this quit))) (send this connect-menu! 'm-select-library-dir (λ () (send this select-library))) (send this update-library) ) (define/public (update-playlist) (let ((html (send playlist to-html))) (send el-playlist set-inner-html! html) (update-track-nr current-track-nr) ) ) (define/public (update-library) (when (eq? current-music-path #f) (set! current-music-path music-library)) (let* ((nr 0) (l (filter (λ (r) (music-lib-relevant? (cadr r))) (map (λ (e) (set! nr (+ nr 1)) (list (format "row-~a" nr) (build-path current-music-path e) (format "path-~a" nr))) (directory-list current-music-path))))) (displayln current-music-path) (displayln music-library) (unless (equal? (format "~a" current-music-path) (format "~a" music-library)) (set! l (cons (list "lib-up" "↰" "lib-up") l)) ) (let ((html (mktable l 'music-library library-formatter))) (let ((handle (send el-library set-inner-html! html))) (hash-set! inner-html-handlers handle (λ (oke) (when oke (send this bind 'dblclick "td.library-entry") (send this bind 'click "td.library-entry") (send this bind 'contextmenu "td.library-entry") (for-each (λ (row) (let ((path-id (string->symbol (caddr row)))) (let ((el (send this new-element path-id))) (send el connect 'dblclick (λ (args) (send this path-choosen (cadr row)))) (send el connect 'click (λ (args) #t)) (send el connect 'contextmenu (λ (evt) (send this context-for-path evt (cadr row)))) ))) l) )))) )) ) (define/override (inner-html-set handle oke) (ww-debug "inner-html-set called") (let ((cb (hash-ref inner-html-handlers handle #f))) (ww-debug (format "got cb = ~a for handle ~a" cb handle)) (when cb (hash-remove! inner-html-handlers handle) (cb oke))) ) (define/public (path-choosen path) (let ((path-part (if (equal? path "↰") ".." (format "~a" path)))) (let ((npath (if (string=? path-part "..") (build-path current-music-path path-part) path))) (when (directory-exists? npath) (set! current-music-path (normalize-path npath)) (send this update-library) ) ) ) ) (define/public (context-for-path evt path) (let ((items (list (menu-item 'm-play-this (tr "Play this") #:callback (λ () (send this play-path path)))))) (when (file-exists? path) (set! items (append items (list (menu-item 'm-add-this (tr "Add this") #:callback (λ () (send this add-path path))))))) (set! items (append items (list (menu-item 'm-booklet (tr "Open booklet") #:callback (λ () (send this open-booklet path))) ;; todo check if pdf file exists (menu-item 'm-folder (tr "Open containing folder") #:callback (λ () (send this open-folder path))) ))) (let* ((mnu (menu 'library-popup items)) (js-evt (hash-ref evt 'js_evt (make-hash))) (clientX (hash-ref js-evt 'clientX 60)) (clientY (hash-ref js-evt 'clientY 60)) ) (send this popup-menu mnu clientX clientY) ) ) ) (define/public (play-path path) (displayln (format "Playing ~a" path)) (set! playlist (new playlist% [start-map path])) (send playlist read-tracks) (displayln (format "number of tracks: ~a" (send playlist length))) (send this update-playlist) (send player play playlist) ) (define/public (add-path path) (send playlist add-track path) (send this update-playlist) ) (define/public (open-booklet path) (displayln (format "Open booklet ~a" path))) (define/public (open-folder path) (displayln path) (let ((folder (if (file-exists? path) (path-only path) path))) (open-file-manager folder))) ;(shell-execute #f folder #f #f 'sw_show))) (define/public (play) (displayln "Play button clicked") ) (define/public (next-track) (send player next) ) (define/public (previous-track) (send player previous) ) (define/public (repeat) (displayln "Repeat") ) (define/public (volume) (displayln "Volume") ) (define/public (seek-to percentage) (displayln (format "Seeking to percentage: ~a" percentage)) ) (define/public (quit) (displayln (format "Quitting")) (send player quit) (set! closed #t) (send this close)) (define/public (select-library) ;(set! music-library "c:\\Users\\Hans") (let ((handle (send this choose-dir (tr "Choose the folder containing your music library") (if (string? music-library) music-library (path->string music-library)) ))) (displayln (format "Selecting Music Library with handle: ~a" handle)) ) ) (define/override (dir-choosen handle choosen dir) (when choosen (set! music-library dir) (send settings set! 'music-library dir) (set! current-music-path #f) (send this update-library))) (begin (let ((lang (send settings get 'lang 'en))) (displayln (format "RktPlayer started, current language: ~a" lang))) ) ) )