{"id":58,"date":"2007-01-06T05:48:38","date_gmt":"2007-01-06T03:48:38","guid":{"rendered":"http:\/\/katastrophos.net\/andre\/blog\/2007\/01\/06\/%e2%80%9cyet-another-zaurus-media-player%e2%80%9d%e2%80%a6-done-differently-phase-21-development-progress-2\/"},"modified":"2007-09-23T13:32:42","modified_gmt":"2007-09-23T11:32:42","slug":"%e2%80%9cyet-another-zaurus-media-player%e2%80%9d%e2%80%a6-done-differently-phase-21-development-progress-2","status":"publish","type":"post","link":"https:\/\/katastrophos.net\/andre\/blog\/2007\/01\/06\/%e2%80%9cyet-another-zaurus-media-player%e2%80%9d%e2%80%a6-done-differently-phase-21-development-progress-2\/","title":{"rendered":"\u00e2\u20ac\u0153Yet Another Zaurus Media Player\u00e2\u20ac\u009d\u00e2\u20ac\u00a6 done differently . (Phase 2.1: Development progress 2)"},"content":{"rendered":"<p>Yet another short update on the development of my <em>still untitled<\/em> media player for the Zaurus. In the meantime it&#8217;s called YAZMP.<\/p>\n<p>Again, I&#8217;ve been working on improving performance &#8211; this time on the performance when loading playlists.<br \/>\nBefore I continue, let me give a brief overview of the structure:<\/p>\n<p><strong><strike>Library<\/strike> -> Playlists < -> Media Cache<\/strong><\/p>\n<p>YAZMP doesn&#8217;t manage a library similar to i*Tunes. Instead it solely relies on playlists. Metadata (title, artist, album, etc.) is kept in the database and will be associated to once the playlist is loaded. The reason for this is pretty simple: Scanning audio files (and media files in general) each and every time a playlist is loaded will definitely take a lot of time. So, for every file the gathered metadata will be saved in the DB. Think of it as a cache.<br \/>\n<!--more--><br \/>\n<a href=\"http:\/\/atty.skr.jp\/zplayer\/\" target=\"_blank\">ZPlayer<\/a>, which YAZMP is based on, follows a similar approach. However, in my opinion it&#8217;s ill-conceived in the current version. It keeps the media cache in memory all the time. With lots of files this can get very expensive. Also, ZPlayer isn&#8217;t very efficient at saving the media cache to disk. It does so for the whole cache, not just changed entries. Furthermore, when saving the cache on quitting, ZPlayer sometimes simply truncates the cache file and all metadata is lost. So, next time you open it, it has to scan all files again&#8230;<br \/>\nPerhaps I&#8217;m nitpicking, perhaps I&#8217;m an optimization dork, whatever it is, please forgive my harsh words for the otherwise excellent work atty and Atmark have done in ZPlayer!<\/p>\n<p><strong>Technical ramblings<\/strong><\/p>\n<p>For YAZMP I rewrote the metadata cache management from scratch and extended it to do a lot more.<br \/>\nAs I already mentioned above, YAZMP doesn&#8217;t manage any library, it only knows about filenames in playlist files.<br \/>\nFor that to work correctly and efficient especially in combination with the media cache, there are four cases that need to be taken care of:<\/p>\n<ol>\n<li><strong>A new file has been added to the playlist which doesn&#8217;t exist in the media cache.<\/strong><br \/>\nThis case is very simple: The player has to scan the file no matter what.<\/li>\n<li><strong>A file has been physically removed and it&#8217;s metadata is still in the cache.<\/strong><br \/>\nIn this case the player has junk entries in its database, which need to be cleaned once in a while.<\/li>\n<li><strong>A file has been renamed, that is, it&#8217;s basename changed<\/strong><\/li>\n<li><strong>A file or a whole directory \/ sub-tree of files have moved to another location.<\/strong><br \/>\nIn a naive implementation both cases would trigger a lengthy rescan of the file(s) that are about to be added in the playlist, because new location can&#8217;t be associated with any data in the cache.<br \/>\nFor YAZMP I&#8217;ll use several approaches + fallbacks to try to limit the impact. The following messy pseudo-code illustrates this:<\/p>\n<pre>\r\n  <strong>get attributes<\/strong> <em>filename<\/em>, <em>directory<\/em> <strong> from current playlist item to be added<\/strong>\r\n  <strong>get attributes<\/strong> <em>filesize<\/em>, <em>lastmoddate<\/em> <strong>for file<\/strong> (<em>directory<\/em> + <em>filename<\/em>)\r\n\r\n  <font color=\"#080\">\/\/ NOTE: check via checksum \/ hash for faster lookup<\/font>\r\n  <strong>if exactly one<\/strong> entry <strong>by hash value of<\/strong> (<em>directory<\/em> + <em>filename<\/em>) <strong>in DB then<\/strong>\r\n    <font color=\"#080\">\/\/ NOTE: did the file change since the last scan?<\/font>\r\n    <strong>if<\/strong> entry.filesize <strong>not equal<\/strong> <em>filesize<\/em> <strong>or<\/strong> entry.lastmoddate <strong>not equal<\/strong> <em>lastmoddate<\/em> <strong>in FS then<\/strong>\r\n      <strong>rescan file<\/strong> (<em>directory<\/em> + <em>filename<\/em>) <strong>and update<\/strong> entry <strong>in DB<\/strong>\r\n    <strong>return<\/strong> entry\r\n  <strong>else if exactly one<\/strong> entry <strong>by tuple of<\/strong> (<em>filename<\/em>, <em>filesize<\/em>, <em>lastmoddate<\/em>) <strong>in DB then<\/strong>\r\n    <font color=\"#080\">\/\/ Was the file moved to a new directory? (case 4)\r\n    \/\/ If so, check for other files in the old directory and change them\r\n    \/\/ to the new location...\r\n    \/\/ NOTE: this is purely predictive. It might not always yield a benefit.<\/font>\r\n    <strong>if directory by value of<\/strong> entry.location <strong>not in FS then<\/strong>\r\n      <strong>change every<\/strong> siblingentry <strong>in DB where<\/strong> siblingentry.location <strong>is<\/strong> entry.location\r\n        <strong>set<\/strong> siblingentry.location to <em>directory<\/em>\r\n        <strong>set<\/strong> siblingentry.locationHash <strong>to hash value of<\/strong> (<em>directory<\/em> + siblingentry.filename)\r\n      <strong>until no<\/strong> siblingentry <strong>is left<\/strong>\r\n    <font color=\"#080\">\/\/ NOTE: did the file change since the last scan?<\/font>\r\n    <strong>if<\/strong> entry.filesize <strong>not equal<\/strong> <em>filesize<\/em> <strong>or<\/strong> entry.lastmoddate <strong>not equal<\/strong> <em>lastmoddate<\/em> <strong>in FS then<\/strong>\r\n      <strong>rescan file<\/strong> (<em>directory<\/em> + <em>filename<\/em>) <strong>and update<\/strong> entry <strong>in DB<\/strong>\r\n    <strong>return<\/strong> entry\r\n  <font color=\"#080\">\/\/ NOTE: For case 3 one could check for a smaller tuple of (filesize, lastmoddate)<\/font>\r\n  <font color=\"#080\">\/\/ which would result in the almost the same code as above<\/font>\r\n  <font color=\"#080\">\/\/ The likeness of wrong results is a lot higher though.<\/font>\r\n  <strong>else<\/strong>\r\n    <strong>scan file<\/strong> (<em>directory<\/em> + <em>filename<\/em>) <strong>as<\/strong> newentry <strong>and add<\/strong> newentry <strong>to DB<\/strong>\r\n    <strong>return<\/strong> newentry\r\n<\/pre>\n<\/li>\n<\/ol>\n<p><strong>Conclusion<\/strong><\/p>\n<p>What that means is pretty simple: You can move your media files to a different location, perhaps a SD-Card, and the player will still be able to associate the new location in the playlist file with the data it has in cache. And that means: No useless scanning of files and fast playlist load times.<\/p>\n<p><strong>Yet another feature&#8230;<\/strong><\/p>\n<p>I tend to copy several albums to my Zaurus for listening on the road. So, once I&#8217;ve copied new stuff, it&#8217;s always the same mindless and inconvenient procedure in most players: Clear the playlist, add files or directories, play stuff.<br \/>\nAnd that brings me straight to another new feature: Dynamic Playlists<br \/>\nIn general the idea is pretty simple: Instead of just listing every file&#8217;s location in a playlist file, one may also list folders that are automatically scanned for audio or video files. Here is an example:<\/p>\n<p>Music.dynplaylist<\/p>\n<pre>\r\n\/hdd3\/Music\/test1.mp3\r\n\/mnt\/card\/Music\/Mozart\/\r\n\/hdd3\/Music\/Beethoven\/**\r\n<\/pre>\n<p>This will add a single file &#8220;test1.mp3&#8221;, all files in &#8220;\/mnt\/card\/Music\/Mozart\/&#8221; and all files in &#8220;\/hdd3\/Music\/Beethoven\/&#8221; including all files in its sub-directories.<\/p>\n<p><strong>Conclusion<\/strong><\/p>\n<p>Instead of adding files, you just define what the layout of the playlist should look like and the player does the rest automatically. It can even rescan for changes. It&#8217;s all wrapped up in a nice UI.<br \/>\nCombined with the flexibility of the metadata cache and the fact that one isn&#8217;t forced into this library thing, I think this makes for a pretty unique solution. What do you think?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Yet another short update on the development of my still untitled media player for the Zaurus. In the meantime it&#8217;s called YAZMP. Again, I&#8217;ve been working on improving performance &#8211; this time on the performance when loading playlists. Before I continue, let me give a brief overview of the structure: Library -> Playlists < -> [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[20,4,11,24,15,6],"class_list":["post-58","post","type-post","status-publish","format-standard","hentry","tag-development","tag-gadgets","tag-linux","tag-quasar","tag-random","tag-zaurus"],"_links":{"self":[{"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/posts\/58"}],"collection":[{"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/comments?post=58"}],"version-history":[{"count":0,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/posts\/58\/revisions"}],"wp:attachment":[{"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/media?parent=58"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/categories?post=58"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/katastrophos.net\/andre\/blog\/wp-json\/wp\/v2\/tags?post=58"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}