Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

CD Beaver - Optical Media Storage Management

4.75/5 (4 votes)
31 Mar 2009CPOL6 min read 32.3K   321  
Solution for Archiving & Managing Optical Media
CD-Beaver

Introduction

CD-Beaver is a simple program for cataloging optical media such as Compact Discs (CD) and Digital Versatile Discs (DVD). The application uses an XML back end as its data store and is a nice partner for disc carrying cases. The purpose of the CD Beaver is to simplify the process of storing and locating discs and the files contained on those discs. In this article, I will demonstrate the various uses of CD Beaver and how it could make archiving your media easier and less stressful.

Background

As with most of my personal projects, this one was born from utter frustration. It all started a few years ago when a colleague of mine who owns a small business was looking for an automated system for storing optical media. He soon came across a solution from Opdicom called Disk Stakka. Below you can see a setup similar to his; five units stacked together providing a storage solution for 500 discs at a reasonable cost of $625 ($125 per unit).

stackka.gif

Following a few months of use, he purchased another 3 units and he insisted this was a solution worth the cost. On his recommendation, I decided to invest in 4 units myself; at a time when I really couldn't justify the expense. The units took a lot of initial overhead getting all the discs into it and cataloging all the files. I was very satisfied initially in having all my DVD movies, video games, backup discs, TV Recordings, Music, and family photos safely stored in the unit.

Within a matter of months, one of the units began to fail and within the year every single unit had failed. The discs consistently got stuck in the unit and ultimately the only way to retrieve a disc from any of the units was to take them apart and guide the disc along its path so it would safely eject from the system. This caused extreme embarrassment as this would always seem to happen when I was entertaining or needed to get access to a disc for some emergency work situation.

What started out as an exciting solution for an old problem for me turned into a nightmare scenario where my media was being held hostage and the only way to get access to my media was to break into this pale beige disc eating beast! I decided several months ago that there was no hope for this ridiculously inept product and I set about creating an alternative.

Design Process

The goals for the project are really simple. I need a solution for storing and retrieving optical media.

The first solution was to use a basic spreadsheet like this:

Image 3

The spreadsheet solution works fine for DVD movies where the file contents are insignificant (AUDIO_TS/VIDEO_TS) but for data discs it poses a problem. Let's take a look at the following work flow:

Image 4

You can see from the above work flow that I have a CD/DVD carrying case named Big Bertha. Contained in sleeve 20C is a disc named PBS Frontline S2004 and contained on that disc are various episodes of the PBS program Frontline. Using the spreadsheet solution, there is no way to relate the files on the disc to the disc itself, or the binder in which it lives without creating a much more sophisticated spreadsheet. If I want to browse the television programs in the collection or search for a specific program, the spreadsheet solution becomes a real hassle. I wanted a solution where I can search disc contents as well as disc titles.

Essentially, entities relate like this: CD-Binders contain Discs and Discs contain Files. Therefore Binders contain Discs as well as Files. Discs have one binder and many files. And Files have one binder and one disc. Refer to the diagram below:

Image 5

With this representation of our physical objects, the original objective can be achieved.

Preparing the Binder for Cataloging

Image 6

The above photograph demonstrates how to setup binders for cataloging. I have added the numbering scheme to the photograph so it's easier to see. Using a black sharpie marker, I number every page starting with 1. If the binder has 4 sleeves per page then each sleeve is labeled from A to D. If the binder only contained 2 sleeves, then the sleeve would be labeled from A to B. So in the photograph above, we are on the 3rd page in the CD-Binder and the sleeve 3A refers to the 3rd page in the binder in the first sleeve (upper-left corner). 3B refers to the 3rd page in the binder in the second sleeve (upper-right corner) and so on.

The binder cover is labeled and the binder is given a name. This binder is named Big-Bertha and has a total capacity of 352 discs (88 pages). Big-Bertha is used to store my family's DVD movie collection. I have another CD-Case named Boob-Tube which stores the family's Tivo Recordings, and various other cases. The CD-Cases are filed safely away in a file cabinet and only the adults have access to them.

The Database

Ok, so I struggle with this choice every time I sit down to startup a project. My choices here may well limit the audience or just be plain inaccessible for some users. MySQL vs MS-SQL vs SQLite, etc, etc. I decided to try something completely crazy this time around and not use a database at all. I have been intrigued by the mass adoption of XML in the last several years and so I decided to try to use it here as my database. Word of caution; the schema I am about to show you is not normalized and violates about every best practice for data storage out there. I'm building a quick and simple solution, not a rocket ship, so please forgive the lack of compliance in that area.

XML
<?xml version="1.0" encoding="utf-8"?>
<database>
  <binders />
  <discs />
  <files />
</database>

Below you can see some sample data:

XML
<?xml version="1.0" encoding="utf-8"?>
<database>
  <binders>
    <binder name="Big-Bertha" />
    <binder name="Boob-Tube" />
  </binders>
  <discs>
    <disc name="BBC Specials 2008 - Disc 01" label="VID01" 
	size="7446 MB" sleeve="1A" binder="Boob-Tube" comments="" />
    <disc name="PBS Battlefield Vietnam" label="BFV" 
	size="7707 MB" sleeve="1B" binder="Boob-Tube" comments="" />
  </discs>
  <files>
    <file name="BBC American Future - A History part 01.avi" 
        size="467 MB" 
        created="10/23/2008 12:00:34 AM" 
        modified="10/23/2008 12:00:34 AM" 
        path="E:\" 
        disc="BBC Specials 2008 - Disc 01" 
        binder="Boob-Tube" />
    <file name="BBC American Future - A History part 02.avi" 
    	size="467 MB" 
        created="10/25/2008 12:10:22 AM" 
        modified="10/25/2008 12:10:22 AM" 
        path="E:\" 
        disc="BBC Specials 2008 - Disc 01" 
        binder="Boob-Tube" />
    <file name="BBC American Future - A History part 03.avi" 
    	size="467 MB" 
        created="10/27/2008 2:44:32 PM" 
        modified="10/27/2008 2:44:32 PM" 
        path="E:\" 
        disc="BBC Specials 2008 - Disc 01" 
        binder="Boob-Tube" />
   ...

Interesting XML CRUD Operations

I've not had the pleasure of working much with LINQ yet and I'm extremely pleased with it so far. Below I'm going to show a few interesting CRUD operations I'm using with this XML database (I use the term database in this context very loosely).

SELECT Directly from XML

I prefer to use LINQ to Objects over any other kind of LINQ since it's so simple and elegant but there are a few cases where I need to talk directly to the XML file and below you can see one of them. In this routine, I get the binders directly from the XML file and use it to populate my businessObject entities (Binder, Disc, File, etc.).

C#
XDocument _xdoc = XDocument.Load(Globals.Database);

var bq = from f in _xdoc.Elements("database").Elements("binders").Elements("binder")
         select f;

foreach (var cdcase in bq)
{
   Binder cdbinder = new Binder();
   cdbinder.Name = cdcase.Attribute("name").Value;
   ....
}

SELECT from Entities

Below you can see a member of Binder which determines if there is already a Disc stored in the sleeve entered by the user to store the Disc:

C#
public static bool SleeveOccupied(string sleeve, string binderName)
{
  Disc cd = null;
  try
  {
     cd = (from d in Globals.Discs where d.Sleeve == sleeve && 
           d.Binder.Name == binderName select d).Single();
  }
  catch (InvalidOperationException) { }
  
  return (cd != null) ? true : false;
}

No LIKE Statement?

I could not find a LIKE statement in LINQ similar to what we find in T-SQL syntax. I decided to try RegularExpressions and luckily, they worked perfectly!

C#
List<Disc> m_discs = new List<Disc>();

if (radioButton1.Checked && cbCase.SelectedIndex == 0)
{
   m_discs = (from d in Globals.Discs
              where Regex.IsMatch(d.Name, textBox1.Text, RegexOptions.IgnoreCase)
              orderby d.Name
              select d).ToList();
}

Cascade Delete

This is a ghetto pretend way of cascade deletion (it's not really cascade but I pretend it is!). When the user deletes a disc, all the files in that disc need to be deleted as well.

C#
XDocument xmldoc = XDocument.Load(Globals.Database);
string xpqd = String.Format("database/discs/disc[@name = '{0}']", 
				listView1.SelectedItems[0].Text);
string xpqf = String.Format("database/files/file[@disc = '{0}']", 
				listView1.SelectedItems[0].Text);
xmldoc.XPathSelectElement(xpqd).Remove();
xmldoc.XPathSelectElements(xpqf).Remove();
xmldoc.Save(Globals.Database);
listView1.SelectedItems[0].Remove();
Utility.LoadDb();

Conclusion

Building the system was very fun and simple; a weekend project that actually turned out nicely. Finding discs and archived files is no longer a pain in the ass and because things went so well, I had 30 minutes of pure pleasure pounding my 4 Disk Stakka units with a steel baseball bat and watching them shatter into hundreds of pieces; which I later melted in a bonfire!

History

  • 03/31/2009 - Submitted article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)