Browse Source

feat: Add stdout from process to the template

Cristian 5 years ago
parent
commit
c971e00c9c
4 changed files with 276 additions and 208 deletions
  1. 10 0
      archivebox/config/__init__.py
  2. 18 7
      archivebox/core/views.py
  3. 224 201
      archivebox/themes/default/add_links.html
  4. 24 0
      archivebox/util.py

+ 10 - 0
archivebox/config/__init__.py

@@ -133,6 +133,16 @@ DEFAULT_CLI_COLORS = {
 }
 ANSI = {k: '' for k in DEFAULT_CLI_COLORS.keys()}
 
+COLOR_DICT = {
+    '00': [(0, 0, 0), (0, 0, 0)],
+    '31': [(255, 0, 0), (128, 0, 0)],
+    '32': [(0, 200, 0), (0, 128, 0)],
+    '33': [(255, 255, 0), (128, 128, 0)],
+    '34': [(0, 0, 255), (0, 0, 128)],
+    '35': [(255, 0, 255), (128, 0, 128)],
+    '36': [(0, 255, 255), (0, 128, 128)],
+}
+
 STATICFILE_EXTENSIONS = {
     # 99.999% of the time, URLs ending in these extensions are static files
     # that can be downloaded as-is, not html pages that need to be rendered

+ 18 - 7
archivebox/core/views.py

@@ -8,6 +8,9 @@ from django.conf import settings
 
 from core.models import Snapshot
 
+from contextlib import redirect_stdout
+from io import StringIO
+
 from ..index import load_main_index, load_main_index_meta
 from ..config import (
     OUTPUT_DIR,
@@ -16,7 +19,7 @@ from ..config import (
     PUBLIC_INDEX,
     PUBLIC_SNAPSHOTS,
 )
-from ..util import base_url
+from ..util import base_url, ansi_to_html
 from .. main import add
 
 
@@ -55,12 +58,20 @@ class AddLinks(View):
     def post(self, request):
         url = request.POST['url']
         print(f'[+] Adding URL: {url}')
-        add(
-            import_str=url,
-            update_all=False,
-            out_dir=OUTPUT_DIR,
-        )
-        return redirect('/')
+        add_stdout = StringIO()
+        with redirect_stdout(add_stdout):
+            extracted_links = add(
+                import_str=url,
+                update_all=False,
+                out_dir=OUTPUT_DIR,
+            )
+        print(add_stdout.getvalue())
+
+        context = {
+            "stdout": ansi_to_html(add_stdout.getvalue())
+        }
+
+        return render(template_name=self.template, request=request, context=context)
 
 
 class LinkDetails(View):

+ 224 - 201
archivebox/themes/default/add_links.html

@@ -2,208 +2,231 @@
 
 <!DOCTYPE html>
 <html lang="en">
-    <head>
-        <title>Archived Sites</title>
-        <meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1">
-        <style>
-            html, body {
-                width: 100%;
-                height: 100%;
-                font-size: 18px;
-                font-weight: 200;
-                text-align: center;
-                margin: 0px;
-                padding: 0px;
-                font-family: "Gill Sans", Helvetica, sans-serif;
-            }
-            .header-top small {
-                font-weight: 200;
-                color: #efefef;
-            }
-            
-            .header-top {
-                width: 100%;
-                height: auto;
-                min-height: 40px;
-                margin: 0px;
-                text-align: center;
-                color: white;
-                font-size: calc(11px + 0.84vw);
-                font-weight: 200;
-                padding: 4px 4px;
-                border-bottom: 3px solid #aa1e55;
-                background-color: #aa1e55;
-            }
-            input[type=search] {
-                width: 22vw;
-                border-radius: 4px;
-                border: 1px solid #aeaeae;
-                padding: 3px 5px;
-            }
-            .nav > div {
-                min-height: 30px;
-            }
-            .header-top a {
-                text-decoration: none;
-                color: rgba(0,0,0,0.6);
-            }
-            .header-top a:hover {
-                text-decoration: none;
-                color: rgba(0,0,0,0.9);
-            }
-            .header-top .col-lg-4 {
-                text-align: center;
-                padding-top: 4px;
-                padding-bottom: 4px;
-            }
-            .header-archivebox img {
-                display: inline-block;
-                margin-right: 3px;
-                height: 30px;
-                margin-left: 12px;
-                margin-top: -4px;
-                margin-bottom: 2px;
-            }
-            .header-archivebox img:hover {
-                opacity: 0.5;
-            }
+  <head>
+    <title>Archived Sites</title>
+    <meta
+      charset="utf-8"
+      name="viewport"
+      content="width=device-width, initial-scale=1"
+    />
+    <style>
+      html,
+      body {
+        width: 100%;
+        height: 100%;
+        font-size: 18px;
+        font-weight: 200;
+        text-align: center;
+        margin: 0px;
+        padding: 0px;
+        font-family: "Gill Sans", Helvetica, sans-serif;
+      }
+      .header-top small {
+        font-weight: 200;
+        color: #efefef;
+      }
 
-            #table-bookmarks_length, #table-bookmarks_filter {
-                padding-top: 12px;
-                opacity: 0.8;
-                padding-left: 24px;
-                padding-right: 22px;
-                margin-bottom: -16px;
-            }
-            table {
-                padding: 6px;
-                width: 100%;
-            }
-            table thead th {
-                font-weight: 400;
-            }
-            table tr {
-                height: 35px;
-            }
-            tbody tr:nth-child(odd) {
-               background-color: #ffebeb !important;
-            }
-            table tr td {
-                white-space: nowrap;
-                overflow: hidden;
-                /*padding-bottom: 0.4em;*/
-                /*padding-top: 0.4em;*/
-                padding-left: 2px;
-                text-align: center;
-            }
-            table tr td a {
-                text-decoration: none;
-            }
-            table tr td img, table tr td object {
-                display: inline-block;
-                margin: auto;
-                height: 24px;
-                width: 24px;
-                padding: 0px;
-                padding-right: 5px;
-                vertical-align: middle;
-                margin-left: 4px;
-            }
-            #table-bookmarks {
-                width: 100%; 
-                overflow-y: scroll;
-                table-layout: fixed;
-            }
-            .dataTables_wrapper {
-                background-color: #fafafa;
-            }
-            table tr a span[data-archived~=False] {
-                opacity: 0.4;
-            }
-            .files-spinner {
-                height: 15px;
-                width: auto;
-                opacity: 0.5;
-                vertical-align: -2px;
-            }
-            .in-progress {
-                display: none;
-            }
-            body[data-status~=finished] .files-spinner {
-                display: none;
-            }
-            /*body[data-status~=running] .in-progress {
+      .header-top {
+        width: 100%;
+        height: auto;
+        min-height: 40px;
+        margin: 0px;
+        text-align: center;
+        color: white;
+        font-size: calc(11px + 0.84vw);
+        font-weight: 200;
+        padding: 4px 4px;
+        border-bottom: 3px solid #aa1e55;
+        background-color: #aa1e55;
+      }
+      input[type="search"] {
+        width: 22vw;
+        border-radius: 4px;
+        border: 1px solid #aeaeae;
+        padding: 3px 5px;
+      }
+      .nav > div {
+        min-height: 30px;
+      }
+      .header-top a {
+        text-decoration: none;
+        color: rgba(0, 0, 0, 0.6);
+      }
+      .header-top a:hover {
+        text-decoration: none;
+        color: rgba(0, 0, 0, 0.9);
+      }
+      .header-top .col-lg-4 {
+        text-align: center;
+        padding-top: 4px;
+        padding-bottom: 4px;
+      }
+      .header-archivebox img {
+        display: inline-block;
+        margin-right: 3px;
+        height: 30px;
+        margin-left: 12px;
+        margin-top: -4px;
+        margin-bottom: 2px;
+      }
+      .header-archivebox img:hover {
+        opacity: 0.5;
+      }
+
+      #table-bookmarks_length,
+      #table-bookmarks_filter {
+        padding-top: 12px;
+        opacity: 0.8;
+        padding-left: 24px;
+        padding-right: 22px;
+        margin-bottom: -16px;
+      }
+      table {
+        padding: 6px;
+        width: 100%;
+      }
+      table thead th {
+        font-weight: 400;
+      }
+      table tr {
+        height: 35px;
+      }
+      tbody tr:nth-child(odd) {
+        background-color: #ffebeb !important;
+      }
+      table tr td {
+        white-space: nowrap;
+        overflow: hidden;
+        /*padding-bottom: 0.4em;*/
+        /*padding-top: 0.4em;*/
+        padding-left: 2px;
+        text-align: center;
+      }
+      table tr td a {
+        text-decoration: none;
+      }
+      table tr td img,
+      table tr td object {
+        display: inline-block;
+        margin: auto;
+        height: 24px;
+        width: 24px;
+        padding: 0px;
+        padding-right: 5px;
+        vertical-align: middle;
+        margin-left: 4px;
+      }
+      #table-bookmarks {
+        width: 100%;
+        overflow-y: scroll;
+        table-layout: fixed;
+      }
+      .dataTables_wrapper {
+        background-color: #fafafa;
+      }
+      table tr a span[data-archived~="False"] {
+        opacity: 0.4;
+      }
+      .files-spinner {
+        height: 15px;
+        width: auto;
+        opacity: 0.5;
+        vertical-align: -2px;
+      }
+      .in-progress {
+        display: none;
+      }
+      body[data-status~="finished"] .files-spinner {
+        display: none;
+      }
+      /*body[data-status~=running] .in-progress {
                 display: inline-block;
             }*/
-            tr td a.favicon img {
-                padding-left: 6px;
-                padding-right: 12px;
-                vertical-align: -4px;
-            }
-            tr td a.title {
-                font-size: 1.4em;
-                text-decoration:none;
-                color:black;
-            }
-            tr td a.title small {
-                background-color: #efefef;
-                border-radius: 4px;
-                float:right
-            }
-            input[type=search]::-webkit-search-cancel-button {
-                -webkit-appearance: searchfield-cancel-button;
-            }
-            .title-col {
-                text-align: left;
-            }
-            .title-col a {
-                color: black;
-            }
-        </style>
-        <link rel="stylesheet" href="{% static 'bootstrap.min.css' %}">
-        <link rel="stylesheet" href="{% static 'jquery.dataTables.min.css' %}"/>
-        <script src="{% static 'jquery.min.js' %}"></script>
-        <script src="{% static 'jquery.dataTables.min.js' %}"></script>
-        <script>
-            document.addEventListener('error', function(e) {
-              e.target.style.opacity = 0;
-            }, true)
-            jQuery(document).ready(function() {
-                jQuery('#table-bookmarks').DataTable({
-                    stateSave: true, // save state (filtered input, number of entries shown, etc) in localStorage
-                    dom: '<lf<t>ip>', // how to show the table and its helpers (filter, etc) in the DOM
-                    order: [[0, 'desc']],
-                    iDisplayLength: 100,
-                });
-            });
-        </script>
-    </head>
-    <body data-status="finished">
-        <header>
-            <div class="header-top container-fluid">
-                <div class="row nav">
-                    <div class="col-sm-2">
-                        <a href="/" class="header-archivebox" title="Last updated: {{updated}}">
-                            <img src="{% static 'archive.png' %}" alt="Logo"/>
-                            ArchiveBox: Add
-                        </a>
-                    </div>
-                    <div class="col-sm-10" style="text-align: right">
-                        <a href="/">Main Index</a> &nbsp; | &nbsp; 
-                        <a href="/admin/">Admin</a> &nbsp; | &nbsp; 
-                        <a href="https://github.com/pirate/ArchiveBox/wiki">Docs</a>
-                    </div>
-                </div>
-            </div>
-        </header>
-        <center>
-            <br/><br/>
-            <form action="?" method="POST">{% csrf_token %}
-                Add new links...<br/>
-                <input type="text" name="url" placeholder="URL of page or feed..."/><br/>
-                <button role="submit">Add</button>
-            </form>
-        </center>
-        
-    </body>
+      tr td a.favicon img {
+        padding-left: 6px;
+        padding-right: 12px;
+        vertical-align: -4px;
+      }
+      tr td a.title {
+        font-size: 1.4em;
+        text-decoration: none;
+        color: black;
+      }
+      tr td a.title small {
+        background-color: #efefef;
+        border-radius: 4px;
+        float: right;
+      }
+      input[type="search"]::-webkit-search-cancel-button {
+        -webkit-appearance: searchfield-cancel-button;
+      }
+      .title-col {
+        text-align: left;
+      }
+      .title-col a {
+        color: black;
+      }
+    </style>
+    <link rel="stylesheet" href="{% static 'bootstrap.min.css' %}" />
+    <link rel="stylesheet" href="{% static 'jquery.dataTables.min.css' %}" />
+    <script src="{% static 'jquery.min.js' %}"></script>
+    <script src="{% static 'jquery.dataTables.min.js' %}"></script>
+    <script>
+      document.addEventListener(
+        "error",
+        function (e) {
+          e.target.style.opacity = 0;
+        },
+        true
+      );
+      jQuery(document).ready(function () {
+        jQuery("#table-bookmarks").DataTable({
+          stateSave: true, // save state (filtered input, number of entries shown, etc) in localStorage
+          dom: "<lf<t>ip>", // how to show the table and its helpers (filter, etc) in the DOM
+          order: [[0, "desc"]],
+          iDisplayLength: 100,
+        });
+      });
+    </script>
+  </head>
+  <body data-status="finished">
+    <header>
+      <div class="header-top container-fluid">
+        <div class="row nav">
+          <div class="col-sm-2">
+            <a
+              href="/"
+              class="header-archivebox"
+              title="Last updated: {{updated}}"
+            >
+              <img src="{% static 'archive.png' %}" alt="Logo" />
+              ArchiveBox: Add
+            </a>
+          </div>
+          <div class="col-sm-10" style="text-align: right;">
+            <a href="/">Main Index</a> &nbsp; | &nbsp;
+            <a href="/admin/">Admin</a> &nbsp; | &nbsp;
+            <a href="https://github.com/pirate/ArchiveBox/wiki">Docs</a>
+          </div>
+        </div>
+      </div>
+    </header>
+    <center>
+      {{ stdout | safe }}
+      <br /><br />
+      <form action="?" method="POST">
+        {% csrf_token %} Add new links...<br />
+        <input
+          type="text"
+          name="url"
+          placeholder="URL of page or feed..."
+        /><br />
+        <button role="submit">Add</button>
+      </form>
+
+      <a href="{% url 'admin:core_snapshot_changelist' %}"
+        >Go back to Snapshot list</a
+      >
+    </center>
+  </body>
 </html>

+ 24 - 0
archivebox/util.py

@@ -20,6 +20,7 @@ from .config import (
     CHECK_SSL_VALIDITY,
     WGET_USER_AGENT,
     CHROME_OPTIONS,
+    COLOR_DICT
 )
 
 try:
@@ -69,6 +70,8 @@ URL_REGEX = re.compile(
     re.IGNORECASE,
 )
 
+COLOR_REGEX = re.compile(r'\[(?P<arg_1>\d+)(;(?P<arg_2>\d+)(;(?P<arg_3>\d+))?)?m')
+
 
 def enforce_types(func):
     """
@@ -195,6 +198,27 @@ def chrome_args(**options) -> List[str]:
     
     return cmd_args
 
+def ansi_to_html(text):
+    """
+    Based on: https://stackoverflow.com/questions/19212665/python-converting-ansi-color-codes-to-html
+    """
+    TEMPLATE = '<span style="color: rgb{}"><br>'
+    text = text.replace('[m', '</span>')
+
+    def single_sub(match):
+        argsdict = match.groupdict()
+        if argsdict['arg_3'] is None:
+            if argsdict['arg_2'] is None:
+                bold, color = 0, argsdict['arg_1']
+            else:
+                bold, color = argsdict['arg_1'], argsdict['arg_2']
+        else:
+            bold, color = argsdict['arg_3'], argsdict['arg_2']
+
+        return TEMPLATE.format(COLOR_DICT[color][0])
+
+    return COLOR_REGEX.sub(single_sub, text)
+
 
 class ExtendedEncoder(pyjson.JSONEncoder):
     """