Request for input/assistance on project concept

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

DonnyBahama
Posts: 1
Joined: Mon Dec 30, 2013 5:58 am UTC

Request for input/assistance on project concept

Postby DonnyBahama » Tue Dec 31, 2013 6:49 pm UTC

In a nutshell, I want to build an automated media ripper for my headless Linux-based media server that will allow me to quickly and easily rip a bunch of CDs, movies and/or TV show episodes as a background task while I'm doing other things. It should also provide me a quick, easy way of making sure that files are properly named, tagged and filed/located so I don't have to go back later and fix a bunch of mistakes.

Basic workflow:
  1. automatically detect media insertion
  2. identify disc and get media info via online database
  3. rip media to the proper file format (FLAC or MP3 for CDs, MKV for DVDs and Blu-rays)
  4. recompress (DVDs & BrDs) using handbrake - possibly using logic to set compression level based on release year (i.e. use higher compression levels for old movies & TV shows where quality isn't going to be as good anyway.)
  5. place file in the proper directory/structure with the properly formatted (for XBMC) filename and with the proper ID3 tags
  6. notify me (by text message & email) that the rip is finished - with a link to a web page on the server that will let me edit the filename/tags in case the disc is misidentified and/or the retrieved info needs to be modified in some way (poorly chosen genre, format of artist name, etc.) and, if I make changes, edit the filename and/or ID3 tags and move the file (creating and deleting directories) as necessary
  7. eject the disc
For item 6, I chose that methodology because my server is headless (no monitor or keyboard) and I wanted a reasonably simple way to make changes to the final ripped files when necessary. I'm very particular about my music files. (I use "Setzer, Brian Orchestra" rather than "Brian Setzer Orchestra" because I expect to find that artist under "S", and if I don't omit "The" entirely, I at least want it at the end- "Man With The Horn, The" because I expect to find that album after "Kind of Blue" but before "Miles Ahead") so quick, easy drop-downs on a web page for selecting genre and artist/album names seems like a good way to accomplish this quickly and easily. But I'm open to suggestions on this.

Design aside, there are significant implementation issues for me...
  • I've looked into detecting disc insertion on Linux and it doesn't appear to be trivial. (Help?)
  • I have no idea how to implement code that will get genre/year/artist/album/track number/title info for CDs, or movie title/year info for movies, or series name/season/episode number/title for TV shows. (Help?)
  • If I can fetch the aforementioned info, I'm comfortable using it to populate a text/XML file, then using that info to populate editable fields on a php page. I'm even comfortable renaming/moving files and/or creating/removing directories via php - but I have no idea how to programmatically update ID3 tags. (Help?)
  • I'm not even sure what to use to as a coding platform. My inclination is to use Python- partly because I've been wanting to learn it, and partly because I like the idea that someone else would be able to reuse much of the code if they wanted to port it to Mac or Windows. If I do go with Python, I'd really like to find a python-centric forum where I could get good help as I try to muddle my way through this project. Any recommendations?
Thank you for your time and consideration.

User avatar
Mat
Posts: 414
Joined: Fri Apr 21, 2006 8:19 pm UTC

Re: Request for input/assistance on project concept

Postby Mat » Fri Jan 03, 2014 11:09 am UTC

For music, have a look at http://musicbrainz.org/

The Picard tagger software does most of what you want out of the box, and if you can't easily automate that there are c/c++ libraries available http://musicbrainz.org/doc/Developer_Resources

Karrion
Posts: 92
Joined: Fri Jun 22, 2007 12:14 am UTC
Location: Melbourne, AU

Re: Request for input/assistance on project concept

Postby Karrion » Sun Jan 05, 2014 2:27 am UTC

For detecting media insertion, you can recieve DBUS signals from the freedesktop HAL subsystem (which should be available even on headless setups I think); here's some test code I wrote (some years ago, so YMMV) that just dumps the kind of event information you can get to standard out:

Code: Select all

/*
 * eg see http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/hal/tools/lshal.c
 * device properties see http://people.freedesktop.org/~david/hal-spec/hal-spec.html#device-properties-volume-disc
 *
 * For low level DBUS tutorial, see http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hal/libhal.h>
#include <hal/libhal-storage.h>

void hal_device_added(LibHalContext *ctx, const char *udi) {
   DBusError error;
   
   dbus_error_init(&error);
   printf("** DEVICE ADDED: %s", udi);
   if (libhal_device_property_exists(ctx, udi, "volume.disc.is_videodvd", &error) && libhal_device_get_property_bool(ctx, udi, "volume.disc.is_videodvd", &error)) {
      printf(" (DVD Video: %s)", libhal_device_get_property_string(ctx, udi, "volume.label", &error));
      // refresh display
      printf("\n");
   } else {
      printf("\n*** Sending mount command\n");
      // send mount command
      // TODO: send command directly instead of executing external command
      // see http://git.gnome.org/cgit/gnome-mount/tree/src/gnome-mount.c
      char *buf = malloc((strlen(udi) + 18) * sizeof(char));
      sprintf(buf, "gnome-mount -h %s", udi);
      printf("* EXECUTING: %s\n", buf);
      system(buf);
   }
}

void hal_device_removed(LibHalContext *ctx, const char *udi) {
   printf("** DEVICE REMOVED: %s\n", udi);
}

void hal_modified(LibHalContext *ctx, const char *udi, const char *key, dbus_bool_t is_removed, dbus_bool_t is_added) {
   DBusError error;
   
   dbus_error_init(&error);
   printf("** MODIFIED: %s: %s = %s (%s)\n", udi, key, libhal_device_get_property_string(ctx, udi, key, &error), libhal_device_get_property_bool(ctx, udi, key, &error)?"true":"false");
   if (strcmp(key, "volume.is_mounted") == 0) {
      if (libhal_device_get_property_bool(ctx, udi, key, &error)) {
         // now mounted, refresh display
      } else {
         // unmounted
         printf("*** Sending eject command\n");
         // send eject command
         char **options = NULL;
         unsigned int num_options = 0;
         DBusConnection *conn = libhal_ctx_get_dbus_connection(ctx);
         DBusMessage *msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi, "org.freedesktop.Hal.Device.Volume", "Eject");
         dbus_message_append_args (msg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options, DBUS_TYPE_INVALID);
         dbus_connection_send_with_reply_and_block(conn, msg, -1, &error);
         // do we need to unref the connection here?
      }
   }
}

void hal_condition(LibHalContext *ctx, const char *udi, const char *name, const char *detail) {
   DBusError error;
   
   dbus_error_init(&error);
   printf("** CONDITION: %s: %s %s\n", udi, name, detail);
   if (strcmp(name, "EjectPressed") == 0) {
      printf("*** Sending unmount command\n");
      // find the volume UDI from the drive UDI the condition gives
      char **vols;
      int num_vols;
      DBusConnection *conn = libhal_ctx_get_dbus_connection(ctx);
      dbus_connection_ref(conn);
      LibHalDrive *drive = libhal_drive_from_udi(ctx, udi);
      for (vols = libhal_drive_find_all_volumes(ctx, drive, &num_vols); num_vols; num_vols--) {
         // send unmount command
         char **options = NULL;
         unsigned int num_options = 0;
         DBusMessage *msg = dbus_message_new_method_call ("org.freedesktop.Hal", vols[num_vols-1], "org.freedesktop.Hal.Device.Volume", "Unmount");
         dbus_message_append_args (msg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options, DBUS_TYPE_INVALID);
         dbus_connection_send_with_reply_and_block(conn, msg, -1, &error);
      }
      libhal_drive_free(drive);
      dbus_connection_unref(conn);
   }
}

int main(int argc, char *argv[]) {
   int num, i;
   DBusError error;
   DBusConnection *conn;

   dbus_error_init(&error);
   conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
   LibHalContext *ctx = libhal_ctx_new();
   libhal_ctx_set_dbus_connection(ctx, conn);
   libhal_ctx_init(ctx, &error);
   //char **list = libhal_get_all_devices(ctx, &num, &error);
   char **list = libhal_find_device_by_capability(ctx, "volume.disc", &num, &error);

   for (i=0; i<num; ++i) {
      //printf("Device: %s\n", list[i]);
      if (libhal_device_property_exists(ctx, list[i], "volume.disc.is_videodvd", &error) && libhal_device_get_property_bool(ctx, list[i], "volume.disc.is_videodvd", &error)) {
         printf("%s\n", libhal_device_get_property_string(ctx, list[i], "volume.label", &error));
      }
   }
   
   libhal_ctx_set_device_added(ctx, hal_device_added);
   libhal_ctx_set_device_removed(ctx, hal_device_removed);
   libhal_ctx_set_device_property_modified(ctx, hal_modified);
   libhal_ctx_set_device_condition(ctx, hal_condition);
   
   // listen for signals from the HAL device manager
   dbus_bus_add_match(conn, "type='signal',interface='org.freedesktop.Hal.Manager',member='DeviceAdded'", &error);
   dbus_bus_add_match(conn, "type='signal',interface='org.freedesktop.Hal.Manager',member='DeviceRemoved'", &error);
   dbus_bus_add_match(conn, "type='signal',interface='org.freedesktop.Hal.Device',member='PropertyModified'", &error);
   dbus_bus_add_match(conn, "type='signal',interface='org.freedesktop.Hal.Device',member='Condition'", &error);
   dbus_connection_flush(conn);
   if (dbus_error_is_set(&error)) {
      fprintf(stderr, "Match Error (%s)\n", error.message);
   }
   
   dbus_connection_unref(conn);
   
   // loop listening for signals being emitted
   while (1) {
      // non blocking read of the next available message
      dbus_connection_read_write_dispatch(conn, 1000);
   }
   
   return 0;
}


This is in C, obviously, but I'd be pretty surprised if there wasn't e.g. at least on Python library to talk to DBUS, though DBUS itself is platform-specific.


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 8 guests