"FULL COMBO" NPS + "AMO-RED" App Style

This aims to be the most feature-complete NPS theme available, including dynamic support for Classical music and Audiobooks.

The secondary “Mini-Player” view doubles as a dedicated Chapter player. It displays a secondary progress bar tied to the current chapter.

Latest Changelog can be found here


:play_button: Now Playing Screen:

Full Combo NPS - Celorien - Portrait

Full Combo NPS - Celorien - Landscape

In Portrait, tap the Album Art to toggle “Mini Player” mode! (While in this mode, you’ll have to long-press the Album Artist name to go to their page.)

In Portrait’s “Full Player,” you can tap the bottom string to switch the displayed info.

NPS Details

There’s so many features packed into this (thanks Tolriq!) that I can’t include pictures for everything, but I’ll do my best to explain the highlights! (Or you can just try it out for yourself!)

Still here? Hope you don’t mind reading :nerd_face:

The information displayed is very dynamic, but the positioning is kept mostly fixed. My goal was to show as much useful information as possible, without having the buttons jump around.


SPECIAL FEATURES

Expand

That’s right! Everyone loves some Special Features! This is where the fun comes in:

  • Fully-Featured Queue Info!
    • See pics above for demo
    • Copy/paste version is included below!
  • Mini-Player!
    • Using the new “Toggle bool” feature, we can hide a bunch of strings with just a tap!
    • Multi-line Strings taking too much room? Just toggle them off!
  • Album Name
    • For Singles - if Album Name matches Track Name, hides Album Name
      • Avoids duplicate info
      • Only hides if the track’s Release Type is set to “single”
        • In other words, Michael Jackson’s “Thriller” album name will still display when playing the “Thriller” track
  • Disc Number and Disc Title
    • Only shown when Disc Title is present
      • Avoids useless “Disc 1” showing for your entire library
  • Album Artist vs Performing Artists
    • If they match, only displays one
    • If they don’t match, you’ll see the Album Artist (bold and underlined) on its own line, with the Performing Artists filling the line after
      • BUT
    • If the Album Artist is listed among the Performing Artists, then you’ll see them all together
      • This allows featured artists to be displayed without showing the Album Artist twice!
      • Example 1: two Performing Artists - “Gawr Gura” and “Houshou Marine” have a collaboration unit name of “GuraMarine” (which is the Album Artist)
        • You will see “GuraMarine” on the first line and “Gawr Gura, Houshou Marine” on the second line!
      • Example 2: Album Artist “Hoshimachi Suisei” has a track on her album featuring additional artist “KAF”
        • You will see “Hoshimachi Suisei, KAF” on a single line!
  • Various Artists - Be gone!
    • If the Album Artist is “Various Artists”, it will be hidden!
    • You will only see the Performing Artists
  • Cover songs
    • If an Original Artist is set, checks to see if the Album Artist is the Original Artist
    • If they don’t match, the artist line will show “Covered by: “ before the names!
    • Alternately: if you set the track’s Release Type to “covers”, this header will be shown without needing an Original Artist
  • Technical Stuff - Minimum Spacing
    • Normally, if you set a string to “At Most 2” lines and it has no content to display, it will disappear (zero lines taken)
    • I added cases which print Braille blanks (U+2800) to ensure that an “empty” string will collapse to a minimum of one line
      • This allows e.g. String 2 to maintain the one-line spacing between the Title and Ratings when empty, while still being able to increase the line count if needed to fit the content!
      • “But wait, there’s more!” This also allows me to still collapse the line completely when a bool is toggled!


Screenshots

In addition to the still shots from the animation above, the “Tag Demo” shows all the fields that can be displayed.


Dark Mode


Light Mode


Tag Demo



…plus fully-featured Queue Info!

Celorien - Queue Info - Split Line

This changes to reflect: repeat, shuffle, Smart Queue, Smart Flow, Sleep Timer, and end-of-queue… And it fits all of that in a normal screen-width!

But wait, there’s more! Now includes chapter info as well. Just look for the ⚐ flag. (Pics coming soon.) Top line shows ⚐ current chapter number / total number of chapters. Bottom row shows the next chapter title when available.

Copy/Paste Ready

Feel free to use these in your own themes! If you want to know how they work, take a peek at the Exploded View section in the post below.

Split-Line Version

This is what you’re seeing in the demo above.

Top Line:

{⠀String by Celorien⠀%|!=%}{--------------------%|!=%}{# Headers%|!=%}{    %|!=%}{## Standard%|!=%}{𝄚𝄞𝄚⠀%|providername==empty || queue.position!=queue.size || player.repeat.mode=="One"%}{    %|!=%}{## End-of-Queue%|!=%}{𝄚𝄚𝄂⠀%|providername!=empty && queue.position==queue.size && player.repeat.mode!="One"%}{--------------------%|!=%}{# Sleep Timer%|!=%}{    %|!=%}{## Immediate Stop%|!=%}{⎅ %sleep.timer|sleep.timer.eos=="false" && sleep.timer.seconds>"0"%}{    %|!=%}{## Finish Track before Stop%|!=%}{⏲ %sleep.timer|sleep.timer.eos=="true" && sleep.timer.seconds>"0"%}{    %|!=%}{## Waiting to Stop%|!=%}{⚠⠀Ending playback⠀⚠%|sleep.timer.seconds=="0"%}{    %|!=%}{## Separator%|!=%}{ • %|sleep.timer.seconds>"0" && (providername!=empty && next.title!=empty)%}{--------------------%|!=%}{# Repeat All enabled, End-of-Queue reached%|!=%}{Restarting Queue%|(providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}{    %|!=%}{## If set, show Queue Name; else ellipsis%|!=%}{: "%queue.name|(queue.name!=empty && queue.name!="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%"}{...%|(queue.name==empty || queue.name=="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}{--------------------%|!=%}{# Queue Position%|!=%}{    %|!=%}{## Standard%|!=%}{Playing %queue.position|(smartqueue=="Disabled" && smartflow=="Disabled" && chapter.count=="0" && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{ / %queue.size|(smartqueue=="Disabled" && smartflow=="Disabled" && chapter.count=="0" && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{    %|!=%}{## Condensed - single-digit values padded to two digits%|!=%}{### Used when Smart Queue/Flow and Sleep Timer are enabled%|!=%}{### Also used when chapters are present%|!=%}{▶ 0%queue.position|queue.position<"10" && ((smartqueue!="Disabled" || smartflow!="Disabled" || chapter.count!="0") && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{▶ %queue.position|queue.position>"9" && ((smartqueue!="Disabled" || smartflow!="Disabled" || chapter.count!="0") && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{--------------------%|!=%}{# Chapter handling%|!=%}{    %|!=%}{## Show chapter index and count%|!=%}{## NOTE: separated by Fraction Slash (U+2044)%|!=%}{ • %|chapter.count!="0" && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{⚐ %chapter.index|chapter.count!="0"%}{⁄%chapter.count|chapter.count!="0"%}{--------------------%|!=%}{# Smart Queue and Flow Indicators%|!=%}{## Condensed indicators used when Sleep Timer is enabled or Chapters are present%|!=%}{ • %|((smartqueue!="Disabled" || smartflow!="Disabled") && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}{Smart %|((smartqueue!="Disabled" || smartflow!="Disabled") && chapter.count=="0" && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}{    %|!=%}{Queue%|(smartqueue!="Disabled" && chapter.count=="0" && sleep.timer.seconds=="-1") && (providername!=empty && next.title!=empty)%}{[Q]%|(smartqueue!="Disabled" && (sleep.timer.seconds>"0" || (sleep.timer.seconds=="-1" && chapter.count!="0"))) && (providername!=empty && next.title!=empty)%}{    %|!=%}{ & %|((smartqueue!="Disabled" && smartflow!="Disabled") && chapter.count=="0" && sleep.timer.seconds=="-1") && (providername!=empty && next.title!=empty)%}{    %|!=%}{Flow%|(smartflow!="Disabled" && chapter.count=="0" && sleep.timer.seconds=="-1") && (providername!=empty && next.title!=empty)%}{[F]%|(smartflow!="Disabled" && (sleep.timer.seconds>"0" || (sleep.timer.seconds=="-1" && chapter.count!="0"))) && (providername!=empty && next.title!=empty)%}

Bottom Line:

{⠀String by Celorien⠀%|!=%}{--------------------%|!=%}{# Headers%|!=%}{    %|!=%}{## Standard%|!=%}{Up Next:⠀%|(player.repeat.mode!="One" && player.shuffle.mode=="false") && (providername!=empty && next.title!=empty) && (chapter.count=="0" || chapter.index==chapter.count)%}{    %|!=%}{## Chapters%|!=%}{Next Ch:⠀%|chapter.count!="0" && chapter.index!=chapter.count%}{    %|!=%}{## Shuffle or Unknown Provider (e.g. Internet Radio)%|!=%}{⠀?????%|(player.repeat.mode!="One" && player.shuffle.mode=="true") && (providername!=empty && next.title!=empty) && (chapter.count=="0" || chapter.index==chapter.count)%}{⠀?????%|providername==empty%⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀}{    %|!=%}{## Repeat One%|!=%}{Repeat:⠀%|player.repeat.mode=="One" && (providername!=empty && next.title!=empty) && (chapter.count=="0" || chapter.index==chapter.count)%}{    %|!=%}{## End-of-Queue%|!=%}{⠀FIN%|(queue.position==queue.size && (providername!=empty && next.title==empty)) && (chapter.count=="0" || chapter.index==chapter.count)%}{--------------------%|!=%}{# Chapter handling%|!=%}{    %|!=%}{%next.chapter.title|chapter.count!="0" && chapter.index!=chapter.count%}{--------------------%|!=%}{# Audiobook Handling%|!=%}{    %|!=%}{%next.title|providername!=empty && (album.type+="audiobook" || isaudiobook=="true") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (chapter.count=="0" || (chapter.count!="0" && chapter.index==chapter.count))%}{ ‖ %next.albumartist|providername!=empty && (album.type+="audiobook" || isaudiobook=="true") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (chapter.count=="0" || (chapter.count!="0" && chapter.index==chapter.count)) && (next.albumartist!=empty && next.albumartist!=albumartist)%}{--------------------%|!=%}{# Music Handling%|!=%}{    %|!=%}{## Show Work name if set, else show Title%|!=%}{%next.work|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work!=empty && (album.type-="audiobook" && isaudiobook=="false")%}{%next.title|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work==empty && (album.type-="audiobook" && isaudiobook=="false")%}{    %|!=%}{## Show Album Artist if not set to "Various Artists", else show Performing Artists%|!=%}{ ‖ %next.albumartist|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist!="Various Artists" && (album.type-="audiobook" && isaudiobook=="false")%}{ ‖ %next.artist|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist=="Various Artists" && (album.type-="audiobook" && isaudiobook=="false")%}{--------------------%|!=%}{#!# EDIT BELOW to fit your tags%|!=%}{# Classical Genre%|!=%}{    %|!=%}{## Show Composer name (only if different from current)%|!=%}{%next.composer|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (next.composer!=empty && next.composer!=composer) && (album.type-="audiobook" && isaudiobook=="false")%'s }{    %|!=%}{## Show Work name (only if different from current)%|!=%}{%next.work|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer!=composer) || ((next.composer!=empty && next.composer==composer) && next.work!=work)) && (work.type-="audiobook" && isaudiobook=="false")%}{    %|!=%}{## Show Movement Number as a Roman numeral (only if Movement name is set and Work matches current)%|!=%}{%next.movement.number.roman|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.number!=empty && next.movement.name!=empty) && (work.type-="audiobook" && isaudiobook=="false")%. }{    %|!=%}{## Show Movement name (only if Work matches current%|!=%}{%next.movement.name|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer==composer) && next.work==work) && (work.type-="audiobook" && isaudiobook=="false")%}{    %|!=%}{## Show Title (only if Work matches but Movement name is empty%|!=%}{%next.title|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.name==empty) && (work.type-="audiobook" && isaudiobook=="false")%}{#!# END OF EDIT ZONE%|!=%}

Single-Line Version

This version is used at the bottom of my Landscape layout. It’s not as pretty, but it shows the same info.

{⠀String by Celorien⠀%|!=%}{--------------------%|!=%}{⠀Mode and Position  %|!=%}{--------------------%|!=%}{# Sleep Timer%|!=%}{    %|!=%}{## Immediate Stop%|!=%}{⎅ %sleep.timer|sleep.timer.eos=="false" && sleep.timer.seconds>"0"%}{    %|!=%}{## Finish Track before Stop%|!=%}{⏲ %sleep.timer|sleep.timer.eos=="true" && sleep.timer.seconds>"0"%}{    %|!=%}{## Waiting to Stop%|!=%}{⚠⠀Ending playback⠀⚠%|sleep.timer.seconds=="0"%}{    %|!=%}{## Separator%|!=%}{ • %|sleep.timer.seconds>"0" && (providername!=empty && next.title!=empty)%}{--------------------%|!=%}{# Repeat All enabled, End-of-Queue reached%|!=%}{Restarting Queue%|(providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}{    %|!=%}{## If set, show Queue Name; else ellipsis%|!=%}{: "%queue.name|(queue.name!=empty && queue.name!="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%"}{...%|(queue.name==empty || queue.name=="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}{--------------------%|!=%}{# Queue Position%|!=%}{    %|!=%}{## Condensed - single-digit values padded to two digits%|!=%}{### Used when Smart Queue/Flow or Sleep Timer are enabled%|!=%}{▶ 0%queue.position|sleep.timer.seconds!="0" && queue.position<"10" && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{▶ %queue.position|sleep.timer.seconds!="0" && queue.position>"9" && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{--------------------%|!=%}{# Chapter handling%|!=%}{    %|!=%}{## Show chapter index and count%|!=%}{## NOTE: separated by Fraction Slash (U+2044)%|!=%}{ • %|chapter.count!="0" && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}{⚐ %chapter.index|chapter.count!="0"%}{⁄%chapter.count|chapter.count!="0"%}{--------------------%|!=%}{# Smart Queue and Flow Indicators%|!=%}{## Always uses condensed indicators%|!=%}{ • %|((smartqueue!="Disabled" || smartflow!="Disabled") && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}{[Q]%|(smartqueue!="Disabled" && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}{[F]%|(smartflow!="Disabled" && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}{--------------------%|!=%}{⠀Next Track Info    %|!=%}{--------------------%|!=%}{# Headers%|!=%}{    %|!=%}{## Standard%|!=%}{ • Up Next:%|sleep.timer.seconds!="0" && player.repeat.mode!="One" && (providername!=empty && next.title!=empty) && (chapter.count=="0" || chapter.index==chapter.count)%}{    %|!=%}{## Chapters%|!=%}{ • Next Ch:%|sleep.timer.seconds!="0" && (chapter.count!="0" && chapter.index!=chapter.count)%}{    %|!=%}{## Shuffle%|!=%}{⠀?????%|sleep.timer.seconds!="0" && ((player.repeat.mode!="One" && player.shuffle.mode=="true") && (providername!=empty && next.title!=empty) && (chapter.count=="0" || chapter.index==chapter.count))%}{    %|!=%}{## Repeat One%|!=%}{ • Repeat:%|sleep.timer.seconds!="0" && player.repeat.mode=="One" && (providername!=empty && next.title!=empty) && (chapter.count=="0" || chapter.index==chapter.count)%}{    %|!=%}{## End-of-Queue%|!=%}{### Bullet Spacer shows when Sleep Timer active%|!=%}{### Split string "\t" keeps Header position fixed when Marquee enabled%|!=%}{ • %|(queue.position==queue.size && (providername!=empty && next.title==empty)) && (chapter.count=="0" || chapter.index==chapter.count) && sleep.timer.seconds>"0"%}{FIN%|(queue.position==queue.size && (providername!=empty && next.title==empty)) && (chapter.count=="0" || chapter.index==chapter.count) && sleep.timer.seconds!="0"%}\t{--------------------%|!=%}{# Chapter Handling%|!=%}{    %|!=%}{%next.chapter.title|sleep.timer.seconds!="0" && (chapter.count!="0" && chapter.index!=chapter.count)%}{--------------------%|!=%}{# Audiobook Handling%|!=%}{    %|!=%}{%next.title|sleep.timer.seconds!="0" && providername!=empty && (album.type+="audiobook" || isaudiobook=="true") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (chapter.count=="0" || (chapter.count!="0" && chapter.index==chapter.count))%}{ • %next.albumartist|sleep.timer.seconds!="0" && providername!=empty && (album.type+="audiobook" || isaudiobook=="true") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (chapter.count=="0" || (chapter.count!="0" && chapter.index==chapter.count)) && (next.albumartist!=empty && next.albumartist!=albumartist)%}{--------------------%|!=%}{# Music Handling%|!=%}{    %|!=%}{## Show Work name if set, else show Title%|!=%}{%next.work|sleep.timer.seconds!="0" && (providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work!=empty && (album.type-="audiobook" && isaudiobook=="false")%}{%next.title|sleep.timer.seconds!="0" && (providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work==empty && (album.type-="audiobook" && isaudiobook=="false")%}{    %|!=%}{## Show Album Artist if not set to "Various Artists", else show Performing Artists%|!=%}{ • %next.albumartist|sleep.timer.seconds!="0" && (providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist!="Various Artists" && (album.type-="audiobook" && isaudiobook=="false")%}{ • %next.artist|sleep.timer.seconds!="0" && (providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist=="Various Artists" && (album.type-="audiobook" && isaudiobook=="false")%}{--------------------%|!=%}{#!# EDIT BELOW to match your tags%|!=%}{# Classical Genre%|!=%}{    %|!=%}{## Show Composer name (only if different from current)%|!=%}{%next.composer|sleep.timer.seconds!="0" && (providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (next.composer!=empty && next.composer!=composer) && (album.type-="audiobook" && isaudiobook=="false")%'s }{    %|!=%}{## Show Work name (only if different from current)%|!=%}{%next.work|sleep.timer.seconds!="0" && (providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer!=composer) || ((next.composer!=empty && next.composer==composer) && next.work!=work)) && (album.type-="audiobook" && isaudiobook=="false")%}{    %|!=%}{## Show Movement Number as a Roman numeral (only if Movement name is set and Work matches current)%|!=%}{%next.movement.number.roman|sleep.timer.seconds!="0" && (providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.number!=empty && next.movement.name!=empty) && (album.type-="audiobook" && isaudiobook=="false")%. }{    %|!=%}{## Show Movement name (only if Work matches current%|!=%}{%next.movement.name|sleep.timer.seconds!="0" && (providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer==composer) && next.work==work) && (album.type-="audiobook" && isaudiobook=="false")%}{    %|!=%}{## Show Title (only if Work matches but Movement name is empty%|!=%}{%next.title|sleep.timer.seconds!="0" && (providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.name==empty) && (album.type-="audiobook" && isaudiobook=="false")%}{#!# END OF EDIT ZONE%|!=%}{--------------------%|!=%}{# Blanks extend tap region when queue is finished%|!=%}{\t⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀%|queue.position==queue.size && (providername!=empty && next.title==empty) && sleep.timer.seconds!="0"%}



:house: App Style

App Style Details

I know the theme is called “AMO-RED,” but all the red parts will actually change color once you start playing music!

Suggested Music - Uses two Smart Playlists to help you pick something to listen to. Check the “Playlist Setup” section below to get this set up.

Dark is the new Black - Make sure not to “Black” mode, as it overrides some of my colors. “Dark” mode has fully black backgrounds in this theme anyway.

Designed as a 2-3 page layout - I left some gaps to hide my phone’s camera cutout, but you’ll probably need to adjust the spacing a bit to suit your phone. Fortunately, the app now makes that pretty easy to do!

Minimal design - I only use the Home Page as a landing screen, so I didn’t want too much on it. There are quick shortcuts to most media options, but the Library tab is where I do my browsing.

Newly-added music page will pop in at the top if you add some new music. If you use my Playlist settings below, the page will also go away once you’ve listened to the new stuff.

Scrollable previews:

Expand

No, the “Recently Added” section isn’t cut off. It scrolls horizontally. (These pictures don’t, of course.)


Media Overviews:

Here we have: Artist, Album, Playlist, and Genre views! The durations on the Artist and Album pages use my localized strings to show a prettier estimate instead of the exact duration.

Dark Mode

Light Mode

Playlist Setup:

For both Recents and Suggested sections

To get you started, I enabled the default “Recently Added” categories. I strongly recommend replacing them with your own Smart Playlists, using the following settings:

Playlist Settings

Same info, however you want to see it.
“Max Item count” can be whatever you prefer.

Text
  • Recently Added Albums
    • (Date Added: within 14)
      • AND
    • (Play Count: less than 1)
      • AND
    • (Track count: greater than 1).
    • Sort by “Date added” with the arrow pointing UP
    • Inherit providers
  • Recently Added Tracks:
    • (Date Added: within 14)
      • AND
    • (Play Count: is 0)
    • Sort by “Date added” with the arrow pointing UP
    • Inherit providers
  • Suggested Albums:
    • GROUP: (Last Played: is missing) OR (Last Played: over 30)
      • AND
    • (Track Count: greater than 1)
    • Sort by “Random”
    • Inherit providers
  • Suggested Tracks:
    • (Last Played: is missing)
      • OR
    • (Last Played: over 30)
    • Sort by “Random”
    • Inherit providers

Images

Once you have your playlists added to the Home page, you can just copy the layout from the existing Recents rows. (There’s new buttons at the bottom, when editing the Home page rows.) Easy!

For the Suggested Albums/Tracks, you can instead use the new “Change Playlist” button to replace the empty section with your newly-created playlist.

This setup for Recents let you customize how many days new songs will stay in the list, but more importantly it will remove a song from the Recents list once you have listened to it. (Get that list cleared! :laughing: )



:file_folder: Downloads

The latest versions will be available on the Symfonium styles website.

Expand

These versions are not kept up to date. Please follow link above.

Full-size version available here:

nps_FULL COMBO_v2.1.1.json (92.0 KB)

NavBar version is scaled down for those that have shorter screens, available here:

nps_FULL COMBO (NavBar)_v2.1.1.json (92.4 KB)


No changes were made to the App Style, other than updating the NPS.

APPSTYLE_AMO-RED_v2.1.1.json (151.6 KB)


Color theme only:

COLORS_AMO-RED.json (2.7 KB)



Image credits:

Screenshot frames sourced from MockUPhone.com
Text-art created by CoolText.com

12 Likes

Known problems:

Expand

Actual bugs:

None

Not bugs:

  • No way to access “Composers” when playing Classical music in Landscape!
    • Trade-offs were made. It’s normally accessed by tapping String 6, but this was replaced by queue info since I didn’t have room to fit the Top Bar
  • Tapping Artist’s name in Mini-Player mode takes me to the album!
    • Use Long-press instead to go to Artist
    • I can change the text but not the tap actions. This is still using the Album’s “string slot” since I couldn’t collapse that one
  • If Top Bar string is too long to fit or if marquee is enabled, treble clef header gets height-cropped
    • The character is actually taller than single-line height, and gets cropped to fit. So don’t add too many buttons!
    • I did my best to keep the text length short. “It works on mine” :stuck_out_tongue:
  • Artists don’t display if Album Artist line is long
    • Just how it works, they’re both inside a single string
    • Tweak the font sizing if you need to
  • If NPS adaptive color picks white, you can’t see the state of Repeat/Shuffle buttons
    • Just white on white. I could change to non-colored buttons but this issue is uncommon and I prefer the colored style
  • Not friendly to shorter aspect ratios, since most of the layout has fixed vertical sizing
    • Mini-Player helps with this! (Tap the Album Art to toggle)
  • The “Covered by” header doesn’t show on this cover of XYZ!
    • Either you don’t have “Original Artist” tag set or it has multiple values
    • If you add “covers” as a Release Type, it’ll work 100% of the time

NPS gets cut off on your screen?

Expand

Did you try the “NavBar” version of my NPS? I already pre-shrank a bunch of stuff there, so you can make things bigger from there.

Otherwise, try these easy tweaks:

  • Set Top Bar size to “Small”
  • Set Progress Bar “Scale Factor” to 1.0
  • Shrink the Play button (Control buttons, icon 4) to size 40
    • Go even smaller? Set icons 3-5 to size 32, and icons 2 & 6 to size 22

Still doesn’t fit? Try these tweaks to the strings:

  • Don’t use Movement Name?
    • Disable String 2
  • Don’t use Disc Title?
    • Change “String 4 line count” to “2 (At most)”
  • Don’t mind buttons moving?
    • Change String 4 & 5 “line count” to “2 (At most)”

As a last resort, scale down all of the strings by 0.1 at a time until you’ve made enough room.


When the spacing is just right, you’ll see the corners of the album art are rounded in the Full Player. (Mini Player doesn’t have rounded corners in my version.)

If you set the “Cover art style” to “Visible” instead of Fill size, the art will always be rounded. (But this will also move the Rating Bar for the Mini Player on top of the art. Can’t be helped.)




Custom Strings :nerd_face:

All of my NPS’s custom strings will be added here, for ease-of-use by the community. These will be exploded into multiple lines for readability.

If you plan to edit these strings, I would strongly recommend doing so in a proper text editor. You can easily expand the flat versions by using Find } and Replace }\n

HUGE thanks to Tolriq for adding mixed operator support! Some of these strings were 3x longer before the update (imagine that lol).


Note for Classical listeners:

Expand

Most of these strings have special cases to handle the “Classical” genre vs everything else. All relevant portions have been marked! Simply use your text editor to search for #!# and you will jump straight to the parts to be edited.

The comments will tell you what information should be displayed by a given line, helping you to replace the tag field with the one you use to hold that info.


These should be up-to-date with the latest version of my Portrait theme. (Landscape versions simply drop the bool5 conditions.)

Exploded View

Do not copy and paste these directly into Symfonium–they will not work.

Everything inside a sub-header is part of the same string.

If you’re not familiar with coding, the lines that start with a {# are my “Comments” that explain what the stuff below is doing. Lines like {⠀String by Celorien⠀%|!=%} will never be visible. These are all safe to leave in, just remember to flatten to one line!


Queue Info

Expand

This section has the longest strings by far, so buckle your seat belts. Read the comments for an explanation of what’s happening. If you have any questions, feel free to ask below!

The two split strings are the original format and the Single-Line version is trimmed and reorganized from those.

Mode and Position

Used for the first line of my Top Bar in portrait.

{⠀String by Celorien⠀%|!=%}
{--------------------%|!=%}
{# Headers%|!=%}

{## Standard%|!=%}
{𝄚𝄞𝄚⠀%|providername==empty || queue.position!=queue.size || player.repeat.mode=="One"%}

{## End-of-Queue%|!=%}
{𝄚𝄚𝄂⠀%|providername!=empty && queue.position==queue.size && player.repeat.mode!="One"%}
{# Sleep Timer%|!=%}

{## Immediate Stop%|!=%}
{⎅ %sleep.timer|sleep.timer.eos=="false" && sleep.timer.seconds>"0"%}

{## Finish Track before Stop%|!=%}
{⏲ %sleep.timer|sleep.timer.eos=="true" && sleep.timer.seconds>"0"%}

{## Waiting to Stop%|!=%}
{⚠⠀Ending playback⠀⚠%|sleep.timer.seconds=="0"%}

{## Separator%|!=%}
{ • %|sleep.timer.seconds>"0" && (providername!=empty && next.title!=empty)%}
{# Repeat All enabled, End-of-Queue reached%|!=%}
{Restarting Queue%|(providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}

{## If set, show Queue Name; else ellipsis%|!=%}
{: "%queue.name|(queue.name!=empty && queue.name!="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%"}
{...%|(queue.name==empty || queue.name=="No name") && (providername!=empty && queue.position==queue.size && player.repeat.mode=="All")%}
{# Queue Position%|!=%}

{## Standard%|!=%}
{Playing %queue.position|(smartqueue=="Disabled" && smartflow=="Disabled" && chapter.count=="0" && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}
{ / %queue.size|(smartqueue=="Disabled" && smartflow=="Disabled" && chapter.count=="0" && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}

{## Condensed - single-digit values padded to two digits%|!=%}
{### Used when Smart Queue/Flow and Sleep Timer are enabled%|!=%}
{### Also used when chapters are present%|!=%}
{▶ 0%queue.position|queue.position<"10" && ((smartqueue!="Disabled" || smartflow!="Disabled" || chapter.count!="0") && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}
{▶ %queue.position|queue.position>"9" && ((smartqueue!="Disabled" || smartflow!="Disabled" || chapter.count!="0") && sleep.timer.seconds!="0") && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}
{# Chapter handling%|!=%}

{## Show chapter index and count%|!=%}
{## NOTE: separated by Fraction Slash (U+2044)%|!=%}
{ • %|chapter.count!="0" && providername!=empty && (queue.position!=queue.size || (queue.position==queue.size && player.repeat.mode=="One"))%}
{⚐ %chapter.index|chapter.count!="0"%}
{⁄%chapter.count|chapter.count!="0"%}
{# Smart Queue and Flow Indicators%|!=%}
{## Condensed indicators used when Sleep Timer is enabled or Chapters are present%|!=%}
{ • %|((smartqueue!="Disabled" || smartflow!="Disabled") && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}
{Smart %|((smartqueue!="Disabled" || smartflow!="Disabled") && chapter.count=="0" && sleep.timer.seconds!="0") && (providername!=empty && next.title!=empty)%}

{Queue%|(smartqueue!="Disabled" && chapter.count=="0" && sleep.timer.seconds=="-1") && (providername!=empty && next.title!=empty)%}
{[Q]%|(smartqueue!="Disabled" && (sleep.timer.seconds>"0" || (sleep.timer.seconds=="-1" && chapter.count!="0"))) && (providername!=empty && next.title!=empty)%}

{ & %|((smartqueue!="Disabled" && smartflow!="Disabled") && chapter.count=="0" && sleep.timer.seconds=="-1") && (providername!=empty && next.title!=empty)%}

{Flow%|(smartflow!="Disabled" && chapter.count=="0" && sleep.timer.seconds=="-1") && (providername!=empty && next.title!=empty)%}
{[F]%|(smartflow!="Disabled" && (sleep.timer.seconds>"0" || (sleep.timer.seconds=="-1" && chapter.count!="0"))) && (providername!=empty && next.title!=empty)%}

Next Track Info

Used for the second line of my Top Bar in portrait.

{⠀String by Celorien⠀%|!=%}
{--------------------%|!=%}
{# Headers%|!=%}

{## Standard%|!=%}
{Up Next:⠀%|(player.repeat.mode!="One" && player.shuffle.mode=="false") && (providername!=empty && next.title!=empty) && (chapter.count=="0" || chapter.index==chapter.count)%}

{## Chapters%|!=%}
{Next Ch:⠀%|chapter.count!="0" && chapter.index!=chapter.count%}

{## Shuffle or Unknown Provider (e.g. Internet Radio)%|!=%}
{⠀?????%|(player.repeat.mode!="One" && player.shuffle.mode=="true") && (providername!=empty && next.title!=empty) && (chapter.count=="0" || chapter.index==chapter.count)%}
{⠀?????%|providername==empty%⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀}

{## Repeat One%|!=%}
{Repeat:⠀%|player.repeat.mode=="One" && (providername!=empty && next.title!=empty) && (chapter.count=="0" || chapter.index==chapter.count)%}

{## End-of-Queue%|!=%}
{⠀FIN%|(queue.position==queue.size && (providername!=empty && next.title==empty)) && (chapter.count=="0" || chapter.index==chapter.count)%}
{# Chapter handling%|!=%}

{%next.chapter.title|chapter.count!="0" && chapter.index!=chapter.count%}
{# Audiobook Handling%|!=%}

{%next.title|providername!=empty && (album.type+="audiobook" || isaudiobook=="true") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (chapter.count=="0" || (chapter.count!="0" && chapter.index==chapter.count))%}
{ • %next.albumartist|providername!=empty && (album.type+="audiobook" || isaudiobook=="true") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (chapter.count=="0" || (chapter.count!="0" && chapter.index==chapter.count)) && (next.albumartist!=empty && next.albumartist!=albumartist)%}
{# Music Handling%|!=%}

{## Show Work name if set, else show Title%|!=%}
{%next.work|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work!=empty && (album.type-="audiobook" && isaudiobook=="false")%}
{%next.title|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.work==empty && (album.type-="audiobook" && isaudiobook=="false")%}

{## Show Album Artist if not set to "Various Artists", else show Performing Artists%|!=%}
{ • %next.albumartist|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist!="Various Artists" && (album.type-="audiobook" && isaudiobook=="false")%}
{ • %next.artist|(providername!=empty && next.genre-="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && next.albumartist=="Various Artists" && (album.type-="audiobook" && isaudiobook=="false")%}
{#!# EDIT BELOW to fit your tags%|!=%}
{# Classical Genre%|!=%}

{## Show Composer name (only if different from current)%|!=%}
{%next.composer|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (next.composer!=empty && next.composer!=composer) && (album.type-="audiobook" && isaudiobook=="false")%'s }
{'%|!=%}

{## Show Work name (only if different from current)%|!=%}
{%next.work|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer!=composer) || ((next.composer!=empty && next.composer==composer) && next.work!=work)) && (album.type-="audiobook" && isaudiobook=="false")%}

{## Show Movement Number as a Roman numeral (only if Movement name is set and Work matches current)%|!=%}
{%next.movement.number.roman|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.number!=empty && next.movement.name!=empty) && (album.type-="audiobook" && isaudiobook=="false")%. }

{## Show Movement name (only if Work matches current%|!=%}
{%next.movement.name|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && ((next.composer!=empty && next.composer==composer) && next.work==work) && (album.type-="audiobook" && isaudiobook=="false")%}

{## Show Title (only if Work matches but Movement name is empty%|!=%}
{%next.title|(providername!=empty && next.genre+="Classical") && (player.shuffle.mode=="false" || (player.shuffle.mode=="true" && player.repeat.mode=="One")) && (((next.composer!=empty && next.composer==composer) && next.work==work) && next.movement.name==empty) && (album.type-="audiobook" && isaudiobook=="false")%}
{#!# END OF EDIT ZONE%|!=%}

Single-Line

Used as String 6 in landscape. I won’t break it down beyond the build instructions, since almost everything is copied from the two above this.

Building from Split-Line Version

Was accurate for the 14.0.0 version but there may be a few additions for the chapter/audiobook stuff. I’ll double-check eventually…

Modes and Position
------------------
Insert blank before "FIN" when sleep timer is running
Queue position - Condensed only, drop Smart Genre/Flow checks
Smart Queue/Flow - Condensed only, drop ampersand
    change condition {sleep.timer.seconds>"0"} to {sleep.timer.seconds!="0"}

Next Track Info
---------------
Drop shuffle condition from "Up Next: " header
Drop trailing space from "Up Next: " and "Repeat: "
Add spaced bullet (" • ") before "Up Next:" and "Repeat:"
Drop "?????" case for empty providername
Drop trailing blanks from shuffle header (remaining "?????" header)
Split leading blank from "FIN" header and swap to spaced bullet (" • ")
    Only show leading blank when 'Sleep Timer + FIN'
Add trailing blanks after "FIN" header and split-operator
    ("⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀" 15 Braille blanks)

(!!!) Add {sleep.timer.seconds!="0" && } condition to start of ALL lines
    (Except the split blank from FIN, which is already covered)
(!!!) Add "\t" after Headers section (but before trailing blanks)

Remainder is direct copy


End Result section has been left out for now. I keep hitting character limits and this section adds nothing of value. The copy-and-paste version is still in the main post above, if you need it.


1 Like

Exploded View (continued)

Main Info Panel

Expand

This is the heart of the Now Playing Screen.

Main Title

Explicit tag shows here for music if a Movement Name is not set.

{# Music handling%|!=%}
{## Shows Work name (if present) or Title (if not)%|!=%}
{## Prepends Explicit indicator to Work name when applicable%|!=%}

{%explicit|explicit!=empty && work!=empty && ((movement.name==empty && bool5=="false") || bool5=="true") && (album.type-="audiobook" && isaudiobook=="false")%⠀}
{%work|genre-="Classical" && work!=empty && (album.type-="audiobook" && isaudiobook=="false") && (bool5=="false" || (bool5=="true" && chapter.title==empty))%}
{⠀%|genre-="Classical" && work==empty && movement.name!=empty && (album.type-="audiobook" && isaudiobook=="false") && (bool5=="false" || (bool5=="true" && chapter.title==empty))%}
{%title|genre-="Classical" && work==empty && movement.name==empty && (album.type-="audiobook" && isaudiobook=="false") && (bool5=="false" || (bool5=="true" && chapter.title==empty))%}
{#!# ---EDIT BELOW to fit your tags---%|!=%}
{# Classical Genre%|!=%}
{## Shows Work Name (does not change for Mini Player)%|!=%}
{%work|genre+="Classical" && (album.type-="audiobook" && isaudiobook=="false")%}
{#!# ---END OF EDIT ZONE---%|!=%}
{# Audiobook handling%|!=%}
{## Display chapter number without title%|!=%}
{Chapter %chapter.index|(album.type+="audiobook" || isaudiobook=="true") && chapter.count!="0" && bool5=="false"%}
{%album|(album.type+="audiobook" || isaudiobook=="true") && chapter.count=="0"%}
{%title|(album.type+="audiobook" || isaudiobook=="true") && chapter.count=="0" && album==empty%}
{# Mini Player: Alt-Text%|!=%}
{## Displays chapter title%|!=%}
{## Index and Separator only shown for audiobooks%|!=%}
{__Chapter %chapter.index|(album.type+="audiobook" || isaudiobook=="true") && chapter.count!="0" && bool5=="true"%__}
{__:__ %|(album.type+="audiobook" || isaudiobook=="true") && (chapter.title!=empty && movement.name!=empty) && bool5=="true"%}
{%chapter.title|(genre-="Classical" || (album.type+="audiobook" || isaudiobook=="true")) && bool5=="true"%}

{## Spacer%|!=%}
{\n%|bool5=="true"%}

Subtitle

Shows one of the following, in order of priority:

  1. Movement Info (music only)
  2. Chapter Info (always shown for audiobooks)
  3. blank for spacing

For music, the Explicit tag is shown here if Movement Name is set; otherwise it shows on the Main Title.

{# Music handling%|!=%}
{## Movement Info%|!=%}
{### Shown whenever Movement Name is set%|!=%}

{%explicit|explicit!=empty && movement.name!=empty && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%⠀}
{__%movement.number.roman|movement.number!=empty && movement.name!=empty && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%.__⠀}
{%movement.name|bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}
{⠀%|movement.name==empty && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}
{## Chapter Info%|!=%}
{### Shown only if Movement Name is not set and Chapters are present%|!=%}
{### If no chapter name is set, shows chapter number. Else shows only chapter title%|!=%}
{### Displays chapter number AND title for Classical genre (mimics Movement Info)%|!=%}

{Chapter %chapter.index|genre-="Classical" && (chapter.count!="0" && movement.name==empty && chapter.title==empty) && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}
{__%chapter.index|genre+="Classical" && (chapter.count!="0" && movement.name==empty && chapter.title==empty) && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%.__⠀}
{%chapter.title|movement.name==empty && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}
{# Audiobook handling%|!=%}
{## Drops movement name checks, since audiobooks use that field for series name%|!=%}
{## Displays only chapter title%|!=%}

{%chapter.title|(album.type+="audiobook" || isaudiobook=="true") && bool5=="false"%}
{# Mini-Player: Chapter Progress%|!=%}
{## Displays chapter position. Toggle bool3 to swap chapter's Duration to chapter's Remaining Time%|!=%}

{## For chapters shorter than 20 mins, show simple bar%|!=%}
{⟣**%chapter.position|(chapter.count!="0" && chapter.duration.seconds<"1200") && bool5=="true"%**―――――}
{―――――**%chapter.duration|(chapter.count!="0" && chapter.duration.seconds<"1200") && (bool3=="false" && bool5=="true")%**⟢}
{―――――**(%chapter.remaining|(chapter.count!="0" && chapter.duration.seconds<"1200") && (bool3=="true" && bool5=="true")%)**⟢}

{## For chapters longer than 20 mins, show segmented bar with progress indicator%|!=%}
{### Displays progress through the final hour of a chapter (12 segments, each representing 5mins)%|!=%}
{### Indicator stays at left-most position until final hour is reached%|!=%}

{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && chapter.remaining.seconds>"3299"%}
{⟣%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && chapter.remaining.seconds<"3300"%}
{**%chapter.position|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%**}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"3300" && chapter.remaining.seconds>"3000")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"3001" && chapter.remaining.seconds>"2700")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"2701" && chapter.remaining.seconds>"2400")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"2401" && chapter.remaining.seconds>"2100")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"2101" && chapter.remaining.seconds>"1800")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"1801" && chapter.remaining.seconds>"1500")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"1501" && chapter.remaining.seconds>"1200")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"1201" && chapter.remaining.seconds>"900")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"901" && chapter.remaining.seconds>"600")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && (chapter.remaining.seconds<"601" && chapter.remaining.seconds>"300")%}
{—%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true"%}
{**%chapter.duration|(chapter.count!="0" && chapter.duration.seconds>"1199") && (bool3=="false" && bool5=="true")%**}
{**(%chapter.remaining|(chapter.count!="0" && chapter.duration.seconds>"1199") && (bool3=="true" && bool5=="true")"%)**}
{⟢%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && chapter.remaining.seconds>"300"%}
{◈%|(chapter.count!="0" && chapter.duration.seconds>"1199") && bool5=="true" && chapter.remaining.seconds<"301"%}

Rating Bar & Chapter Times

The Ratings portion is not an original idea, this has been done in several other themes on the forum. My additions:

  1. an “Icon Bank” at the top (useful when switching fonts)
  2. bool conditions to hide the string when toggled

I’ve now combined the chapter progress/duration/remaining indicators into this string. Chapter progress has priority over the Ratings info, when available.

{⬤◐●◉⦿◎◯◑○★☆⭒%|!=% Alt Icons}
{# When no chapters, show rating%|!=%}

{《 %|bool5=="false" && chapter.count=="0"%}
{○○○○○%|bool5=="false" && userrating.5=="0" && chapter.count=="0"%}
{◉○○○○%|bool5=="false" && userrating.5=="0.5" && chapter.count=="0"%}
{●○○○○%|bool5=="false" && userrating.5=="1" && chapter.count=="0"%}
{●◉○○○%|bool5=="false" && userrating.5=="1.5" && chapter.count=="0"%}
{●●○○○%|bool5=="false" && userrating.5=="2" && && chapter.count=="0"%}
{●●◉○○%|bool5=="false" && userrating.5=="2.5" && chapter.count=="0"%}
{●●●○○%|bool5=="false" && userrating.5=="3" && chapter.count=="0"%}
{●●●◉○%|bool5=="false" && userrating.5=="3.5" && chapter.count=="0"%}
{●●●●○%|bool5=="false" && userrating.5=="4" && chapter.count=="0"%}
{●●●●◉%|bool5=="false" && userrating.5=="4.5" && chapter.count=="0"%}
{●●●●●%|bool5=="false" && userrating.5=="5" && chapter.count=="0"%}
{ 》%|bool5=="false" && chapter.count=="0"%}
{# Displays chapter position and duration%|!=%}
{## Toggle bool3 to show Time Remaining in chapter%|!=%}

{⧖ %|bool5=="false" && chapter.count!="0"%}
{%chapter.position|(bool3=="false" && bool5=="false") && chapter.count!="0"%}
{ / %chapter.duration|(bool3=="false" && bool5=="false") && chapter.count!="0"%}
{%chapter.remaining|(bool3=="true" && bool5=="false") && chapter.count!="0"%}
{ ⧗%|bool5=="false" && chapter.count!="0"%}

Album Details & Audiobook Series/Book Info
{# Music handling%|!=%}

{## Chapter Info%|!=%}
{__Chapter %chapter.index|genre-="Classical" && (chapter.count!="0" && movement.name!=empty) && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%__}
{__:__ %chapter.title|genre-="Classical" && (chapter.title!=empty && movement.name!=empty) && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}

{## Separator%|!=%}
{\n%|genre-="Classical" && (chapter.count!="0" && movement.name!=empty) && album!=empty && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}
{# Album Name and Version%|!=%}
{## IF ReleaseType is NOT "single" or "covers", always show%|!=%}
{## ELSE show only if Album name doesn't match Work/Title%|!=%}

{**%album.raw|genre-="Classical" && ((album.type!="single" && album.type-="covers") || ((album.type=="single" || album.type+="covers") && ((work!=empty && album.raw!=work) || (work==empty && album.raw!=title)))) && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%**}
{ • **%album.version|genre-="Classical" && album.version!=empty && ((album.type!="single" && album.type-="covers") || ((album.type=="single" || album.type+="covers") && ((work!=empty && album.raw!=work) || (work==empty && album.raw!=title)))) && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%**}

{## Separator%|!=%}
{\n%|genre-="Classical" && (disctitle!=empty || (chapter.count!="0" && movement.name!=empty)) && album!=empty && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}
{# Disc Number and Title%|!=%}
{## Shown only if Disc Title not empty%|!=%}
{## Pad Disc Number to two digits%|!=%}

{Disc #--%|genre-="Classical" && disc==empty && disctitle!=empty && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%: }
{Disc #0%disc|genre-="Classical" && (disc!=empty && disc<"10") && disctitle!=empty && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%: }
{Disc #%disc|genre-="Classical" && disc>"9" && disctitle!=empty && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%: }
{//%disctitle|genre-="Classical" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%//}
{#!# ---EDIT BELOW to fit your tags---%|!=%}
{# Classical Genre%|!=%}

{## Show Album/Release Name and Release Date%|!=%}
{%album|genre+="Classical" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}

{## Show Release Year/Date after Album/Release Name, only if different from "Year" tag%|!=%}
{ (%releasedate.year|(genre+="Classical" && releasedate!=empty && releasedate.year!=year) && album!=empty && bool4=="false" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%)}
{ (%releasedate|(genre+="Classical" && releasedate!=empty && releasedate.year!=year) && album!=empty && bool4=="true" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%)}
{#!# ---END OF EDIT ZONE---%|!=%}
{# Audiobook handling%|!=%}

{## Show Series name and part number%|!=%}
{**__%movement.name|bool5=="false" && movement.name!=empty && (album.type+="audiobook" || isaudiobook=="true")%__**}
{ #%movement.number|bool5=="false" && movement.number!=empty && (album.type+="audiobook" || isaudiobook=="true")%}

{## Separator between Series info and Book info%|!=%}
{\n%|bool5=="false" && (movement.name!=empty || movement.number!=empty) && (album!=empty || title!=empty) && chapter.count!="0" && (album.type+="audiobook" || isaudiobook=="true")%}

{## Book info%|!=%}
{### Hidden if no chapters (will be shown elsewhere)%|!=%}
{%album|(album.type+="audiobook" || isaudiobook=="true") && chapter.count!="0"%}
{%title|album==empty && (album.type+="audiobook" || isaudiobook=="true") && chapter.count!="0"%}
{# Mini Player: Alt-Text%|!=%}

{## Show Album Artist if not set to "Various Artists", else show Performing Artists%|!=%}
{### Show Performing Artists instead if Album Artist is included%|!=%}
{**%albumartist|genre-="Classical" && albumartist!="Various Artists" && bool5=="true" && (album.type-="audiobook" && isaudiobook=="false")%**}
{**%artist|genre-="Classical" && (albumartist=="Various Artists" || albumartist==empty) && bool5=="true" && (album.type-="audiobook" && isaudiobook=="false")%**}
{**%albumartist|genre+="Classical" && bool5=="true" && (album.type-="audiobook" && isaudiobook=="false")%**}

{## Audiobooks%|!=%}
{## Show Author's name when there are no chapters (since Book info has moved)%|!=%}
{**__%albumartist|bool5=="true" && (album.type+="audiobook" || isaudiobook=="true") && chapter.count=="0"%__**}
{**__%artist|bool5=="true" && albumartist=="empty" && (album.type+="audiobook" || isaudiobook=="true") && chapter.count=="0"%__**}


All Artists
{# Show label for song covers (aka songs not performed by the original artist)%|!=%}
{## Based on ReleaseType (guaranteed, no errors) or comparison to original artist (reliable but not perfect)%|!=%}

{//Covered by: %|album.type+="covers" && (album.type-="audiobook" && isaudiobook=="false") && bool5=="false"%//}
{//Covered by: %|album.type-="covers" && (album.type-="audiobook" && isaudiobook=="false") && genre-="Classical" && original.artist!=empty && albumartist!=original.artist && bool5=="false"%//}
{# Show Album Artist if not set to "Various Artists", else show Performing Artists%|!=%}
{## Show Performing Artists instead if Album Artist is included%|!=%}
{## The newline after Artist is there to force two-line minimum spacing for the string%|!=%}

{**__%albumartist|genre-="Classical" && artist-=albumartist && albumartist!="Various Artists" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%__**\n}
{**%artist|genre-="Classical" && (artist+=albumartist || albumartist=="Various Artists") && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%**\n}
{%artist|genre-="Classical" && artist-=albumartist && albumartist!="Various Artists" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%\n}
{#!# ---EDIT BELOW to fit your tags---%|!=%}
{# Classical Genre%|!=%}

{## Shows Composer on first line followed by Performing Artists on second line%|!=%}
{**__%composer|genre+="Classical" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%__**\n}
{%artist|genre+="Classical" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}
{#!# ---END OF EDIT ZONE---%|!=%}
{# Audiobook handling%|!=%}

{## Show author's name (Album Artist as preferred source)%|!=%}
{**__%albumartist|albumartist!=empty && bool5=="false" && (album.type+="audiobook" || isaudiobook=="true")%__**}
{**__%artist|albumartist==empty && bool5=="false" && (album.type+="audiobook" || isaudiobook=="true")%__**}

{## Separator between author and narrator%|!=%}
{\n%|bool5=="false" && (album.type+="audiobook" || isaudiobook=="true")%}

{## Show narrator's name%|!=%}
{%composer|bool5=="false" && (album.type+="audiobook" || isaudiobook=="true")%}

Composer and Extras
{# Mode 1: Year, Mode 2: Release Date%|!=%}
{# Classical Mode 1: Year, Mode 2: Original Date%|!=%}

{**%year|bool4=="false" && bool5=="false"%**}
{%releasedate|genre-="Classical" && bool4=="true" && bool5=="false"%}
{**%originaldate|genre+="Classical" && bool4=="true" && bool5=="false"%**}

\t

{# Mode 1: Composer, Mode 2: File Info%|!=%}
{## Classical Genre - File Info only%|!=%}
{## Audiobooks - Show Publisher instead of Composer%|!=%}

{//Composed by: %|genre-="Classical" && composer!=empty && bool4=="false" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%//}
{%composer|genre-="Classical" && composer!=empty && bool4=="false" && bool5=="false" && (album.type-="audiobook" && isaudiobook=="false")%}
{**%format|(genre-="Classical" || (album.type+="audiobook" || isaudiobook=="true")) && bool4=="true" && format!="Unknown" && bool5=="false"%**}
{%format|(genre+="Classical" || ((album.type+="audiobook" || isaudiobook=="true") && bool4=="false")) && format!="Unknown" && bool5=="false"%}
{//Loading track info...%|(genre+="Classical" || bool4=="true" || (album.type+="audiobook" || isaudiobook=="true")) && format=="Unknown" && bool5=="false"%//}

\t

{# Mode 1: Equalizer indicator, Mode 2: Provider Name%|!=%}
{## Classical Genre - Same info, just Bold%|!=%}

{**☐ EQ%|eq.config=="Disabled" && bool4=="false" && bool5=="false"%**}
{**☑ EQ%|eq.config!="Disabled" && bool4=="false" && bool5=="false"%**}
{%providername|genre-="Classical" && bool4=="true" && bool5=="false"%}
{**%providername|genre+="Classical" && bool4=="true" && bool5=="false"%**}


Compact Player

Expand

Can’t forget about this guy! There’s nothing too special here, it’s just a few snippets from the main NPS.

Top String

Pulls from “Main Title” string for non-Classical vs from “Subtitle” for Classical genre.

Shows, for example: Ch. 1: Chapter Title for audiobooks.

{# Music Handling%|!=%}
{    %|!=%}
{## Shows Work name (if present) or Title (if not)%|!=%}
{%explicit|genre-="Classical" && explicit!=empty && work!=empty && (album.type-="audiobook" && isaudiobook=="false")%⠀}
{%work|genre-="Classical" && work!=empty && (album.type-="audiobook" && isaudiobook=="false")%}
{%title|genre-="Classical" && work==empty && (album.type-="audiobook" && isaudiobook=="false")%}
{#!# ---EDIT BELOW to fit your tags---%|!=%}
{# Classical Genre%|!=%}

{## Shows Movement info (if present) or Title (if not)%|!=%}
{%explicit|genre+="Classical" && explicit!=empty && (album.type-="audiobook" && isaudiobook=="false")%⠀}
{%movement.number.roman|genre+="Classical" && movement.number!=empty && movement.name!=empty && (album.type-="audiobook" && isaudiobook=="false")%.⠀}
{%movement.name|genre+="Classical" && (album.type-="audiobook" && isaudiobook=="false")%}
{%title|genre+="Classical" && movement.name==empty && (album.type-="audiobook" && isaudiobook=="false")%}
{#!# ---END OF EDIT ZONE---%|!=%}
{# Audiobook Handling%|!=%}
{## Shows Chapter info (if present) or Book info (if not)%|!=%}

{Ch. %chapter.index|(album.type+="audiobook" || isaudiobook=="true") && chapter.count!="0"%}
{: %chapter.title|(album.type+="audiobook" || isaudiobook=="true") && chapter.count!="0" && chapter.title!=empty%}
{%album|(album.type+="audiobook" || isaudiobook=="true") && chapter.count=="0"%}
{%title|(album.type+="audiobook" || isaudiobook=="true") && chapter.count=="0" && album==empty%}


Bottom String

For non-Classical: copies “All Artists” string, but without the “Covered by” stuff

For Classical: copies relevant portion of “Main Title” string

For audiobooks: shows the Book Title (a.k.a. Album name) if not already shown above

{# Music Handling%|!=%}

{## Show Album Artist if not set to "Various Artists", else show Performing Artists%|!=%}
{## Show Performing Artists instead if Album Artist is included%|!=%}

{%albumartist|genre-="Classical" && (artist-=albumartist && albumartist!="Various Artists") && (album.type-="audiobook" && isaudiobook=="false")%}
{%artist|genre-="Classical" && (artist+=albumartist || albumartist=="Various Artists") && (album.type-="audiobook" && isaudiobook=="false")%}
{#!# ---EDIT BELOW to fit your tags---%|!=%}
{# Classical Genre%|!=%}

{## Show Composer's name%|!=%}
{%composer|genre+="Classical" && (album.type-="audiobook" && isaudiobook=="false")%'s }
{'%!=%}

{## Show Work name%|!=%}
{%work|genre+="Classical" && (album.type-="audiobook" && isaudiobook=="false")%}
{#!# ---END OF EDIT ZONE---%|!=%}
{# Audiobook Handling%|!=%}

{%album|album!=empty && (album.type+="audiobook" || isaudiobook=="true") && chapter.count!="0"%}
{%title|album==empty && (album.type+="audiobook" || isaudiobook=="true") && chapter.count!="0"%}

This is so cool. Not really my style but showcases so many cool tricks I had no idea Symfonium could do and gives me so many ideas for ways to make my own theme more dynamic. I’m curious, how are you doing the rating as circles rather than stars? Is it just a conditional string translating each possible rating to a corresponding set of unicode characters?

2 Likes

That’s exactly it! I’ll update the third post with the full string later but it’s just:

{●●◉○○%|userrating.5=="2.5"%}

For each step from 0 to 5

Also, I wanted to use stars but my phone somehow doesn’t support the Unicode for half-stars lol. So I had to find a replacement.

Makes sense. I actually like the dots, especially since I prefer to use them not as a true rating system but as a sort of “how often am I willing to have this come up in my playlist?” thing. So I may “borrow” that for my setup.

Final release of my themes are now out for use with the new app version. I don’t expect to change much about it, but let me know if you run into any problems.

1 Like

hi & thanks for the dedication. this is exactly what i was expecting when i browsing for a theme, and it’s a beautiful one with that.

that said, i was able to import the “APPSTYLE_AMO-RED v1 OP” but got “Invalid style detected!” with any other file (v.1.0 ,v0.10, v0.9, NavBar, etc.).

I was wondering if maybe you were on the beta and i am not (beta is full so i can’t join anymore), or if you had any leads about why i may get that error.

with care

Sounds like the NPS styles are the issue? The “FULL COMBO” files?

There’s a separate Import button for those. Go to Settings > Interface > Now Playing Screens and use the Import button there.

Lmk if that still doesn’t work.

1 Like

haha yup that was it. you’re the best!

1 Like

Looks like I finished just-in-time! Theme update for v14.1.0 has been uploaded. (See main post for Downloads.)

MFW I keep hitting char limits on my posts

tenor


No changes to the App Style. Feel free to update just the NPS if you have made your own changes to the former.

FULL COMBO v2.0 Changelog

Chapters

The biggest thing that was missing from the theme was support for chapters. Thanks to the new update, we can finally add chapter info to our custom strings. I’ve added the following string-based chapter-progress bars. No, you cannot drag them to seek

Swap to the Mini-Player for this chapter-focused view.

Short chapter bar (long-press to toggle time remaining):

Simple

Segmented chapter bar (shown for chapters over 20 mins):

Segmented

  • Chapter Title now displays below Work/Track Title if there is no Movement Name set
    • Will show above the Album name if Movement Name is set
  • Rating Bar will instead display Chapter Progress, when available
    • Tap to set Rating, Long-Press to toggle (Duration/Remaining Time) modes
    • Ratings are still visible when Paused in Mini-Player mode or when no chapters are present
  • Top Bar (Queue info) will show chapter details as well
    • Current chapter number / total number shown next to the flag icon
    • Next Chapter Title shows instead of the next Track Title, when applicable

Audiobooks

Much like Classical genre music, Audiobooks now have their own layout built-in. The layout is based on Audible/audiobookshelf metadata usage, so it should hopefully show what you expect. (I don’t use those services to confirm, but I did do my research.)

There are two ways to access this layout.

  1. Set your media’s Release Type field to include audiobook
    Or
  2. Set your source folder as “Audiobook” using the new Media Provider settings

In a nutshell, this layout will have more focus on chapters. Here’s a quick rundown:

  • Top half of NPS shows chapter info.
    • The Chapters button on the Rating Bar has been replaced with Playback Speed
    • Simply tap the chapter number or name to go to the Chapters view
  • Prev/Next Chapter buttons added to Controls row
    • Also accessible in landscape via buttons added over the Cover art
  • Bottom half of NPS shows:
    • Series info (bold and underlined) followed by the Book name
    • Author’s name (bold and underlined) followed by the Narrator’s name

Compact Player improvements

  • Now shows Time Remaining for all media types
  • Audiobooks will show Chapter Number and Title on top line, and the Chapter Progress on the bottom

Plus some misc. fixes…

  • Fixed Album Artist formatting when no other artists are present
  • Fixed " & " showing when only one of Smart Queue or Flow was enabled

I do think the theme is now truly finished. I won’t pretend I did it for y’all :stuck_out_tongue: but posting it here is my way of giving back for all the hard work Tolriq puts in. Thanks for delivering the best music player on the market!