Advertisement
wickedsun

kodisql.pl -- MySQL-Kodi Dynamic Database Replicator

Jan 19th, 2016
1,588
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 10.43 KB | None | 0 0
  1. #!/usr/bin/perl
  2. use DBI;
  3. use experimental 'smartmatch';
  4. use Getopt::Long;
  5.  
  6.  
  7. $result = GetOptions    ("user=s" => \$USER,
  8.              "pass=s" => \$PASS,
  9.              "target=s" => \$TARGET,
  10.              "host=s" => \$HOST,
  11.              "h=s" => \$HOST,
  12.              "u=s" => \$USER,
  13.              "p=s" => \$PASS,
  14.              "port=i" => \$PORT,
  15.                          "config=s" => \$CONFIG,
  16.                          "main=s" => \$MAINDB,
  17.              "help" => \$HELP);
  18.  
  19.  
  20. if ((!($USER)) || (!($PASS)) || (!($TARGET)) || (!($MAINDB))) {
  21.    &help;
  22.    exit;
  23. }
  24.  
  25. if (!($PORT)) {
  26.   $PORT = "3306";
  27. }
  28.  
  29. if (!($CONFIG)) {
  30.   $CONFIG="kodisql_config";
  31. }
  32.  
  33. $mysqlhost="$HOST";
  34. $mysqluser="$USER";
  35. $mysqlpass="$PASS";
  36. $mysqlport="$PORT";
  37.  
  38.  
  39. # Temporary settings.. will be command line options later
  40. $target = "$TARGET";
  41. # The master kodi database to replicate from (whatever is declared in your advancedsettings.xml).
  42. $maindb = "$MAINDB";
  43.  
  44.  
  45. $globaltable = "globalfiles";
  46. @file_exceptions = ("playCount","lastPlayed");
  47.  
  48. # The database where we keep the list of clients and other info we may need on upgrades.
  49. $configdb = "$CONFIG";
  50.  
  51.  
  52. ################################################
  53. # Connect to the database.
  54. $dbh = DBI->connect("DBI:mysql:host=$mysqlhost", "$mysqluser", "$mysqlpass", {'RaiseError' => 1});
  55. @dbs = $dbh->func('_ListDBs');
  56. foreach (@dbs) {
  57.   if ($_ =~ /${maindb}\d+/) {
  58.     push @kodidbs, $_;
  59.   }
  60. }
  61. my $highdb;
  62. my $highrev;
  63. foreach (@kodidbs) {
  64.   $db = $_;
  65.   $rev = $_;;
  66.   $highrev = $highdb;
  67.   ($rev) = $rev =~ m/${maindb}(\d+)/;
  68.   ($highrev) = $highdb =~ m/${maindb}(\d+)/;
  69.   if (($rev > $highrev) || (!$highrev)) {
  70.     $highrev = $rev;
  71.     $highdb = "${maindb}$highrev";
  72.   }
  73.   if ((($oldrev < $ref) && ($rev < $highref)) || (!($oldrev))) {
  74.     my $exists = &checkifexists("$target$oldrev");
  75.     if ($exists) {
  76.       $oldrev = $rev;
  77.     }
  78.   }
  79. }
  80.  
  81. sub help {
  82.   print "MySQL-Kodi Dynamic database replicator\n";
  83.   print "wickedsun 2016\n";
  84.   print "  --user,-u\t\tSet MySQL USERNAME\t(required)\n";
  85.   print "  --pass,-p\t\tSet MySQL PASSWORD\t(required)\n";
  86.   print "  --port\t\tSet MySQL PORT\t\t(Default: 3306)\n";
  87.   print "  --target\t\tSet TARGET Database\t(required)\n";
  88.   print "  --main\t\tSet MAIN Database\t(required)\n";
  89.   print "  --config\t\tSet CONFIG Database\t(Default: kodisql_config)\n";
  90.   print "  --update\t\tUpdate oldest slave DB to current schema (not yet implemented!)\n";
  91.   print "  --help\t\tThis help message\n";
  92. }
  93.  
  94. # check if the target db exists and create it if it doesn't
  95. sub checkdb {
  96.   my $db = @_[0];
  97.   my $exists = &checkifexists($db);
  98.   if (!($exists)) {
  99.     $dbh->do("CREATE DATABASE $db");
  100.   }
  101. }
  102.  
  103. # check if globalfiles exists
  104. sub checkglobalfiles {
  105.   my ($db,$target) = @_;
  106.   my @statement;
  107.   my $exists = &checkifexists($db,$globaltable);
  108.   if (!($exists)) {
  109.     $dbh->do("USE $db");
  110.     $dbh->do("RENAME TABLE `files` TO `$globaltable`");
  111.   }
  112. }
  113.  
  114. # check if columns for target exist, and create if they do not.
  115. sub checkslavecol {
  116.   my ($db,$target) = @_;
  117.   $exists = &checkifexists("${target}$highrev","bookmark");
  118.   if (!($exists)) {
  119.     # could probably make this table dynamic instead of this.. basically just recreate the table; updates might be tricky like comparing the new and old tables of the main db
  120.     # and figuring out what was added after copying -- needs investigating, and need to see if this table ever changes and if so, how often.
  121.     $dbh->do("CREATE TABLE `${target}$highrev`.`bookmark` ( idBookmark integer primary key AUTO_INCREMENT, idFile integer, timeInSeconds double, totalTimeInSeconds double, thumbNailImage text, player text, playerState text, type integer)");
  122.     $dbh->do("USE ${target}$highrev");
  123.     $dbh->do("CREATE INDEX ix_bookmark ON bookmark (idFile, type);");
  124.   }
  125. }
  126.  
  127. sub createviews {
  128.   ($db,$target,$rev) = @_;
  129.   my $exists = &checkifexists($db,$globaltable,"${target}_playCount");
  130.   if (!($exists)) {
  131.     $dbh->do("ALTER TABLE `$db`.`$globaltable` ADD `${target}_playCount` INT( 11 ) NULL DEFAULT NULL , ADD `${target}_lastPlayed` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL");
  132.     $dbh->do("USE ${target}$highrev");
  133.   }
  134.   $sth = $dbh->prepare("SHOW COLUMNS FROM $db.$globaltable");
  135.   $sth->execute;
  136.   my $statement;
  137.   my $header;
  138.   my $tail;
  139.   my $exists;
  140.   while (my $ref = $sth->fetchrow_hashref()) {
  141.     my $field = $ref->{'Field'};
  142.     foreach (@file_exceptions) {
  143.       $exc = $_;
  144.       if ($field eq $exc) {
  145.     # CREATE VIEW `User1Videos93`.`files` AS idFile, idPath, strFilename, playCount1 AS playCount, lastPlayed1 AS lastPlayed, dateAdded FROM `MyVideos93`.`globalfiles`;
  146.     push @statement, "${target}_$exc AS $exc";
  147.         push @statement_main, "${maindb}_$exc AS $exc";
  148.         print "Pushed ${maindb}_$exc AS $exc\n";
  149.       }
  150.     }
  151.     if ((!($field ~~ @statement)) && (!($field ~~ @file_exceptions))) {
  152.       my $skip = 0;
  153.       foreach (@file_exceptions) {
  154.     my $except = $_;
  155.     if ($field =~ m/.*_$except/) { $skip = 1;}
  156.       }
  157.       if ($skip == 0) {
  158.         push @statement, "$field";
  159.         push @statement_main, "$field";
  160.       }
  161.     }
  162.   }
  163.   foreach (@statement) {
  164.       my $part = $_;
  165.       if ($statement) {
  166.     $statement = "$statement, $part";
  167.     $statement_main = "$statement_main, $part";
  168.       } else {
  169.     $statement = "$part";
  170.     $statement_main = "$part";
  171.       }
  172.   }
  173.   $header = "CREATE VIEW `${target}${rev}`.`files` AS SELECT";
  174.   $header_main = "CREATE VIEW `$highdb`.`files` AS SELECT";
  175.   $footer = "FROM `$highdb`.`$globaltable`";
  176.   $footer_main = "FROM `$highdb`.`$globaltable`";
  177.   $exists = &checkifexists("${target}${rev}","files");
  178.   if (!($exists)) {
  179.     print "creating VIEW: $header $statement $footer\n";
  180.     $dbh->do("$header $statement $footer");
  181.   }
  182.   $exists = &checkifexists("$highdb","files");
  183.   if (!($exists)) {
  184.     print "creating VIEW_MAIN: $header_main $statement_main $footer_main\n";
  185.     $dbh->do("$header_main $statement_main $footer_main");
  186.   }
  187.   $sth = $dbh->prepare("SELECT TABLE_NAME FROM information_schema.columns WHERE table_schema = '$db' GROUP BY TABLE_NAME");
  188.   $sth->execute;
  189.   while (my $ref = $sth->fetchrow_hashref()) {
  190.     # CREATE VIEW `User1Videos93`.`actor_link` AS SELECT * FROM `MyVideos93`.`actor_link`;
  191.     if (!(($ref->{'TABLE_NAME'} eq "files") || ($ref->{'TABLE_NAME'} eq "bookmark") || ($ref->{'TABLE_NAME'} eq "$globaltable"))) {
  192.       push @tables, $ref->{'TABLE_NAME'};
  193.     }
  194.   }
  195.   foreach (@tables) {
  196.     $table = $_;
  197.     $exists = &checkifexists("${target}${rev}","$table");
  198.     if (!($exists)) {      
  199.       $dbh->do("CREATE VIEW `${target}${rev}`.`$table` AS SELECT * FROM `$db`.`$table`");
  200.     }
  201.   }
  202. }
  203.  
  204. sub createviewhash {
  205.   my ($highdb,$target,$highrev) = @_;
  206.   my @views_order;
  207.   # get a list of the views in the video DB from the highest revision
  208.   $sth = $dbh->prepare("SHOW FULL TABLES IN $highdb WHERE TABLE_TYPE LIKE 'VIEW'");
  209.   $sth->execute;
  210.   $dbh->do("USE ${target}$highrev");
  211.   while (my $ref = $sth->fetchrow_hashref()) {
  212.     my $view = $ref->{"Tables_in_$highdb"};
  213.     push @views, $view;
  214.   }
  215.   foreach (@views) {
  216.     my $view = $_;
  217.     print "Fetching create info for $_\n";
  218.     $sth = $dbh->prepare("SHOW CREATE VIEW `$highdb`.`$view`");
  219.     $sth->execute;
  220.     my $statement;
  221.     while (my $ref = $sth->fetchrow_hashref()) {
  222.       $statement = $ref->{'Create View'};
  223.     }
  224.     # change all the dbs to the target.
  225.     ($statement = $statement) =~ s/$highdb/${target}$highrev/g;
  226.     # remove the security statements from the CREATE.
  227.     ($statement = $statement) =~ s/^CREATE .* VIEW `${target}$highrev`.`$view`/CREATE VIEW `${target}$highrev`.`$view`/g;
  228.     # whenever we see files, make sure we use globalfiles from the master database instead
  229.     #($statement = $statement) =~ s/`${target}_Videos$highrev`.`globalfiles`/`$highdb`.`globalfiles`/g;
  230.     foreach (@file_exceptions) {
  231.       my $exc = $_;
  232.       # make sure we change the exceptions (playCount, lastPlayed for now) changed to the target ones.
  233.       #($statement = $statement) =~ s/`${target}_Videos$highrev`.`files`.`$exc`/`$highdb`.`globalfiles`.`${target}_$exc`/;
  234.     }
  235.     $view_hash{$view}{'statement'} = "$statement";
  236.     foreach (@views) {
  237.       my $testview = $_;
  238.       if ($statement =~ m/SELECT .*`$testview`/i) {
  239.         print "view $view requires $testview\n";
  240.         push @{$view_hash{$view}{'dep'}}, $testview;
  241.       }
  242.     }
  243.   }
  244. }
  245.  
  246. sub recreatehighviews {
  247.   foreach (keys %view_hash) {
  248.     &checkdeps($view);
  249.   }
  250. }
  251.  
  252.  
  253. sub checkdeps {
  254.   my $exists;
  255.   print "view check: $_\n";
  256.   my ($view) = $_;
  257.   my $depview;
  258.   if ($view_hash{$view}{'dep'}) {
  259.     foreach (@{$view_hash{$view}{'dep'}}) {
  260.       $depview = $_;
  261.       if (!($view_hash{$depview}{'pushed'})) {
  262.         print "Need to check $depview, missing dep\n";
  263.         &checkdeps($depview);
  264.       }
  265.     }
  266.     if (!($view_hash{$view}{'pushed'}) == 1) {
  267.       $exists = &checkifexists("${target}$highrev","$view");
  268.       if (!($exists)) {
  269.         $dbh->do("$view_hash{$view}{'statement'}");
  270.       }
  271.       $view_hash{$depview}{'pushed'} = 1;
  272.     } else {
  273.       print "$view already pushed\n";
  274.     }
  275.   } else {
  276.     if (!($view_hash{$view}{'pushed'} == 1)) {
  277.       $exists = &checkifexists("${target}$highrev","$view");
  278.       if (!($exists)) {
  279.         $dbh->do("$view_hash{$view}{'statement'}");
  280.       }
  281.       $view_hash{$view}{'pushed'} = 1;
  282.     } else {
  283.       print "$view already pushed\n";
  284.     }
  285.   }
  286. }
  287.  
  288. sub checkifexists {
  289.   my ($db,$table,$column) = @_;
  290.   if ($column) {
  291.     $sth = $dbh->prepare("SELECT * FROM information_schema.columns WHERE table_schema = '$db' AND table_name = '$table' AND COLUMN_NAME = '$column'");
  292.   } elsif ($table) {
  293.       $sth = $dbh->prepare("SELECT * FROM information_schema.columns WHERE table_schema = '$db' AND table_name = '$table'");
  294.   } else {
  295.     $sth = $dbh->prepare("SELECT * FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = '$db'");
  296.   }
  297.   $sth->execute;
  298.   my $rows = $sth->rows;
  299.   $sth->finish;
  300.   if ($rows > 0) {
  301.     print "table/column exists -- $db $table $column \n";
  302.     return 1;
  303.   } else {
  304.     print "table/column does not exists -- $db $table $column\n";
  305.     return;
  306.   }
  307. }
  308.  
  309. sub main {
  310.   &checkdb("$configdb");
  311.   $dbh->do("USE $configdb");
  312.   &checkdb("${target}${highrev}");
  313.   # make sure the target has the required columns
  314.   &checkslavecol("$highdb","$target");
  315.   &checkglobalfiles("$highdb","$target");
  316.   &createviews("$highdb","$target","$highrev");
  317.   &createviewhash("$highdb","$target","$highrev");
  318.   &recreatehighviews;
  319. }
  320.  
  321. &main;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement