summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flake.lock24
-rw-r--r--flake.nix2
-rw-r--r--src/cache/entry.h33
-rw-r--r--src/codec/ffmpeg/decoder.c6
-rw-r--r--src/codec/ffmpeg/demuxer.c1
-rw-r--r--src/fruits/cmc/cmc.c10
-rw-r--r--src/fruits/cmc/cmc.h2
-rw-r--r--src/fruits/cmc/ui/panes/list.c244
-rw-r--r--src/fruits/cmc/ui/widgets/now_playing.c4
-rw-r--r--src/fruits/cmsrv/cmsrv.c16
-rw-r--r--src/fruits/cmsrv/ui.c9
-rw-r--r--src/fruits/cmv/cmv.c16
-rw-r--r--src/liana/list.c13
-rw-r--r--src/liana/list.h4
-rw-r--r--src/liana/list_cmp.h27
-rw-r--r--src/liana/server.c11
-rw-r--r--src/liana/server.h1
-rw-r--r--src/libsink/sink.c1
-rw-r--r--src/server/common.h14
-rw-r--r--src/server/resource.h42
-rw-r--r--src/server/server.c146
-rw-r--r--subprojects/libalabaster.wrap2
22 files changed, 169 insertions, 459 deletions
diff --git a/flake.lock b/flake.lock
index 89311ee..7c734e5 100644
--- a/flake.lock
+++ b/flake.lock
@@ -23,11 +23,11 @@
]
},
"locked": {
- "lastModified": 1746177088,
- "narHash": "sha256-hmHKl4meWr6ryzqQAwRD3+3Ihfb/Y/0CbK+WnE+oa6Q=",
+ "lastModified": 1746632058,
+ "narHash": "sha256-Mp5Bbvb+YlFEZ76C/0wFS6C1lRfH3D60u465wFNlnS0=",
"owner": "nix-community",
"repo": "home-manager",
- "rev": "f045bd46b73c3b0ed4e46cdb6036b3d5823d7dee",
+ "rev": "708074ae6db9e0468e4f48477f856e8c2d059795",
"type": "github"
},
"original": {
@@ -39,11 +39,11 @@
},
"nixos-hardware": {
"locked": {
- "lastModified": 1745955289,
- "narHash": "sha256-mmV2oPhQN+YF2wmnJzXX8tqgYmUYXUj3uUUBSTmYN5o=",
+ "lastModified": 1746621361,
+ "narHash": "sha256-T9vOxEqI1j1RYugV0b9dgy0AreiZ9yBDKZJYyclF0og=",
"owner": "NixOS",
"repo": "nixos-hardware",
- "rev": "72081c9fbbef63765ae82bff9727ea79cc86bd5b",
+ "rev": "2ea3ad8a1f26a76f8a8e23fc4f7757c46ef30ee5",
"type": "github"
},
"original": {
@@ -59,11 +59,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
- "lastModified": 1744290088,
- "narHash": "sha256-/X9XVEl0EiyisNbF5srrxXRSVoRqdwExuqyspYqqEjQ=",
+ "lastModified": 1746453552,
+ "narHash": "sha256-r66UGha+7KVHkI7ksrcMjnw/mm9Sg4l5bQlylxHwdGU=",
"owner": "nix-community",
"repo": "NixOS-WSL",
- "rev": "60b4904a1390ac4c89e93d95f6ed928975e525ed",
+ "rev": "be618645aa0adf461f778500172b6896d5ab2d01",
"type": "github"
},
"original": {
@@ -91,11 +91,11 @@
},
"nixpkgs_2": {
"locked": {
- "lastModified": 1746141548,
- "narHash": "sha256-IgBWhX7A2oJmZFIrpRuMnw5RAufVnfvOgHWgIdds+hc=",
+ "lastModified": 1746461020,
+ "narHash": "sha256-7+pG1I9jvxNlmln4YgnlW4o+w0TZX24k688mibiFDUE=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "f02fddb8acef29a8b32f10a335d44828d7825b78",
+ "rev": "3730d8a308f94996a9ba7c7138ede69c1b9ac4ae",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index 0c49aff..7f54f8a 100644
--- a/flake.nix
+++ b/flake.nix
@@ -161,7 +161,7 @@
export PATH=${pkgs.lib.makeBinPath shell.nativeBuildInputs}:$PATH
export PKG_CONFIG_PATH=${pkgs.lib.makeSearchPathOutput "dev" "lib/pkgconfig" shell.buildInputs}:$PKG_CONFIG_PATH
export PKG_CONFIG_PATH=${pkgs.lib.makeSearchPathOutput "dev" "share/pkgconfig" shell.buildInputs}:$PKG_CONFIG_PATH
- ${pkgs.bashInteractive}/bin/bash --rcfile <(echo '${shell.shellHook}') $@
+ ${shell.shellHook}
'';
});
diff --git a/src/cache/entry.h b/src/cache/entry.h
index c74870d..d6eb86a 100644
--- a/src/cache/entry.h
+++ b/src/cache/entry.h
@@ -18,6 +18,17 @@
- dvd/bluray (drive or iso)
- live source (radio antenna)
+ Chapters:
+ - Potential resources with chapters:
+ - Single wav with cue.
+ - Multiple wav with cue.
+ - Single mp4 with chapters in metadata.
+ - DVD iso image.
+ - DVD/CD drive.
+ - Chapter as seek or node per chapter?
+ - Node per chapter definitely seems to make the most sense. Maybe we could
+ have a "ignore chapters" flag for single containers like mp4.
+
Other considerations:
- Chapter priority (disc drive)
- For now 1 user and 1 list owns the drive totally.
@@ -28,10 +39,32 @@
- Networked backing?
- Is a liana "node handler" necessary?
+ Flow of a resource:
+ - Entry:
+ - Holds the data but as of now doesn't read metadata.
+ - Server:
+ - Informs clients about entry metadata.
+ - Liana node:
+ - Only place currently setup to block for reading duration.
+ - Disc drive resources need to block to read chapters as well.
+ - File open could likely benefit from being non-blocking/threaded.
+ - Fit into server list_callback()? Could be as simple as replacing lia_node_get_duration().
+ - Any catches to moving duration retrival to the cache entry?
+ - Cache objects? (Disc drive)
+ - Handles it's own state on demand.
+ - Re-evaluate list behavior without duration.
+
Cache/liana refactor:
[x] get/set_size() no longer make sense.
[x] // @TODO: Unknown size is unhandled in backings.
- Handle keeps reading but will eventually have a wait cut short after a backing finalize.
+ [ ] Expanding list entry.
+ - How to handle unload?
+ - Disc drive can trigger unload.
+ - Make sure we can skip around in list while an entry load is pending.
+ - Also cancel load requests on unload.
+ - Consider pitfalls in the idea of having a "cd object" in the list that can be unloaded
+ and loaded with different cds.
[ ] Separate cch_handler from cch_entry.
- Move waits to entry?
- Create cache entry with argument like file, cue, http, hls, etc.
diff --git a/src/codec/ffmpeg/decoder.c b/src/codec/ffmpeg/decoder.c
index 80b1d12..7401698 100644
--- a/src/codec/ffmpeg/decoder.c
+++ b/src/codec/ffmpeg/decoder.c
@@ -260,9 +260,9 @@ static bool ff_decoder_init(struct camu_decoder *dec, struct camu_renderer *rend
}
if (cpus > 0) {
av->codec_context->thread_count = cpus;
- // FF_THREAD_FRAME or FF_THREAD_SLICE.
- // SLICE seems to work better based on videos that got completely choked
- // in THREAD_FRAME mode (4k60 HEVC, FFmpeg 7.1.1, AMD 3900x).
+ // FF_THREAD_FRAME, FF_THREAD_SLICE or both.
+ // Slice seems to work better based on videos that get completely choked
+ // in frame-only or both (4k60 HEVC, FFmpeg 7.1.1, AMD 3900x).
av->codec_context->thread_type = FF_THREAD_SLICE;
log_info("Using %i threads for decoder.", cpus);
}
diff --git a/src/codec/ffmpeg/demuxer.c b/src/codec/ffmpeg/demuxer.c
index c5ca20c..4cf52dd 100644
--- a/src/codec/ffmpeg/demuxer.c
+++ b/src/codec/ffmpeg/demuxer.c
@@ -41,6 +41,7 @@ static bool ff_demuxer_init(struct camu_demuxer *demux, struct cch_handle *handl
// AVIO_SEEKABLE_NORMAL should always be unset if we know we cannot seek.
// However, it should also never be unset if we *do* want to seek because it
// disables long seeks (Not tested in a long time).
+ // This will also make some resources (e.g. .opus) fail to determine a duration.
av->io_context->seekable &= ~AVIO_SEEKABLE_NORMAL;
} else {
av->io_context->seekable |= AVIO_SEEKABLE_NORMAL;
diff --git a/src/fruits/cmc/cmc.c b/src/fruits/cmc/cmc.c
index 43f1144..39423c1 100644
--- a/src/fruits/cmc/cmc.c
+++ b/src/fruits/cmc/cmc.c
@@ -37,11 +37,11 @@ static void read_list_entry(struct nn_packet *packet, struct cmc_list_entry *ent
entry->start = nn_packet_read_u64(packet);
entry->paused_at = nn_packet_read_u64(packet);
entry->offset = nn_packet_read_u64(packet);
- wstr w;
- nn_packet_read_wstr(packet, &w);
- if (!al_wstr_eq(&entry->name, &w)) {
- al_wstr_free(&entry->name);
- al_wstr_clone(&entry->name, &w);
+ str brief;
+ nn_packet_read_str(packet, &brief);
+ if (!al_str_eq(&entry->brief, &brief)) {
+ al_str_free(&entry->brief);
+ al_str_clone(&entry->brief, &brief);
}
}
diff --git a/src/fruits/cmc/cmc.h b/src/fruits/cmc/cmc.h
index 817b8b0..b65b336 100644
--- a/src/fruits/cmc/cmc.h
+++ b/src/fruits/cmc/cmc.h
@@ -29,7 +29,7 @@ struct cmc_list_entry {
u64 start;
u64 paused_at;
u64 offset;
- wstr name;
+ str brief;
struct {
struct camu_audio_format fmt;
u8 *data;
diff --git a/src/fruits/cmc/ui/panes/list.c b/src/fruits/cmc/ui/panes/list.c
index 34e18f9..15554b6 100644
--- a/src/fruits/cmc/ui/panes/list.c
+++ b/src/fruits/cmc/ui/panes/list.c
@@ -265,15 +265,16 @@ static void render_list_entries(struct cmc_list_pane *lp, struct cmc_list_tab *t
ncplane_set_fg_palindex(n, 4);
}
s32 y = i - top;
- u32 length = entry->name.length;
+ char *c_str = al_str_to_c_str(&entry->brief);
if (tab->move.active && i == tracked) {
- cmc_ui_putnwstr_yx(n, y, 0, length, &entry->name);
+ ncplane_putstr_yx(n, y, 0, c_str);
} else if (on_current) {
ncplane_putchar_yx(n, y, 1, '>');
- cmc_ui_putnwstr_yx(n, y, 3, length, &entry->name);
+ ncplane_putstr_yx(n, y, 3, c_str);
} else {
- cmc_ui_putnwstr_yx(n, y, 1, length, &entry->name);
+ ncplane_putstr_yx(n, y, 1, c_str);
}
+ al_free(c_str);
if (styled) {
ncplane_set_styles(n, NCSTYLE_NONE);
ncplane_set_fg_default(n);
@@ -291,229 +292,6 @@ void cmc_list_pane_render(struct cmc_list_pane *lp)
cmc_now_playing_render(&lp->np, tab_current_entry(tab));
}
-//void cmc_lp_init(struct cmc_ui *ui)
-//{
-// al_array_init(ui->lp.tabs);
-// ui->lp.current = 0;
-//}
-//
-//void cmc_lp_layout(struct cmc_ui *ui, struct ncplane *parent)
-//{
-// if (ui->lp.n) ncplane_destroy(ui->lp.n);
-// struct ncplane_options nopts = { 0 };
-// nopts.rows = ncplane_dim_y(parent);
-// nopts.cols = ncplane_dim_x(parent);
-// ui->lp.n = ncplane_create(parent, &nopts);
-//}
-//
-//void cmc_lp_add_list(struct cmc_ui *ui, struct cmc_list *list)
-//{
-// struct cmc_list_tab tab = {
-// .list = list,
-// .selected = list->current,
-// .leader_pressed = false
-// };
-// al_array_push(ui->lp.tabs, tab);
-//}
-//
-//bool cmc_lp_handle_input(struct cmc_ui *ui, struct ncinput *input)
-//{
-// if (!ui->lp.tabs.count) return false;
-//
-// struct cmc_list_tab *tab = &al_array_at(ui->lp.tabs, ui->lp.current);
-// s32 count = (s32)tab->list->entries.count;
-// bool leader = tab->leader_pressed;
-// if (cmc_ui_consider_input(input, false)) {
-// tab->leader_pressed = false;
-// }
-//
-// switch (input->id) {
-// case 'g':
-// CONSIDER_INPUT();
-// if (leader) {
-// if (tab->move.active) {
-// tab->move.offset = -tab->move.selected;
-// } else {
-// tab->selected = 0;
-// }
-// } else {
-// tab->leader_pressed = true;
-// }
-// break;
-// case 'G':
-// CONSIDER_INPUT();
-// if (tab->move.active) {
-// tab->move.offset = count - 1 - tab->move.selected;
-// } else {
-// tab->selected = count - 1;
-// }
-// break;
-// case 'f':
-// //CONSIDER_INPUT_MOD_CTRL();
-// CONSIDER_INPUT_REPEAT();
-// if (tab->move.active) {
-// if (tab->selected + tab->move.offset < count - tab->page_length) {
-// tab->move.offset += tab->page_length;
-// } else {
-// tab->move.offset = count - 1 - tab->move.selected;
-// }
-// } else {
-// if (tab->selected < count - tab->page_length) {
-// tab->selected += tab->page_length;
-// } else {
-// tab->selected = count - 1;
-// }
-// }
-// break;
-// case 'b':
-// //CONSIDER_INPUT_MOD_CTRL();
-// CONSIDER_INPUT_REPEAT();
-// if (tab->move.active) {
-// if (tab->selected + tab->move.offset > tab->page_length) {
-// tab->move.offset -= tab->page_length;
-// } else {
-// tab->move.offset = -tab->selected;
-// }
-// } else {
-// if (tab->selected > tab->page_length) {
-// tab->selected -= tab->page_length;
-// } else {
-// tab->selected = 0;
-// }
-// }
-// break;
-// case 'j':
-// CONSIDER_INPUT_REPEAT();
-// if (tab->move.active) {
-// if (tab->selected + tab->move.offset < count - 1) {
-// tab->move.offset++;
-// }
-// } else {
-// if (tab->selected < count - 1) {
-// tab->selected++;
-// }
-// }
-// break;
-// case 'k':
-// CONSIDER_INPUT_REPEAT();
-// if (tab->move.active) {
-// if (tab->selected + tab->move.offset > 0) {
-// tab->move.offset--;
-// }
-// } else {
-// if (tab->selected > 0) {
-// tab->selected--;
-// }
-// }
-// break;
-// case 'i':
-// CONSIDER_INPUT();
-// if (!tab->move.active) {
-// tab->move.active = true;
-// tab->move.selected = tab->selected;
-// tab->move.offset = 0;
-// }
-// break;
-// case NCKEY_ESC:
-// CONSIDER_INPUT();
-// if (tab->move.active) {
-// tab->move.active = false;
-// }
-// break;
-// case NCKEY_RETURN:
-// CONSIDER_INPUT();
-// if (tab->move.active) {
-// tab->move.active = false;
-// } else if (tab->selected != tab->list->current) {
-// camu_client_skipto(&ui->c->client, &tab->list->name, tab->selected);
-// }
-// break;
-// case NCKEY_BUTTON1:
-// CONSIDER_INPUT();
-// if (input->x < 0 || input->y < 0) return false;
-// u32 x = (u32)input->x;
-// u32 y = (u32)input->y;
-// ui->last_mouse_x = x;
-// ui->last_mouse_y = y;
-// u64 now = nn_get_timestamp();
-// u64 last = ui->last_mouse_ts;
-// if (last != (u64)-1 && now - last < 100000) {
-// return false;
-// }
-// ui->last_mouse_ts = now;
-// u32 width = ncplane_dim_x(ui->lp.n);
-// u32 height = ncplane_dim_y(ui->lp.n);
-// if (x >= 2 && x <= width && y > height - 3) { // @TODO: Make this defined, more to nowplaying.
-// struct cmc_list_entry *entry = al_array_at(tab->list->entries, tab->list->current);
-// u64 pos = ((x - 2) / (f64)(width - 2)) * entry->duration;
-// camu_client_seek(&ui->c->client, &tab->list->name, entry->id, pos);
-// }
-// break;
-// }
-//
-// return true;
-//}
-//
-//void cmc_lp_erase(struct cmc_ui *ui)
-//{
-// ncplane_erase(ui->lp.n);
-//}
-//
-//static void render_list_entries(struct cmc_ui *ui, struct cmc_list_tab *tab, s32 max_height)
-//{
-// struct cmc_list *list = tab->list;
-// struct ncplane *n = ui->lp.n;
-// s32 tracked = tab->move.active ? tab->move.selected + tab->move.offset : tab->selected;
-// s32 count = (s32)list->entries.count;
-// s32 midpoint = max_height / 2;
-// s32 rounded = (max_height + 1) / 2;
-// s32 top, end;
-// if (tracked < midpoint) {
-// top = 0;
-// end = MIN(top + max_height, count);
-// } else if (tracked + rounded >= count) {
-// top = count - max_height;
-// end = count;
-// } else {
-// top = tracked - midpoint;
-// end = tracked + rounded;
-// }
-// s32 index;
-// struct cmc_list_entry *entry;
-// for (s32 i = top; i < end; i++) {
-// index = i;
-// if (tab->move.active) {
-// if (index == tracked) {
-// index = tab->move.selected;
-// } else {
-// if (index > tracked) index--;
-// if (index >= tab->move.selected) index++;
-// }
-// }
-// entry = al_array_at(list->entries, index);
-// bool on_current = list->current >= 0 && index == list->current;
-// bool styled = i == tracked;
-// if (styled) {
-// ncplane_set_styles(n, NCSTYLE_BOLD);
-// ncplane_set_fg_palindex(n, 4);
-// }
-// s32 y = i - top;
-// u32 length = entry->name.length;
-// if (tab->move.active && i == tracked) {
-// cmc_ui_putnwstr_yx(n, y, 0, length, &entry->name);
-// } else if (on_current) {
-// ncplane_putchar_yx(n, y, 1, '>');
-// cmc_ui_putnwstr_yx(n, y, 3, length, &entry->name);
-// } else {
-// cmc_ui_putnwstr_yx(n, y, 1, length, &entry->name);
-// }
-// if (styled) {
-// ncplane_set_styles(n, NCSTYLE_NONE);
-// ncplane_set_fg_default(n);
-// }
-// }
-//}
-//
//static void render_now_playing(struct cmc_ui *ui, struct cmc_list_tab *tab, u32 y, u32 height)
//{
// struct ncplane *n = ui->lp.n;
@@ -676,15 +454,3 @@ void cmc_list_pane_render(struct cmc_list_pane *lp)
// ncplane_cursor_move_yx(n, y, 0);
// ncplane_light_box(n, NCSTYLE_NONE, c, height - 1, width - 1, 0);
//}
-//
-//void cmc_lp_render(struct cmc_ui *ui)
-//{
-// u32 height = ncplane_dim_y(ui->lp.n);
-//
-// if (ui->lp.tabs.count <= ui->lp.current) return;
-// struct cmc_list_tab *tab = &al_array_at(ui->lp.tabs, ui->lp.current);
-//
-// tab->page_length = height - 9;
-// render_list_entries(ui, tab, tab->page_length);
-// render_now_playing(ui, tab, tab->page_length, height);
-//}
diff --git a/src/fruits/cmc/ui/widgets/now_playing.c b/src/fruits/cmc/ui/widgets/now_playing.c
index ebb8361..a90c7e0 100644
--- a/src/fruits/cmc/ui/widgets/now_playing.c
+++ b/src/fruits/cmc/ui/widgets/now_playing.c
@@ -145,7 +145,9 @@ void cmc_now_playing_render(struct cmc_now_playing *np, struct cmc_list_entry *e
ncplane_putstr_yx(np->n, 1, 4 + offset, "⎹");
if (entry) {
- cmc_ui_putnwstr_yx(np->n, 1, 6 + offset, MIN(entry->name.length, (u32)60), &entry->name);
+ char *c_str = al_str_to_c_str(&entry->brief);
+ ncplane_putnstr_yx(np->n, 1, 6 + offset, MIN(al_strlen(c_str), (size_t)60), c_str);
+ al_free(c_str);
}
bool show_remaining = false;
diff --git a/src/fruits/cmsrv/cmsrv.c b/src/fruits/cmsrv/cmsrv.c
index e830831..ca65beb 100644
--- a/src/fruits/cmsrv/cmsrv.c
+++ b/src/fruits/cmsrv/cmsrv.c
@@ -57,22 +57,6 @@ static u8 server_line_callback(void *userdata, str *line)
//lia_list_clear(list);
} else {
struct nn_packet *packet = nn_packet_create();
-#ifdef CAMU_HAVE_PORTAL
- if (al_str_at(line, 0) == ';' || camu_is_url(line, 0)) {
- nn_packet_write_u8(packet, CAMU_RESOURCE_SIMPLE_SEARCH);
-#elif defined NAUNET_HAS_CURL
- if (camu_is_url(line, 0)) {
- nn_packet_write_u8(packet, CAMU_RESOURCE_HTTP);
-#else
- if (0) {
-#endif
-#if CACHE_HAVE_CDIO
- } else if (al_str_cmp(line, &al_str_c("cdda://"), 0, 7) == 0) {
- nn_packet_write_u8(packet, CAMU_RESOURCE_CDIO);
-#endif
- } else {
- nn_packet_write_u8(packet, CAMU_RESOURCE_FILE);
- }
nn_packet_write_str(packet, line);
camu_server_local_add(&s->server, packet);
}
diff --git a/src/fruits/cmsrv/ui.c b/src/fruits/cmsrv/ui.c
index 0a41bfc..c8e9dcb 100644
--- a/src/fruits/cmsrv/ui.c
+++ b/src/fruits/cmsrv/ui.c
@@ -156,15 +156,16 @@ static void render_lists(struct cmsrv_ui *ui)
ncplane_putchar_yx(n, y, 1, '>');
x += 2;
}
- wstr *name = &entry->name;
#ifdef CAMU_HAVE_PORTAL
struct camu_resource *resource = (struct camu_resource *)entry->opaque;
if (resource->type == CAMU_RESOURCE_PORTAL) {
- struct camu_resource_portal *portal = (struct camu_resource_portal *)resource;
- name = &portal->post->title;
+ putnwstr_maxwidth_yx(n, y, x, max_width - x, &resource->post->title);
+ } else {
+ char *c_str = al_str_to_c_str(&entry->brief);
+ ncplane_putstr_yx(n, y, x, c_str);
+ al_free(c_str);
}
#endif
- putnwstr_maxwidth_yx(n, y, x, max_width - x, name);
if (++current_line >= max_height) break;
}
}
diff --git a/src/fruits/cmv/cmv.c b/src/fruits/cmv/cmv.c
index 2dda139..1b9c8c1 100644
--- a/src/fruits/cmv/cmv.c
+++ b/src/fruits/cmv/cmv.c
@@ -185,22 +185,6 @@ s32 window_system_main(u32 argc, str *argv, void *extra)
if (local) {
for (u32 i = 1; i < argc; i++) {
struct nn_packet *packet = nn_packet_create();
-#if defined CAMU_HAVE_PORTAL
- if (al_str_at(&argv[i], 0) == ';' || camu_is_url(&argv[i], 0)) {
- nn_packet_write_u8(packet, CAMU_RESOURCE_SIMPLE_SEARCH);
-#elif defined NAUNET_HAS_CURL
- if (camu_is_url(&argv[i], 0)) {
- nn_packet_write_u8(packet, CAMU_RESOURCE_HTTP);
-#else
- if (0) {
-#endif
-#if CACHE_HAVE_CDIO
- } else if (al_str_cmp(&argv[i], &al_str_c("cdda://"), 0, 7) == 0) {
- nn_packet_write_u8(packet, CAMU_RESOURCE_CDIO);
-#endif
- } else {
- nn_packet_write_u8(packet, CAMU_RESOURCE_FILE);
- }
nn_packet_write_str(packet, &argv[i]);
camu_server_local_add(&c.server, packet);
nn_packet_free(packet);
diff --git a/src/liana/list.c b/src/liana/list.c
index 0ceb591..fd58ea1 100644
--- a/src/liana/list.c
+++ b/src/liana/list.c
@@ -407,7 +407,7 @@ static void handle_seek(struct lia_list *list, s32 sequence, u32 id, u64 pos)
al_assert(entry);
if (entry->duration == 0 || entry->duration == LIANA_TIMESTAMP_INVALID) {
- log_warn("Skipping seek on entry with no duration.");
+ log_warn("Skipping seek on entry with unknown or 0 duration.");
return;
}
@@ -451,6 +451,11 @@ static bool handle_end(struct lia_list *list, u32 id, u32 reset_id)
if (sequence < 0) return true;
struct lia_list_entry *entry = get_entry_from_sequence(list, sequence);
+ // Looping:
+ // - Main issue is rolling back an entry that skipped onto queued
+ // before it's looping state was synced.
+ // - Maybe we can track which sink END comes from.
+
if (reset_id != entry->reset_id) {
log_warn("Got end() with out of order or incorrect reset id, ignoring.");
return true;
@@ -689,7 +694,7 @@ void lia_list_remove_sink(struct lia_list *list, void *userdata)
handle_remove_sink(list, userdata);
}
-void lia_list_add(struct lia_list *list, void *opaque, u64 duration, wstr *name)
+void lia_list_add(struct lia_list *list, void *opaque, u64 duration, str *brief)
{
struct lia_list_entry *entry = al_alloc_object(struct lia_list_entry);
entry->opaque = opaque;
@@ -701,7 +706,7 @@ void lia_list_add(struct lia_list *list, void *opaque, u64 duration, wstr *name)
entry->ended = false;
entry->reset_id = get_incremental_id(list);
entry->duration = duration;
- al_wstr_clone(&entry->name, name);
+ al_str_clone(&entry->brief, brief);
entry->list = list;
struct lia_list_cmd *cmd = al_alloc_object(struct lia_list_cmd);
cmd->op = ADD;
@@ -824,7 +829,7 @@ void lia_list_free(struct lia_list *list)
if (list->cmd) al_free(list->cmd);
struct lia_list_entry *entry;
al_array_foreach(list->entries, i, entry) {
- al_wstr_free(&entry->name);
+ al_str_free(&entry->brief);
al_free(entry);
}
al_array_free(list->entries);
diff --git a/src/liana/list.h b/src/liana/list.h
index 86a9712..36d95a4 100644
--- a/src/liana/list.h
+++ b/src/liana/list.h
@@ -82,7 +82,7 @@ struct lia_list_entry {
bool ended;
u32 reset_id;
u64 duration;
- wstr name;
+ str brief;
struct lia_list *list;
};
@@ -136,7 +136,7 @@ void lia_list_pump(struct lia_list *list);
void lia_list_add_sink(struct lia_list *list, void (*callback)(void *, u8, struct lia_list_entry *, s32, struct lia_timing *), void *userdata);
void lia_list_remove_sink(struct lia_list *list, void *userdata);
-void lia_list_add(struct lia_list *list, void *opaque, u64 duration, wstr *name);
+void lia_list_add(struct lia_list *list, void *opaque, u64 duration, str *brief);
void lia_list_unset(struct lia_list *list);
void lia_list_skipto(struct lia_list *list, s32 sequence, s32 i);
void lia_list_skip(struct lia_list *list, s32 sequence, s32 n);
diff --git a/src/liana/list_cmp.h b/src/liana/list_cmp.h
index 556f5dd..ee9b3f8 100644
--- a/src/liana/list_cmp.h
+++ b/src/liana/list_cmp.h
@@ -6,38 +6,38 @@
AL_IGNORE_WARNING("-Wunused-function")
-static void camu_db_num_from_path(wstr *path, s64 *id, s64 *index)
+static void camu_db_num_from_path(str *path, s64 *id, s64 *index)
{
- u32 last_slash = al_wstr_rfind(path, L'/');
+ u32 last_slash = al_str_rfind(path, '/');
if (last_slash == AL_WSTR_NPOS) {
return;
}
- wstr sub = al_wstr_substr(path, last_slash + 1, path->length);
+ str sub = al_str_substr(path, last_slash + 1, path->length);
// Skip 2 '_' characters.
- if (!(al_wstr_tok(&sub, L'_') && al_wstr_tok(&sub, L'_'))) {
+ if (!(al_str_tok(&sub, '_') && al_str_tok(&sub, '_'))) {
return;
}
- u32 target = al_wstr_find(&sub, L'_');
+ u32 target = al_str_find(&sub, '_');
if (target == AL_WSTR_NPOS) {
return;
}
- sub = al_wstr_substr(&sub, 0, target);
+ sub = al_str_substr(&sub, 0, target);
bool error;
- s64 num = al_wstr_to_long(&sub, 10, &error);
+ s64 num = al_str_to_long(&sub, 10, &error);
if (error) return;
*id = num;
- u32 ext_dot = al_wstr_rfind(path, L'.');
- u32 a_of_media = al_wstr_rfind(path, 'a');
+ u32 ext_dot = al_str_rfind(path, '.');
+ u32 a_of_media = al_str_rfind(path, 'a');
if (ext_dot == AL_WSTR_NPOS || a_of_media == AL_WSTR_NPOS) {
return;
}
- sub = al_wstr_substr(path, a_of_media + 1, ext_dot);
+ sub = al_str_substr(path, a_of_media + 1, ext_dot);
- num = al_wstr_to_long(&sub, 10, &error);
+ num = al_str_to_long(&sub, 10, &error);
if (error) return;
*index = num;
}
@@ -48,8 +48,9 @@ static s32 camu_db_compare(const void *a, const void *b)
struct lia_list_entry *bb = *((struct lia_list_entry **)b);
s64 a_id = -1, a_index = -1;
s64 b_id = -1, b_index = -1;
- camu_db_num_from_path(&aa->name, &a_id, &a_index);
- camu_db_num_from_path(&bb->name, &b_id, &b_index);
+ // Assuming brief is the file path.
+ camu_db_num_from_path(&aa->brief, &a_id, &a_index);
+ camu_db_num_from_path(&bb->brief, &b_id, &b_index);
if (a_id == b_id) {
if (a_index > b_index) return 1;
else if (a_index < b_index) return -1;
diff --git a/src/liana/server.c b/src/liana/server.c
index acb5f31..cc4ceed 100644
--- a/src/liana/server.c
+++ b/src/liana/server.c
@@ -1,7 +1,6 @@
#include "server.h"
#include "handler.h"
#include "handlers.h"
-#include "process.h"
#include "list.h"
static inline u32 get_incremental_id(struct lia_server *server)
@@ -443,13 +442,6 @@ static void duration_signal_callback(void *userdata)
nn_signal_stop(&node->signal);
nn_thread_join(&node->thread);
node->handler->free(&node->handler);
- bool got_visual_data = false;
- struct lia_visual_data visual_data;
-#if 0 // @TODO: This is obviously bad because it blocks the event loop.
- // Evaluate this when getting around to http/hls stream fixes.
- cch_handle_seek(&node->handle, 0, SEEK_SET);
- got_visual_data = lia_prepare_visual_data(&node->handle, &visual_data);
-#endif
cch_entry_return_handle(node->entry, &node->handle);
if (should_free_node(node)) {
free_node(node);
@@ -461,9 +453,6 @@ static void duration_signal_callback(void *userdata)
node->callback(node->userdata, LIANA_NODE_DURATION, &node->duration);
}
}
- if (got_visual_data) {
- node->callback(node->userdata, LIANA_NODE_VISUAL_DATA, &visual_data);
- }
}
void lia_node_get_duration(struct lia_node *node)
diff --git a/src/liana/server.h b/src/liana/server.h
index 55a581e..8035ee1 100644
--- a/src/liana/server.h
+++ b/src/liana/server.h
@@ -27,7 +27,6 @@ struct lia_node_connection {
enum {
LIANA_NODE_DURATION = 0,
- LIANA_NODE_VISUAL_DATA,
LIANA_NODE_ERRORED
};
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index c95096d..392f6b9 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -1530,6 +1530,7 @@ static void identify_callback(void *userdata, struct nn_rpc_connection *conn, st
{
struct camu_sink *sink = (struct camu_sink *)userdata;
(void)sink;
+ // @TODO: Send sink id from list.
nn_packet_stream_return_packet(conn->stream, packet);
}
diff --git a/src/server/common.h b/src/server/common.h
index 9cb9501..5b27768 100644
--- a/src/server/common.h
+++ b/src/server/common.h
@@ -39,20 +39,6 @@ enum {
CAMU_LIST_END
};
-enum {
- CAMU_RESOURCE_FILE = 0,
-#ifdef NAUNET_HAS_CURL
- CAMU_RESOURCE_HTTP,
-#endif
-#ifdef CACHE_HAVE_CDIO
- CAMU_RESOURCE_CDIO,
-#endif
-#ifdef CAMU_HAVE_PORTAL
- CAMU_RESOURCE_PORTAL,
- CAMU_RESOURCE_SIMPLE_SEARCH
-#endif
-};
-
static inline bool camu_is_url(str *s, u32 i)
{
return al_str_cmp(s, &al_str_c("https://"), i, 8) == 0
diff --git a/src/server/resource.h b/src/server/resource.h
index 3158479..cdbb42e 100644
--- a/src/server/resource.h
+++ b/src/server/resource.h
@@ -1,7 +1,21 @@
#pragma once
#include "../liana/server.h"
-#include "../liana/process.h"
+#include "../portal/src/post.h"
+
+enum {
+ CAMU_RESOURCE_FILE = 0,
+#ifdef NAUNET_HAS_CURL
+ CAMU_RESOURCE_HTTP,
+#endif
+#ifdef CACHE_HAVE_CDIO
+ CAMU_RESOURCE_CDIO,
+#endif
+#ifdef CAMU_HAVE_PORTAL
+ CAMU_RESOURCE_PORTAL,
+ CAMU_RESOURCE_SIMPLE_SEARCH
+#endif
+};
struct camu_resource {
u8 type;
@@ -10,28 +24,8 @@ struct camu_resource {
struct lia_node *node;
u64 duration;
u32 ref;
- array(struct lia_list_entry *) pending;
- struct lia_visual_data visual_data;
- struct camu_server *server;
-};
-
-struct camu_resource_file {
- struct camu_resource r;
- str path;
-};
-
-struct camu_resource_http {
- struct camu_resource r;
- str url;
-};
-
-struct camu_resource_portal {
- struct camu_resource r;
- struct camu_post *post;
+ str uri;
u32 index;
-};
-
-struct camu_resource_cdio {
- struct camu_resource r;
- u32 track;
+ struct camu_post *post;
+ array(struct lia_list_entry *) pending;
};
diff --git a/src/server/server.c b/src/server/server.c
index 0275147..6f8da78 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -12,7 +12,6 @@
#ifdef CAMU_HAVE_PORTAL
#include "../portal/src/packet_ext.h"
#endif
-#include "../codec/packet_ext.h"
#include "server.h"
#include "common.h"
@@ -79,7 +78,7 @@ static void write_list_entry(struct lia_list_entry *entry, struct nn_packet *pac
nn_packet_write_u64(packet, entry->start);
nn_packet_write_u64(packet, entry->paused_at);
nn_packet_write_u64(packet, entry->offset);
- nn_packet_write_wstr(packet, &entry->name);
+ nn_packet_write_str(packet, &entry->brief);
}
static void write_initial_user_state(struct camu_server *server, struct camu_user *user, struct nn_packet *packet)
@@ -236,20 +235,6 @@ static void process_pending(struct camu_resource *resource)
al_array_free(pending);
}
-static void send_clients_visual_data(struct camu_server *server, u32 node_id, struct lia_visual_data *visual_data)
-{
- struct camu_server_client *client;
- al_array_foreach(server->clients, i, client) {
- struct nn_packet *packet = nn_rpc_get_packet(client->conn->rpc, CAMU_CLIENT_VISUAL_DATA);
- nn_packet_write_u32(packet, node_id);
- nn_packet_write_audio_format(packet, &visual_data->fmt);
- nn_packet_write_u32(packet, visual_data->size);
- NNWT_PACKET_WRITE_DATA(packet, visual_data->samples, visual_data->size);
- nn_packet_write_u32(packet, visual_data->count);
- nn_rpc_connection_command(client->conn, packet, NULL, NULL);
- }
-}
-
static void node_callback(void *userdata, u8 op, void *opaque)
{
struct camu_resource *resource = (struct camu_resource *)userdata;
@@ -260,10 +245,6 @@ static void node_callback(void *userdata, u8 op, void *opaque)
resource->duration = duration;
break;
}
- case LIANA_NODE_VISUAL_DATA: {
- resource->visual_data = *(struct lia_visual_data *)opaque;
- break;
- }
case LIANA_NODE_ERRORED:
resource->load = LIANA_ENTRY_ERRORED;
break;
@@ -359,20 +340,17 @@ static bool prepare_server_resource(struct camu_server *server, struct camu_reso
{
struct cch_entry *entry = NULL;
switch (resource->type) {
- case CAMU_RESOURCE_FILE: {
- struct camu_resource_file *file = (struct camu_resource_file *)resource;
- entry = cch_handler_file_create(&file->path, &al_str_c("codec"));
+ case CAMU_RESOURCE_FILE:
+ entry = cch_handler_file_create(&resource->uri, &al_str_c("codec"));
if (!entry) {
resource->load = LIANA_ENTRY_ERRORED;
return false;
}
resource->load = LIANA_ENTRY_PREPARED;
break;
- }
#ifdef NAUNET_HAS_CURL
- case CAMU_RESOURCE_HTTP: {
- struct camu_resource_http *http = (struct camu_resource_http *)resource;
- entry = cch_handler_http_create(&http->url, server->loop);
+ case CAMU_RESOURCE_HTTP:
+ entry = cch_handler_http_create(&resource->uri, server->loop);
if (!entry) {
resource->load = LIANA_ENTRY_ERRORED;
return false;
@@ -380,34 +358,29 @@ static bool prepare_server_resource(struct camu_server *server, struct camu_reso
entry->handler->maybe_spawn_worker(entry->handler, 0);
resource->load = LIANA_ENTRY_PREPARED;
break;
- }
#endif
#ifdef CAMU_HAVE_PORTAL
- case CAMU_RESOURCE_PORTAL: {
- struct camu_resource_portal *portal = (struct camu_resource_portal *)resource;
- entry = entry_from_post(server, portal->post, portal->index);
+ case CAMU_RESOURCE_PORTAL:
+ entry = entry_from_post(server, resource->post, resource->index);
if (!entry) {
resource->load = LIANA_ENTRY_ERRORED;
return false;
}
resource->load = LIANA_ENTRY_PREPARED;
break;
- }
#endif
#ifdef CACHE_HAVE_CDIO
- case CAMU_RESOURCE_CDIO: {
- struct camu_resource_cdio *cdio = (struct camu_resource_cdio *)resource;
+ case CAMU_RESOURCE_CDIO:
entry = cch_handler_cdio_create();
if (!entry) {
resource->load = LIANA_ENTRY_ERRORED;
return false;
}
- cdio->track = MIN(cdio->track, entry->chapters.count);
- entry->chapter = &al_array_at(entry->chapters, cdio->track);
+ resource->index = MIN(resource->index, entry->chapters.count);
+ entry->chapter = &al_array_at(entry->chapters, resource->index);
entry->handler->maybe_spawn_worker(entry->handler, entry->chapter->start);
resource->load = LIANA_ENTRY_PREPARED;
break;
- }
#endif
}
@@ -479,7 +452,7 @@ static void list_callback(void *userdata, u8 op, struct lia_list_entry *entry, v
case LIANA_META_REMOVED_ENTRY:
break;
case LIANA_META_CURRENT_CHANGED:
- log_info("Now playing: %ls.", entry->name.data);
+ log_info("Now playing: %.*s.", al_str_x(&entry->brief));
send_clients_current_changed(server, list, entry);
break;
case LIANA_META_ORDER_CHANGED:
@@ -492,7 +465,7 @@ static void list_callback(void *userdata, u8 op, struct lia_list_entry *entry, v
send_clients_entry_seeked(server, list, entry);
break;
case LIANA_META_ENTRY_ERRORED:
- log_error("Failed to load: %ls.", entry->name.data);
+ log_error("Failed to load: %.*s.", al_str_x(&entry->brief));
break;
}
break;
@@ -594,9 +567,7 @@ static bool client_command_callback(void *userdata, struct nn_rpc_connection *co
case CAMU_CLIENT_REQUEST_VISUAL_DATA: {
u32 node_id = nn_packet_read_u32(packet);
struct camu_resource *resource = get_resource_by_node_id(server, node_id);
- if (resource) {
- send_clients_visual_data(resource->server, resource->node->id, &resource->visual_data);
- }
+ (void)resource;
break;
}
}
@@ -611,20 +582,19 @@ out:
static void simple_search_portal_callback(void *userdata0, void *userdata1, struct camu_portal_result *result)
{
struct camu_server *server = (struct camu_server *)userdata0;
- struct camu_resource_portal *portal = (struct camu_resource_portal *)userdata1;
- struct camu_resource *resource = (struct camu_resource *)portal;
+ struct camu_resource *resource = (struct camu_resource *)userdata1;
switch (result->op) {
case CAMU_CLIENT_CREATE_SEARCH:
if (result->id == -1) {
break;
}
- camu_portal_get_page(&server->bridge, result->id, 0, simple_search_portal_callback, portal);
+ camu_portal_get_page(&server->bridge, result->id, 0, simple_search_portal_callback, resource);
return;
case CAMU_CLIENT_GET_PAGE:
if (result->id == -1 || !result->page || !result->page->list.count) {
break;
}
- portal->post = camu_post_cache_get(&server->cache, &al_array_at(result->page->list, 0));
+ resource->post = camu_post_cache_get(&server->cache, &al_array_at(result->page->list, 0));
resource->type = CAMU_RESOURCE_PORTAL;
prepare_server_resource(server, resource);
process_pending(resource);
@@ -636,31 +606,41 @@ static void simple_search_portal_callback(void *userdata0, void *userdata1, stru
}
#endif
+static u8 parse_resource_type(str *line)
+{
+#if defined CAMU_HAVE_PORTAL
+ if (al_str_at(line, 0) == ';' || camu_is_url(line, 0)) {
+ return CAMU_RESOURCE_SIMPLE_SEARCH;
+#elif defined NAUNET_HAS_CURL
+ if (camu_is_url(line, 0)) {
+ return CAMU_RESOURCE_HTTP;
+#else
+ if (0) {
+#endif
+#if CACHE_HAVE_CDIO
+ } else if (al_str_cmp(line, &al_str_c("cdda://"), 0, 7) == 0) {
+ return CAMU_RESOURCE_CDIO;
+#endif
+ } else {
+ return CAMU_RESOURCE_FILE;
+ }
+}
+
static void handle_add_command(struct camu_server *server, struct lia_list *list, struct nn_packet *packet)
{
- u8 op = nn_packet_read_u8(packet);
- struct camu_resource *resource = NULL;
- wstr name;
- switch (op) {
+ struct camu_resource *resource = al_alloc_object(struct camu_resource);
+ str line;
+ nn_packet_read_str(packet, &line);
+ switch (parse_resource_type(&line)) {
case CAMU_RESOURCE_FILE: {
- str path;
- nn_packet_read_str(packet, &path);
- struct camu_resource_file *file = al_alloc_object(struct camu_resource_file);
- al_str_clone(&file->path, &path);
- al_wstr_from_str(&name, &path);
- resource = (struct camu_resource *)file;
+ al_str_clone(&resource->uri, &line);
resource->type = CAMU_RESOURCE_FILE;
resource->load = LIANA_ENTRY_UNLOADED;
break;
}
#ifdef NAUNET_HAS_CURL
case CAMU_RESOURCE_HTTP: {
- str url;
- nn_packet_read_str(packet, &url);
- struct camu_resource_http *http = al_alloc_object(struct camu_resource_http);
- al_str_clone(&http->url, &url);
- al_wstr_from_str(&name, &url);
- resource = (struct camu_resource *)http;
+ al_str_clone(&resource->uri, &line);
resource->type = CAMU_RESOURCE_HTTP;
resource->load = LIANA_ENTRY_UNLOADED;
break;
@@ -668,20 +648,15 @@ static void handle_add_command(struct camu_server *server, struct lia_list *list
#endif
#ifdef CACHE_HAVE_CDIO
case CAMU_RESOURCE_CDIO: {
- str url;
- nn_packet_read_str(packet, &url);
u32 track = 0;
- if (url.length > 7) {
+ if (line.length > 7) {
bool error;
- s64 index = al_str_to_long(&al_str_substr(&url, 7, url.length), 10, &error);
+ s64 index = al_str_to_long(&al_str_substr(&line, 7, line.length), 10, &error);
if (!error && index > 0) {
track = (u32)index - 1;
}
}
- struct camu_resource_cdio *cdio = al_alloc_object(struct camu_resource_cdio);
- cdio->track = track;
- al_wstr_from_cstr(&name, "cdio");
- resource = (struct camu_resource *)cdio;
+ resource->index = track;
resource->type = CAMU_RESOURCE_CDIO;
resource->load = LIANA_ENTRY_UNLOADED;
break;
@@ -689,37 +664,30 @@ static void handle_add_command(struct camu_server *server, struct lia_list *list
#endif
#ifdef CAMU_HAVE_PORTAL
case CAMU_RESOURCE_SIMPLE_SEARCH: {
- str search;
- nn_packet_read_str(packet, &search);
- struct camu_resource_portal *portal = al_alloc_object(struct camu_resource_portal);
- portal->post = NULL;
- al_wstr_from_str(&name, &search);
- resource = (struct camu_resource *)portal;
+ resource->post = NULL;
resource->type = CAMU_RESOURCE_SIMPLE_SEARCH;
resource->load = LIANA_ENTRY_PREPARING;
str query;
- if (al_str_at(&search, 0) == ';') { // search.
- al_str_clone(&query, &al_str_substr(&search, 1, search.length));
+ if (al_str_at(&line, 0) == ';') { // search.
+ al_str_clone(&query, &al_str_substr(&line, 1, line.length));
} else {
al_str_from(&query, "link:");
- al_str_cat(&query, &search);
+ al_str_cat(&query, &line);
}
- camu_portal_create_search(&server->bridge, &al_str_c("youtube"), &query,
- simple_search_portal_callback, portal);
+ camu_portal_create_search(&server->bridge, &al_str_c("youtube"),
+ &query, simple_search_portal_callback, resource);
al_str_free(&query);
break;
}
#endif
}
al_assert(resource);
- resource->server = server;
al_array_push(server->data.resources, resource);
resource->entry = NULL;
resource->node = NULL;
resource->duration = LIANA_TIMESTAMP_INVALID;
al_array_init(resource->pending);
- lia_list_add(list, resource, resource->duration, &name);
- al_wstr_free(&name);
+ lia_list_add(list, resource, resource->duration, &line);
}
static bool list_action_callback(void *userdata, struct nn_rpc_connection *conn,
@@ -953,13 +921,12 @@ void camu_server_free(struct camu_server *server)
al_array_free(resource->pending);
switch (resource->type) {
case CAMU_RESOURCE_FILE: {
- struct camu_resource_file *file = (struct camu_resource_file *)resource;
- al_str_free(&file->path);
- al_free(file);
+ al_str_free(&resource->uri);
break;
}
#ifdef NAUNET_HAS_CURL
case CAMU_RESOURCE_HTTP:
+ al_str_free(&resource->uri);
break;
#endif
#ifdef CACHE_HAVE_CDIO
@@ -968,17 +935,14 @@ void camu_server_free(struct camu_server *server)
#endif
#ifdef CAMU_HAVE_PORTAL
case CAMU_RESOURCE_PORTAL: {
- struct camu_resource_portal *portal = (struct camu_resource_portal *)resource;
- al_free(portal);
break;
}
case CAMU_RESOURCE_SIMPLE_SEARCH: {
- struct camu_resource_portal *portal = (struct camu_resource_portal *)resource;
- al_free(portal);
break;
}
#endif
}
+ al_free(resource);
}
al_array_free(server->data.resources);
diff --git a/subprojects/libalabaster.wrap b/subprojects/libalabaster.wrap
index fe96079..6ab8612 100644
--- a/subprojects/libalabaster.wrap
+++ b/subprojects/libalabaster.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/libalabaster
-revision = a5516233d66eb1552cf7fdfe03779b2bf896c82d
+revision = 34a5e136b6c79092eee73857bbad25892fe8047a
depth = 1