package MediaLib; use 5.008_008; use strict; use warnings; require File::Spec; require DBIx::Class::Schema; require DBIx::Class; require MediaLib::SchemaBase; our %Type_Map = ( # Basic types 'integer' => 'integer', 'float' => 'float', 'text' => 'varchar', # Derived types with special meaning 'artist' => 'varchar', 'year' => 'integer', ); # library_init: # # You call this function on a pathname, and get # back a library object for the media library at # that pathname. If there was no existing media # library there (just MP3s, no DB), you must also # supply a pathname to a schema definition to use. # You can also optionally supply an alternate path # for the DB/schema files associated with this library, # in the case that you don't want them stored in the # library dir. sub library_init { my $class = shift; my $args = { @_ }; my $root_path = $args->{root_path}; # Where the media is my $schema_class = $args->{schema_class}; # Where the schema def is # (optional for existing library) my $db_path = $args->{db_path}; # Alt loc for db/schema, # for no writes to $root_path my $load_scan_code = $args->{load_scan_code} ? 1 : 0; die "Must supply a library pathname" unless defined $root_path; die "Library pathname '$root_path' does not exist or is not a directory" unless -d $root_path; # $db_path defaults to $root_path/_smldb, and schema there is the default # location to load (or save) schema def at. unless($db_path) { $db_path = File::Spec->catdir($root_path, '_smldb'); } unless(-d $db_path) { mkdir($db_path) or die "Cannot create db dir '$db_path': $!"; } # Get a schema class for this schema_data _find_or_create_schema_classes($schema_class, $load_scan_code); # Instantiate the object for our db_file my $schema_obj = $schema_class->mlconnect( db_path => $db_path, root_path => $root_path, ); return $schema_obj; } sub _find_or_create_schema_classes { my ($schema_class, $load_scan_code) = @_; # We're doing funny stuff from here no strict 'refs'; return if defined ${$schema_class . '::VERSION'}; eval "require $schema_class"; die "Failed to load schema class '$schema_class': $@" if $@; if($load_scan_code) { eval "require MediaLib::SchemaScan"; die "Failed to load MediaLib::SchemaScan: $@" if $@; } # Define the base of the schema class @{$schema_class . '::ISA'} = ( 'DBIx::Class::Schema', 'MediaLib::SchemaBase', $load_scan_code ? 'MediaLib::SchemaScan' : (), ); my $meta_class = $schema_class . '::Meta'; ${$meta_class . '::VERSION'} = 1; @{$meta_class . '::ISA'} = 'DBIx::Class'; $meta_class->load_components('Core'); $meta_class->table('meta'); $meta_class->add_columns( 'k' => { 'data_type' => 'varchar', 'is_nullable' => 0, }, 'v' => { 'data_type' => 'varchar', 'is_nullable' => 0, }, ); $meta_class->set_primary_key('k'); # Album basics my $album_class = $schema_class . '::Album'; ${$album_class . '::VERSION'} = 1; @{$album_class . '::ISA'} = 'DBIx::Class'; $album_class->load_components('Core'); $album_class->table('album'); $album_class->add_columns( 'id' => { 'data_type' => 'integer', 'is_nullable' => 0, 'is_auto_increment' => 1, }, 'title' => { 'data_type' => 'varchar', 'is_nullable' => 0, }, %{$schema_class->album_attrs()}, ); $album_class->set_primary_key('id'); # Track basics my $track_class = $schema_class . '::Track'; ${$track_class . '::VERSION'} = 1; @{$track_class . '::ISA'} = 'DBIx::Class'; $track_class->load_components('Core'); $track_class->table('track'); $track_class->add_columns( 'id' => { 'data_type' => 'integer', 'is_nullable' => 0, 'is_auto_increment' => 1, }, 'path' => { 'data_type' => 'varchar', 'is_nullable' => 0, }, 'start' => { 'data_type' => 'integer', 'is_nullable' => 0, }, 'stop' => { 'data_type' => 'integer', 'is_nullable' => 0, }, 'album' => { 'data_type' => 'integer', 'is_nullable' => 0, }, 'tracknum' => { 'data_type' => 'integer', 'is_nullable' => 0, }, 'title' => { 'data_type' => 'varchar', 'is_nullable' => 0, }, 'duration' => { 'data_type' => 'integer', 'is_nullable' => 0, }, %{$schema_class->track_attrs()}, ); $track_class->set_primary_key('id'); $track_class->add_unique_constraint(['path', 'start', 'stop']); $track_class->add_unique_constraint(['album', 'tracknum']); # Contrib basics my $contrib_class = $schema_class . '::Contrib'; ${$contrib_class . '::VERSION'} = 1; @{$contrib_class . '::ISA'} = 'DBIx::Class'; $contrib_class->load_components('Core'); $contrib_class->table('contrib'); $contrib_class->add_columns( 'id' => { 'data_type' => 'integer', 'is_nullable' => 0, 'is_auto_increment' => 1, }, 'name' => { 'data_type' => 'varchar', 'is_nullable' => 0, }, ); $contrib_class->set_primary_key('id'); $contrib_class->add_unique_constraint(['name']); # TrackContrib basics my $track_contrib_class = $schema_class . '::TrackContrib'; ${$track_contrib_class . '::VERSION'} = 1; @{$track_contrib_class . '::ISA'} = 'DBIx::Class'; $track_contrib_class->load_components('Core'); $track_contrib_class->table('track_contrib'); $track_contrib_class->add_columns( 'track' => { 'data_type' => 'integer', 'is_nullable' => 0, }, 'role' => { 'data_type' => 'varchar', 'is_nullable' => 0, }, 'contrib' => { 'data_type' => 'integer', 'is_nullable' => 0, }, ); $track_contrib_class->set_primary_key('track', 'role', 'contrib'); # Album <-> Track (1:M) $track_class->belongs_to('album' => $album_class); $album_class->has_many('tracks' => $track_class => 'album'); # Contrib <-> Track (1:M) $track_contrib_class->belongs_to('track' => $track_class); $track_contrib_class->belongs_to('contrib' => $contrib_class); $contrib_class->has_many('track_contribs' => $track_contrib_class => 'contrib'); $track_class->has_many('track_contribs' => $track_contrib_class => 'track'); $contrib_class->many_to_many('tracks' => 'track_contribs' => 'track'); $track_class->many_to_many('contribs' => 'track_contribs' => 'contrib'); $schema_class->load_classes(qw/Meta Album Contrib Track TrackContrib/); # From here down is only relevant when scan code is requested: return unless $load_scan_code; # File-scan regex my $filescan_re_str = join(q{|}, map { qq{\\.$_} } keys %{$schema_class->ext_map()}); my $filescan_re = qr/(?:$filescan_re_str)$/i; *{$schema_class . '::filescan_re'} = sub { $filescan_re }; } 1;