Initial revision
authorAnders Holst <aholst@users.schwerkraft.elitedvb.net>
Tue, 22 Dec 2009 13:30:54 +0000 (13:30 +0000)
committerAnders Holst <aholst@users.schwerkraft.elitedvb.net>
Tue, 22 Dec 2009 13:30:54 +0000 (13:30 +0000)
reconstructapsc/CONTROL/control [new file with mode: 0644]
reconstructapsc/Makefile.am [new file with mode: 0644]
reconstructapsc/Readme-ReconstructApSc.txt [new file with mode: 0644]
reconstructapsc/src_cc/Makefile.am [new file with mode: 0644]
reconstructapsc/src_cc/reconstruct_apsc.cc [new file with mode: 0644]
reconstructapsc/src_py/Makefile.am [new file with mode: 0644]
reconstructapsc/src_py/__init__.py [new file with mode: 0644]
reconstructapsc/src_py/maintainer.info [new file with mode: 0644]
reconstructapsc/src_py/plugin.py [new file with mode: 0644]

diff --git a/reconstructapsc/CONTROL/control b/reconstructapsc/CONTROL/control
new file mode 100644 (file)
index 0000000..bf164be
--- /dev/null
@@ -0,0 +1,8 @@
+Package: enigma2-plugin-extensions-reconstruct_apsc
+Version: 1.0-20091214
+Description: Reconstruct missing or corrupt .ap and .sc files of movies
+Section: extra
+Priority: optional
+Architecture: mipsel
+Maintainer: Anders Holst <aho@sics.se>
+Depends: enigma2(>=2.6git20091014)
diff --git a/reconstructapsc/Makefile.am b/reconstructapsc/Makefile.am
new file mode 100644 (file)
index 0000000..307ca55
--- /dev/null
@@ -0,0 +1,3 @@
+SUBDIRS = src_cc src_py meta
+
+EXTRA_DIST = Readme-ReconstructApSc.txt
diff --git a/reconstructapsc/Readme-ReconstructApSc.txt b/reconstructapsc/Readme-ReconstructApSc.txt
new file mode 100644 (file)
index 0000000..f772bc4
--- /dev/null
@@ -0,0 +1,53 @@
+ReconstructApSc-1.0
+-------------------
+2009-12-14
+Anders Holst (aho@sics.se)
+
+
+This plugin makes it possible to reconstruct missing or corrupt .ap
+and .sc files of recorded movies. These files are used for precise
+seeking and winding in the movie, and also for finding the cut
+positions when using the plugin MovieCut to cut movies. 
+
+Situations when this plugin may be useful include:
+ * The .sc files are relatively new, so movies recorded with Enigma2
+   from before the spring of 2009 doesn't have them.
+ * The previous version of MovieCut did not know about .sc, and
+   therefore produced no such file for the resulting cut movie.
+ * If you have cut your movies on a PC, you may not get any .ap or .sc
+   files. 
+ * If a specific movie is impossible to seek correctly in, or just
+   gets black or a frozen picture when trying to fast forward or
+   rewind, then one may suspect that the coresponding .ap and .sc
+   have got corrupt for some reason.
+ * When downloading ts-files from internet, there may not be any
+   provided .ap and .sc.
+ * After a disk crash it may be possible to rescue the .ts files
+   (because their specific structure) but perhaps not the others.
+
+The plugin uses the C++ program "reconstruct_apsc" to scan through the
+.ts file, and store the structural information into the .ap and .sc
+files. You can either tell the program to reconstruct the files for a
+specific movie, in which case any existing .ap and .sc for that movie
+will be removed first. Or, you can tell it to reconstruct all files in
+a specific directory, in which case it only reconstructs missing .ap
+and .sc files. The typical situation is that you have a directory with
+many older movies that don't have any .sc files. Note that
+reconstructing .sc files for all movies in a directory may take
+considerable time and disk activity. If the process should be
+interupted for some reason, it should however be able to continue from
+there next time you start it.
+
+Disclaimer
+
+I have tried to be careful, and to make it reasonably safe to use: It
+should never change or overwrite the actual .ts file, but only the .ap
+and .sc files; It checks for errors during the process and will abort
+in a controlled way if it happens; It remembers which file it was
+processing if it gets interupted so it can start from there next time,
+not leaving any half-finished .ap or .sc.
+
+However, all use will be on your own risk. I will not take
+responsibility for any damage or loss of movies or other files due to
+either bugs in the program or uncareful use. 
+
diff --git a/reconstructapsc/src_cc/Makefile.am b/reconstructapsc/src_cc/Makefile.am
new file mode 100644 (file)
index 0000000..e37e7e7
--- /dev/null
@@ -0,0 +1,7 @@
+installdir = /usr/lib/enigma2/python/Plugins/Extensions/ReconstructApSc/bin
+
+bin_PROGRAMS = reconstruct_apsc
+
+mcut_SOURCES = reconstruct_apsc.cc
+
+install_DATA = reconstruct_apsc
diff --git a/reconstructapsc/src_cc/reconstruct_apsc.cc b/reconstructapsc/src_cc/reconstruct_apsc.cc
new file mode 100644 (file)
index 0000000..1c8eebc
--- /dev/null
@@ -0,0 +1,364 @@
+ /* Copyright (C) 2009 Anders Holst
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License as
+  * published by the Free Software Foundation; either version 2, or
+  * (at your option) any later version.
+  * 
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  */
+
+#define _LARGEFILE64_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <byteswap.h>
+#include <errno.h>
+
+#define LEN 24064
+
+
+char* makefilename(const char* dir, const char* base, const char* ext, const char* post)
+{
+  static char buf[256];
+  int len1, len2, len3;
+  len1 = (dir ? strlen(dir) : 0);
+  len2 = (base ? strlen(base) : 0);
+  len3 = (ext ? strlen(ext) : 0);
+  if (dir) {
+    strcpy(buf, dir);
+    if (buf[len1-1] != '/') {
+      buf[len1++] = '/';
+      buf[len1] = 0;
+    }
+  }
+  if (base)
+    strcpy(buf+len1, base);
+  if (ext && len2>=len3 && !strcmp(base+len2-len3,ext))
+    len2 -= len3;
+  if (ext)
+    strcpy(buf+len1+len2, ext);
+  if (post)
+    strcpy(buf+len1+len2+len3, post);
+  return buf;
+}
+
+int writebufinternal(int f, off64_t sz, off64_t tm)
+{
+  off64_t buf[2];
+  buf[0] = (off64_t)bswap_64((unsigned long long int)sz);
+  buf[1] = (off64_t)bswap_64((unsigned long long int)tm);
+  if (write(f, buf, 16) != 16)
+    return 1;
+  else
+    return 0;
+}
+
+int framepid(unsigned char* buf, int pos)
+{
+  return ((buf[pos+1] & 0x1f) << 8) + buf[pos+2];
+}
+
+off64_t framepts(unsigned char* buf, int pos)
+{
+  int tmp = (buf[pos+3] & 0x20 ? pos+buf[pos+4]+5 : pos+4);
+  off64_t pts;
+  if (buf[pos+1] & 0x40 &&
+      buf[pos+3] & 0x10 &&
+      buf[tmp]==0 && buf[tmp+1]==0 && buf[tmp+2]==1 &&
+      buf[tmp+7] & 0x80) {
+    pts  = ((unsigned long long)(buf[tmp+9]&0xE))  << 29;
+    pts |= ((unsigned long long)(buf[tmp+10]&0xFF)) << 22;
+    pts |= ((unsigned long long)(buf[tmp+11]&0xFE)) << 14;
+    pts |= ((unsigned long long)(buf[tmp+12]&0xFF)) << 7;
+    pts |= ((unsigned long long)(buf[tmp+13]&0xFE)) >> 1;
+  } else
+    pts = 0;
+  return pts;
+}
+
+int framesearch(int fts, int first, off64_t& retpos, off64_t& retpts, off64_t& retpos2, off64_t& retdat)
+{
+  static unsigned char buf[LEN];
+  static int ind;
+  static off64_t pos = -1;
+  static off64_t num;
+  static int pid = -1;
+  static int st = 0;
+  static int sdflag = 0;
+  unsigned char* p;
+  if (pos == -1 || first) {
+    num = read(fts, buf, LEN);
+    ind = 0;
+    pos = 0;
+    st = 0;
+  }
+  while (1) {
+    p = buf+ind+st;
+    ind = -1;
+    for (; p < buf+num-6; p++)
+      if (p[0]==0 && p[1]==0 && p[2]==1) {
+        ind = ((p - buf)/188)*188;
+        if ((p[3] & 0xf0) == 0xe0 && (buf[ind+1] & 0x40) &&
+            (p-buf)-ind == (buf[ind+3] & 0x20 ? buf[ind+4] + 5 : 4)) {
+          pid = framepid(buf, ind);
+        } else if (pid != -1 && pid != framepid(buf, ind)) {
+          ind = -1;
+          continue;
+        }
+        if (p[3]==0 || p[3]==0xb3 || p[3]==0xb8) { // MPEG2
+          if (p[3]==0 && ((p[5] >> 3 & 7) == 1)) {
+            retpts = framepts(buf, ind);
+            retpos = pos + ind;
+          } else {
+            retpts = 0;
+            retpos = -1;
+          }
+          retdat = (unsigned int) p[3] | (p[4]<<8) | (p[5]<<16) | (p[6]<<24);
+          retpos2 = pos + (p - buf);
+          st = (p - buf) - ind + 1;
+          sdflag = 1;
+          return 1; 
+        } else if (!sdflag && p[3]==0x09 && (buf[ind+1] & 0x40)) { // H264
+          if ((p[4] >> 5)==0) {
+            retpts = framepts(buf, ind);
+            retpos = pos + ind;
+          } else {
+            retpts = 0;
+            retpos = -1;
+          }
+          retdat = p[3] | (p[4]<<8);
+          retpos2 = pos + (p - buf);
+          st = (p - buf) - ind + 1;
+          return 1; 
+        } else {
+          ind = -1;
+          continue;
+        }
+      }
+    st = 0;
+    sdflag = 0; // reset to get some fault tolerance
+    if (num == LEN) {
+      pos += num;
+      num = read(fts, buf, LEN);
+      ind = 0;
+    } else if (num) {
+      ind = num;
+      retpts = 0;
+      retdat = 0;
+      retpos = pos + num;
+      num = 0;
+      return -1;
+    } else {
+      retpts = 0;
+      retdat = 0;
+      retpos = 0;
+      return -1;
+    }
+  }
+}
+
+int do_one(int fts, int fap, int fsc)
+{
+  off64_t pos;
+  off64_t pos2;
+  off64_t pts;
+  off64_t dat;
+  int first = 1;
+  while (framesearch(fts, first, pos, pts, pos2, dat) >= 0) {
+    first = 0;
+    if (pos >= 0)
+      if (fap >= 0 && writebufinternal(fap, pos, pts))
+        return 1;
+    if (fsc >= 0 && writebufinternal(fsc, pos2, dat))
+      return 1;
+  }
+  return 0;
+}
+
+int do_movie(char* inname)
+{
+  int f_ts=-1, f_sc=-1, f_ap=-1, f_tmp=-1;
+  char* tmpname;
+  tmpname = makefilename(0, inname, ".ts", 0);
+  f_ts = open(tmpname, O_RDONLY | O_LARGEFILE);
+  if (f_ts == -1) {
+    printf("Failed to open input stream file \"%s\"\n", tmpname);
+    return 1;
+  }
+  tmpname = makefilename(0, inname, ".ts", ".reconstruct_apsc");
+  f_tmp = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0x1a4);
+  if (f_tmp == -1) {
+    printf("Failed to open sentry file \"%s\"\n", tmpname);
+    goto failure;
+  }
+  close(f_tmp);
+  tmpname = makefilename(0, inname, ".ts", ".ap");
+  f_ap = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0x1a4);
+  if (f_ap == -1) {
+    printf("Failed to open output .ap file \"%s\"\n", tmpname);
+    goto failure;
+  }
+  tmpname = makefilename(0, inname, ".ts", ".sc");
+  f_sc = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0x1a4);
+  if (f_sc == -1) {
+    printf("Failed to open output .sc file \"%s\"\n", tmpname);
+    goto failure;
+  }
+
+  printf("  Processing .ap and .sc of \"%s\" ... ", inname);
+  fflush(stdout);
+  if (do_one(f_ts, f_ap, f_sc)) {
+    printf("\nFailed to reconstruct files for \"%s\"\n", inname);
+    goto failure;
+  }
+  printf("done\n");
+
+  close(f_ts);
+  close(f_ap);
+  close(f_sc);
+  unlink(makefilename(0, inname, ".ts", ".reconstruct_apsc"));
+  return 0;
+ failure:
+  if (f_ts != -1)
+    close(f_ts);
+  if (f_ap != -1) {
+    close(f_ap);
+    unlink(makefilename(0, inname, ".ts", ".ap"));
+  }
+  if (f_sc != -1) {
+    close(f_sc);
+    unlink(makefilename(0, inname, ".ts", ".sc"));
+  }
+  unlink(makefilename(0, inname, ".ts", ".reconstruct_apsc"));
+  return 1;
+}
+
+int do_directory(char* dirname)
+{
+  int f_ts, f_sc, f_ap, f_tmp;
+  int do_ap, do_sc;
+  char *inname, *tmpname;
+  DIR* dir = opendir(dirname);
+  dirent* entry;
+  struct stat statbuf;
+  if (dir) {
+    while ((entry = readdir(dir))) {
+      inname = entry->d_name;
+      if (strlen(inname) > 3 && !strcmp(inname + strlen(inname) - 3, ".ts")) {
+        tmpname = makefilename(dirname, inname, ".ts", ".reconstruct_apsc");
+        errno = 0;
+        if (stat(tmpname, &statbuf) != -1)
+          do_ap = do_sc = 1;
+        else {
+          tmpname = makefilename(dirname, inname, ".ts", ".ap");
+          errno = 0;
+          do_ap = (stat(tmpname, &statbuf) == -1 && errno == ENOENT);
+          tmpname = makefilename(dirname, inname, ".ts", ".sc");
+          errno = 0;
+          do_sc = (stat(tmpname, &statbuf) == -1 && errno == ENOENT);
+        }
+        if (do_ap || do_sc) {
+          f_ts=-1, f_sc=-1, f_ap=-1, f_tmp=-1;
+          tmpname = makefilename(dirname, inname, ".ts", 0);
+          f_ts = open(tmpname, O_RDONLY | O_LARGEFILE);
+          if (f_ts == -1) {
+            printf("Failed to open input stream file \"%s\"\n", tmpname);
+            continue;
+          }
+          tmpname = makefilename(dirname, inname, ".ts", ".reconstruct_apsc");
+          f_tmp = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0x1a4);
+          if (f_tmp == -1) {
+            printf("Failed to open sentry file \"%s\"\n", tmpname);
+            goto failure;
+          }
+          close(f_tmp);
+          if (do_ap) {
+            tmpname = makefilename(dirname, inname, ".ts", ".ap");
+            f_ap = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0x1a4);
+            if (f_ap == -1) {
+              printf("Failed to open output .ap file \"%s\"\n", tmpname);
+              goto failure;
+            }
+          } else
+            f_ap = -1;
+          if (do_sc) {
+            tmpname = makefilename(dirname, inname, ".ts", ".sc");
+            f_sc = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0x1a4);
+            if (f_sc == -1) {
+              printf("Failed to open output .sc file \"%s\"\n", tmpname);
+              goto failure;
+            }
+          } else
+            f_sc = -1;
+
+          printf("  Processing %s of \"%s\" ... ", (do_ap ? (do_sc ? ".ap and .sc" : ".ap") : ".sc"), inname);
+          fflush(stdout);
+          if (do_one(f_ts, f_ap, f_sc)) {
+            printf("\nFailed to reconstruct files for \"%s\"\n", inname);
+            close(f_ts);
+            if (f_ap != -1) {
+              close(f_ap);
+              unlink(makefilename(dirname, inname, ".ts", ".ap"));
+            }
+            if (f_sc != -1) {
+              close(f_sc);
+              unlink(makefilename(dirname, inname, ".ts", ".sc"));
+            }
+            unlink(makefilename(dirname, inname, ".ts", ".reconstruct_apsc"));
+            continue;
+          }
+          printf("done\n");
+
+          close(f_ts);
+          close(f_ap);
+          close(f_sc);
+          unlink(makefilename(dirname, inname, ".ts", ".reconstruct_apsc"));
+        }
+      }
+    }
+    closedir(dir);
+  } else {
+    printf("Failed to open directory \"%s\"\n", dirname);
+    return 1;
+  }
+  return 0;
+ failure:
+  closedir(dir);
+  if (f_ts != -1)
+    close(f_ts);
+  if (f_ap != -1) {
+    close(f_ap);
+    unlink(makefilename(dirname, inname, ".ts", ".ap"));
+  }
+  if (f_sc != -1) {
+    close(f_sc);
+    unlink(makefilename(dirname, inname, ".ts", ".sc"));
+  }
+  unlink(makefilename(dirname, inname, ".ts", ".reconstruct_apsc"));
+  return 1;
+}
+
+int main(int argc, char* argv[])
+{
+  if (argc == 2 && *argv[1] != '-') {
+    if (do_movie(argv[1]))
+      exit(1);
+  } else if (argc == 3 && !strcmp(argv[1], "-d")) {
+    if (do_directory(argv[2]))
+      exit(1);
+  } else {
+    printf("Usage: reconstruct_apsc movie_file\n");
+    printf("       reconstruct_apsc -d movie_directory\n");
+    exit(1);
+  }
+}
+
diff --git a/reconstructapsc/src_py/Makefile.am b/reconstructapsc/src_py/Makefile.am
new file mode 100644 (file)
index 0000000..c580c9c
--- /dev/null
@@ -0,0 +1,3 @@
+installdir = /usr/lib/enigma2/python/Plugins/Extensions/ReconstructApSc
+
+install_PYTHON = __init__.py plugin.py maintainer.info
diff --git a/reconstructapsc/src_py/__init__.py b/reconstructapsc/src_py/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/reconstructapsc/src_py/maintainer.info b/reconstructapsc/src_py/maintainer.info
new file mode 100644 (file)
index 0000000..6909418
--- /dev/null
@@ -0,0 +1,2 @@
+aho@sics.se
+ReconstructApSc
diff --git a/reconstructapsc/src_py/plugin.py b/reconstructapsc/src_py/plugin.py
new file mode 100644 (file)
index 0000000..9c67d03
--- /dev/null
@@ -0,0 +1,154 @@
+from Plugins.Plugin import PluginDescriptor
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Screens.ChoiceBox import ChoiceBox
+import Screens.Standby
+from Components.ActionMap import ActionMap
+from enigma import eTimer, eServiceCenter, iServiceInformation, eConsoleAppContainer
+from os import access, chmod, X_OK
+
+recons_path = "/usr/lib/enigma2/python/Plugins/Extensions/ReconstructApSc/bin/reconstruct_apsc"
+# Hack to make sure it is executable
+if not access(recons_path, X_OK):
+       chmod(recons_path, 493)
+
+def main(session, service, **kwargs):
+       session.open(ReconstructApSc, service, **kwargs)
+
+def Plugins(**kwargs):
+       return PluginDescriptor(name="ReconstructApSc", description=_("Reconstruct AP/SC ..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)
+
+
+class ReconstructApSc(ChoiceBox):
+       def __init__(self, session, service):
+               self.service = service
+               serviceHandler = eServiceCenter.getInstance()
+               path = self.service.getPath()
+               info = serviceHandler.info(self.service)
+               if not info:
+                       self.name = path
+               else:
+                       self.name = info.getName(self.service)
+               tlist = [
+                       (_("Don't reconstruct"), "CALLFUNC", self.confirmed0),
+                       (_("Reconstruct the .ap and .sc files of the selected movie"), "CALLFUNC", self.confirmed1),
+                       (_("Reconstruct all missing .ap and .sc files in this directory"), "CALLFUNC", self.confirmed2),
+                       (_("Check any running reconstruct process"), "CALLFUNC", self.confirmed3),
+               ]
+               ChoiceBox.__init__(self, session, _("What would you like to reconstruct?  (\"%s\"") % (self.name), list = tlist, selection = 0)
+               self.skinName = "ChoiceBox"
+
+       def confirmed0(self, arg):
+               self.close()
+
+       def confirmed1(self, arg):
+               ReconstructApScSpawn(self.session, self, [recons_path, self.service.getPath()], self.name, _("movie"))
+
+       def confirmed2(self, arg):
+               dir = self.dirName(self.service.getPath())
+               ReconstructApScSpawn(self.session, self, [recons_path, "-d", dir], dir, _("directory"))
+
+       def confirmed3(self, arg):
+               output = global_recons_queue.checkOutput()
+               if output == False:
+                       mess = "There is no running reconstruction process"
+               else:
+                       mess = "Current reconstruction process output:\n%s" % output
+               self.session.openWithCallback(self.close, MessageBox, mess, MessageBox.TYPE_INFO)
+
+       def dirName(self, str):
+               return '/'.join(str.split('/')[:-1]) + '/'
+
+
+class ReconstructApScQueue:
+       def __init__(self):
+               self.container = eConsoleAppContainer()
+               self.container.appClosed.append(self.runDone)
+               self.container.dataAvail.append(self.collOutput)
+               self.queue = []
+               self.output = ""
+               self.running = False
+
+       def enqueue(self, cb, cmd):
+               self.queue.append((cb, cmd))
+               if not self.running:
+                       self.runNext()
+                       return True
+               else:
+                       return False
+
+       def collOutput(self, data):
+               self.output += data
+
+       def checkOutput(self):
+               if not self.running:
+                       return False
+               else:
+                       return self.output
+
+       def runNext(self):
+               self.output = ""
+               if not self.queue:
+                       self.running = False
+               else:
+                       self.running = True
+                       self.container.execute(*self.queue[0][1])
+
+       def runDone(self, retval):
+               cb = self.queue[0][0]
+               self.queue = self.queue[1:]
+               cb(retval, self.output)
+               self.runNext()
+
+global_recons_errors = [_("The %s \"%s\" is successfully processed:\n%s"),
+                     _("Processing failed for the %s \"%s\":\n%s")]
+
+global_recons_queue = ReconstructApScQueue()
+
+global_recons_block = False
+
+class ReconstructApScSpawn:
+       def __init__(self, session, parent, clist, name, typename):
+               global global_recons_queue
+               global global_recons_block
+               self.session = session
+               self.parent = parent
+               self.name = name
+               self.typename = typename
+               self.clist = [clist[0]] + clist
+               self.mess = ""
+               self.dialog = False
+               self.waitTimer = eTimer()
+               self.waitTimer.callback.append(self.doWaitAck)
+               if global_recons_queue.enqueue(self.doAck, self.clist):
+                       mess = _("The %s \"%s\" is processed in the background.") % (self.typename, self.name)
+               else:
+                       mess = _("Another movie or directory is currently processed.\nThe %s \"%s\" will be processed in the background after it.") % (self.typename, self.name)
+               global_recons_block = True
+               self.dialog = self.session.openWithCallback(self.endc, MessageBox, mess, MessageBox.TYPE_INFO)
+
+       def doAck(self, retval, output):
+               global global_recons_errors
+               self.mess = global_recons_errors[retval] % (self.typename, self.name, output)
+               self.doWaitAck()
+
+       def doWaitAck(self):
+               global global_recons_block
+               if Screens.Standby.inStandby or not self.session.in_exec or (global_recons_block and not self.dialog):
+                       self.waitTimer.start(2000, True)
+               else:
+                       global_recons_block = True
+                       self.session.openWithCallback(self.endw, MessageBox, self.mess, MessageBox.TYPE_INFO)
+
+       def endw(self, arg = 0):
+               global global_recons_block
+               global_recons_block = False
+               if self.session.current_dialog == self.dialog:
+                       self.session.current_dialog.close(True)
+                       self.endc(arg)
+
+       def endc(self, arg = 0):
+               global global_recons_block
+               global_recons_block = False
+               self.dialog = False
+               self.parent.close()