]> git.0d.be Git - botaradio.git/commitdiff
display track duration and elapsed time when playing
authorFrédéric Péters <fpeters@0d.be>
Sun, 22 Mar 2020 10:26:52 +0000 (11:26 +0100)
committerFrédéric Péters <fpeters@0d.be>
Sun, 22 Mar 2020 10:47:47 +0000 (11:47 +0100)
interface.py
media/file.py
media/item.py
templates/index.html
templates/playlist.html

index 70b95efd9d0fc6d12ce93bb60ff51b0690f4cc3e..ee7d8625bcc93118f3b4f38cbaed55a117dced60 100644 (file)
@@ -191,11 +191,13 @@ def status():
         return jsonify({'ver': var.playlist.version,
                         'empty': False,
                         'play': not var.bot.is_pause,
+                        'playhead': var.bot.playhead,
                         'mode': var.playlist.mode})
     else:
         return jsonify({'ver': var.playlist.version,
                         'empty': True,
                         'play': False,
+                        'playhead': -1,
                         'mode': var.playlist.mode})
 
 
index 4718ea38036a8658f5c473273e852a291968eb59..994b4a4e05853aa23c7a2ac2fa48dfe1b156d6d2 100644 (file)
@@ -4,6 +4,7 @@ from io import BytesIO
 import base64
 import hashlib
 import mutagen
+import subprocess
 from PIL import Image
 
 import variables as var
@@ -39,6 +40,36 @@ item_loaders['file'] = file_item_loader
 item_id_generators['file'] = file_item_id_generator
 
 
+def get_duration(filename):
+    # use mediainfo if possible
+    p = subprocess.Popen(['mediainfo', '--Inform=Audio;%Duration%', filename],
+                         close_fds=True,
+                         stdin=subprocess.PIPE,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE)
+    stdout, stderr = p.communicate()
+    try:
+        return int(stdout) / 1000
+    except ValueError:
+        pass
+
+    # fallback on soxi
+    p = subprocess.Popen(['soxi', filename], close_fds=True,
+                         stdin=subprocess.PIPE,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE)
+    stdout, stderr = p.communicate()
+    for line in stdout.splitlines():
+        if not line.startswith('Duration'):
+            continue
+        try:
+            hours, minutes, seconds = re.findall(r'(\d\d):(\d\d):(\d\d)', line)[0]
+        except IndexError:
+            continue
+        return int(hours) * 3600 + int(minutes)*60 + int(seconds)
+    return None
+
+
 class FileItem(BaseItem):
     def __init__(self, bot, path, from_dict=None):
         if not from_dict:
@@ -47,6 +78,7 @@ class FileItem(BaseItem):
             self.title = ""
             self.artist = ""
             self.thumbnail = None
+            self.duration = None
             self.id = hashlib.md5(path.encode()).hexdigest()
             if os.path.exists(self.uri()):
                 self._get_info_from_tag()
@@ -57,6 +89,7 @@ class FileItem(BaseItem):
             self.title = from_dict['title']
             self.artist = from_dict['artist']
             self.thumbnail = from_dict['thumbnail']
+            self.duration = from_dict['duration']
             if not self.validate():
                 self.ready = "failed"
 
@@ -86,6 +119,9 @@ class FileItem(BaseItem):
         file_no_ext = match[1]
         ext = match[2]
 
+        if ext in ("mp3", "ogg", "opus", "aac", "flac", "wav"):
+            self.duration = get_duration(self.uri())
+
         try:
             im = None
             path_thumbnail = file_no_ext + ".jpg"
@@ -144,6 +180,7 @@ class FileItem(BaseItem):
         dict['title'] = self.title
         dict['artist'] = self.artist
         dict['thumbnail'] = self.thumbnail
+        dict['duration'] = self.duration
         return dict
 
     def format_debug_string(self):
index bd09a81dded131b76a8e085bd8ba3bed0edf326e..92ce4dcf97cf4ae33330118c2cd4219024601196 100644 (file)
@@ -97,6 +97,9 @@ class BaseItem:
     def format_debug_string(self):
         return self.id
 
+    def format_duration(self):
+        return '%d:%02d' % (self.duration // 60, self.duration % 60)
+
     def display_type(self):
         return ""
 
index 3816dea5d831c49a1de36dd2195f2696324bf717..50651b3d299a9b29f1a3da77ae1de50cb9caf80f 100644 (file)
                                     onclick="request('post', {action : 'stop'})" disabled>
                                 <i class="fas fa-stop" aria-hidden="true"></i>
                             </button>
+                            <span id="current-time" style="line-height: 200%">
+                            </span>
                         </div>
 
                         <div class="btn-group" style="float: right;">
 
         var playlist_ver = 0;
 
+        function format_duration(secs) {
+            return parseInt(secs/60) + ':' + ('0' + parseInt(secs%60)).slice(-2);
+        }
+
         function request(url, _data, refresh=false){
             $.ajax({
                 type: 'POST',
                             updatePlaylist();
                             playlist_ver = data.ver;
                         }
-                        updateControls(data.empty, data.play, data.mode);
+                        updateControls(data);
                     }
                 }
             });
             });
         }
 
-        function updateControls(empty, play, mode){
+        function updateControls(data) {
+            var empty = data.empty;
+            var play = data.play;
+            var mode = data.mode;
             if(empty){
                 $("#play-btn").prop("disabled", true);
                 $("#pause-btn").prop("disabled", true);
                 $("#random-btn").removeClass("btn-primary").addClass("btn-secondary").prop("disabled", false);
                 $("#autoplay-btn").removeClass("btn-secondary").addClass("btn-primary").prop("disabled", true);
             }
-
+            var current_time = "";
+            if (data.playhead != -1) {
+                var duration = $('#playlist-table .table-active').data('duration');
+                if (duration) {
+                    var elapsed = data.playhead;
+                    current_time = format_duration(elapsed) + '/' + format_duration(duration);
+                }
+            }
+            $('#current-time').text(current_time);
         }
 
         function themeInit(){
                             updatePlaylist();
                             playlist_ver = data.ver;
                         }
-                        updateControls(data.empty, data.play, data.mode);
+                        updateControls(data);
                     }
                 }
             });
-        } , 3000);
+        } , 1000);
 
         themeInit();
         $(document).ready(updatePlaylist);
index c7967b834f27538799b673010bf06d7d6258556d..f01ccedb0a6c364a2d9088cc7faefae4108892e8 100644 (file)
@@ -4,7 +4,7 @@
     </tr>
 {% else %}
 {% if index == playlist.current_index %}
-<tr class="table-active">
+<tr class="table-active" data-duration="{{ m.duration }}">
 {% else %}
 <tr>
 {% endif %}
@@ -26,8 +26,8 @@
             <span class="badge badge-secondary">{{ m.display_type() }}</span>
             <br />
             {% if m.type == 'file' %}
-                {% if m.artist %}
-                    {{ m.artist }}
+                {% if m.duration %}
+                duration: {{ m.format_duration() }}
                 {% else %}
                     ??
                 {% endif %}