Sony XL1B2

From MythTV Official Wiki
Revision as of 09:49, 31 May 2010 by Nodedog (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
The Sony XL1B 200 DVD changer

Overview

The Sony XL1B2 is a Firewire 200-DVD changer with a double-layer burner which is usable in both Linux and MythTV.

Document.png - Specs {{{2}}}

It is possible to use this unit with Linux and MythTv.

To get this device working you will need to ensure that ieee1394, sbp2, scsi changer and scsi cd rom/dvd support are enabled in your Kernel (later kernels have re-written firewire support with different but similar modules / kernel options) . If all is well then the unit will appear as 2 scsi devices 1 changer device and 1 scsi cdrom /dvd device e.g. /dev/sg2 and /dev/sr0. It is also possible to create a udev rule to give the changer a suitable symlink, the changer is a type8 sg device.

This is a sample from the kernel log

[ 3234.866364] scsi10 : SBP-2 IEEE-1394
[ 3234.866415] firewire_core: created device fw1: GUID 003060a100008566, S400
[ 3235.084222] firewire_sbp2: fw1.0: logged in to LUN 0000 (0 retries)
[ 3235.088111] scsi 10:0:0:0: CD-ROM            MATSHITA DVD-RAM SW-9584  B104 PQ: 0 ANSI: 0
[ 3235.104103] sr0: scsi3-mmc drive: 40x/40x writer dvd-ram cd/rw xa/form2 cdda tray
[ 3235.104212] sr 10:0:0:0: Attached scsi CD-ROM sr0
[ 3235.104279] sr 10:0:0:0: Attached scsi generic sg7 type 5
[ 3235.227232] firewire_sbp2: fw1.0: logged in to LUN 0002 (0 retries)
[ 3235.233822] scsi 10:0:0:2: Medium Changer    PowrFile PowerFile R200DL 06A  PQ: 0 ANSI: 3
[ 3235.247419] ch0: reading element address assigment page failed!
[ 3235.247428] ch0: INITIALIZE ELEMENT STATUS, may take some time ...
[ 3235.254242] ch0: ... finished
[ 3235.254244] ch 10:0:0:2: Attached scsi changer ch0
[ 3235.254347] ch 10:0:0:2: Attached scsi generic sg8 type 8

The mtx command is used to control the changer, MythTv and other application can use this command to change discs. You need an up to date version of mtx, when working correctly the device will show something like the following.

# mtx --version
mtx version 1.3.12
# mtx -f /dev/ch0 altres nobarcode status
  Storage Changer /dev/ch0:1 Drives, 201 Slots ( 1 Import/Export )
Data Transfer Element 0:Empty
      Storage Element 1:Full 
      Storage Element 2:Full 
      Storage Element 3:Empty
......
      Storage Element 200:Empty
      Storage Element 201 IMPORT/EXPORT:Empty

Although the nobarcode option is not required it speeds up the status command considerably.

MythTV Integration and Remote Playback

The following fuse file systems allow both local and remote dvd playback and are also compatible with MythVideo and the MythTV Internal player.

  • OfflineFS
    • It's a filesystem intended to join a large collection of ejectable media (like CDs and DVDs) in a single writable filesystem where the files could still be searched, moved around, reorganized in directories, etc. If an user tries to open a file, he's asked to insert the volume where that file's contents are stored. The data is then transparently read from the real medium.
    • Homepage: http://savannah.nongnu.org/projects/offlinefs/
    • This is a very useful fs that allows the import of fs meta data into user defined folders and automatic loading of the correct disc on file access
    • It is compatible with mythtv in a transparent manner including the Internal player
    • It works well with remotefs and can therefore be exported to remote frontends
    • Playback of dvd videos requires either DVDfs or Dvdreadfs
    • It could also be used with data discs rather than video discs (or even a combination) and could provide up to 1680 MBs of additional storage.
    • If someone made a Blu-ray version of this changer or was able to modify the changer to take a Blu-ray drive it could provide close to 10TB of storage!
  • remotefs
    • Remotefs is a fast network file system designed for use with home NAS. Simple to use, few dependencies, reasonable security, completely in user space.
    • Homepage: http://remotefs.sourceforge.net
    • Similar to nfs in that it allows mounting of fs over the network
    • nfs seems to have some issues that may cause stuttering during playback
    • This is a straight forward fs that seems to be well suited to mythtv and can be used for all recordings, music and vidoes etc.
  • DVDfs
    • DVDfs is a userspace filesystem which mounts a DVD using libdvdread (and, by extension, libdvdcss). You can use this to make an exact copy of the DVD video file structure.
    • Homepage: http://www.jspenguin.org/dvdfs/
    • Seems to be tied to /dev/dvd for the dvd device
  • Dvdreadfs

An alternative older approach exists using xine and the cdda_server patch.

Setup

Overview

If you want to use the changer with mythtv and offlinefs the following steps need to be completed.

  • Make sure your kernel is configured for firewire / sbp2 / scsi changer and cd rom etc. or the relevant modules are available as appropriate
  • Install udev file to correct location e.g. /etc/udev/rules.d/
  • Install mtx
  • Install offlinefs
  • Install remotesfs
  • Install dvdreadfs
  • Set DVD region code
  • Create mythdvdchanger / OfflineFS scripts in /usr/local/bin (including modified offimport_cd.sh)
    • Remember to make them executable with chmod +x
  • Mount offlinefs e.g. offlinefs /mnt/movies/changer -o allow_other,intr
  • Run mythdvdchangerimport.sh

You may need to adjust scripts to your environment e.g. /dev/sr0 and some of the paths may need adjustment. Once completed you should be able to continue setup using MythVideo as per regular video files.

Kernel

Sample from Linux Kernel v2.6.33-gentoo-r1 Configuration

Device Drivers ---> SCSI device support --->

 │ │                                                                              <*> SCSI disk support                                                                                                                               │ │  
 │ │                                                                              <*> SCSI CDROM support                                                                                                                              │ │  
 │ │                                                                              [*]   Enable vendor-specific extensions (for SCSI CDROM)                                                                                            │ │  
 │ │                                                                              <*> SCSI media changer support                                                                                                                      │ │  

Device Drivers ---> IEEE 1394 (FireWire) support --->

 │ │                                                                                  *** You can enable one or both FireWire driver stacks. ***                                                                                      │ │  
 │ │                                                                                  *** The newer stack is recommended. ***                                                                                                         │ │  
 │ │                                                                              <M> FireWire driver stack                                                                                                                           │ │  
 │ │                                                                              <M>   OHCI-1394 controllers                                                                                                                         │ │  
 │ │                                                                              <M>   Storage devices (SBP-2 protocol)                                                                                                              │ │  

udev

Sample udev file 55-changer-device.rules

# assign cdrom-permission and create ch0 symlink
KERNEL=="sg[0-9]*", ACTION=="add", ATTRS{type}=="8", GROUP="cdrom", SYMLINK+="ch0"

If you had more than 1 changer I would assume that it would be a good idea to identify them somehow so that they get a consistent numbering scheme.

mtx

Under gentoo

emerge -av mtx

OfflineFS

Download from http://savannah.nongnu.org/projects/offlinefs/ e.g.

wget http://www.very-clever.com/download/nongnu/offlinefs/offlinefs-0.4.tar.gz
tar xfs offlinefs-0.4.tar.gz

You need to make sure that the fuse module is available for you kernel and that you have built the userspace libraries.

e.g. Under gentoo

emerge -av sys-fs/fuse

Boost libraries and Berkeley DB (probably already installed under gentoo) also need to be installed. The README says that the Extended attributes library is also required as are GNU C++ tools, gentoo ships with these but you may need to install the relevant package under other distributions.

emerge -av sys-libs/db 
emerge -av dev-libs/boost

Make and install it

./configure
make
make install
ldconfig

Initial setup

offlinefs --rebuilddb

You may require the [ -o dbroot=<...> ] option to tell the system where to create the offlinefs database file.

Mount

offlinefs /mnt/movies/changer -o allow_other,intr

You may require the [ -o dbroot=<...> ] option to tell the system where to find the offlinefs database file.

List all media

offmedia --list

This uses the -b option to specify the database root.

Import a disc

offimport_cd.sh -i <directory> -l <label>

This uses the -b option to specify the database root.

Sample from /etc/fstab to mount offlinefs

offlinefs /mnt/movies/changer fuse    ro,allow_other,intr  0 0

You can export this using remotefs and the system will work transparently, when a file is accessed the corresponding disc will be loaded automatically and dvdreadfs will provide access. It is also possible to setup a dummy folder to unload the changer and also add static files into the offlinefs file system such as folder.jpg etc. You may require the [ -o dbroot=<...> ] option to tell the system where to find the offlinefs database file.

RemoteFS

It is provided with packages for i386 Debian-based systems, i386 Redhat-based systems, various OpenWrt-based systems (server only) and there are also Gentoo-ebuilds available. See downloads section Homepage: http://remotefs.sourceforge.net

e.g. Under gentoo

After download and digest of ebuilds...

server

emerge -av rfsd

man rfsd
nano /etc/conf.d/rfsd
nano /etc/rfs-exports

rc-update add rfsd default
/etc/init.d/rfsd start

client

emerge -av rfs

remotefs has a similar setup to nfs.

Here is a sample of my /etc/rfs-exports on the backend.

/mnt/movies 10.0.0.0/24 (ro)

Here is a sample of the line from my autofs map file at the frontend.

<key>	-fstype=rfs,ro,intr,allow_other	<server>:<pathname>

Sample to connect to server with rfs

rfs 10.0.0.1:/mnt/usb /home/alex/remote

DvdreadFS

You need to make sure that the fuse module is available for you kernel and that you have built the userspace libraries.

e.g. Under gentoo

emerge -av sys-fs/fuse

Download and untar http://intr.overt.org/misc/dvdreadfs-with-video-ts.tar.gz

PATCH: Remove read ahead

Here is the patch to remove the read ahead code this is based on the VIDEO_TS version of dvdreadfs above

From 08160896255907dddb0425c19baff3d8710b71f9 Mon Sep 17 00:00:00 2001
From: Nodedog
Date: Tue, 25 May 2010 13:22:50 +0100
Subject: [PATCH 1/2] Remove read_ahead caching

---
 dvdreadfs.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/dvdreadfs.c b/dvdreadfs.c
index 01a1895..7f242d2 100644
--- a/dvdreadfs.c
+++ b/dvdreadfs.c
@@ -423,6 +423,14 @@ static int fs_read(const char *path, char *buf, size_t count, off_t offset,
    bk_off = offset / DVD_VIDEO_LB_LEN;
    bk_cnt = count / DVD_VIDEO_LB_LEN;

+   res = DVDReadBlocks(xfi->fi->fd, bk_off, bk_cnt, (unsigned char*)buf);
+   if (res > 0)
+       res *= DVD_VIDEO_LB_LEN;
+
+   //Caching can cause problems with slow drive if READ_AHEAD too high
+   //have also not seen an improvement in performance from using it
+   //Therefore disable and just do read as requested
+#if 0
    /* Is this offset/count contained wholly in the cache? */
    if (xfi->cache_off <= offset && offset+count <= xfi->cache_off+xfi->cache_len) {
        /* Yes, wholly in cache, so use cache only */
@@ -448,6 +456,7 @@ static int fs_read(const char *path, char *buf, size_t count, off_t offset,
        }
        }
    }
+#endif
     } else {
    if (xfi->cache) {
        if (offset >= xfi->fi->len || offset < 0) {
--
1.6.4.4

Save the patch above as 0001-Remove-read_ahead-caching.patch in the same folder and run

patch -i 0001-Remove-read_ahead-caching.patch -l

PATCH: Create .volume_id static file for ISO volume name

Patch to create .volume_id static file for ISO volume name

From 30e3ef757e2aa85d0dbe4234601b84c13b3f8ee6 Mon Sep 17 00:00:00 2001
From: Nodedog
Date: Sun, 30 May 2010 17:39:48 +0100
Subject: [PATCH 2/2] Add .volume_id static file to store ISO volume name

If the ISO volume name is present it will be stored in .volume_id in the root of the fs. Otherwise the file will not be created.
---
 dvdreadfs.c |   43 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/dvdreadfs.c b/dvdreadfs.c
index 7f242d2..159b33f 100644
--- a/dvdreadfs.c
+++ b/dvdreadfs.c
@@ -38,6 +38,7 @@ const off_t   part_size   = (1024*1024*1024) - DVD_VIDEO_LB_LEN;
 #define IS_VOB(d) ((d) == DVD_READ_TITLE_VOBS || (d) == DVD_READ_MENU_VOBS)
 #define IS_IFO(d) (!IS_VOB(d))
 #define READ_AHEAD (1024*1024)*2
+#define VOL_ID_SIZE 1024

 struct file_info {
    dvd_file_t  *fd;
@@ -58,6 +59,8 @@ struct mount_info {
    struct file_info    file[MAX_DOMAIN+1][MAX_TITLE+1];
         const char              *dvdpath;
    int         num_open;
+   struct file_info    vol_id_info;
+   char        vol_id[VOL_ID_SIZE+1];
 };

 /* Eventually make this "per-mount" instead of global */
@@ -160,6 +163,18 @@ static inline int open_dvd(struct mount_info *mi) {

     fprintf(stderr, "Dvdopen(%s) worked\n", mi->dvdpath);

+   memset(mi->vol_id,0,VOL_ID_SIZE+1);
+   mi->vol_id_info.fd=0;
+   if (DVDISOVolumeInfo(mi->dvd, mi->vol_id, VOL_ID_SIZE, NULL,
+                         0) > -1) {
+       mi->vol_id_info.len=strlen(mi->vol_id);
+       fprintf(stderr, "Dvdopen(%s) : Volume id (%s)\n", mi->dvdpath, mi->vol_id);
+   }
+   else {
+       mi->vol_id_info.len=0;
+       fprintf(stderr, "Dvdopen(%s) : Unable to get volume id\n", mi->dvdpath);
+   }
+
     /* Open all the files and determine number of titles. */
     done = 0;
     for (title = 0; !done && title <= MAX_TITLE; ++title) {
@@ -277,6 +292,11 @@ static int fs_getattr(const char *name, struct stat *stbuf)
        stbuf->st_nlink = 2;
        stbuf->st_size = 512;
    }
+   else if (strcmp(name , ".volume_id") == 0) {
+       stbuf->st_mode = 0100444;
+       stbuf->st_nlink = 1;
+       stbuf->st_size = mi.vol_id_info.len;
+   }
    else {
        res = -ENOENT;
    }
@@ -313,6 +333,10 @@ static int fs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
     filler(buf, "..", NULL, 0);

     if (strcmp(path, "/") == 0) {
+       /* Dummy file for volume id */
+       if(mi.vol_id_info.len>0) {
+           filler(buf, ".volume_id", NULL ,0);
+       }
         filler(buf, "VIDEO_TS", NULL, 0);
     } else if (strcmp(path, "/VIDEO_TS") == 0) {
         for (title = 0; title <= mi.num_title; ++title) {
@@ -387,6 +411,25 @@ static int fs_open(const char *name, struct fuse_file_info *fi)
    }
    return 0;
     }
+   else {
+       while (*name == '/')
+           ++name;
+       if (strcmp(name , ".volume_id") == 0) {
+           fi->fh = xfi;
+
+           xfi->cache = NULL;
+           xfi->cache_len = 0;
+           xfi->cache_off = 0;
+
+           if(xfi->cache = malloc(VOL_ID_SIZE+1)) {
+               xfi->fi=&mi.vol_id_info;
+               xfi->fi->fd=-1; //read will not work unless fd is set
+               memcpy(xfi->cache,mi.vol_id,VOL_ID_SIZE+1);
+               fprintf(stderr,"%s\n",xfi->cache);
+               return 0;
+           }
+       }
+   }

     /* Failed so release our reference to dvd */
     dec_refcnt(&mi);
--
1.6.4.4

Save the patch above as 0002-Add-.volume_id-static-file-to-store-ISO-volume-name.patch in the same folder and run

patch -i 0002-Add-.volume_id-static-file-to-store-ISO-volume-name.patch -l

The finally run 'make' and cp dvdreadfs to /usr/local/bin

make
cp dvdreadfs /usr/local/bin

Sample mount

dvdreadfs /dev/sr0 -o allow_other /mnt/dvd

DVD Region

It may also be useful to use the regionset utility to set the region code of the changer drive if you are not in region 1 (current default).

emerge -av regionset

Scripts

OfflineFS

It is possible as an alternative to modifying mythtv to use scripts to load / unload the changer in conjunction with offlinefs. The following scripts are designed to work together to implement a working offlinefs system

  • mythdvdchangerload.sh
  • mythdvdchangerunload.sh
  • mythdvdchangermount.sh
  • mythdvdchangerumount.sh
  • mythdvdchangerisloaded.sh
  • mythdvdchangerimport.sh

OfflineFS allows you to use MythVideo to access your discs and enter meta data / lookup IMDB etc. Just mount it under your movies folder where MythVideo looks for video files, I assume it is also compatible with storage groups.

Stub Files

Another alternative approach may be something like the following

  • Create a stub file with a unique file extension for each dvd changer slot
    • You could use mtx output to create them for only the loaded slots
    • The stub file needs to convey the appropriate information to load / unload the correct slot
  • Configure mythtv to recognise the file extension as a video file and write an appropriate wrapper script to load / play the dvd
  • You will need to put the stub files in your video directory and scan them in with mythvideo so they appear in your video list

This approach should also work with a standard mythtv release. You should be able to enter meta data against your stub files. The scripts below can help to build something like this.

Load

Designed to work with offlinefs and /var/state/mtx-slot cache file and the mythdvdchangerumount.sh and mythdvdchangermount.sh scripts. Pass the slot number required as the 1st parameter.

mythdvdchangerload.sh

#!/bin/bash

#udev rules now creates ch0 for type8 sg device
#obviously this could just be passed in as a parameter
chng=/dev/ch0

#this will decode the symlink
#chng=`ls -l /dev/ch0 | sed -e 's/.*\(sg[0-9]\).*/\/dev\/\1/'`

#read changer device from message log if there is a lot of log activity this could be pushed out of the log
#chng=`dmesg | grep  -e 'ch .*sg[0-9] type 8' | tail -1 |  sed -e 's/.*\(sg[0-9]\).*/\/dev\/\1/'`
#tac can be used to reverse order of lines ... as in cat spelt backwards

if /usr/sbin/mtx -f $chng altres nobarcode status | grep -q "Data Transfer Element 0:Empty"
then
   echo "Good!...Changer drive is empty"
   if [ -f /var/state/mtx-slot ]; then
        rm /var/state/mtx-slot
   fi
else
   echo "Changer is still loaded.... attempting to unload drive"

   mythdvdchangerumount.sh

   if /usr/bin/eject -v /dev/sr0 | grep -q "eject command succeeded"
   then
      echo "Eject command succeeded (drive spindown)"
   else
      exit 1
   fi

   /usr/sbin/mtx -f $chng altres unload

   if /usr/sbin/mtx -f $chng altres nobarcode status | grep -q "Data Transfer Element 0:Empty"
   then
      echo "Good!...Changer drive is now empty"
      if [ -f /var/state/mtx-slot ]; then
            rm /var/state/mtx-slot
      fi
   else
      echo "Error: Changer is still loaded...aborting"
      exit 1
   fi
fi

/usr/sbin/mtx -f $chng altres load $1

if /usr/sbin/mtx -f $chng altres nobarcode status | grep -q "Data Transfer Element 0:Full (Storage Element $1 Loaded)"
then
   echo "Slot $1 load succeeded...waiting for disc to settle"
   mythdvdchangermount.sh
   echo "$1" > /var/state/mtx-slot

   exit 0
else
   exit 1
fi

exit 1

Unload

Designed to work with offlinefs and /var/state/mtx-slot cache file and the mythdvdchangerumount.sh and mythdvdchangermount.sh scripts.

mythdvdchangerunload.sh

#!/bin/bash

#udev rules now creates ch0 for type8 sg device
#obviously this could just be passed in as a parameter
chng=/dev/ch0

#this will decode the symlink
#chng=`ls -l /dev/ch0 | sed -e 's/.*\(sg[0-9]\).*/\/dev\/\1/'`

#read changer device from message log if there is a lot of log activity this could be pushed out of the log
#chng=`dmesg | grep  -e 'ch .*sg[0-9] type 8' | tail -1 |  sed -e 's/.*\(sg[0-9]\).*/\/dev\/\1/'`
#tac can be used to reverse order of lines ... as in cat spelt backwards

if /usr/sbin/mtx -f $chng altres nobarcode status | grep -q "Data Transfer Element 0:Empty"
then
   echo "Good!...Changer drive is empty"
   if [ -f /var/state/mtx-slot ]; then
        rm /var/state/mtx-slot
   fi
else
   echo "Changer is still loaded.... attempting to unload drive"

   mythdvdchangerumount.sh

   if /usr/bin/eject -v /dev/sr0 | grep -q "eject command succeeded"
   then
      echo "Eject command succeeded (drive spindown)"
   else
      exit 1
   fi

   /usr/sbin/mtx -f $chng altres unload

   if /usr/sbin/mtx -f $chng altres nobarcode status | grep -q "Data Transfer Element 0:Empty"
   then
      echo "Good!...Changer drive is now empty"
      if [ -f /var/state/mtx-slot ]; then
           rm /var/state/mtx-slot
      fi
      exit 0
   else
      echo "Error: Changer is still loaded...aborting"
      exit 1
   fi
fi

exit 1

Mount

Mount the changer drive using dvdreadfs in /mnt/dvd and waits till the directory structure is accessible before returning.

mythdvdchangermount.sh

#!/bin/bash

if mount -l | grep -q "/mnt/dvd"
then
    echo "/mnt/dvd already mounted"
else
    dvdreadfs /dev/sr0 /mnt/dvd -o allow_other,intr
    while ls /mnt/dvd 2>&1 | grep -q "No such file or directory";
    do
      sleep 1
    done
fi

Unmount

Unmounts the changer drive.

mythdvdchangerumount.sh

#!/bin/bash

if mount -l | grep -q "/mnt/dvd"
then
    fusermount -u /mnt/dvd
    sleep 10
else
    echo "/mnt/dvd not mounted"
fi

IsLoaded

This reads the /var/state/mtx-slot file to determine which slot is loaded. You pass the slot you are interested in as the 1st parameter or NONE to check if the changer is not loaded. Requires that you use the Load/Unload scripts above to maintain the /var/state/mtx-slot cache file correctly.

mythdvdchangerisloaded.sh

#!/bin/bash

if [ -f /var/state/mtx-slot ]; then
    slt=`cat /var/state/mtx-slot`
else
    slt="NONE"
fi

echo "* mtx cache: slot = $slt"
if [ "$1" == "$slt" ]
then
    echo "* mtx cache: slot $1 present"
    exit 0
else
    echo "* mtx cache: slot $1 NOT present"
fi

exit 1

Inventory

Stub Files

This is designed to work with older versions of the Load/Unload scripts so some modification may be required. The main difference will probably be due to the fact the new scripts will attempt to mount the DVD with dvdreadfs which could cause a problem with lsdvd used below. If the mount command is commented out it should work OK.

Assuming your changer is physically attached to your frontend machine, this perl script will take inventory of the changer and write stub files that you can use to import into MythVideo and change to the correct disc as easy as choosing a video from the menu.

A couple of things to note:

  • lsdvd is required to read the label from the disc to make meaningful stub files
  • The script assumes you have the above scripts working and installed in a location in your $PATH and are executable (I had to modify the scripts above. If you can't get them working, please post to the mythv-users list for help.)
  • The script assumes you'll be using mplayer to play back the video files. Modify if you like.
  • osd_cat is very helpful. If installed, the stub files will make a call to osd_cat to alert the user that the disc is loading. It takes a long time, so a "please wait" message is helpful. You need to manually add a text file called "loading.txt" in the same location as your stub files containing the text that you want displayed in order for this to work. Also, I have chosen to display with the Vera Sans Mono font. Modify to your liking.
  • -startfrom tells the script to ignore any full slots before this number. Suppose you put 30 DVDs in the changer, run the script and then load 30 more, you would use -startfrom 31 to ignore the 30 you already have stub files for. This script will always ignore empty slots. There is no "endon" number so it will always inventory until there are no more full slots. If you fill a slot between slots you've already inventoried, you're on your own.
  • -device, -changer, -location are all set to what I use. Obviously, you need to know this information before you run the script
  • Script will append slot number to stub file name if to avoid overwriting files that already exist (Helpful for generic DVD labels like DVD_VOLUME)
  • The script takes a very long time to run. It essentially unloads the changer, gets the status to see which slots are full, loads each disc is finds (first issuing an unload command in case a disc is still loaded), reads the label from the disc, writes a stub file and moves on to the next one.

mythdvdchangerinventory.pl

#!/usr/bin/perl -w

use strict;
use Getopt::Long;

use vars qw( $opt_startfrom $opt_device $opt_changer $opt_location $opt_help );
my @optl = ( "startfrom=i", "device=s", "changer=s", "help" );

my $lsdvd = `which lsdvd`;

sub usage
{
    print "Usage: $0\n" .
          "       -startfrom # - Slot # to start taking inventory from (default: 0)\n" .
          "       -device      - device DVD Changer mounts DVDs as (deault: /dev/dvd2)\n" .
          "       -changer     - DVD Changer device (deault: /dev/sg10)\n" .
          "       -location    - Loation of stub files (default: /entertainment/changer)\n" .
          "       -help        - Display this usage message\n";
    print "ERROR: lsdvd is required. Please install and try again\n" if ( !$lsdvd );
    exit;
}

usage() if ( !GetOptions( @optl ) || $opt_help || !$lsdvd );

$opt_startfrom = 0 if ( !$opt_startfrom );
$opt_device = "/dev/dvd2" if ( !$opt_device );
$opt_changer = "/dev/sg10" if ( !$opt_changer );
$opt_location = "/entertainment/changer" if ( !$opt_location );
my $osdcat = `which osd_cat`;
chomp( $osdcat );
$osdcat = "`" . $osdcat . " -w -p middle -d 1 -l 1 -A center -c yellow -O 5 -f -bitstream-bitstream\\ vera\\ sans\\ mono-bold-r-*-*-36-*-* " . $opt_location . "/loading.txt`\n" if ( $osdcat );

print "Unloading DVD changer...\n";
system("mythdvdchangerunload.sh");
print "Reading disc inventory...";
my $changerStatus = `mtx -f ${opt_changer} altres status`;
print "done\n";
my @changerStatus = split(/\n/, $changerStatus);

foreach( @changerStatus ) {
    my $thisdisc;
    my $ismounted;
    if ( $_ =~ /Storage Element (\d+):Full/ )
    {
        my $slot = $1;
        next if ( $slot < $opt_startfrom );
        print "Slot #$slot has a disc. Loading disc for more information...";
        system("mythdvdchangerload.sh ".$1);
        my $count;
        do {
            $ismounted = `lsdvd ${opt_device} | grep 'Disc Title:'`;
            if ( $ismounted ) {
                $thisdisc = $1 if ( $ismounted =~ /Disc Title: (.*)/ );
            }
            $count++;
            $thisdisc = "No Disc" if ( $count > 10 );
            sleep 1;
        } while ( !$thisdisc );

        if ( $thisdisc ne "No Disc" ) {
            print "Writing a stub file for: " . $thisdisc . "\n";
            $thisdisc .= $slot if ( -e $opt_location . "/" . $thisdisc . ".stub" );
            open (STUB, ">" . $opt_location . "/" . $thisdisc . ".stub");
            my $stubfile = "#!/bin/bash\n" . $osdcat . "`mythdvdchangerload.sh " . $slot . "`\n`mplayer -fs -ao alsa -vo xv -dvd-device " . $opt_device . " dvdnav://`\n";
            print STUB $stubfile;
            close (STUB);
        } else {
            print "Could not read disc name after 10 seconds. Moving onto next disc...\n";
        }
    }
}
print "All done!\n";
exit;

If all goes well, you now have a directory filled with .stub files that represent each disc in your changer. Now go into MythVideo Settings and add a new filetype called "stub". The player command should be set to "sh" so MyhVideo knows to run it as a shell script. Then go into the MythVideo manager and scan for updates. Now you can add metadata to each file using your favorite lookup script (tmdb, etc.)

Now when you select a video from the list, the .stub file is executed which displays a loading message and issues the script to load the correct DVD and plays it with mplayer.

OfflineFS

This is based on the script above to import your DVDs into the offlinefs folder system. The folder structure provided by mythdvdchangermount.sh will be used (which uses /mnt/dvd as the mount point) so I would recommend using the dvdreadfs with VIDEO_TS modification for this. It is designed to work with the modified offimport_cd.sh script below.

mythdvdchangerimport.sh

#!/usr/bin/perl -w

use strict;
use Getopt::Long;

use vars qw( $opt_startfrom $opt_device $opt_changer $opt_location $opt_help );
my @optl = ( "startfrom=i", "device=s", "changer=s", "help" );

sub usage
{
    print "Usage: $0\n" .
          "       -startfrom # - Slot # to start taking inventory from (default: 0)\n" .
          "       -device      - DVD mount location (deault: /mnt/dvd)\n" .
          "       -changer     - DVD Changer device (deault: /dev/ch0)\n" .
          "       -location    - Loation to import files (default: /mnt/movies/changer)\n" .
          "       -help        - Display this usage message\n";
    exit;
}

usage() if ( !GetOptions( @optl ) || $opt_help );

$opt_startfrom = 0 if ( !$opt_startfrom );
$opt_device = "/mnt/dvd" if ( !$opt_device );
$opt_changer = "/dev/ch0" if ( !$opt_changer );
$opt_location = "/mnt/movies/changer" if ( !$opt_location );

print "Unloading DVD changer...\n";
system("mythdvdchangerunload.sh");
print "Reading disc inventory...";
my $changerStatus = `mtx -f ${opt_changer} altres nobarcode status`;
print "done\n";
my @changerStatus = split(/\n/, $changerStatus);
print "------------------------------------------------------------\n";
 
foreach( @changerStatus ) {
    my $thisdisc;
    my $ismounted;
    if ( $_ =~ /Storage Element (\d+):Full/ )
    {
        my $slot = $1;
        next if ( $slot < $opt_startfrom );
        print "Slot #$slot has a disc. Loading disc for more information...\n";
        system("mythdvdchangerload.sh ".$slot);

        my $imp_location = $opt_location . "/" . $slot;
        mkdir( $imp_location );
        system("offimport_cd.sh -i " . $opt_device . " -l " . $slot .  " -p /" . $slot . "/");

        my $hash;
        #This line performs a database lookup to match the directory names with existing mythtv video meta data
        #chomp($hash=`mythdvdchangerfindhash.sh $slot`);
        #This uses the dvdreadfs .volume_id patch to rename according to the ISO volume name
        chomp($hash=`cat /mnt/dvd/.volume_id`);
        if($hash)
        {
            my $imp_hash = $opt_location . "/" . $hash . ".dvd";
            print "Hash=$hash\nimp_loc=$imp_location\nout=$imp_hash\n";
            rename( $imp_location, $imp_hash);
        }

        system("mythdvdchangerunload.sh");
        print "------------------------------------------------------------\n";
    }
}
print "All done!\n";
exit;

mythdvdchangerfindhash.sh

Just a sample script to use with the above inventory script in case you want to retrieve some more data. I'm no perl expert otherwise this could obviously be done in perl easily as well.

#!/bin/bash

#replace <PW> with your mythconverg database password
mysql -N -s -r -u mythtv -p<PW> mythconverg -e "select substring(hash,1,64) from tblname where slot=$1;"

offimport_cd.sh

This is a modified version of the import script that ships with offlinefs. Save this over the existing offimport_cd.sh script (/usr/local/bin/offimport_cd.sh)

#!/bin/bash
#     Copyright (C) 2008 Francisco Jerez
#     This file is part of offlinefs.

#     offlinefs 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 3 of the License, or
#     (at your option) any later version.

#     offlinefs 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 offlinefs.  If not, see <http://www.gnu.org/licenses/>.

usage(){
    echo "usage: offimport_cd.sh -i <cdroot> -l <label> [-s <insertion script>] [-b <dbroot>] [-p <prefix>]"
    echo "Import a directory tree (<cdroot>) into the offlinefs with database at <dbroot>, prefixing each path with <prefix>. <insert script> will get called when trying to access any of the imported files."
    exit 1
}

get_size(){
    device=`grep " ${cdroot%%/} " /etc/mtab |cut -d' ' -f1`
    total_size=0

    if [ -n "$device" -a -n "`which cdrecord 2>/dev/null`" ]; then
    sec_size=`cdrecord -v -minfo dev=$device:@ 2>/dev/null |sed -n 's/Current Secsize\W\+//p'`
    phys_sec=`cdrecord -v -minfo dev=$device:@ 2>/dev/null |sed -n 's/phys size\W\+//p'`
    free_sec=`cdrecord -v -minfo dev=$device:@ 2>/dev/null |sed -n 's/Remaining writable size\W\+//p'`

    total_size=$((sec_size*phys_sec))
    free_size=$((sec_size*free_sec))
    fi

    if [ "$total_size" -eq 0 ]; then
    block_size=`stat -f -c "%S" "$cdroot"`
    fs_total_blocks=`stat -f -c "%b" "$cdroot"`
    fs_free_blocks=`stat -f -c "%f" "$cdroot"`

    total_size=$((fs_total_blocks*block_size))
    free_size=$((fs_free_blocks*block_size))
    fi
}

dbroot="$HOME/.offlinefs"
insert="/usr/local/etc/offlinefs/insert"
cdroot=""
label=""
prefix="/"

while getopts l:i:b:s:p: opt; do
    case $opt in
    l) label=$OPTARG;;
    i) cdroot=$OPTARG;;
    s) insert=$OPTARG;;
    d) dbroot=$OPTARG;;
    p) prefix=$OPTARG;;
    esac
done
[ $((OPTIND-1)) != $# ] && usage

[ -z $label ] && usage
[ -z $cdroot ] && usage
[ ${cdroot:0:1} == "/" ] || cdroot=`pwd`"/$cdroot"

echo "cdroot: $cdroot"
echo "label: $label"
echo "dbroot: $dbroot"
echo "insert script: $insert"
echo "prefix: $prefix"

echo "Adding medium to the database..."
fingerprint=`stat -f -c %a%b%c%d%f%i%l%s%S%t $cdroot`

if (offmedia --dbroot "$dbroot" --label "$label" --list |grep "[medium]" >> /dev/null); then
    echo "$0: Error: Specified label \"$label\" already exists."
    exit 1
fi

offmedia --dbroot "$dbroot" --label "$label" --add insert || exit 1
offmedia --dbroot "$dbroot" --label "$label" --set directory "$cdroot" || exit 1
#offmedia --dbroot "$dbroot" --label "$label" --set checkcmd "test $fingerprint = \`stat -f -c %a%b%c%d%f%i%l%s%S%t $cdroot\`" || exit 1
offmedia --dbroot "$dbroot" --label "$label" --set checkcmd "mythdvdchangerisloaded.sh $label" || exit 1
#offmedia --dbroot "$dbroot" --label "$label" --set insertcmd "$insert \"$label\" \"$cdroot\"" || exit 1
offmedia --dbroot "$dbroot" --label "$label" --set insertcmd "mythdvdchangerload.sh $label" || exit 1

get_size

offmedia --dbroot "$dbroot" --label "$label" --set "total_size" $total_size || exit 1
offmedia --dbroot "$dbroot" --label "$label" --set "free_size" $free_size || exit 1

echo "Adding files to the database..."
find $cdroot -printf '%U %G %m %y %A@ %T@ %s %P //// %l\n' |awk 'NR!=1 {print $0}' |offimport -c -M "$label" -b "$dbroot" -p "$prefix" -f  '%U %G %m %y %A@ %T@ %s %P //// %l'
echo "Done"

These 2 misc scripts help determine if import has been successful

countsub.sh

#!/bin/bash

sub=`find "$1" -maxdepth 1 -type d | wc -l`
echo "=$sub:$1"

foldercount.sh

#!/bin/bash

find . -maxdepth 1 -type d -exec countsub.sh "{}" \;

To show folders with no sub folders issue

foldercount.sh | grep =1:

To show folders with 1 sub folder issue

foldercount.sh | grep =2:

To count all folders with 1 sub folder issue

foldercount.sh | grep =2: | wc -l

This should be the same as the number of DVD video discs that you have imported.

Ripping 200 DVDs at a Time

In addition to playing the disks off the drive, it is possible to use the unit as a "Batch Ripper." Go to this web site and click the "XL1B Media Download" link at the top. Also, install the prerequisite packages (Ubuntu Example):

apt-get install python mtx abcde lame cdparanoia id3v2 sg3-utils python-cddb wodim fakeroot

You will also need to install a copy of HandBrake if you wish to rip and compress at the same time.

Unzip the XL1B Media ZIP somewhere and change to that directory. Create a new file called "XL1B-DVD-RipCompress.py" and insert the following:

#! /usr/bin/python
import os, sys, time, commands, glob, re
modules_path = os.getcwd() + '/modules'
sys.path.append(modules_path)
import mtx_module as mtx

## author: Kevin Valentine
## email: kevin.valentine@gmail.com
## Adapted by Robert McNamara for transcoding (plus some cleanups)

## This small command line script will create an ISO image from all DVDs found
## in the XL1B2 changer's carousel. All ISOs will be stored in a new folder
## created in your home directory.
## 
## Known dependencies:
## python mtx (<=1.3.11) sg3-utils
## 
# This first path is the location where you want to store the encoded
# files.  It defaults to using the operators home directory.
home_dir = '/MythMedia/rips'
path_changers = '/dev/sg*'
path_drives = '/dev/sr*'
scsi_changer = '/dev/sg4'
scsi_drive = '/dev/scd1'
descriptor_changer = 'VAIOChanger1    '
descriptor_drive = 'DVD-RAM SW-9584 '

# Set the device path of the changer mechanism
scsi_changer = mtx.find_device(path_changers, descriptor_changer)
print 'This CHANGER mechanism has been detected --> %s' %scsi_changer

# Set the device path of the drive mechanism
scsi_drive = mtx.find_device(path_drives, descriptor_drive)
print 'This DRIVE mechanism has been detected --> %s' %scsi_drive

# Define these globals after the attached devices
# have been detected and defined
changer_command = 'mtx -f %s nobarcode altres ' %scsi_changer
# isorip_command = "dd if=%s of=dvd.iso" %scsi_drive

def start_new_session():
    print 'Any DVDs found in the changer will be automatically\nconverted to H.264/AC3 MKV files.\nThese files will be placed here:\n%s' %home_dir
    return home_dir

def dvd_copy(slot, base_path):
    start_time = time.time()
    vol_ID_command = 'sleep 30 && fakeroot vol_id -L %s' %scsi_drive
    vol_ID_string = commands.getstatusoutput(vol_ID_command)[1]
    iso_path = '%s/%s' %(base_path, vol_ID_string)
    isorip_command = "sleep 30 %% HandBrakeCLI -i /dev/scd1 -o \"%s.mkv\" -e x264 -q 0.600000023841858 -a 1 -E ac3 -B 160 -R Auto -6 auto -f mkv -p -m -x ref=3:mixed-refs:bframes=3:b pyramid:weightb:filter=-2,-1:trellis=1:analyse=all:8x8dct:me=umh:subme=9:psy-rd=1,1" %(iso_path)
    print 'Running %s' %isorip_command
    command_status = commands.getstatusoutput(isorip_command)
    duration = (time.time() - start_time) / 60
    print 'Slot %d has taken %.2f (min) to process.' %(slot, duration)

def main():
    slot_status = mtx.get_status(changer_command, scsi_drive)
    new_session_folder = start_new_session()
    for n in range(len(slot_status)):
        if slot_status[n]:
            slot = n+1
            mtx.load_CD(slot, changer_command)
            dvd_copy(slot, new_session_folder)
            mtx.unload_CD(slot, changer_command, scsi_drive)

if __name__ == '__main__':
    main()

Edit the variables at the top to suit your destination. The drive devices *should* autodetect, but you can hard code these if necessary.

Save the file, and make it executable by running the following command on it:

chmod +x XL1B-DVD-RipCompress.py

You can now run the script and the ripping process should begin.