aboutsummaryrefslogtreecommitdiff
path: root/lib/App/MediaControl
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2024-01-20 17:30:25 +0000
committerdakkar <dakkar@thenautilus.net>2024-01-20 17:35:21 +0000
commit491dc1aeab9b445ee28f972e19a1cbb4cb9f3af7 (patch)
treeb4213dfea7e02f16e286b6b814dc196c19714cdf /lib/App/MediaControl
parentquite the ScanDir supply when files are being modified (diff)
downloadmedia-control-491dc1aeab9b445ee28f972e19a1cbb4cb9f3af7.tar.gz
media-control-491dc1aeab9b445ee28f972e19a1cbb4cb9f3af7.tar.bz2
media-control-491dc1aeab9b445ee28f972e19a1cbb4cb9f3af7.zip
look at fs on demand, don't watch it
ScanDir (well, fs notifications in raku) is too slow to keep up with actual fs changes (especially when e.g. a file is being downloaded) there's actually no need to watch fs changes, we can just sync the db with the file system we look at each directory
Diffstat (limited to 'lib/App/MediaControl')
-rw-r--r--lib/App/MediaControl/DB.rakumod16
-rw-r--r--lib/App/MediaControl/FS.rakumod36
-rw-r--r--lib/App/MediaControl/Model.rakumod101
-rw-r--r--lib/App/MediaControl/Web.rakumod14
4 files changed, 150 insertions, 17 deletions
diff --git a/lib/App/MediaControl/DB.rakumod b/lib/App/MediaControl/DB.rakumod
index e69efad..dd26e22 100644
--- a/lib/App/MediaControl/DB.rakumod
+++ b/lib/App/MediaControl/DB.rakumod
@@ -10,8 +10,8 @@ class App::MediaControl::DB {
# we need an explicit LEAVE block because on 2021.10, `will
# leave { .finish }` kills precomp
LEAVE { .finish with $conn };
- $conn.begin;
$conn.execute('PRAGMA foreign_keys=true');
+ $conn.begin;
KEEP { .commit with $conn };
return $code($conn) with $conn;
}
@@ -91,15 +91,11 @@ class App::MediaControl::DB {
}
}
- method remove-entry(Str:D() :$path! is copy, Str:D() :$name!) {
- $path ~~ s{<!after '/'>$} = '/';
- $path ~~ s{<!before '/'>^} = '/';
-
+ method remove-entry(Int:D() $id) {
self!db: {
- .query(q:to/END/, :$path, :$name);
+ .query(q:to/END/, :$id);
DELETE FROM files
- WHERE name=$name
- AND path=$path
+ WHERE id=$id
END
}
}
@@ -110,7 +106,7 @@ class App::MediaControl::DB {
my ($clause, @binds) = {*};
self!db: {
.query(qq:to/END/,|@binds).hashes;
- SELECT id, name, is_dir, watched_time
+ SELECT id, path, name, is_dir, watched_time
FROM files
WHERE parent_id $clause
ORDER BY name ASC
@@ -168,7 +164,7 @@ class App::MediaControl::DB {
ORDER BY watched_time DESC
LIMIT ?
)
- SELECT files.id, files.name, files.is_dir, recent.watched_time
+ SELECT files.id, files.path, files.name, files.is_dir, recent.watched_time
FROM files
JOIN recent ON files.id=recent.id
END
diff --git a/lib/App/MediaControl/FS.rakumod b/lib/App/MediaControl/FS.rakumod
new file mode 100644
index 0000000..b8f68d2
--- /dev/null
+++ b/lib/App/MediaControl/FS.rakumod
@@ -0,0 +1,36 @@
+use v6.d;
+
+class App::MediaControl::FS {
+ has IO::Path() $.root is required;
+ has $!extensions;
+
+ submethod TWEAK(:$extensions) {
+ $!extensions = any($extensions.Slip);
+ }
+
+ method get-children-of(Str $path) {
+ my $base = $!root.child($path);
+ return @() unless $base.d;
+
+ my @children = eager $base.dir(
+ test => -> $f {
+ my $based-f = $base.child($f);
+
+
+ $based-f.d ?? $f ~~ $*SPEC.curupdir !!
+ ($based-f.extension ~~ $!extensions) ?? True !!
+ False;
+ },
+ );
+
+ return @children.map(
+ -> $f {
+ %( name => $f.basename, is_dir => $f.d );
+ }
+ ).sort({ .<name> });
+ }
+
+ method exists(Str $path) {
+ return $!root.child($path).e;
+ }
+}
diff --git a/lib/App/MediaControl/Model.rakumod b/lib/App/MediaControl/Model.rakumod
new file mode 100644
index 0000000..6d1c771
--- /dev/null
+++ b/lib/App/MediaControl/Model.rakumod
@@ -0,0 +1,101 @@
+use v6.d;
+use App::MediaControl::FS;
+use App::MediaControl::DB;
+
+class App::MediaControl::Model {
+ has App::MediaControl::FS $.fs is required;
+ has App::MediaControl::DB $.db is required;
+
+ method get-children-of($id) {
+ my @db-children = self.db.get-children-of($id);
+ # [{id,path,name,is_dir,watched_time}]
+
+ my $path;
+ if (@db-children) {
+ $path = @db-children[0]<path>; # they all have the same path
+ } elsif ($id.defined) {
+ my $entry = self.db.get-entry($id);
+ $path = "{$entry<path>}{$entry<name>}";
+ } else {
+ $path = '/';
+ }
+
+ my @fs-children = self.fs.get-children-of($path);
+ # [{name,is_dir}]
+
+ my ($db-idx, $fs-idx, $changed) = 0, 0, False;
+
+ sub add-to-db() {
+ self.db.add-entry(
+ :$path,
+ name => @fs-children[$fs-idx]<name>,
+ is-dir => @fs-children[$fs-idx].<is_dir>,
+ );
+ $changed=True;
+ ++$fs-idx;
+ }
+ sub remove-from-db() {
+ self.db.remove-entry(@db-children[$db-idx]<id>);
+ $changed=True;
+ ++$db-idx;
+ }
+
+ while ($db-idx < @db-children && $fs-idx < @fs-children) {
+ given @db-children[$db-idx]<name> leg @fs-children[$fs-idx]<name> {
+ when Order::Same {
+ ++$db-idx; ++$fs-idx;
+ }
+
+ when Order::Less {
+ remove-from-db();
+ }
+
+ when Order::More {
+ add-to-db();
+ }
+ }
+ }
+
+ while ($db-idx < @db-children) {
+ remove-from-db();
+ }
+
+ while ($fs-idx < @fs-children) {
+ add-to-db();
+ }
+
+ if $changed {
+ @db-children = self.db.get-children-of($id);
+ }
+
+ return @db-children;
+ }
+
+ method get-parents-of(Int:D() $id) {
+ return self.db.get-parents-of($id);
+ }
+
+ method get-entry(Int:D() $id) {
+ return self.db.get-entry($id);
+ }
+
+ method mark-entry-watched(Int:D() $id) {
+ return self.db.mark-entry-watched($id);
+ }
+
+ method get-recently-watched-folders(Int:D() $limit=20) {
+ my @db-folders = self.db.get-recently-watched-folders($limit);
+ my $changed = False;
+ for @db-folders -> $f {
+ next if self.fs.exists("{$f<path>}{$f<name>}");
+ self.db.remove-entry($f<id>);
+ $changed = True;
+ }
+
+ if $changed {
+ @db-folders = self.db.get-recently-watched-folders($limit);
+ }
+
+ return @db-folders;
+ }
+}
diff --git a/lib/App/MediaControl/Web.rakumod b/lib/App/MediaControl/Web.rakumod
index 77d8e05..86c748e 100644
--- a/lib/App/MediaControl/Web.rakumod
+++ b/lib/App/MediaControl/Web.rakumod
@@ -4,12 +4,12 @@ use Cro::HTTP::Router;
use Cro::WebApp::Template;
use Vlc::Client;
use Lirc::Commands;
-use App::MediaControl::DB;
+use App::MediaControl::Model;
class App::MediaControl::Web {
has Vlc::Client $.vlc is required;
has Lirc::Commands $.lirc is required;
- has App::MediaControl::DB $.db is required;
+ has App::MediaControl::Model $.model is required;
has Int $.port = 8080;
has Str $.host = '*';
has Cro::Service $!service handles <stop>;
@@ -18,11 +18,11 @@ class App::MediaControl::Web {
my $vlc = route {
post -> 'play' { await self.vlc.command('pl_play') }
post -> 'play', Int:D $id {
- my $file = self.db.get-entry($id);
+ my $file = self.model.get-entry($id);
await self.vlc.play-file(|%(
$file<path name>:p # no comma!
));
- self.db.mark-entry-watched($id);
+ self.model.mark-entry-watched($id);
}
post -> 'pause' { await self.vlc.command('pl_pause') }
post -> 'stop' { await self.vlc.command('pl_stop') }
@@ -46,14 +46,14 @@ class App::MediaControl::Web {
my $media = route {
get -> $id=Nil {
- my %reply = children => @(self.db.get-children-of($id));
+ my %reply = children => @(self.model.get-children-of($id));
with $id {
- %reply<parents> = self.db.get-parents-of($id);
+ %reply<parents> = self.model.get-parents-of($id);
};
content 'application/json', %reply;
}
get -> 'recent' {
- content 'application/json', @(self.db.get-recently-watched-folders());
+ content 'application/json', @(self.model.get-recently-watched-folders());
}
};