Module lib.webpage.historicpage

Function define the web page to view recent motion detection

Expand source code
# Distributed under Pycameresp License
# Copyright (c) 2023 Remi BERTHOLET
""" Function define the web page to view recent motion detection """
# pylint:disable=anomalous-unicode-escape-in-string
import server.httpserver
from htmltemplate          import *
import webpage.mainpage
import webpage.streamingpage
import motion
import video.video
import tools.lang
import tools.info
import tools.strings
import tools.tasking

def get_days_pagination(last_days, request):
        """ Get the pagination html part of days """
        current_day = b""
        last_days = list(last_days)

        if len(last_days) > 0:
                last_days.sort()
                last_days.reverse()

                pages = []

                day_id = int(request.params.get(b"day_id",0))
                max_days = 5
                if len(last_days) > max_days:
                        if day_id - 2 < 0:
                                begin_day = 0
                                end_day   = len(last_days) if len(last_days) < max_days else max_days
                        elif day_id + 3 > len(last_days):
                                end_day   = len(last_days)
                                begin_day = 0 if len(last_days) < max_days else len(last_days) - max_days
                        else:
                                begin_day = day_id - max_days // 2
                                end_day   = day_id + (max_days//2 + 1)

                        if begin_day > 0:
                                pages.append(PageItem(text=b"...", class_=b"", href=b"/historic?day_id=%d"%(begin_day-1)))
                else:
                        begin_day = 0
                        end_day = len(last_days)

                i = begin_day
                for day in last_days[begin_day:end_day]:
                        pages.append(PageItem(text=day[8:10],
                                class_=b"",
                                active=b"active" if day_id == i else b"",
                                href=b"/historic?day_id=%d"%i if day_id != i else b""))
                        if day_id == i:
                                current_day = day
                        i += 1

                if len(last_days) > max_days:
                        if end_day < len(last_days):
                                pages.append(PageItem(text=b"...", class_=b"", href=b"/historic?day_id=%d"%(end_day)))

                pagination_begin = Pagination(pages, class_=b"pagination-sm", id=b"pagination_begin")
                pagination_end   = Pagination(pages, class_=b"pagination-sm", id=b"pagination_end")
                result = pagination_begin, pagination_end,current_day
        else:
                result = None,None,current_day
        return result


@server.httpserver.HttpServer.add_route(b'/historic', menu=tools.lang.menu_motion, item=tools.lang.item_historic, available=tools.info.iscamera() and video.video.Camera.is_activated() and tools.features.features.motion)
async def historic(request, response, args):
        """ motion.historic.Historic motion detection page """
        webpage.streamingpage.Streaming.stop()
        motion.historic.Historic.get_root()
        last_days = await motion.historic.Historic.get_last_days()
        pagination_begin, pagination_end,current_day = get_days_pagination(last_days, request)

        if pagination_end is not None and pagination_begin is not None:
                page_content = [\
                        pagination_begin,
                        Tag(b"""

                        <div class="modal" id="zoom_window">
                                <div class="modal-dialog modal-fullscreen">
                                        <div class="modal-content">
                                                <div class="modal-header">
                                                        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                                                </div>
                                                <div class="modal-body" >
                                                        <canvas id="zoom_image" class="w-100" data-bs-dismiss="modal"/>
                                                </div>
                                        </div>
                                </div>
                        </div>

                        <script type='text/javascript'>

                        window.onload = load_historic;

                        var current_day = '%s';

                        var historic = null;
                        var last_id = 0;
                        var historic_request = new XMLHttpRequest();
                        var image_request    = new XMLHttpRequest();

                        const MOTION_FILENAME =0;
                        const MOTION_WIDTH    =1;
                        const MOTION_HEIGHT   =2;
                        const MOTION_DIFFS    =3;
                        const MOTION_SQUAREX  =4;
                        const MOTION_SQUAREY  =5;

                        function load_historic()
                        {
                                historic_request.onreadystatechange = historic_loaded;
                                historic_request.open("GET","historic/historic.json",true);
                                historic_request.send();
                        }

                        function historic_loaded() 
                        {
                                if (historic_request.readyState === XMLHttpRequest.DONE)
                                {
                                        if (historic_request.status === 200)
                                        {
                                                historic = JSON.parse(historic_request.responseText);
                                                select_day();
                                                load_image();
                                        }
                                }
                        }

                        function load_image()
                        {
                                if (historic.length > 0)
                                {
                                        var motion = historic[last_id];
                                        image_request.onreadystatechange = image_loaded;
                                        image_request.open("GET","/historic/images/" + motion[MOTION_FILENAME],true);
                                        image_request.send();
                                }
                        }

                        function rtrim(x, characters)
                        {
                                var start = 0;
                                var end = x.length - 1;
                                while (characters.indexOf(x[end]) >= 0)
                                {
                                        end -= 1;
                                }
                                return x.substr(0, end + 1);
                        }

                        function ltrim(x, characters) 
                        {
                                var start = 0;
                                while (characters.indexOf(x[start]) >= 0)
                                {
                                        start += 1;
                                }
                                var end = x.length - 1;
                                return x.substr(start);
                        }

                        function get_day(id)
                        {
                                var filename = historic[id][MOTION_FILENAME];
                                filename = ltrim(filename, "/");
                                filename = filename.split("/");
                                return filename[1]+"/"+filename[2]+"/"+filename[3];
                        }

                        function select_day()
                        {
                                if (historic.length > 0)
                                {
                                        for (i = 0; i < historic.length; i++)
                                        {
                                                if (get_day(i) == current_day)
                                                {
                                                        last_id = i;
                                                        break;
                                                }
                                        }
                                }
                        }

                        function get_quality()
                        {
                                if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent))
                                {
                                        return 1;
                                }
                                else
                                {
                                        return 2;
                                }
                        }

                        function image_loaded()
                        {
                                if (image_request.readyState === XMLHttpRequest.DONE)
                                {
                                        if (image_request.status === 200)
                                        {
                                                var motion = historic[last_id];

                                                var div = document.createElement("div");
                                                        div.className = "col-lg-2  pb-1";

                                                        var canvas = document.createElement("canvas");
                                                                canvas.width     = motion[MOTION_WIDTH ] * get_quality();
                                                                canvas.height    = motion[MOTION_HEIGHT] * get_quality();
                                                                canvas.id        = last_id;
                                                                canvas.className = "w-100";
                                                                canvas.setAttribute("data-bs-toggle","modal");
                                                                canvas.setAttribute("data-bs-target","#zoom_window");
                                                                canvas.onclick = e => 
                                                                        {
                                                                                var view = document.getElementById('zoom_image');
                                                                                var destCtx = view.getContext('2d');
                                                                                view.width     = motion[MOTION_WIDTH ] * get_quality();
                                                                                view.height    = motion[MOTION_HEIGHT] * get_quality();
                                                                                destCtx.drawImage(canvas, 0, 0);
                                                                        };

                                                        var image = new Image();
                                                                image.src        = 'data:image/jpeg;base64,' + image_request.response;
                                                                image.onload     = function(){show_motion(canvas.id, image);};

                                                        div.appendChild(canvas);

                                                if (last_id == 0)
                                                {
                                                        document.getElementById('motions').replaceChildren(div);
                                                }
                                                else
                                                {
                                                        document.getElementById('motions').appendChild(div);
                                                }
                                                last_id = last_id + 1;
                                                if (last_id < historic.length-1)
                                                {
                                                        if (get_day(last_id) == current_day)
                                                        {
                                                                setTimeout(load_image, 1);
                                                        }
                                                }
                                        }
                                        else 
                                        {
                                                setTimeout(load_image, 1);
                                        }
                                }
                        }

                        function get_difference(motion, x, y)
                        {
                                var squarex = motion[MOTION_SQUAREX];
                                var maxx    = motion[MOTION_WIDTH] /squarex;
                                var bitpos  = y*maxx + x;
                                if (typeof motion[MOTION_DIFFS] === 'string')
                                {
                                        return motion[MOTION_DIFFS][bitpos];
                                }
                                else
                                {
                                        var word = parseInt(bitpos/32);
                                        var bit  = 31-bitpos%%32;
                                        var mask = 1 << bit;
                                        var val  = motion[MOTION_DIFFS][word];
                                        if (val & mask)
                                        {
                                                return "#";
                                        }
                                        else
                                        {
                                                return " ";
                                        }
                                }
                        }

                        function show_motion(id, image)
                        {
                                var x;
                                var y;

                                var motion = historic[id];
                                var canvas = document.getElementById(id);
                                var ctx = canvas.getContext('2d');

                                var squarex = motion[MOTION_SQUAREX] * get_quality();
                                var squarey = motion[MOTION_SQUAREY] * get_quality();
                                var maxx = (motion[MOTION_WIDTH] /squarex) * get_quality();
                                var maxy = (motion[MOTION_HEIGHT]/squarey) * get_quality();

                                ctx.drawImage(image, 0, 0, motion[MOTION_WIDTH ]  , motion[MOTION_HEIGHT], 0, 0, motion[MOTION_WIDTH ] * get_quality(), motion[MOTION_HEIGHT] * get_quality());

                                ctx.strokeStyle = "red";
                                ctx.lineWidth =  1 * get_quality();
                                for (y = 0; y < maxy; y ++)
                                {
                                        for (x = 0; x < maxx; x ++)
                                        {
                                                var detection = get_difference(motion, x, y);
                                                if (x >= 1)
                                                {
                                                        var previous = get_difference(motion, x-1, y);
                                                        if (previous != detection)
                                                        {
                                                                ctx.beginPath();
                                                                ctx.moveTo(0 + x*squarex, 0 + y*squarey);
                                                                ctx.lineTo(0 + x*squarex, 0 + y*squarey + squarey);
                                                                ctx.stroke();
                                                        }
                                                }
                                        }
                                }
                                
                                for (x = 0; x < maxx; x ++)
                                {
                                        for (y = 0; y < maxy; y ++)
                                        {
                                                var detection = get_difference(motion, x, y);
                                                if (y >= 1)
                                                {
                                                        var previous = get_difference(motion, x, y-1);
                                                        if (previous != detection)
                                                        {
                                                                ctx.beginPath();
                                                                ctx.moveTo(0 + x*squarex, 0 + y*squarey);
                                                                ctx.lineTo(0 + x*squarex + squarex, 0 + y*squarey);
                                                                ctx.stroke();
                                                        }
                                                }
                                        }
                                }


                                var font_size = 35 * get_quality();
                                ctx.font = font_size + "px monospace";
                                var width = ctx.measureText(get_name(motion[MOTION_FILENAME])).width;
                                ctx.fillStyle = 'rgba(0,0,0,0.3)';

                                ctx.fillRect(0,(motion[MOTION_HEIGHT] * get_quality())-font_size, 
                                        width+get_quality(), font_size+10);

                                ctx.fillStyle = 'rgba(255,255,255,0.5)';
                                ctx.fillText(get_name(motion[MOTION_FILENAME]),  3, (motion[MOTION_HEIGHT] * get_quality() - 5*get_quality() ));
                        }

                        // Convert the filename into text displayed
                        function get_name(filename)
                        {
                                filename = filename.split(".")[0];
                                lst = filename.split("/");
                                filename = lst[lst.length-1];
                                filename = filename.replace("D= ","D=");
                                spl = filename.split(" ");

                                if (spl.length == 3)
                                {
                                        date = spl[0].split("_")[0];
                                        hour = spl[0].split("_")[1];

                                        date = date.replaceAll("-","/") + " " + hour.replaceAll("-",":");
                                        last = spl[1] + " " + spl[2];
                                        result = date + " "+ last;
                                }
                                else
                                {
                                        result = filename;
                                }
                                return result;
                        }

                        function get_date(filename)
                        {
                                return get_name(filename).substring(0,10);
                        }
                        </script>
                        <div id="motions" class="row"></div>
                        """%(current_day)),
                        Br(),
                        pagination_end
                ]
        else:
                page_content = Tag(b"<span>%s</span>"%tools.lang.historic_not_available)
        page = webpage.mainpage.main_frame(request, response, args,tools.lang.last_motion_detections,Form(page_content))
        await response.send_page(page)

@server.httpserver.HttpServer.add_route(b'/historic/historic.json', available=tools.info.iscamera() and video.video.Camera.is_activated() and tools.features.features.motion)
async def historic_json(request, response, args):
        """ Send historic json file """
        tools.tasking.Tasks.slow_down()
        try:
                await response.send_buffer(b"historic.json", await motion.historic.Historic.get_json())
        except Exception as err:
                await response.send_not_found(err)

@server.httpserver.HttpServer.add_route(b'/historic/images/.*', available=tools.info.iscamera() and video.video.Camera.is_activated() and tools.features.features.motion)
async def historic_image(request, response, args):
        """ Send historic image """
        tools.tasking.Tasks.slow_down()
        reserved = await video.video.Camera.reserve(motion.historic.Historic, timeout=5, suspension=10)
        try:
                if reserved:
                        await motion.historic.Historic.acquire()
                        await response.send_file(tools.strings.tostrings(request.path[len("/historic/images/"):]), base64=True)
                else:
                        await response.send_not_found()
        finally:
                if reserved:
                        await motion.historic.Historic.release()
                        await video.video.Camera.unreserve(motion.historic.Historic)

@server.httpserver.HttpServer.add_route(b'/historic/download/.*', available=tools.info.iscamera() and video.video.Camera.is_activated() and tools.features.features.motion)
async def download_image(request, response, args):
        """ Download historic image """
        tools.tasking.Tasks.slow_down()
        reserved = await video.video.Camera.reserve(motion.historic.Historic, timeout=5, suspension=10)
        try:
                if reserved:
                        await motion.historic.Historic.acquire()
                        await response.send_file(tools.strings.tostrings(request.path[len("/historic/download/"):]), base64=False)
                else:
                        await response.send_not_found()
        finally:
                if reserved:
                        await motion.historic.Historic.release()
                        await video.video.Camera.unreserve(motion.historic.Historic)

Functions

async def download_image(request, response, args)

Download historic image

Expand source code
@server.httpserver.HttpServer.add_route(b'/historic/download/.*', available=tools.info.iscamera() and video.video.Camera.is_activated() and tools.features.features.motion)
async def download_image(request, response, args):
        """ Download historic image """
        tools.tasking.Tasks.slow_down()
        reserved = await video.video.Camera.reserve(motion.historic.Historic, timeout=5, suspension=10)
        try:
                if reserved:
                        await motion.historic.Historic.acquire()
                        await response.send_file(tools.strings.tostrings(request.path[len("/historic/download/"):]), base64=False)
                else:
                        await response.send_not_found()
        finally:
                if reserved:
                        await motion.historic.Historic.release()
                        await video.video.Camera.unreserve(motion.historic.Historic)
def get_days_pagination(last_days, request)

Get the pagination html part of days

Expand source code
def get_days_pagination(last_days, request):
        """ Get the pagination html part of days """
        current_day = b""
        last_days = list(last_days)

        if len(last_days) > 0:
                last_days.sort()
                last_days.reverse()

                pages = []

                day_id = int(request.params.get(b"day_id",0))
                max_days = 5
                if len(last_days) > max_days:
                        if day_id - 2 < 0:
                                begin_day = 0
                                end_day   = len(last_days) if len(last_days) < max_days else max_days
                        elif day_id + 3 > len(last_days):
                                end_day   = len(last_days)
                                begin_day = 0 if len(last_days) < max_days else len(last_days) - max_days
                        else:
                                begin_day = day_id - max_days // 2
                                end_day   = day_id + (max_days//2 + 1)

                        if begin_day > 0:
                                pages.append(PageItem(text=b"...", class_=b"", href=b"/historic?day_id=%d"%(begin_day-1)))
                else:
                        begin_day = 0
                        end_day = len(last_days)

                i = begin_day
                for day in last_days[begin_day:end_day]:
                        pages.append(PageItem(text=day[8:10],
                                class_=b"",
                                active=b"active" if day_id == i else b"",
                                href=b"/historic?day_id=%d"%i if day_id != i else b""))
                        if day_id == i:
                                current_day = day
                        i += 1

                if len(last_days) > max_days:
                        if end_day < len(last_days):
                                pages.append(PageItem(text=b"...", class_=b"", href=b"/historic?day_id=%d"%(end_day)))

                pagination_begin = Pagination(pages, class_=b"pagination-sm", id=b"pagination_begin")
                pagination_end   = Pagination(pages, class_=b"pagination-sm", id=b"pagination_end")
                result = pagination_begin, pagination_end,current_day
        else:
                result = None,None,current_day
        return result
async def historic(request, response, args)

motion.historic.Historic motion detection page

Expand source code
@server.httpserver.HttpServer.add_route(b'/historic', menu=tools.lang.menu_motion, item=tools.lang.item_historic, available=tools.info.iscamera() and video.video.Camera.is_activated() and tools.features.features.motion)
async def historic(request, response, args):
        """ motion.historic.Historic motion detection page """
        webpage.streamingpage.Streaming.stop()
        motion.historic.Historic.get_root()
        last_days = await motion.historic.Historic.get_last_days()
        pagination_begin, pagination_end,current_day = get_days_pagination(last_days, request)

        if pagination_end is not None and pagination_begin is not None:
                page_content = [\
                        pagination_begin,
                        Tag(b"""

                        <div class="modal" id="zoom_window">
                                <div class="modal-dialog modal-fullscreen">
                                        <div class="modal-content">
                                                <div class="modal-header">
                                                        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                                                </div>
                                                <div class="modal-body" >
                                                        <canvas id="zoom_image" class="w-100" data-bs-dismiss="modal"/>
                                                </div>
                                        </div>
                                </div>
                        </div>

                        <script type='text/javascript'>

                        window.onload = load_historic;

                        var current_day = '%s';

                        var historic = null;
                        var last_id = 0;
                        var historic_request = new XMLHttpRequest();
                        var image_request    = new XMLHttpRequest();

                        const MOTION_FILENAME =0;
                        const MOTION_WIDTH    =1;
                        const MOTION_HEIGHT   =2;
                        const MOTION_DIFFS    =3;
                        const MOTION_SQUAREX  =4;
                        const MOTION_SQUAREY  =5;

                        function load_historic()
                        {
                                historic_request.onreadystatechange = historic_loaded;
                                historic_request.open("GET","historic/historic.json",true);
                                historic_request.send();
                        }

                        function historic_loaded() 
                        {
                                if (historic_request.readyState === XMLHttpRequest.DONE)
                                {
                                        if (historic_request.status === 200)
                                        {
                                                historic = JSON.parse(historic_request.responseText);
                                                select_day();
                                                load_image();
                                        }
                                }
                        }

                        function load_image()
                        {
                                if (historic.length > 0)
                                {
                                        var motion = historic[last_id];
                                        image_request.onreadystatechange = image_loaded;
                                        image_request.open("GET","/historic/images/" + motion[MOTION_FILENAME],true);
                                        image_request.send();
                                }
                        }

                        function rtrim(x, characters)
                        {
                                var start = 0;
                                var end = x.length - 1;
                                while (characters.indexOf(x[end]) >= 0)
                                {
                                        end -= 1;
                                }
                                return x.substr(0, end + 1);
                        }

                        function ltrim(x, characters) 
                        {
                                var start = 0;
                                while (characters.indexOf(x[start]) >= 0)
                                {
                                        start += 1;
                                }
                                var end = x.length - 1;
                                return x.substr(start);
                        }

                        function get_day(id)
                        {
                                var filename = historic[id][MOTION_FILENAME];
                                filename = ltrim(filename, "/");
                                filename = filename.split("/");
                                return filename[1]+"/"+filename[2]+"/"+filename[3];
                        }

                        function select_day()
                        {
                                if (historic.length > 0)
                                {
                                        for (i = 0; i < historic.length; i++)
                                        {
                                                if (get_day(i) == current_day)
                                                {
                                                        last_id = i;
                                                        break;
                                                }
                                        }
                                }
                        }

                        function get_quality()
                        {
                                if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent))
                                {
                                        return 1;
                                }
                                else
                                {
                                        return 2;
                                }
                        }

                        function image_loaded()
                        {
                                if (image_request.readyState === XMLHttpRequest.DONE)
                                {
                                        if (image_request.status === 200)
                                        {
                                                var motion = historic[last_id];

                                                var div = document.createElement("div");
                                                        div.className = "col-lg-2  pb-1";

                                                        var canvas = document.createElement("canvas");
                                                                canvas.width     = motion[MOTION_WIDTH ] * get_quality();
                                                                canvas.height    = motion[MOTION_HEIGHT] * get_quality();
                                                                canvas.id        = last_id;
                                                                canvas.className = "w-100";
                                                                canvas.setAttribute("data-bs-toggle","modal");
                                                                canvas.setAttribute("data-bs-target","#zoom_window");
                                                                canvas.onclick = e => 
                                                                        {
                                                                                var view = document.getElementById('zoom_image');
                                                                                var destCtx = view.getContext('2d');
                                                                                view.width     = motion[MOTION_WIDTH ] * get_quality();
                                                                                view.height    = motion[MOTION_HEIGHT] * get_quality();
                                                                                destCtx.drawImage(canvas, 0, 0);
                                                                        };

                                                        var image = new Image();
                                                                image.src        = 'data:image/jpeg;base64,' + image_request.response;
                                                                image.onload     = function(){show_motion(canvas.id, image);};

                                                        div.appendChild(canvas);

                                                if (last_id == 0)
                                                {
                                                        document.getElementById('motions').replaceChildren(div);
                                                }
                                                else
                                                {
                                                        document.getElementById('motions').appendChild(div);
                                                }
                                                last_id = last_id + 1;
                                                if (last_id < historic.length-1)
                                                {
                                                        if (get_day(last_id) == current_day)
                                                        {
                                                                setTimeout(load_image, 1);
                                                        }
                                                }
                                        }
                                        else 
                                        {
                                                setTimeout(load_image, 1);
                                        }
                                }
                        }

                        function get_difference(motion, x, y)
                        {
                                var squarex = motion[MOTION_SQUAREX];
                                var maxx    = motion[MOTION_WIDTH] /squarex;
                                var bitpos  = y*maxx + x;
                                if (typeof motion[MOTION_DIFFS] === 'string')
                                {
                                        return motion[MOTION_DIFFS][bitpos];
                                }
                                else
                                {
                                        var word = parseInt(bitpos/32);
                                        var bit  = 31-bitpos%%32;
                                        var mask = 1 << bit;
                                        var val  = motion[MOTION_DIFFS][word];
                                        if (val & mask)
                                        {
                                                return "#";
                                        }
                                        else
                                        {
                                                return " ";
                                        }
                                }
                        }

                        function show_motion(id, image)
                        {
                                var x;
                                var y;

                                var motion = historic[id];
                                var canvas = document.getElementById(id);
                                var ctx = canvas.getContext('2d');

                                var squarex = motion[MOTION_SQUAREX] * get_quality();
                                var squarey = motion[MOTION_SQUAREY] * get_quality();
                                var maxx = (motion[MOTION_WIDTH] /squarex) * get_quality();
                                var maxy = (motion[MOTION_HEIGHT]/squarey) * get_quality();

                                ctx.drawImage(image, 0, 0, motion[MOTION_WIDTH ]  , motion[MOTION_HEIGHT], 0, 0, motion[MOTION_WIDTH ] * get_quality(), motion[MOTION_HEIGHT] * get_quality());

                                ctx.strokeStyle = "red";
                                ctx.lineWidth =  1 * get_quality();
                                for (y = 0; y < maxy; y ++)
                                {
                                        for (x = 0; x < maxx; x ++)
                                        {
                                                var detection = get_difference(motion, x, y);
                                                if (x >= 1)
                                                {
                                                        var previous = get_difference(motion, x-1, y);
                                                        if (previous != detection)
                                                        {
                                                                ctx.beginPath();
                                                                ctx.moveTo(0 + x*squarex, 0 + y*squarey);
                                                                ctx.lineTo(0 + x*squarex, 0 + y*squarey + squarey);
                                                                ctx.stroke();
                                                        }
                                                }
                                        }
                                }
                                
                                for (x = 0; x < maxx; x ++)
                                {
                                        for (y = 0; y < maxy; y ++)
                                        {
                                                var detection = get_difference(motion, x, y);
                                                if (y >= 1)
                                                {
                                                        var previous = get_difference(motion, x, y-1);
                                                        if (previous != detection)
                                                        {
                                                                ctx.beginPath();
                                                                ctx.moveTo(0 + x*squarex, 0 + y*squarey);
                                                                ctx.lineTo(0 + x*squarex + squarex, 0 + y*squarey);
                                                                ctx.stroke();
                                                        }
                                                }
                                        }
                                }


                                var font_size = 35 * get_quality();
                                ctx.font = font_size + "px monospace";
                                var width = ctx.measureText(get_name(motion[MOTION_FILENAME])).width;
                                ctx.fillStyle = 'rgba(0,0,0,0.3)';

                                ctx.fillRect(0,(motion[MOTION_HEIGHT] * get_quality())-font_size, 
                                        width+get_quality(), font_size+10);

                                ctx.fillStyle = 'rgba(255,255,255,0.5)';
                                ctx.fillText(get_name(motion[MOTION_FILENAME]),  3, (motion[MOTION_HEIGHT] * get_quality() - 5*get_quality() ));
                        }

                        // Convert the filename into text displayed
                        function get_name(filename)
                        {
                                filename = filename.split(".")[0];
                                lst = filename.split("/");
                                filename = lst[lst.length-1];
                                filename = filename.replace("D= ","D=");
                                spl = filename.split(" ");

                                if (spl.length == 3)
                                {
                                        date = spl[0].split("_")[0];
                                        hour = spl[0].split("_")[1];

                                        date = date.replaceAll("-","/") + " " + hour.replaceAll("-",":");
                                        last = spl[1] + " " + spl[2];
                                        result = date + " "+ last;
                                }
                                else
                                {
                                        result = filename;
                                }
                                return result;
                        }

                        function get_date(filename)
                        {
                                return get_name(filename).substring(0,10);
                        }
                        </script>
                        <div id="motions" class="row"></div>
                        """%(current_day)),
                        Br(),
                        pagination_end
                ]
        else:
                page_content = Tag(b"<span>%s</span>"%tools.lang.historic_not_available)
        page = webpage.mainpage.main_frame(request, response, args,tools.lang.last_motion_detections,Form(page_content))
        await response.send_page(page)
async def historic_image(request, response, args)

Send historic image

Expand source code
@server.httpserver.HttpServer.add_route(b'/historic/images/.*', available=tools.info.iscamera() and video.video.Camera.is_activated() and tools.features.features.motion)
async def historic_image(request, response, args):
        """ Send historic image """
        tools.tasking.Tasks.slow_down()
        reserved = await video.video.Camera.reserve(motion.historic.Historic, timeout=5, suspension=10)
        try:
                if reserved:
                        await motion.historic.Historic.acquire()
                        await response.send_file(tools.strings.tostrings(request.path[len("/historic/images/"):]), base64=True)
                else:
                        await response.send_not_found()
        finally:
                if reserved:
                        await motion.historic.Historic.release()
                        await video.video.Camera.unreserve(motion.historic.Historic)
async def historic_json(request, response, args)

Send historic json file

Expand source code
@server.httpserver.HttpServer.add_route(b'/historic/historic.json', available=tools.info.iscamera() and video.video.Camera.is_activated() and tools.features.features.motion)
async def historic_json(request, response, args):
        """ Send historic json file """
        tools.tasking.Tasks.slow_down()
        try:
                await response.send_buffer(b"historic.json", await motion.historic.Historic.get_json())
        except Exception as err:
                await response.send_not_found(err)