--- /dev/null
+Package: enigma2-plugin-extensions-moviecut
+Version: 1.2-20080717
+Description: Perform the cuts specified with the Cutlist editor of DM7025
+Section: extra
+Priority: optional
+Architecture: mipsel
+Maintainer: Anders Holst <aho@sics.se>
+Depends: enigma2(>2.5cvs20080628)
--- /dev/null
+SUBDIRS = src_cc src_py
+
+EXTRA_DIST = Readme-MovieCut-1.2.txt
--- /dev/null
+MovieCut-1.2
+------------
+2008-07-17
+Anders Holst (aho@sics.se)
+
+
+This module makes it possible to execute the cuts specified by the
+Cutlist editor on DM7025, i.e. the specified sections are actually
+removed from the file, saving disk space and simplifying if the movie
+is to be e.g. burned to a DVD. When installed it will be accessible
+from the file list menu (i.e. selecting a file in the file list and
+pressing the menu button) under the name "Execute cuts...". First use
+the Cutlist editor to set the appropriate cut marks. Then select
+"Execute cuts..." and it will give some options for usage.
+
+The real work is done by the program "mcut". It was inspired and
+guided by the similar package "moviecutter" bu Georges. However, you
+need not wait over the night but it can run in the background as you
+keep watching on your dreambox, and a typical movie takes about 15-20
+minutes to process. The program "mcut" can also be called directly
+from a shell. With no arguments it will give a brief description of
+the options.
+
+
+News since version 1.1
+
+* Nothing really is supposed to have changed functionally. However,
+ the plugin is updated to the skin changes of 2008-04-14, and the
+ location changes of 2008-06-28, and it uses eConsoleAppContainer
+ instead of spawnv/waitpid to launch "mcut".
+
+
+News since version 1.0
+
+* Cutting is still in the background, but now a notification will pop
+ up either when cutting is successfully finished, or with a suitable
+ error message if cutting fails.
+
+* The cutting can now be done quite exact: If all cut marks are placed
+ at GOP boundaries (singlestep GOPs by pausing and pressing either
+ "rewind" or with the appropriate settings "pause"), then the
+ retained part will start with the same frame you were at when you
+ placed the IN cut, and it will stop just a few frames before
+ (typically 3 frames before - I can't easily get rid of this) the one
+ where you placed the OUT cut. Note however that due to a bug in
+ enigma2 (still there 2008-02-28) the CutListEditor will set the mark
+ at the right place, but when revisiting the mark you will end up one
+ GOP later. Trust the original mark position, not where it seems to
+ be when jumping to it.
+
+* There are no flickering between cuts any more! There is a small (3
+ frames long) pause before the next cut starts, but all flickering
+ and squares seem to be gone. I believe this is as good as it can get
+ without remuxing.
+
+
+Caveats
+
+* Since some time in the spring 2008 there seems to be a change/bug in
+ enigma2 that makes the elapsed time calculation somewhat offset
+ compared to earlier. This appears such that cut marks placed with
+ earlier versions appears to have drifted when watched in the new
+ version of enigma2. MovieCut is still callibrated to the earlier
+ calculation, and may thus also place the cuts somewhat offset from
+ the intended places. Always cut to a new file and check the result
+ to be on the safe side!
+
+* The manual specification of cuts in the "advanced cut parameter"
+ dialogue is as awkward as before to specify with the remote control:
+ "0" is mapped to ".","0", and ":" since this was most
+ straightforward to implement using existing python code. Also note
+ that the cuts are given in pairs of *included* sections, not
+ *excluded* as in the cutlist editor (i.e. there is first an IN time,
+ followed by OUT, and so on).
+
+* Although the flickering between cuts may be gone when looking at the
+ movie on the dreambox, there is no guarantee that they are gone when
+ trying to convert it to mpeg for burning to DVD or similar. It is
+ not even guaranteed that they can be easily converted at all - at
+ least one program is reported to fail to continue after the first
+ cut.
+
+
+Disclaimer
+
+I have really tried to be careful and make it reasonably "safe": It
+checks for errors during the process and will abort in a controlled
+way if it happens; If "-r"="replace" is specified, it will not remove
+any files until the whole cutting is successfully done; if "-r" is not
+given it will not overwrite any existing file with the same name as the
+destination; if no cuts are specified nothing will be done; etc.
+
+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.
+
--- /dev/null
+installdir = /usr/bin/
+
+bin_PROGRAMS = mcut
+
+mcut_SOURCES = mcut.cc
+
--- /dev/null
+ /* Copyright (C) 2007, 2008 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA
+ */
+
+#define _LARGEFILE64_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LEN 24064
+
+static off64_t* buf0 = 0;
+static off64_t* buf1 = 0;
+static off64_t time_offset;
+static off64_t size_offset;
+
+int use_leadin = 1;
+int use_leadout = 1;
+
+inline int absless(long long int x, int lim)
+{
+ return (x<lim && x>-lim);
+}
+
+double strtotime(char* str)
+{
+ int i=0, t1, tt;
+ char *p1, *p2;
+ double tmp;
+ p1 = str;
+ tt = strtol(p1, &p2, 10);
+ if (p1==p2) return -1.0;
+ while (*p2 == ':' && i<2) {
+ i++;
+ if (p2-p1>2) return -1.0;
+ p1 = p2+1;
+ t1 = strtol(p1, &p2, 10);
+ if (p1==p2) return -1.0;
+ tt = 60*tt + t1;
+ }
+ if (i>0 && p2-p1>2) return -1.0;
+ if (*p2 == 0) return (double)tt;
+ if (*p2 != '.') return -1.0;
+ p1 = p2+1;
+ t1 = strtol(p1, &p2, 10);
+ for (i=0, tmp=1.0; i<(p2-p1); tmp*=0.1, i++);
+ return (double)tt + tmp*t1;
+}
+
+char* timetostr(double tm)
+{
+ static char buf[15];
+ int r = (int)(tm/60);
+ sprintf(buf, "%d:%d:%.3f", r/60, r%60, tm-60*r);
+ return buf;
+}
+
+inline unsigned int byteswop(unsigned int n)
+{
+ return ((n&0xff000000)>>24) | ((n&0xff0000)>>8) | ((n&0xff00)<<8) | ((n&0xff)<<24);
+}
+
+inline unsigned long long int byteswopl(unsigned long long int n)
+{
+ return (n>>56) | ((n>>40)&0xff00) | ((n>>24)&0xff0000) | ((n>>8)&0xff000000) | ((n&0xff000000)<<8) | ((n&0xff0000)<<24) | ((n&0xff00)<<40) | ((n&0xff)<<56);
+}
+
+double inttotime(unsigned int t1, unsigned int t2)
+{
+ return (byteswop(t2)*1.1111111111111112e-05 + byteswop(t1)*47721.858844444447);
+}
+
+double lltotime(long long int t)
+{
+ return ((unsigned int)(t&0xffffffff)*1.1111111111111112e-05) + ((unsigned int)(t>>32)*47721.858844444447);
+}
+
+void timetoint(double tm, unsigned int& t1, unsigned int& t2)
+{
+ double tmp=tm/47721.858844444447;
+ t1 = byteswop((unsigned int)tmp);
+ t2 = byteswop((unsigned int)((tm - t1*47721.858844444447)*90000));
+}
+
+int readbufinternal(int f)
+{
+ off64_t* buf;
+ buf = buf0;
+ buf0 = buf1;
+ buf1 = buf;
+ if (read(f, buf, 16) != 16)
+ return 0;
+ buf[0] = (off64_t)byteswopl((unsigned long long int)buf[0]);
+ buf[1] = (off64_t)byteswopl((unsigned long long int)buf[1]);
+ return 1;
+}
+
+void writebufinternal(int f)
+{
+ off64_t buf2[2];
+ buf2[0] = (off64_t)byteswopl((unsigned long long int)buf0[0] - size_offset);
+ buf2[1] = (off64_t)byteswopl((unsigned long long int)buf0[1]);
+ write(f, buf2, 16);
+}
+
+off64_t readoff(int f, int fo, double t, int beg, double& tr)
+{
+ static off64_t lastreturn;
+ static double last;
+ static int endp;
+ off64_t sizetmp;
+ double tt, lt;
+ if (!buf0) {
+ buf0 = new off64_t[2];
+ buf1 = new off64_t[2];
+ if (!(readbufinternal(f) && readbufinternal(f))) {
+ printf("The corresponding \".ap\"-file is empty.\n");
+ exit(8);
+ }
+ time_offset = buf0[1];
+ if (buf1[1] > buf0[1] && buf1[1] - buf0[1] < 900000)
+ time_offset -= (buf1[1]-buf0[1])*buf0[0]/(buf1[0]-buf0[0]);
+ size_offset = buf0[0];
+ lastreturn = 0;
+ last = 0.0;
+ endp = 0;
+ }
+ if (t < last && t != -1.0) {
+ sizetmp = buf0[0];
+ lseek(f, 0, SEEK_SET);
+ readbufinternal(f);
+ readbufinternal(f);
+ time_offset = buf0[1];
+ if (buf1[1]>buf0[1] && buf1[1]-buf0[1]<900000)
+ time_offset -= (buf1[1]-buf0[1])*buf0[0]/(buf1[0]-buf0[0]);
+ size_offset += buf0[0] - sizetmp;
+ lastreturn = 0;
+ last = 0.0;
+ endp = 0;
+ }
+ if (t == last || endp == 1) {
+ return lastreturn;
+ }
+ if (!beg)
+ writebufinternal(fo);
+ last = t;
+ lt = lltotime(buf0[1] - time_offset);
+ tt = lltotime(buf1[1] - time_offset);
+ sizetmp = buf0[0];
+ while (tt < t || t == -1.0) {
+ if (!readbufinternal(f))
+ endp = 1;
+ if (!beg)
+ writebufinternal(fo);
+ if (endp)
+ break;
+ if (buf1[1] < buf0[1] || buf1[1] - buf0[1] > 900000) {
+ if (absless(buf1[1] + ((long long int)1)<<33 - buf0[1], 900000))
+ time_offset -= ((long long int)1)<<33;
+ else
+ time_offset += buf1[1] - buf0[1];
+ }
+ lt = tt;
+ tt = lltotime(buf1[1] - time_offset);
+ }
+ if (endp) {
+ tr = tt;
+ } else if (beg ? (lt == tt || (t-lt > tt-t && tt-t<0.18)) : (t-lt >= tt-t || t-lt>0.18)) {
+ if (!readbufinternal(f))
+ endp = 1;
+ if (!beg)
+ writebufinternal(fo);
+ tr = tt;
+ } else {
+ tr = lt;
+ }
+ if (beg)
+ size_offset += buf0[0] - sizetmp;
+ lastreturn = buf0[0];
+ return lastreturn;
+}
+
+int framepid(char* buf, int pos)
+{
+ return ((buf[pos+1] & 0x1f) << 8) + buf[pos+2];
+}
+
+int framesearch_f(char* buf, int start, int stop, int pid)
+{
+ char* p;
+ int pos = -1;
+ for (p = buf+start; p < buf+stop-3; p++)
+ if (p[0]==0 && p[1]==0 && p[2]==1 && p[3]==0) {
+ pos = ((p - buf)/188)*188;
+ if (pid == -1 || framepid(buf, pos) == pid)
+ return pos;
+ }
+ return -1;
+}
+
+int framesearch_b(char* buf, int start, int stop, int pid)
+{
+ char* p;
+ int pos = -1;
+ for (p = buf+stop-1; p >= buf+start+3; p--)
+ if (p[0]==0 && p[-1]==1 && p[-2]==0 && p[-3]==0) {
+ pos = ((p - buf)/188)*188;
+ if (pid == -1 || framepid(buf, pos) == pid)
+ return pos;
+ }
+ return -1;
+}
+
+int transfer_start(int f_ts, int f_out, off64_t n1, off64_t& n1ret)
+{
+ off64_t num;
+ int pos, tmp;
+ char buf[LEN];
+ if (use_leadin) {
+ num = 0;
+ tmp = 0;
+ do {
+ num += LEN;
+ if (num > n1)
+ tmp = LEN - (int)(num - n1), num = n1;
+ else
+ tmp = LEN;
+ lseek64(f_ts, n1 - num, SEEK_SET);
+ if (read(f_ts, buf, tmp) != tmp) return 1;
+ } while ((pos = framesearch_b(buf, 0, tmp, -1)) == -1 && num < n1);
+ if (pos != -1) {
+ if (write(f_out, buf+pos, tmp-pos) != tmp-pos) return 1;
+ n1ret = n1 - (num - pos);
+ size_offset -= (num - pos);
+ num -= tmp;
+ while (num > 0) {
+ if (read(f_ts, buf, LEN) != LEN) return 1;
+ if (write(f_out, buf, LEN) != LEN) return 1;
+ num -= LEN;
+ }
+ } else {
+ n1ret = n1;
+ }
+ return 0;
+ }
+ else {
+ lseek64(f_ts, n1, SEEK_SET);
+ n1ret = n1;
+ return 0;
+ }
+}
+
+int transfer_rest(int f_ts, int f_out, off64_t n1, off64_t n2, off64_t& n2ret)
+{
+ off64_t i;
+ int num, pos, st, pid, tmp;
+ char buf[LEN];
+ static off64_t lastn2 = -1, lastn2ret;
+ if (n1 == lastn2) {
+ i = lastn2ret;
+ lseek64(f_ts, i, SEEK_SET);
+ } else
+ i = n1;
+ for (; i+LEN<=n2; i+=LEN) {
+ if (read(f_ts, buf, LEN) != LEN) return 1;
+ if (write(f_out, buf, LEN) != LEN) return 1;
+ }
+ if (use_leadout) {
+ num = read(f_ts, buf, LEN);
+ pid = framepid(buf, n2-i);
+ st = (i < n2 ? n2-i : 0);
+ tmp = -st;
+ st += 188;
+ while ((pos = framesearch_f(buf, st, num, pid)) == -1 && num == LEN) {
+ if (write(f_out, buf, LEN) != LEN) return 1;
+ num = read(f_ts, buf, LEN);
+ st = 0;
+ tmp += LEN;
+ }
+ if (st && num < st)
+ return 1;
+ else if (pos == -1) {
+ if (write(f_out, buf, num) != num) return 1;
+ tmp += num;
+ size_offset -= tmp;
+ } else {
+ if (write(f_out, buf, pos) != pos) return 1;
+ tmp += pos;
+ size_offset -= tmp;
+ }
+ lastn2 = n2;
+ lastn2ret = n2ret = n2 + tmp;
+ return 0;
+ } else {
+ if (i < n2) {
+ if (read(f_ts, buf, n2-i) != n2-i) return 1;
+ if (write(f_out, buf, n2-i) != n2-i) return 1;
+ }
+ lastn2 = lastn2ret = n2ret = n2;
+ return 0;
+ }
+}
+
+int donextinterval1(int fc, int fco, int fa, int fao, int fts, int ftso)
+{
+ static int n = -1;
+ static double tlast, toff = 0.0;
+ static off64_t c2;
+ off64_t c1, c1ret, c2ret;
+ double ttmp;
+ unsigned int buf[3];
+ unsigned int tmp, lcheck=0;
+ if (n==0)
+ return 0;
+ else if (n==-1) {
+ n = lseek(fc, 0, SEEK_END) / 12;
+ lseek(fc, 0, SEEK_SET);
+ while (1) {
+ if (n == 0)
+ return 0;
+ read(fc, buf, 12);
+ n--;
+ tmp = byteswop(buf[2]);
+ if (tmp == 1) {
+ c1 = readoff(fa, fao, 0.0, 1, toff);
+ if (transfer_start(fts, ftso, c1, c1ret)) return -1;
+ c2 = readoff(fa, fao, inttotime(buf[0], buf[1]), 0, tlast);
+ if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
+ printf("Interval: %lld - %lld\n", c1ret, c2ret);
+ // move all passed marks
+ lseek(fc, 0, SEEK_SET);
+ read(fc, buf, 12);
+ while (byteswop(buf[2]) != 1) {
+ write(fco, buf, 12);
+ read(fc, buf, 12);
+ }
+ return 1;
+ } else if (tmp == 0) {
+ c1 = readoff(fa, fao, inttotime(buf[0], buf[1]), 1, toff);
+ if (transfer_start(fts, ftso, c1, c1ret)) return -1;
+ if (lcheck) {
+ buf[0] = buf[1] = 0;
+ write(fco, buf, 12);
+ }
+ break;
+ } else if (tmp == 3)
+ lcheck = 1;
+ }
+ } else {
+ while (1) {
+ read(fc, buf, 12);
+ n--;
+ tmp = byteswop(buf[2]);
+ if (tmp == 0) {
+ c1 = readoff(fa, fao, inttotime(buf[0], buf[1]), 1, ttmp);
+ if (c1 != c2)
+ if (transfer_start(fts, ftso, c1, c1ret)) return -1;
+ toff += ttmp - tlast;
+ break;
+ } else if (tmp == 3) {
+ timetoint(tlast-toff, buf[0], buf[1]);
+ write(fco, buf, 12);
+ }
+ if (n == 0)
+ return 0;
+ }
+ }
+ while (1) {
+ if (n == 0) {
+ c2 = readoff(fa, fao, -1.0, 0, tlast);
+ if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
+ printf("Interval: %lld - %lld\n", c1ret, c2ret);
+ return 1;
+ }
+ read(fc, buf, 12);
+ n--;
+ tmp = byteswop(buf[2]);
+ if (tmp == 1) {
+ c2 = readoff(fa, fao, inttotime(buf[0], buf[1]), 0, tlast);
+ if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
+ printf("Interval: %lld - %lld\n", c1ret, c2ret);
+ return 1;
+ } else if (tmp != 0) {
+ timetoint(inttotime(buf[0], buf[1])-toff, buf[0], buf[1]);
+ write(fco, buf, 12);
+ }
+ }
+ return 0;
+}
+
+int donextinterval2(int barg, int earg, char* argv[], int fc, int fco, int fa, int fao, int fts, int ftso)
+{
+ static int n = -1, i, lio = -1, lcheck=0;
+ static double tlast = 0.0, toff = 0.0;
+ static off64_t c2 = -1;
+ off64_t c1, c1ret, c2ret;
+ double ttmp, ttmp2;
+ int j, io = -1;
+ unsigned int buf[3];
+ unsigned int buff[3];
+ unsigned int tmp;
+ if (i>=earg) {
+ if (!lcheck && n!=-1) {
+ lseek(fc, 0, SEEK_SET);
+ for (j=0; j<n; j++) {
+ read(fc, buf, 12);
+ tmp = byteswop(buf[2]);
+ if (tmp == 3) {
+ timetoint(tlast-toff, buf[0], buf[1]);
+ write(fco, buf, 12);
+ break;
+ }
+ }
+ }
+ if (lio != -1) { // Add an extra "out" at the end to avoid bug in playback
+ buff[2] = byteswop(1);
+ timetoint(tlast-toff, buff[0], buff[1]);
+ write(fco, buff, 12);
+ }
+ return 0;
+ }
+ if (n==-1) {
+ i = barg;
+ n = lseek(fc, 0, SEEK_END) / 12;
+ }
+ c1 = readoff(fa, fao, strtotime(argv[i]), 1, ttmp);
+ if (c1 != c2)
+ if (transfer_start(fts, ftso, c1, c1ret)) return -1;
+ toff += ttmp - tlast;
+ c2 = readoff(fa, fao, strtotime(argv[i+1]), 0, tlast);
+ if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
+ printf("Interval: %lld - %lld\n", c1ret, c2ret);
+ lseek(fc, 0, SEEK_SET);
+ for (j=0; j<n; j++) {
+ read(fc, buf, 12);
+ tmp = byteswop(buf[2]);
+ ttmp2=inttotime(buf[0], buf[1]);
+ if (tmp == 3) {
+ if (!lcheck) {
+ if (ttmp2 <= ttmp) {
+ timetoint(ttmp-toff, buf[0], buf[1]);
+ write(fco, buf, 12);
+ lcheck = 1;
+ } else if (ttmp2 <= tlast) {
+ timetoint(ttmp2-toff, buf[0], buf[1]);
+ write(fco, buf, 12);
+ lcheck = 1;
+ }
+ }
+ } else if (ttmp2 >= ttmp && ttmp2 <= tlast) {
+ if (tmp < 2) {
+ if (lio != io && lio != -1) {
+ buff[2] = byteswop(io);
+ timetoint(ttmp-toff, buff[0], buff[1]);
+ write(fco, buff, 12);
+ }
+ lio = io = tmp;
+ }
+ timetoint(ttmp2-toff, buf[0], buf[1]);
+ write(fco, buf, 12);
+ } else if (tmp < 2) {
+ io = tmp;
+ }
+ }
+ i+=2;
+ return 1;
+}
+
+char* makefilename(const char* base, const char* pre, const char* ext, const char* post)
+{
+ static char buf[256];
+ int len1, len2, len3;
+ len1 = strlen(base);
+ len2 = (pre ? strlen(pre) : 0);
+ len3 = (ext ? strlen(ext) : 0);
+ strcpy(buf, base);
+ if (ext && len1>=len3 && !strcmp(base+len1-len3,ext))
+ len1 -= len3;
+ if (pre)
+ strcpy(buf+len1, pre);
+ if (ext)
+ strcpy(buf+len1+len2, ext);
+ if (post)
+ strcpy(buf+len1+len2+len3, post);
+ return buf;
+}
+
+void copymeta(int n, int f1, int f2, const char* title, const char* suff, const char* descr)
+{
+ int i, j, k;
+ char* buf = new char[n];
+ read(f1, buf, n);
+ for (i=0; i<n; i++)
+ if (buf[i] == 10)
+ break;
+ write(f2, buf, i);
+ if (i == n) return;
+ for (j=i+1; j<n; j++)
+ if (buf[j] == 10)
+ break;
+ if (title) {
+ write(f2, buf+i, 1);
+ for (k=0; title[k] && title[k] != 10; k++);
+ write(f2, title, k);
+ } else {
+ write(f2, buf+i, j-i);
+ if (suff && j-i>1)
+ write(f2, suff, strlen(suff));
+ }
+ if (j == n) return;
+ i = j;
+ for (j=i+1; j<n; j++)
+ if (buf[j] == 10)
+ break;
+ if (descr) {
+ write(f2, buf+i, 1);
+ for (k=0; descr[k] && descr[k] != 10; k++);
+ write(f2, descr, k);
+ } else {
+ write(f2, buf+i, j-i);
+ }
+ if (j < n)
+ write(f2, buf+j, n-j);
+ delete [] buf;
+}
+
+int main(int argc, char* argv[])
+{
+ int f_ts, f_out, f_cuts, f_cutsout, f_ap, f_apout, f_meta, f_metaout;
+ char* tmpname;
+ const char* suff = 0;
+ char* inname = 0;
+ char* outname = 0;
+ char* title = 0;
+ char* descr = 0;
+ int cutarg = 0, cutargend = 0;
+ int replace = 0;
+ int i, j, ok, bad = 0;
+ double t1, t2;
+ struct stat statbuf;
+ struct stat64 statbuf64;
+
+ for (i=1; i<argc; i++) {
+ if (!strcmp(argv[i], "-r"))
+ replace = 1;
+ else if (!strcmp(argv[i], "-o")) {
+ if (i == argc-1) {
+ bad = 1;
+ break;
+ }
+ outname = argv[++i];
+ } else if (!strcmp(argv[i], "-n")) {
+ if (i == argc-1) {
+ bad = 1;
+ break;
+ }
+ title = argv[++i];
+ } else if (!strcmp(argv[i], "-d")) {
+ if (i == argc-1) {
+ bad = 1;
+ break;
+ }
+ descr = argv[++i];
+ } else if (!strcmp(argv[i], "-c")) {
+ cutarg = ++i;
+ for (j=i; j<argc; j+=2) {
+ t1 = strtotime(argv[j]);
+ t2 = (j+1<argc ? strtotime(argv[j+1]) : -1.0);
+ if (t1 < 0 || t2 < 0)
+ break;
+ else if (t1 > t2) {
+ printf("Bad time interval: %s - %s\n", argv[j], argv[j+1]);
+ bad = 1;
+ break;
+ }
+ }
+ cutargend = i = j;
+ if (bad)
+ break;
+ } else if (*argv[i] == '-' && (*(argv[i]+1) == 0 ||*(argv[i]+2) == 0)) {
+ bad = 1;
+ break;
+ } else if (!inname)
+ inname = argv[i];
+ else {
+ bad = 1;
+ break;
+ }
+ }
+ if (argc == 1 || bad) {
+ printf("Usage: mcut [-r] [-o output_ts_file] [-n title] [-d description] ts_file [-c start1 end1 [start2 end2] ... ]\n");
+ printf(" -r : Replace (= remove) the original movie.\n");
+ printf(" -o : Filename of resulting movie (defaults to the original name appended by \" cut\", unless -r is given).\n");
+ printf(" -n : Title of resulting movie.\n");
+ printf(" -d : Description of resulting movie.\n");
+ printf(" -c : A sequence of starttime and endtime pairs. Each time is given as hour:min:sec. The portion between start and end is retained (i.e. not cut away).\n");
+ exit(1);
+ }
+ if (outname) {
+ suff = 0;
+ } else {
+ outname = inname;
+ suff = (replace ? "_" : " cut");
+ }
+ tmpname = makefilename(inname, 0, ".ts", 0);
+ f_ts = open(tmpname, O_RDONLY | O_LARGEFILE);
+ if (f_ts == -1) {
+ printf("Failed to open input stream file \"%s\"\n", tmpname);
+ exit(2);
+ }
+ tmpname = makefilename(inname, 0, ".ts", ".cuts");
+ f_cuts = open(tmpname, O_RDONLY);
+ if (f_cuts == -1) {
+ printf("Failed to open input cuts file \"%s\"\n", tmpname);
+ close(f_ts);
+ exit(3);
+ }
+ tmpname = makefilename(inname, 0, ".ts", ".ap");
+ f_ap = open(tmpname, O_RDONLY);
+ if (f_ap == -1) {
+ printf("Failed to open input ap file \"%s\"\n", tmpname);
+ close(f_ts);
+ close(f_cuts);
+ exit(4);
+ }
+ if (fstat64(f_ts, &statbuf64)) {
+ printf("Failed to stat input stream file.\n");
+ close(f_ts);
+ close(f_cuts);
+ close(f_ap);
+ exit(2);
+ }
+ tmpname = makefilename(outname, suff, ".ts", 0);
+ f_out = open(tmpname, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, statbuf64.st_mode & 0xfff);
+ if (f_out == -1) {
+ printf("Failed to open output stream file \"%s\"\n", tmpname);
+ close(f_ts);
+ close(f_cuts);
+ close(f_ap);
+ exit(5);
+ }
+ if (fstat(f_cuts, &statbuf)) {
+ printf("Failed to stat input cuts file.\n");
+ close(f_ts);
+ close(f_cuts);
+ close(f_ap);
+ close(f_out);
+ unlink(makefilename(outname, suff, ".ts", 0));
+ exit(3);
+ }
+ tmpname = makefilename(outname, suff, ".ts", ".cuts");
+ f_cutsout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
+ if (f_cutsout == -1) {
+ printf("Failed to open output cuts file \"%s\"\n", tmpname);
+ close(f_ts);
+ close(f_cuts);
+ close(f_ap);
+ close(f_out);
+ unlink(makefilename(outname, suff, ".ts", 0));
+ exit(6);
+ }
+ if (fstat(f_ap, &statbuf)) {
+ printf("Failed to stat input ap file.\n");
+ close(f_ts);
+ close(f_cuts);
+ close(f_ap);
+ close(f_out);
+ close(f_cutsout);
+ unlink(makefilename(outname, suff, ".ts", 0));
+ unlink(makefilename(outname, suff, ".ts", ".cuts"));
+ exit(4);
+ }
+ tmpname = makefilename(outname, suff, ".ts", ".ap");
+ f_apout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
+ if (f_apout == -1) {
+ printf("Failed to open output ap file \"%s\"\n", tmpname);
+ close(f_ts);
+ close(f_cuts);
+ close(f_ap);
+ close(f_out);
+ close(f_cutsout);
+ unlink(makefilename(outname, suff, ".ts", 0));
+ unlink(makefilename(outname, suff, ".ts", ".cuts"));
+ exit(7);
+ }
+
+ if (cutarg)
+ ok = donextinterval2(cutarg, cutargend, argv, f_cuts, f_cutsout, f_ap, f_apout, f_ts, f_out);
+ else
+ ok = donextinterval1(f_cuts, f_cutsout, f_ap, f_apout, f_ts, f_out);
+ if (!ok) {
+ printf("There are no cuts specified. Leaving the movie as it is.\n");
+ close(f_ts);
+ close(f_cuts);
+ close(f_ap);
+ close(f_out);
+ close(f_cutsout);
+ close(f_apout);
+ unlink(makefilename(outname, suff, ".ts", 0));
+ unlink(makefilename(outname, suff, ".ts", ".cuts"));
+ unlink(makefilename(outname, suff, ".ts", ".ap"));
+ exit(9);
+ }
+
+ while (ok > 0) {
+ if (cutarg)
+ ok = donextinterval2(cutarg, cutargend, argv, f_cuts, f_cutsout, f_ap, f_apout, f_ts, f_out);
+ else
+ ok = donextinterval1(f_cuts, f_cutsout, f_ap, f_apout, f_ts, f_out);
+ }
+
+ close(f_ts);
+ close(f_cuts);
+ close(f_ap);
+ close(f_out);
+ close(f_cutsout);
+ close(f_apout);
+ if (ok < 0) {
+ printf("Copying %s failed, read/write error.\n", makefilename(inname, 0, ".ts", 0));
+ unlink(makefilename(outname, suff, ".ts", 0));
+ unlink(makefilename(outname, suff, ".ts", ".cuts"));
+ unlink(makefilename(outname, suff, ".ts", ".ap"));
+ exit(10);
+ }
+
+ tmpname = makefilename(inname, 0, ".ts", ".meta");
+ f_meta = open(tmpname, O_RDONLY);
+ if (f_meta == -1) {
+ printf("Failed to open input meta file \"%s\"\n", tmpname);
+ exit(0);
+ }
+ if (fstat(f_meta, &statbuf)) {
+ printf("Failed to stat input meta file.\n");
+ close(f_meta);
+ exit(0);
+ }
+ tmpname = makefilename(outname, suff, ".ts", ".meta");
+ f_metaout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
+ if (f_metaout == -1) {
+ printf("Failed to open output meta file \"%s\"\n", tmpname);
+ close(f_meta);
+ exit(0);
+ }
+ copymeta((int)statbuf.st_size, f_meta, f_metaout, title, (replace ? 0 : suff), descr);
+ close(f_meta);
+ close(f_metaout);
+
+ if (replace) {
+ if (suff) {
+ tmpname = makefilename(inname, 0, ".ts", 0);
+ tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
+ unlink(tmpname);
+ rename(makefilename(outname, suff, ".ts", 0), tmpname);
+ tmpname = makefilename(inname, 0, ".ts", ".cuts");
+ tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
+ unlink(tmpname);
+ rename(makefilename(outname, suff, ".ts", ".cuts"), tmpname);
+ tmpname = makefilename(inname, 0, ".ts", ".ap");
+ tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
+ unlink(tmpname);
+ rename(makefilename(outname, suff, ".ts", ".ap"), tmpname);
+ tmpname = makefilename(inname, 0, ".ts", ".meta");
+ tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
+ unlink(tmpname);
+ rename(makefilename(outname, suff, ".ts", ".meta"), tmpname);
+ } else {
+ unlink(makefilename(inname, 0, ".ts", 0));
+ unlink(makefilename(inname, 0, ".ts", ".cuts"));
+ unlink(makefilename(inname, 0, ".ts", ".ap"));
+ unlink(makefilename(inname, 0, ".ts", ".meta"));
+ }
+ }
+}
+
--- /dev/null
+installdir = /usr/lib/enigma2/python/Plugins/Extensions/MovieCut
+
+install_PYTHON = __init__.py plugin.py
--- /dev/null
+from Plugins.Plugin import PluginDescriptor
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Screens.ChoiceBox import ChoiceBox
+from Screens.LocationBox import MovieLocationBox
+import Screens.Standby
+from Components.config import *
+from Components.ActionMap import ActionMap, NumberActionMap
+from Components.ConfigList import ConfigList, ConfigListScreen
+from Components.Button import Button
+from Components.Label import Label
+from Components.Pixmap import Pixmap
+from enigma import eTimer, eServiceReference, eServiceCenter, iServiceInformation, eConsoleAppContainer
+from os import WIFEXITED, WEXITSTATUS
+
+def main(session, service, **kwargs):
+ session.open(MovieCut, service, **kwargs)
+
+def Plugins(**kwargs):
+ return PluginDescriptor(name="MovieCut", description="Execute cuts...", where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)
+
+
+class MovieCut(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 = []
+ tlist.append(("Don't cut", "CALLFUNC", self.confirmed0))
+ tlist.append(("Replace the original movie with the cut movie", "CALLFUNC", self.confirmed1))
+ tlist.append(("Place the cut movie in a new file ending with \" cut\"", "CALLFUNC", self.confirmed2))
+ tlist.append(("Advanced cut parameter settings...", "CALLFUNC", self.confirmed3))
+ ChoiceBox.__init__(self, session, _("How would you like to cut \"%s\"?") % (self.name), list = tlist, selection = 0)
+ self.skinName = "ChoiceBox"
+
+ def confirmed0(self, arg):
+ self.close()
+
+ def confirmed1(self, arg):
+ MovieCutSpawn(self.session, self, ["/usr/bin/mcut", "-r", self.service.getPath()], self.name)
+
+ def confirmed2(self, arg):
+ MovieCutSpawn(self.session, self, ["/usr/bin/mcut", self.service.getPath()], self.name)
+
+ def confirmed3(self, arg):
+ serviceHandler = eServiceCenter.getInstance()
+ info = serviceHandler.info(self.service)
+ self.path = self.service.getPath()
+ self.name = info.getName(self.service)
+ self.descr = info.getInfoString(self.service, iServiceInformation.sDescription)
+ self.session.openWithCallback(self.advcutConfirmed, AdvancedCutInput, self.name, self.path, self.descr)
+
+ def advcutConfirmed(self, ret):
+ if len(ret) <= 1 or not ret[0]:
+ self.close()
+ return
+ clist = ["/usr/bin/mcut"]
+ if ret[1] == True:
+ clist.append("-r")
+ clist.append(self.service.getPath())
+ if ret[2] != False:
+ clist += ["-o", ret[2]]
+ if ret[3] != False:
+ clist += ["-n", ret[3]]
+ if ret[4] != False:
+ clist += ["-d", ret[4]]
+ if ret[5] != False:
+ clist.append("-c")
+ clist += ret[5]
+ MovieCutSpawn(self.session, self, clist, self.name)
+
+class AdvancedCutInput(Screen, ConfigListScreen):
+ skin = """
+ <screen name="AdvancedCutInput" position="80,100" size="550,320" title="Cut Parameter Input">
+ <widget name="config" position="5,10" size="530,250" />
+ <widget name="ok" position="90,265" size="140,40" pixmap="skin_default/buttons/green.png" alphatest="on" />
+ <widget name="oktext" position="90,265" size="140,40" valign="center" halign="center" zPosition="2" font="Regular;20" transparent="1" />
+ <widget name="cancel" position="320,265" size="140,40" pixmap="skin_default/buttons/red.png" alphatest="on" />
+ <widget name="canceltext" position="320,265" size="140,40" valign="center" halign="center" zPosition="2" font="Regular;20" transparent="1" />
+ </screen>"""
+
+ def __init__(self, session, name, path, descr):
+ self.skin = AdvancedCutInput.skin
+ Screen.__init__(self, session)
+
+ self["oktext"] = Label(_("OK"))
+ self["canceltext"] = Label(_("Cancel"))
+ self["ok"] = Pixmap()
+ self["cancel"] = Pixmap()
+
+ if self.baseName(path) == self.baseName(name):
+ self.title = ""
+ else:
+ self.title = name
+ self.dir = self.dirName(path)
+ self.file = self.baseName(path) + " cut"
+ self.descr = descr
+ self.input_replace = ConfigSelection(choices = [("no", _("No")), ("yes", _("Yes"))], default = "no")
+ self.input_file = ConfigText(default = self.file, fixed_size = False, visible_width = 45)
+ self.input_title = ConfigText(default = self.title, fixed_size = False, visible_width = 45)
+ self.input_descr = ConfigText(default = self.descr, fixed_size = False, visible_width = 45)
+ tmp = config.movielist.videodirs.value
+ if not self.dir in tmp:
+ tmp.append(self.dir)
+ self.input_dir = ConfigSelection(choices = tmp, default = self.dir)
+ self.input_manual = ConfigSelection(choices = [("no", _("Cutlist")), ("yes", _("Manual specification"))], default = "no")
+ self.input_space = ConfigNothing()
+ self.input_manualcuts = ConfigText(default = "", fixed_size = False)
+ self.input_manualcuts.setUseableChars(" 0123456789:.")
+ self["actions"] = NumberActionMap(["SetupActions"],
+ {
+ "ok": self.keySelectOrGo,
+ "save": self.keyGo,
+ "cancel": self.keyCancel,
+ }, -2)
+
+ self.list = []
+ ConfigListScreen.__init__(self, self.list)
+ self.entry_replace = getConfigListEntry(_("Replace original:"), self.input_replace)
+ self.entry_file = getConfigListEntry(_("New filename:"), self.input_file)
+ self.entry_title = getConfigListEntry(_("New title:"), self.input_title)
+ self.entry_descr = getConfigListEntry(_("New description:"), self.input_descr)
+ self.entry_dir = getConfigListEntry(_("New location:"), self.input_dir)
+ self.entry_manual = getConfigListEntry(_("Cut source:"), self.input_manual)
+ self.entry_space = getConfigListEntry(_("Cuts (an IN OUT IN OUT ... sequence of hour:min:sec)"), self.input_space)
+ self.entry_manualcuts = getConfigListEntry(_(":"), self.input_manualcuts)
+ self.createSetup(self["config"])
+
+ def createSetup(self, configlist):
+ self.list = []
+ self.list.append(self.entry_replace)
+ if self.input_replace.value == "no":
+ self.list.append(self.entry_file)
+ self.list.append(self.entry_dir)
+ self.list.append(self.entry_title)
+ self.list.append(self.entry_descr)
+ self.list.append(self.entry_manual)
+ if self.input_manual.value == "yes":
+ self.list.append(self.entry_space)
+ self.list.append(self.entry_manualcuts)
+ configlist.list = self.list
+ configlist.l.setList(self.list)
+
+ def keyLeft(self):
+ ConfigListScreen.keyLeft(self)
+ cc = self["config"].getCurrent()
+ if cc is self.entry_replace or cc is self.entry_manual:
+ self.createSetup(self["config"])
+
+ def keyRight(self):
+ ConfigListScreen.keyRight(self)
+ cc = self["config"].getCurrent()
+ if cc is self.entry_replace or cc is self.entry_manual:
+ self.createSetup(self["config"])
+
+ def pathSelected(self, res):
+ if res is not None:
+ if config.movielist.videodirs.value != self.input_dir.choices:
+ self.input_dir.setChoices(config.movielist.videodirs.value, default=res)
+ self.input_dir.value = res
+
+ def keySelectOrGo(self):
+ if self["config"].getCurrent() == self.entry_dir:
+ self.session.openWithCallback(
+ self.pathSelected,
+ MovieLocationBox,
+ _("Choose target folder"),
+ self.input_dir.value,
+ )
+ else:
+ self.keyGo()
+
+ def keyGo(self):
+ if self.input_replace.value == "yes":
+ path = False
+ else:
+ path = self.rejoinName(self.input_dir.value, self.input_file.value)
+ if self.input_manual.value == "no":
+ cuts = False
+ else:
+ cuts = self.input_manualcuts.value.split(' ')
+ while "" in cuts:
+ cuts.remove("")
+ self.close((True, self.input_replace.value, path, self.input_title.value, self.input_descr.value, cuts))
+
+ def keyCancel(self):
+ self.close((False,))
+
+ def baseName(self, str):
+ name = str.split('/')[-1]
+ if name.endswith(".ts") is True:
+ return name[:-3]
+ else:
+ return name
+
+ def dirName(self, str):
+ return '/'.join(str.split('/')[:-1]) + '/'
+
+ def rejoinName(self, dir, name):
+ name = name.strip()
+ if name.endswith(".ts") is True:
+ return dir + name[:-3]
+ else:
+ return dir + name
+
+class MovieCutQueue:
+ def __init__(self):
+ self.container = eConsoleAppContainer()
+ self.container.appClosed.get().append(self.runDone)
+ self.queue = []
+ self.running = False
+
+ def enqueue(self, cb, cmd):
+ self.queue.append((cb, cmd))
+ if not self.running:
+ self.running = True
+ self.runNext()
+ return True
+ else:
+ return False
+
+ def runNext(self):
+ if not self.queue:
+ self.running = False
+ else:
+ self.container.execute(self.queue[0][1][0], self.queue[0][1])
+
+ def runDone(self, retval):
+ cb = self.queue[0][0]
+ self.queue = self.queue[1:]
+ cb(retval)
+ self.runNext()
+
+global_mcut_errors = ["The movie \"%s\" is successfully cut",
+ "Cutting failed for movie \"%s\":\nBad arguments",
+ "Cutting failed for movie \"%s\":\nCouldn't open input .ts file",
+ "Cutting failed for movie \"%s\":\nCouldn't open input .cuts file",
+ "Cutting failed for movie \"%s\":\nCouldn't open input .ap file",
+ "Cutting failed for movie \"%s\":\nCouldn't open output .ts file",
+ "Cutting failed for movie \"%s\":\nCouldn't open output .cuts file",
+ "Cutting failed for movie \"%s\":\nCouldn't open output .ap file",
+ "Cutting failed for movie \"%s\":\nEmpty .ap file",
+ "Cutting failed for movie \"%s\":\nNo cuts specified",
+ "Cutting failed for movie \"%s\":\nRead/write error (disk full?)",
+ "Cutting was aborted for movie \"%s\""]
+
+global_mcut_queue = MovieCutQueue()
+
+global_mcut_block = False
+
+class MovieCutSpawn:
+ def __init__(self, session, parent, clist, name):
+ global global_mcut_queue
+ global global_mcut_block
+ self.session = session
+ self.parent = parent
+ self.name = name
+ self.clist = clist
+ self.mess = ""
+ self.dialog = False
+ self.waitTimer = eTimer()
+ self.waitTimer.callback.append(self.doWaitAck)
+ if global_mcut_queue.enqueue(self.doAck, clist):
+ mess = _("The movie \"%s\" is cut in the background.") % (self.name)
+ else:
+ mess = _("Another movie is currently cut.\nThe movie \"%s\" will be cut in the background after it.") % (self.name)
+ global_mcut_block = True
+ self.dialog = self.session.openWithCallback(self.endc, MessageBox, mess, MessageBox.TYPE_INFO)
+
+ def doAck(self, retval):
+ global global_mcut_errors
+# if WIFEXITED(retval):
+# self.mess = global_mcut_errors[WEXITSTATUS(retval)] % (self.name)
+# else:
+# self.mess = global_mcut_errors[-1] % (self.name)
+ self.mess = global_mcut_errors[retval] % (self.name)
+ self.doWaitAck()
+
+ def doWaitAck(self):
+ global global_mcut_block
+ if Screens.Standby.inStandby or not self.session.in_exec or (global_mcut_block and not self.dialog):
+ self.waitTimer.start(2000, True)
+ else:
+ global_mcut_block = True
+ self.session.openWithCallback(self.endw, MessageBox, self.mess, MessageBox.TYPE_INFO)
+
+ def endw(self, arg = 0):
+ global global_mcut_block
+ global_mcut_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_mcut_block
+ global_mcut_block = False
+ self.dialog = False
+ self.parent.close()
+# self.session.current_dialog.close()
--- /dev/null
+Package: enigma2-plugin-extensions-movieretitle
+Version: 1.2-20080717
+Description: Change the filename, title, description and location of a movie
+Section: extra
+Priority: optional
+Architecture: mipsel
+Maintainer: Anders Holst <aho@sics.se>
+Depends: enigma2(>2.5cvs20080628)
--- /dev/null
+SUBDIRS = src
+
+EXTRA_DIST = Readme-MovieRetitle-1.2.txt
--- /dev/null
+MovieRetitle-1.2
+----------------
+2008-07-17
+Anders Holst (aho@sics.se)
+
+
+With this module it is possible to change the filename, title,
+description, and location of a movie. When installed it will show up
+in the movie list menu (i.e. find a movie in the movie selection list
+and press the menu button) under the name "Change name...".
+
+This version requires an Enigma2 version from later than 2008-06-28,
+but should work on any Dreambox model running Enigma2.
+
+News since version 1.1:
+
+* It is possible to move to another location. If the other location is
+ on another device, moving will be performed in the background and a
+ notification given when moving is finished. The old movie will not
+ disappear from the movie list until then. It is possible to request
+ moving more movies before the first one has finished, but they will
+ be queued up and moved after each other not the stress the external
+ media too much.
+
+* Adapted to the skin changes from 2008-04-14.
+
--- /dev/null
+installdir = /usr/lib/enigma2/python/Plugins/Extensions/MovieRetitle
+
+install_PYTHON = __init__.py plugin.py
--- /dev/null
+from Plugins.Plugin import PluginDescriptor
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Screens.LocationBox import MovieLocationBox
+import Screens.Standby
+from Components.config import *
+from Components.ActionMap import ActionMap, NumberActionMap
+from Components.ConfigList import ConfigList, ConfigListScreen
+from Components.Button import Button
+from Components.Label import Label
+from Components.Pixmap import Pixmap
+from enigma import eTimer, eServiceReference, eServiceCenter, iServiceInformation, eConsoleAppContainer
+import os
+
+def main(session, service, **kwargs):
+ session.open(MovieRetitle, service, session.current_dialog, **kwargs)
+
+def Plugins(**kwargs):
+ return PluginDescriptor(name="MovieRetitle", description=_("Change name..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)
+
+
+class MovieRetitle(Screen, ConfigListScreen):
+ skin = """
+ <screen name="TitleDescrInput" position="100,150" size="500,200" title="Name and Description Input">
+ <widget name="config" position="10,10" size="480,120" />
+ <widget name="ok" position="70,140" size="140,40" pixmap="skin_default/buttons/green.png" alphatest="on" />
+ <widget name="oktext" position="70,140" size="140,40" valign="center" halign="center" zPosition="2" font="Regular;20" transparent="1" />
+ <widget name="cancel" position="290,140" size="140,40" pixmap="skin_default/buttons/red.png" alphatest="on" />
+ <widget name="canceltext" position="290,140" size="140,40" valign="center" halign="center" zPosition="2" font="Regular;20" transparent="1" />
+ </screen>"""
+
+ def __init__(self, session, service, parent, args = 0):
+ Screen.__init__(self, session)
+ self.session = session
+ self.service = service
+ self.parentscreen = parent
+ Screen.__init__(self, session)
+ serviceHandler = eServiceCenter.getInstance()
+ info = serviceHandler.info(self.service)
+ self.path = self.service.getPath()
+ if self.path.endswith(".ts") is True:
+ self.path = self.path[:-3]
+ self.dir = self.dirName(self.path)
+ self.file = self.baseName(self.path)
+ self.name = info.getName(self.service)
+ if self.file == self.baseName(self.name):
+ self.title = ""
+ else:
+ self.title = self.name
+ self.descr = info.getInfoString(self.service, iServiceInformation.sDescription)
+
+ self["oktext"] = Label(_("OK"))
+ self["canceltext"] = Label(_("Cancel"))
+ self["ok"] = Pixmap()
+ self["cancel"] = Pixmap()
+
+ self.input_file = ConfigText(default = self.file, fixed_size = False, visible_width = 42)
+ self.input_title = ConfigText(default = self.title, fixed_size = False, visible_width = 42)
+ self.input_descr = ConfigText(default = self.descr, fixed_size = False, visible_width = 42)
+ tmp = config.movielist.videodirs.value
+ if not self.dir in tmp:
+ tmp.append(self.dir)
+ self.input_dir = ConfigSelection(choices = tmp, default = self.dir)
+
+ self["actions"] = NumberActionMap(["SetupActions"],
+ {
+ "ok": self.keySelectOrGo,
+ "save": self.keyGo,
+ "cancel": self.keyCancel,
+ }, -2)
+
+ self.list = []
+ ConfigListScreen.__init__(self, self.list)
+ self.createSetup(self["config"])
+
+ def createSetup(self, configlist):
+ self.list = []
+ self.list.append(getConfigListEntry(_("Filename"), self.input_file))
+ self.list.append(getConfigListEntry(_("Title"), self.input_title))
+ self.list.append(getConfigListEntry(_("Description"), self.input_descr))
+ self.list.append(getConfigListEntry(_("Location"), self.input_dir))
+ configlist.list = self.list
+ configlist.l.setList(self.list)
+
+ def pathSelected(self, res):
+ if res is not None:
+ if config.movielist.videodirs.value != self.input_dir.choices:
+ self.input_dir.setChoices(config.movielist.videodirs.value, default=res)
+ self.input_dir.value = res
+
+ def keySelectOrGo(self):
+ if self["config"].getCurrent() == self.list[3]:
+ self.session.openWithCallback(
+ self.pathSelected,
+ MovieLocationBox,
+ _("Choose target folder"),
+ self.input_dir.value,
+ )
+ else:
+ self.keyGo()
+
+ def keyGo(self):
+ if self.input_title.value != self.title or self.input_descr.value != self.descr:
+ self.setTitleDescr(self.path, self.input_title.value, self.input_descr.value)
+ if self.input_file.value != self.file or self.input_dir.value != self.dir:
+ self.maybeMoveMovieFiles(self.path, self.rejoinName(self.input_dir.value, self.input_file.value))
+ else:
+ self.exitDialog()
+
+ def keyCancel(self):
+ self.close()
+
+ def setTitleDescr(self, file, title, descr):
+ if os.path.exists(file + ".ts.meta"):
+ metafile = open(file + ".ts.meta", "r")
+ sid = metafile.readline()
+ oldtitle = metafile.readline().rstrip()
+ olddescr = metafile.readline().rstrip()
+ rest = metafile.read()
+ metafile.close()
+ if not title and title != "":
+ title = oldtitle
+ if not descr and descr != "":
+ descr = olddescr
+ metafile = open(file + ".ts.meta", "w")
+ metafile.write("%s%s\n%s\n%s" %(sid, title, descr, rest))
+ metafile.close()
+
+ def maybeMoveMovieFiles(self, fr, to):
+ if os.path.exists(to+".ts"):
+ self.inter_fr = fr
+ self.inter_to = to
+ self.session.openWithCallback(self.confirmedReplace, MessageBox, _("Target file %s.ts already exist.\nDo you want to replace it?") % (to), MessageBox.TYPE_YESNO)
+ elif os.path.isdir(os.path.dirname(to)):
+ self.moveMovieFiles(fr, to)
+ else:
+ self.session.openWithCallback(self.exitDialog, MessageBox, _("The target directory is not found. The file is not renamed."), MessageBox.TYPE_ERROR)
+
+ def confirmedReplace(self, answer):
+ if answer == True:
+ self.moveMovieFiles(self.inter_fr, self.inter_to)
+
+ def moveMovieFiles(self, fr, to):
+ try:
+ os.rename(fr + ".ts", to + ".ts")
+ except OSError:
+ print "Moving in background"
+ global_background_mover.enqueue(self.exitDialog, self.session, fr, to)
+ else:
+ print "Moving in foreground"
+ for suff in [".ts.meta", ".ts.cuts", ".ts.ap", ".eit"]:
+ if os.path.exists(fr + suff):
+ os.rename(fr + suff, to + suff)
+ self.exitDialog()
+
+ def exitDialog(self, dummy=None):
+ self.close()
+ # This will try to get back to an updated movie list.
+ # A proper way to do this should be provided in enigma2.
+ try:
+ self.parentscreen.csel.reloadList()
+ self.parentscreen.close()
+ except AttributeError:
+ pass
+
+ def baseName(self, str):
+ name = str.split('/')[-1]
+ if name.endswith(".ts") is True:
+ return name[:-3]
+ else:
+ return name
+
+ def dirName(self, str):
+ return '/'.join(str.split('/')[:-1]) + '/'
+
+ def rejoinName(self, dir, name):
+ name = name.strip()
+ if name.endswith(".ts") is True:
+ return dir + name[:-3]
+ else:
+ return dir + name
+
+class MovieRetitleBackgroundMover:
+ def __init__(self):
+ self.container = eConsoleAppContainer()
+ self.container.appClosed.get().append(self.moveNextSuffBG)
+ self.currid = 0;
+ self.queue = []
+ self.running = False
+ self.messageQueue = []
+ self.messageTimer = eTimer()
+ self.messageTimer.callback.append(self.tryLaunchMessage)
+
+ def message(self, session, id, cb, txt):
+ global global_message_block
+ done = False
+ if global_message_block and global_message_block == id:
+ self.messageQueue = [(session, id, txt)] + self.messageQueue
+ else:
+ i = 0
+ for ele in self.messageQueue:
+ if ele[1] == id:
+ self.messageQueue[i] = (session, id, txt)
+ done = True
+ break
+ i += 1
+ if not done:
+ self.messageQueue.append((session, id, txt))
+ self.tryLaunchMessage(callback = cb)
+
+ def tryLaunchMessage(self, dummy=0, callback = None):
+ global global_message_block
+ self.messageTimer.stop()
+ if not self.messageQueue:
+ if callback:
+ callback()
+ elif not Screens.Standby.inStandby and self.messageQueue[0][0].in_exec and (not global_message_block or global_message_block == self.messageQueue[0][1]):
+ self.messageTimer.stop()
+ session = self.messageQueue[0][0]
+ id = self.messageQueue[0][1]
+ mess = self.messageQueue[0][2]
+ self.messageQueue = self.messageQueue[1:]
+ if global_message_block == id:
+ closeprev = session.current_dialog
+ else:
+ closeprev = None
+ global_message_block = id
+ try:
+ session.openWithCallback(lambda x: self.tryLaunchMessageCallback(callback, closeprev), MessageBox, mess, MessageBox.TYPE_INFO)
+ except:
+ global_message_block = False
+ self.tryLaunchMessage()
+ else:
+ self.messageTimer.start(1500, True)
+ if callback:
+ callback()
+
+ def tryLaunchMessageCallback(self, callback, closeprev):
+ global global_message_block
+ global_message_block = False
+ if closeprev:
+ closeprev.close(True)
+ self.tryLaunchMessage(callback = callback)
+
+ def enqueue(self, cb, session, fr, to):
+ self.currid += 1
+ mess = _("The movie is moved in the background from %s to %s.") % (os.path.dirname(fr), os.path.dirname(to))
+ self.message(session, self.currid, cb, mess)
+ self.queue.append((session, self.currid, fr, to))
+ if not self.running:
+ self.running = True
+ self.runNext()
+ return True
+ else:
+ return False
+
+ def runNext(self):
+ if not self.queue:
+ self.running = False
+ else:
+ self.moveMovieFilesBackground(self.queue[0])
+
+ def runDone(self, retval):
+ ele = self.queue[0]
+ self.queue = self.queue[1:]
+ self.runNext()
+
+ def moveMovieFilesBackground(self, ele):
+ self.ele = ele
+ self.sufflst = [".ts.meta", ".ts.cuts", ".ts.ap", ".eit", ".ts"]
+ self.sufflst2 = self.sufflst
+ self.moveNextSuffBG(0)
+
+ def moveNextSuffBG(self, retval):
+ if self.sufflst and not retval:
+ fr = self.ele[2] + self.sufflst[0]
+ to = self.ele[3] + self.sufflst[0]
+ self.sufflst = self.sufflst[1:]
+ print "Moving %s to %s" % (fr, to)
+ if os.path.exists(fr):
+ self.container.execute("/bin/cp", ["/bin/cp", fr, to])
+ else:
+ self.moveNextSuffBG(0)
+ elif retval:
+ for suff in self.sufflst2:
+ if os.path.exists(self.ele[3] + suff) and os.path.exists(self.ele[2] + suff):
+ os.unlink(self.ele[3] + suff)
+ mess = "Failed to move the movie %s to %s in the background" % (self.ele[2], self.ele[3])
+ self.message(self.ele[0], self.ele[1], None, mess)
+ self.runDone(1)
+ else:
+ for suff in self.sufflst2:
+ if os.path.exists(self.ele[2] + suff) and os.path.exists(self.ele[3] + suff):
+ os.unlink(self.ele[2] + suff)
+ mess = "Successfully moved the movie %s" % (self.ele[2])
+ self.message(self.ele[0], self.ele[1], None, mess)
+ self.runDone(0)
+
+global_background_mover = MovieRetitleBackgroundMover()
+
+global_message_block = False
+