# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------
# dvdbackup.py - CD Backup plugin for ripping/backing up DVDs to
# your hard drive
# -----------------------------------------------------------------------
# $Id: dvdbackup.py,v 1.00 2004/09/10 12:33:37 dischi Exp $
#
# Notes: This is the dvdbackup module which can be accessed from the audio menu
# by hitting 'e' or enter (not return) while a DVD is selected.
#
# It allows you to backup DVDs as .vob files.
#
# To Activate Plugin, add the following to local_conf.py:
# plugin.activate('video.dvdbackup')
#
# Todo:
#
# Get this to work
#
# -----------------------------------------------------------------------
# $Log: dvdbackup.py,v $
# Revision 1.00  2004/09/10 12:33:37  dischi
# header cleanup
#
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# 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 of the License, 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 MER-
# CHANTABILITY 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 program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# ----------------------------------------------------------------------- */


import os
import time
import string
import sys
import threading
import re
import shutil

import config
import menu
import util
import plugin
import rc

from gui.AlertBox import AlertBox
from event import *

from util import popen3

# For BackupInfo class
from mmpython import mediainfo

# Included to be able to access the info for Video DVDs
import mmpython

class PluginInterface(plugin.ItemPlugin):
    """
    Backup DVDs in .vob format, perfect rip.

    The following variables are needed by this plugin. Please set them in
    your local_conf.py:

    Directory that you want to backup your DVD to.
    VIDEO_DVD_DIR = '/myth/video'

    vobcopy is used to rip the DVD to your hard drive. The actual command that
    will be executed is stored in DVDPAR_CMD.
    DVDBACKUP_CMD = 'vobcopy'

    To activate this plugin, add the following to local_conf.py:
    plugin.activate('video.dvdbackup')

    Finally, to actually backup a DVD within Freeevo, when you are in the
    Movie menu, highlight/choose a DVD, then hit 'e' on keyboard or 'ENTER' on
    your remote control and you will be able to access the rip/backup menu.

    Here is a list of all the above mentioned parameters for simple cutting and
    pasting:

    #The following are for adding and configuring the CD Audio backup plugin:
    VIDEO_DVD_DIR = '/myth/video'

    DVDBACKUP_CMD = 'vobcopy'
    DVD_RIP_OPTS  = '-l'

    #To activate the dvdbackup plugin:
    plugin.activate('video.dvdbackup')
    """

    def __init__(self):
        plugin.ItemPlugin.__init__(self)
        self.thread = None
        self.info = BackupInfo()

    def config(self):
        """
        list of config variables this plugin needs
        """
        return (('VIDEO_DVD_DIR', config.VIDEO_ITEMS[ 0 ][ 1 ],
                 'directory where to put the encoded files'),
                ('DVDBACKUP_CMD', config.CONF.vobcopy, ''),
                ('DVD_RIP_OPTS', '-l', ''))

    def actions(self, item):
        self.item = item

        try:
            if item.mode == 'dvd' and hasattr(item.media, 'devicename'):
                
                # Keep user selection if same dvd
                if self.info.title != self.item.info['label']:
                    self.info.reset(self.item.media.devicename, self.item.info['label'])
        
                _debug_('devicename = %s' % self.info.device)
                
                # Show different menu when ripping
                if self.thread and self.thread.progress != -1:
                    return [ ( self.stop_dvd_backup,
                        _('Stop ripping the DVD')) ]
                else:
                    return [ ( self.start_dvd_backup,
                        _('Backup the DVD to the hard drive')) ]
        except:
            pass
        return []


    def shutdown(self):
        if self.thread and self.thread.progress != -1:
            self.thread.shutdown()
            
    #
    # Start DVD backup
    #
    
    def start_dvd_backup(self, arg,  menuw=None):

        # Find title of DVD
        if (self.item.info['label'] == None):
            _debug_( _('WARNING' ) + ': ' + _( 'DVD title not found with mmpython' ) ,2)
            current_time = time.strftime('%d-%b-%y-%I:%M%P')
            title = _( 'Unknown Title' ) + ' ' + current_time + ' - ' + _( 'RENAME' )
        else:
            self.info.title = self.item.info['label']
            _debug_( 'Title found : %s' % self.info.title )
        self.thread = main_backup_thread(info=self.info)
        self.thread.start()
        
        AlertBox(text=_('Ripping started') ).show()
     
    #
    # Stop DVD backup
    #
    
    def stop_dvd_backup(self, arg,  menuw=None):
        pop = AlertBox(text=_('Interrupting backup process...'))
        pop.show()

        self.thread.stop()
        self.thread = None
  
        pop.destroy()
                  
        AlertBox(text=_('Backup interrupted')).show()
        
#        
# Main backup thread
#

class main_backup_thread(threading.Thread):
    def __init__(self, info=None):
        threading.Thread.__init__(self)
        self.info = info
        self.pid = -1
        self.childs = []
        self.stopping = False
        self.progress = 0
        
        
    def run(self):
        self.dvd_backup_threaded(self)

                
    def stop(self):
        # Ask the thread to terminate
        self.stopping = True
        
        #Wait 10 sec for the thread to terminate
        starttime = time.time()
        while self.stopping and (time.time()- starttime < 10):
            _debug_('Waiting for the thread to terminate...')
            time.sleep(1)
       
        for media in config.REMOVABLE_MEDIA:
            if media.devicename == self.info.device:
                media.type = 'video'

        AlertBox(text=_('Backup interrupted')).show()
        rc.post_event(Event(OSD_MESSAGE, arg=_('DVD Backup interrupted')))
               
        
    def shutdown(self):
        # Shutdown is used when you shutdown freevo (no popup)
        # Ask the thread to terminate
        self.stopping = True
        
        #Wait 2 sec for the thread to terminate
        starttime = time.time()
        while self.stopping and (time.time()-starttime < 2):
            _debug_('Waiting for the thread to terminate...')
            time.sleep(1)
            
            
    def dvd_backup_threaded(self):
        # Compute time elapsed
        starttime = time.time()

        # Don't know why it is used...
        for media in config.REMOVABLE_MEDIA:
            if media.devicename == self.info.device:
                media.type = 'dvdrip'

        # backup the whole dvd with dvdbackup
        if self.info.ripper == config.DVDBACKUP_CMD:
            dvdbackup_command = '%s %s -i %s -o %s >/tmp/dvdlog' % (config.DVDBACKUP_CMD, config.DVD_RIP_OPTS, self.info.device, config.VIDEO_DVD_DIR)
            child = popen3.run(dvdbackup_command, self, 9)

#            if self.abort:
#                rc.post_event(Event(OSD_MESSAGE, arg=_('Ripping aborted')))
#                self.current_track = -1
    
            while child.poll() < 0:
                _debug_('Backing up dvd ...')
                if self.stopping:
                    self.cleanup()
                    self.stopping = False
                    return
                time.sleep(1)
            elapsed = (time.time() - starttime)
            _debug_('Time elapsed: %d' % elapsed)
            self.progress = -1
            if (elapsed > 60):
                pop = AlertBox(text=_( "Backup complete\n (%s min %d sec)" % (int(elapsed/60),(elapsed%60))))
            else:
                pop = AlertBox(text=_( "Backup complete\n (%d sec)" % elapsed))
            pop.show()
            return
         
    def cleanup(self):
        for child in self.childs:
            try:
                _debug_('killing process group %d with signal 15' % (child.pid))
                os.kill(-child.pid, signal.SIGTERM)
            except OSError:
                _debug_('killing process group %d FAILED' % (child.pid))
                pass
                    
            for i in range(20):
                #_debug_('Waiting for process group %d to terminate...' % (child.pid))
                try:
                    p,s = os.waitpid((child.pid), os.WNOHANG)
                except OSError:
                    _debug_('Process group %d exited' % (child.pid))
                    break
                if p == child.pid:
                    _debug_('Process group %d terminated with signal 15' % child.pid)
                    break
                time.sleep(0.1)
            else:
                try:
                    _debug_('killing process group %d with signal 9' % (child.pid))
                    os.kill(-child.pid, signal.SIGKILL)
                except OSError:
                    _debug_('killing process group %d FAILED' % (child.pid))
                    pass
                for i in range(20):
                    #_debug_('Waiting for process group %d to terminate...' % (child.pid))
                    try:
                        p,s = os.waitpid(child.pid, os.WNOHANG)
                    except OSError:
                        _debug_('Process group %d exited' % (child.pid))
                        break
                    if p == child.pid:
                        _debug_('Process group %d terminated with signal 9' % child.pid)
                        break
                    time.sleep(0.1)

            self.progress = -1

# Class to handle user selection
# AVInfo class with misc info
class BackupInfo(mediainfo.AVInfo):
    def __init__(self):
        mediainfo.AVInfo.__init__(self)
        self.keys.append('device')
        self.keys.append('ripper')
        self.keys.append('title')
        self.keys.append('chapters')
        self.keys.append('angles')
        self.keys.append('id')
        self.keys.append('backup_dir')
        
        self.ripper = config.DVDBACKUP_CMD
        self.trackno = -1

    def reset(self, device, title):
        self.audio = []
        self.video = []
        self.subtitles = []
        self.device = device
        self.ripper = config.DVDBACKUP_CMD
        self.title = title
        self.chapters = 0
        self.angles = 0
        self.id = 0
        self.backup_dir = ''
        self.trackno = -1

