From b8086ec822095570ff5095410b558df9ac75bc32 Mon Sep 17 00:00:00 2001 From: Wil-B <35600752+Wil-B@users.noreply.github.com> Date: Sat, 13 May 2023 12:19:08 +0100 Subject: [PATCH] v1.4.0 --- CHANGELOG.md | 49 ++++ LICENSE.txt | 2 +- README.md | 2 +- assets/html/config.html | 160 ++++++++----- assets/html/confirm.html | 3 +- assets/images/Logo.png | Bin 51464 -> 119862 bytes main.js | 2 + package.json | 2 +- scripts/allmusic.js | 24 +- scripts/buttons.js | 109 ++++----- scripts/callbacks.js | 89 +++++-- scripts/filmstrip.js | 21 +- scripts/helpers.js | 11 +- scripts/images.js | 90 +++++--- scripts/interface.js | 231 +++++++++++++------ scripts/language.js | 484 +++++++++++++++++++++++++++++++++++++++ scripts/lastfm.js | 24 +- scripts/library.js | 2 + scripts/lyrics.js | 39 +++- scripts/menu.js | 214 ++++++++--------- scripts/names.js | 21 +- scripts/panel.js | 258 ++++++++++++--------- scripts/popupbox.js | 16 +- scripts/properties.js | 47 ++-- scripts/resize.js | 10 +- scripts/scrollbar.js | 1 + scripts/server.js | 8 +- scripts/settings.js | 151 +++++++++++- scripts/tagger.js | 54 +++-- scripts/text.js | 395 ++++++++++++++++++-------------- scripts/wikipedia.js | 52 +++-- 31 files changed, 1822 insertions(+), 749 deletions(-) create mode 100644 scripts/language.js diff --git a/CHANGELOG.md b/CHANGELOG.md index a7d8d37..43f726f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,52 @@ +# v1.4.0 + +### Added +- summary + - option to disable compact style for the summary + - allows showing all genres, moods, members & composers in summary rather than limiting each to one line + - menu > display or click summary to toggle +- nowplaying display + - image effects + - user configurable (options > textreader & lyrics tab) + - use in it's own right or as a fallback +- layout + - full overlay preset (menu > layout) + - shows text over an album art background + - configurable. Press CTRL or options > behaviour tab > overlay where there is also an example of usage +- advanced radio stream parser + - designed for use with streams that contain the artist name and song title in a non-standard format + - user configurable (open in a text editor) + - location: ...foo_spider_monkey_panel\package_data\{BA9557CE-7B4B-4E0E-9373-99F511E81252} + - won't be overwritten if biography is updated +- language support for menu + - simplified Chinese + - traditional Chinese + - enable: panel properties > 'Language [Menu]...' +- lyrics + - drop shadow effect (options > textreader & lyrics tab) + - larger sync line options (options > textreader & lyrics tab) +- customisable playlist interaction for follow selected item + - default settings should be fine for most users. If required: + - load delay can now be increased if moving through playlist is laggy (options > advanced tab) [regorxxx] + - leading item can now be set to use the delay or load immediately (options > advanced tab) + +### Changed +- improve support for other scripts wishing to use biography virtual tags +- scripts can now use ```window.NotifyOthers('bio_syncTags', focus)``` when the current track changes. Focus is either false (prefer nowplaying) or true (follow selected track) + - overrides biography tag settings so that: + - biography always notifies the tags + - that the notified tags include a core set of tags + - that the notified tags include the correct selection mode +- the notification also now has the artist and album name + +### Fixed +- rare on_size error +- occasional ActiveX Object error triggered by a site change +- title format regression introduced in 1.3.4 with certain special characters +- workaround for apparent folder locking stopping some film strip images loading during downloading + +
+ # v1.3.6 ### Added diff --git a/LICENSE.txt b/LICENSE.txt index b0512e1..c7fca6b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ License -Copyright (c) 2021-2022 WilB +Copyright (c) 2021-2023 WilB The above copyright notice shall be included in all copies or substantial portions of the Software. diff --git a/README.md b/README.md index 6a0d725..19d25cc 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Feature rich biography plug-in for [foobar2000](https://www.foobar2000.org) - Lyrics: synced and unsynced - Textreader - Item properties +- Nowplaying display - Layout presets: top, left, right, bottom and overlay. Fully customisable with freestyle layouts - Themes: user interface, dark, blend, light and random - Custom colours and fonts @@ -63,4 +64,3 @@ Standard grouping Alternative grouping ![item_properties_alternative_grouping](https://user-images.githubusercontent.com/35600752/179354332-31ef0545-4834-4837-bcbc-a86f912181e6.png) - diff --git a/assets/html/config.html b/assets/html/config.html index b14aacd..bb4777d 100644 --- a/assets/html/config.html +++ b/assets/html/config.html @@ -46,12 +46,11 @@ .div_text8 {padding:1.25em 0 0.31em;} .div_text9 {padding:1.57em 0 0;} .div_text10 {padding:0 0 0.7em;} - .div_text11 {padding:3.57em 0 0.5em;} - .div_text12 {padding:1.57em 0 0.31em;} - .div_text14 {padding:1.25em 0 0.91em;} - .div_text15 {padding:0.725em 0 0;} - .div_text16 {padding:0.58em 0 0;} - .alignLH, .alignRH, .alignLeftH, .alignRightH, .div_text1, .div_text2, .div_text3, .div_text6, .div_text7, .div_text8, .div_text9, .div_text10, .div_text11, .div_text12, .div_text14, .div_text15, .div_text16 { + .div_text11 {padding:1.57em 0 0.31em;} + .div_text12 {padding:1.25em 0 0.91em;} + .div_text13 {padding:0.725em 0 0;} + .div_text14 {padding:0.58em 0 0;} + .alignLH, .alignRH, .alignLeftH, .alignRightH, .div_text1, .div_text2, .div_text3, .div_text6, .div_text7, .div_text8, .div_text9, .div_text10, .div_text11, .div_text12, .div_text13, .div_text14 { font:bold 1.15em Segoe UI; color:#0066CC; word-wrap:break-word; white-space:pre-wrap; } @@ -129,6 +128,7 @@ .input35, .input40, .input73, .input96 { display:inline-block; vertical-align:middle; } + .input99 {display:inline-block; vertical-align:middle; width:98%; border:none !important; background:none} .inp_label1 {display:block; margin:0.31em 0;} .inp_label2 {padding-left:0.46em;} @@ -236,7 +236,7 @@
Fallback text: Heading|NoHeading
-
Scroll
+
Scroll
Bar: @@ -260,7 +260,11 @@
-
Used by overlay & custom layouts. Flat & rounded support custom colours.
+
Flat & rounded support custom colours. + +As an alternative for lyrics, try menu > layout > full overlay with: flat style, + +
Extra menu items
@@ -293,8 +297,10 @@
Highlight
-
Items disabled if custom colour set.
-
Image and filmStrip size
+
Items disabled if custom colour set. Line option disabled if dark lines enabled.
+
Line style
+
+
Image and filmStrip size
Press Ctrl and drag lines to adjust image:text ratio or filmstrip size (panel needs focus, e.g. click it).
@@ -416,7 +422,7 @@
-
Seeker & counter
+
Seeker & counter
@@ -438,7 +444,7 @@ -
Reflection
+
Reflection
@@ -451,16 +457,16 @@
- Supports text, lyrics and item properties. Uses first found for each view. + Displays text, lyrics, item properties and nowplaying info. Uses first found for each view.
-
Artist view
+
Artist view
Use Name Full path or tag* Lyrics source
-
Album view
+
Album view
Use Name Full path or tag* @@ -468,13 +474,18 @@
- *Evaluated as a path if backslash present, else as a tag. Filename & extension support wildcards. + *Evaluated as a path if backslash present, else as a tag.
Filename & extension support wildcards.
+ + + - + +
+
-
+
Lyrics:
@@ -498,7 +509,7 @@
-
+
Item properties:
@@ -510,7 +521,16 @@
-
Textreader, lyrics & item properties help
+
+
Nowplaying:
+
+
+
+
+
+
+
+
Help
Download integration
Biography doesn't download lyrics, but can synchronise with lyrics plug-ins as follows.
-
  • Check "synchronise..." above
  • Ensure a lyric show 3 or openlyrics panel is in the layout (can be hidden) & that lyric saving is enabled
  • Ensure save settings in the lyrics panel match load settings above
-
If lyric show 3 is used, install veksha's multi-source for lyrics. Disable the default lyrics 3 sources as mostly they don't work anymore. Enable NEW DB: Minilyrics, NEW DB: QQ Music & NEW DB: Netease for synced lyrics or others as required.
+
  • Check "synchronise..." above
  • Ensure a lyric show 3 or ESLyric panel is in the layout (can be hidden) & that lyric saving is enabled
  • Ensure save settings in the lyrics panel match load settings above
+
If lyric show 3 is used, install veksha's multi-source for lyrics. Disable the default lyrics 3 sources as mostly they don't work anymore. Enable NEW DB: Minilyrics, NEW DB: QQ Music & NEW DB: Netease for synced lyrics or others as required. Although lyrics obtained by openlyrics can be displayed, that plug-in isn't suited to download integration.
Fade height
Increase if fade effect is too close to top / bottom, e.g. try 0.5.
Lyrics in tags
@@ -531,15 +551,15 @@
Scroll time is the milliseconds taken to scroll a line. The recommended range is 400-1500. Lyrics cannot be autoscrolled if text sources are amalgamated
Item properties
Item properties can be displayed by pasting in one of the paths below. Both display the same information, but use different groupings.
-
-
-
  • item_properties splits metadata into organised groups
  • item_properties_alternative_grouping shows metadata in a single group
  • both show other groups like youtube, statistics, location, general and replaygain
  • either can be edited in a text editor to add, remove or re-order groups and properties
-
Overlay
-
Lyrics, item properties, text, biographies and reviews can all be overlayed on top of faded cycling artist images or album cover for an alternative view, e.g.
-
  • Enable the overlay layout (menu > layout). Press ctrl & drag the red line to top
  • Image tab: set an auto-fill image type under image+text
  • Filmstrip: place at bottom*, e.g. with circular image type
  • Display tab: set image margins to 0 & adjust text margins if required. Set theme to dark, random or personal preference
  • Use shift + wheel to adjust image strength
-* Other positions work best with the filmstrip set to overlay the image area (menu > layout > filmstrip), with adjustment of text margins and full width heading off (heading tab)
+
+
+
  • item_properties splits metadata into organised groups
  • item_properties_alternative_grouping shows metadata in a single group
  • both show other groups like youtube, statistics, location, general and replaygain
  • either can be edited in a text editor to add, remove or re-order groups and properties
  • other files can be used provided item_properties is present in the name
+
Nowplaying
+
Nowplaying info can be displayed by pasting in the path below.
+
+
  • for ease of editing the nowplaying source uses a saved file
  • other files can be used provided nowplaying is present in the name
  • alignment N/A if text sources are amalgamated
Look-up (drop down menu)
-
  • Usage is optional
  • Textreader would need to to load info for artists (artist view) or albums (album view). Use %BIO_ARTIST%, %BIO_ALBUMARTIST% or %BIO_ALBUM%, as appropriate
  • Lyrics and item properties, if displayed, automatically show for the current track
+
  • Usage is optional
  • Textreader would need to load info for artists (artist view) or albums (album view). Use %BIO_ARTIST%, %BIO_ALBUMARTIST% or %BIO_ALBUM%, as appropriate
  • Lyrics, item properties and nowplaying info, if displayed, automatically show for the current track
@@ -593,6 +613,8 @@
Image: Smooth transition level
+
Playlist interaction (follow selected item)
+
Server name
This option enables the server to be set per panel. Panels having the same server name use the same server. The OK button must be clicked after changing the server name.
@@ -607,9 +629,12 @@
-The variables are used in searching, & should be used in save folder paths and headings to ensure correct functionality.
+The variables are used in searching, & should be used in save folder paths and headings to ensure correct functionality. + +An advanced radio stream parser designed for use with streams that contain the artist name and song title in a non-standard format is located in foo_spider_monkey_panel\package_data\{BA9557CE-7B4B-4E0E-9373-99F511E81252}. It's user configurable (open in a text editor). +
-
Classical music extension
+
Classical music extension
Extends auto-download of allmusic & wikipedia reviews to compositions. N/A for last.fm. Enable and set tag below. To display, per panel, use sources\text sub-menu. If preferred, composer can be used by changing %BIO_ALBUMARTIST% above so $meta(composer,0) is the first item.
@@ -690,11 +715,11 @@ @@ -1068,7 +1097,7 @@ else { if (!/title_format|download|photo|cover|tagger|misc/.test(cur.serverPage)) cur.serverPage = 'download' } - document.title = 'Biography Options'; + document.title = 'Biography ' + cur.version; ppt = JSON.parse(parsed_args[0]); cfg = JSON.parse(parsed_args[1]); if (ppt.multiServer_internal.value) { @@ -1097,11 +1126,12 @@ var btnLabels = ['No label', 'Text', 'Icon', 'Background', 'Red last.fm']; var cacheLabels = ['Cache items for EVER (recommended)', 'Cache items for 1 MONTH', 'Cache items for 2 WEEKS', 'Cache items for 1 WEEK', 'Cache items for 1 DAY', 'Times are since last accessed']; var clickLabels = ['Single-click of panel', 'Double-click of panel']; - var colourLabels = ['Text', 'Highlight', 'Button', 'Heading', 'Rating stars', 'Summary', 'Overlay fill', 'Overlay border', 'Background', 'Line', 'Transparent fill', 'Film selected']; + var colourLabels = ['Text', 'Highlight', 'Button', 'Heading', 'Rating stars', 'Summary', 'Overlay', 'Freestyle border', 'Background', 'Line', 'Transparent fill', 'Film selected']; var covLabels = ['Auto-download [lastfm best quality]', 'Save folder', 'Save file name', 'Check custom paths', 'Custom path 1', 'Custom path 2', 'Custom path 3']; var cusScrollLabels = ['Step (0 = page)', 'Smooth duration max (ms)', 'Touch duration max (ms)', 'Touch sensitivity 0-10']; var cusTextLabels = ['Supports tags or full paths: text, lyrics and item properties. Uses first found for each view.', 'Preview', 'Synchronise with newly downloaded', 'Scroll synced', 'Scroll unsynced']; var dlLabels = ['Custom', 'Default (package data)', 'Review', 'Biography', 'Review', 'Biography', 'Photo', 'Review albumart', 'Similar artists', 'Review', 'Biography', 'Enable']; + var dropShadowLevelLabels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var fallbackLabels = ['Biography', 'Review']; var filmStripLabels = ['Flush', 'Align with image', 'Apply image margins','Align with text', 'Apply text margins', ] var filterLabels = ['KB max size', 'KB min size', 'PX min resolution', 'Min number (top priority)']; @@ -1114,7 +1144,7 @@ var headingPaddingLabels = ['Heading', 'Bottom Line', 'Button']; var highlightLabels = ['Button', 'Heading', 'Line', 'Image border', 'Overlay border', 'Rating stars', 'Subheadings', 'Summary', 'Text']; var langLabels = ['English', 'Deutsch', 'Espa\u00f1ol', 'Fran\u00e7ais', 'Italiano', '\u65e5\u672c\u8a9e', 'Polski', 'Portugu\u00eas', '\u0420\u0443\u0441\u0441\u043a\u0438\u0439', 'Svenska', 'T\u00fcrk\u00e7e', '\u7b80\u4f53\u4e2d\u6587']; - var layoutLabels = ['Double-click toggle', 'Same layout for artist & album', 'Enable look-up of related items']; + var layoutLabels = ['Double-click toggle', 'Same layout for artist & album']; var lookupLabels = ['Hide', 'Top left', 'Prefer heading', 'Auto-cycle time (seconds; min 30)']; var lineLabels = ['None', 'Bottom', 'Center']; var lyricsScrollMethodLabels = ['Scroll time average (ms)', 'Scroll time max (ms)']; @@ -1138,9 +1168,9 @@ var textStyleLabels = ['Top align', 'Expand lists', 'Auto-optimise if multiple items shown']; var tfLabels = ['Biography', 'Review', 'Title', 'Lyrics', 'Title']; var tfServLabels = ['%BIO_ALBUMARTIST%', '%BIO_ARTIST%', '%BIO_ALBUM%', '%BIO_TITLE%', 'Composition tag']; - var themeLabels = ['User interface', 'Dark', 'Blend', 'Light', 'Random', 'Always cover based', 'Swap colours']; - var themeBlurLabels = ['Auto-fill', 'Level (%)', 'Opacity (%)', '%']; - var whitelistLabels = ['Write all last.fm tags', 'Apply filter to remove non-standard genres using this whitelist',]; + var themeLabels = !ppt['themed_internal'].value ? ['User interface', 'Dark', 'Blend', 'Light', 'Random', 'Always cover based', 'Swap colours'] : ['N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set', 'N/A: central set']; + var themeBlurLabels = ['Auto-fill', 'Level (%)', 'Opacity (%)', '%', 'Load delay (ms)', 'Leading item: load immediately (ignore delay)']; + var whitelistLabels = ['Write all last.fm tags', 'Apply filter to remove non-standard genres using this whitelist',]; var imgTable1 = ['artStyleDual', 'covStyleDual', 'artStyleImgOnly', 'covStyleImgOnly', 'filmPhotoStyle', 'filmCoverStyle']; var imgTable2 = [['artReflDual', 'covReflDual', 'artReflImgOnly', 'covReflImgOnly', '', 'Reflection'], ['artBorderDual', 'covBorderDual', 'artBorderImgOnly', 'covBorderImgOnly', '', 'Border'], ['artShadowDual', 'covShadowDual', 'artShadowImgOnly', 'covShadowImgOnly', '', 'Shadow']]; @@ -1177,8 +1207,8 @@ type : ['text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text'] }, advanced: { - name: ['scrollStep', 'durationScroll', 'durationTouchFlick', 'flickDistance', 'sbarWidth', 'narrowSbarWidth', 'sbarArrowWidth', 'sbarPad', 'sbarGripHeight', 'arrowSymbol', 'butCustIconFont', 'sbarButPad', 'blurTemp', 'blurAlpha', 'transLevel', 'serverName'], - type : ['float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'text', 'text', 'float', 'float', 'float', 'float', 'text'] + name: ['scrollStep', 'durationScroll', 'durationTouchFlick', 'flickDistance', 'sbarWidth', 'narrowSbarWidth', 'sbarArrowWidth', 'sbarPad', 'sbarGripHeight', 'arrowSymbol', 'butCustIconFont', 'sbarButPad', 'blurTemp', 'blurAlpha', 'transLevel', 'focusLoadRate', 'serverName'], + type : ['float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'float', 'text', 'text', 'float', 'float', 'float', 'float', 'float', 'text'] }, title_format: { name: ['tfAlbumArtist', 'tfArtist', 'tfAlbum', 'tfTitle', 'tfComposition'], @@ -1210,12 +1240,12 @@ input.all.type = input.behaviour.type.concat(input.display.type, input.headings.type, input.image.type, input.textreader.type, input.custom.type, input.advanced.type, input.title_format.type, input.download.type, input.photo.type, input.cover.type, input.tagger.type, input.misc.type); var box = { behaviour: ['dblClickToggle', 'sameStyle', 'sbarWinMetrics', 'smooth', 'touchControl'], - display: ['topAlign', 'expandLists', 'autoOptimiseText', 'covBlur', 'swapCol', 'highlightHdBtn', 'highlightHdText', 'highlightHdLine', 'highlightImgBor', 'highlightOvBor', 'highlightStars', 'highlightSubHd', 'highlightSummary', 'highlightText', 'amRating', 'lfmRating', 'summaryGenre', 'summaryDate', 'summaryLocale', 'summaryPopNow', 'summaryLatest', 'summaryOther'], + display: ['topAlign', 'expandLists', 'autoOptimiseText', 'covBlur', 'swapCol', 'highlightHdBtn', 'highlightHdText', 'highlightHdLine', 'highlightImgBor', 'highlightOvBor', 'highlightStars', 'highlightSubHd', 'highlightSummary', 'highlightText', 'colLineDark', 'amRating', 'lfmRating', 'summaryGenre', 'summaryDate', 'summaryLocale', 'summaryPopNow', 'summaryLatest', 'summaryOther'], headings: ['hdShowTitle', 'hdBtnShow', 'fullWidthHeading', 'hdShowBtnBg', 'hdShowRedLfm', 'bioFlagShow', 'revFlagShow'], image: ['autoEnlarge', 'imgSmoothTrans', 'imgFilterLfm', 'imgFilterMaxSzEnabled', 'imgFilterMinSzEnabled', 'imgFilterMinPxEnabled', 'autoFilm', 'imgCounter', 'imgSeeker', 'artReflDual', 'covReflDual', 'artReflImgOnly', 'covReflImgOnly', 'artBorderDual', 'covBorderDual', 'artBorderImgOnly', 'covBorderImgOnly', 'artShadowDual', 'covShadowDual', 'artShadowImgOnly', 'covShadowImgOnly'], - textreader: ['txtReaderEnable', 'useTxtReader0', 'lyricsTxtReader0', 'useTxtReader1', 'lyricsTxtReader1', 'useTxtReader2', 'lyricsTxtReader2', 'useTxtReader3', 'lyricsTxtReader3', 'useTxtReader4', 'lyricsTxtReader4', 'useTxtReader5', 'lyricsTxtReader5', 'useTxtReader6', 'lyricsTxtReader6', 'useTxtReader7', 'lyricsTxtReader7', 'syncTxtReaderLyrics', 'scrollSynced', 'scrollUnsynced', 'rowStripes', 'lineDividers'], + textreader: ['txtReaderEnable', 'useTxtReader0', 'lyricsTxtReader0', 'useTxtReader1', 'lyricsTxtReader1', 'useTxtReader2', 'lyricsTxtReader2', 'useTxtReader3', 'lyricsTxtReader3', 'useTxtReader4', 'lyricsTxtReader4', 'useTxtReader5', 'lyricsTxtReader5', 'useTxtReader6', 'lyricsTxtReader6', 'useTxtReader7', 'lyricsTxtReader7', 'largerSyncLyricLine', 'syncTxtReaderLyrics', 'scrollSynced', 'scrollUnsynced', 'rowStripes', 'lineDividers', 'vCenter', 'hCenter'], custom: ['custFontUse', 'custHeadFontUse', 'textUse', 'text_hUse', 'headingBtnUse', 'headingTextUse', 'starsUse', 'summaryUse', 'rectOvUse', 'rectOvBorUse', 'bgUse', 'lineUse', 'bgTransUse', 'frameUse'], - advanced: ['sbarFullHeight', 'blurAutofill'], + advanced: ['sbarFullHeight', 'blurAutofill', 'focusLoadImmediate'], title_format: ['classicalModeEnable'], download: ['languageFallback', 'wikipediaEnGenres', 'dlAmRev', 'dlAmBio', 'dlLfmRev', 'dlLfmBio', 'dlArtImg', 'dlRevImg', 'dlLfmSim', 'dlWikiRev', 'dlWikiBio', 'supCache'], photo: ['photoAutoAdd'], @@ -1383,6 +1413,12 @@ } } + function setDropShadowLevel(n) { + window.shadowFontStyle_dropdown.innerHTML = 'Lyrics & nowplaying drop shadow level: ' + dropShadowLevelLabels[n]; + ppt['dropShadowLevel_internal'].value = n; + window.shadowLevel.style.display = 'none'; + } + function setFilmAlignment(n) { window.film_dropdown.innerHTML = filmStripLabels[n]; ppt['filmStripMargin_internal'].value = n; @@ -1402,7 +1438,7 @@ function setHeadingFont(n, type) { var prop = ['headFontStyle', 'trackStyle', 'sourceStyle', 'wikiStyle', 'lyricsFontStyle', 'summaryStyle'][type]; - window[prop + '_dropdown'].innerHTML = (type == 3 ? 'Wikipedia section heading: ' : type == 4 ? 'Lyrics font style: ' : type == 5 ? 'Summary font style: ' : '') + headingFontLabels[n]; + window[prop + '_dropdown'].innerHTML = (type == 3 ? 'Wikipedia section heading: ' : type == 4 ? 'Lyrics & nowplaying: font style: ' : type == 5 ? 'Summary font style: ' : '') + headingFontLabels[n]; ppt[prop + '_internal'].value = n; setHeadingFontStyle(n, type); switch (type) { @@ -1518,6 +1554,7 @@ for (i = 0; i < 5; i++) {window.themeBg.insertAdjacentHTML('beforeend', ''+themeLabels[i]+'');} for (i = 5; i < 7; i++) {window.themeStyle.insertAdjacentHTML('beforeend', '
');} for (i = 0; i < 9; i++) {window.highlight.insertAdjacentHTML('beforeend', '
');} + window.lines.insertAdjacentHTML('beforeend', '
'); for (i = 0; i < 2; i++) {window.rating1.insertAdjacentHTML('beforeend', '
');} for (i = 2; i < 4; i++) {window.rating2.insertAdjacentHTML('beforeend', '
');} @@ -1617,10 +1654,13 @@ for (i = 0; i < 4; i++) {window.cusText2.insertAdjacentHTML('beforeend', '
')} for (i = 4; i < 8; i++) {window.cusText3.insertAdjacentHTML('beforeend', '
')} - window.lyricsFontStyle_dropdown.innerHTML = 'Lyrics font style: ' + headingFontLabels[ppt['lyricsFontStyle_internal'].value]; + window.lyricsFontStyle_dropdown.innerHTML = 'Lyrics & nowplaying: font style: ' + headingFontLabels[ppt['lyricsFontStyle_internal'].value]; + window.shadowFontStyle_dropdown.innerHTML = 'Lyrics & nowplaying drop shadow level: ' + dropShadowLevelLabels[ppt['dropShadowLevel_internal'].value]; setHeadingFontStyle(ppt['lyricsFontStyle_internal'].value, 4); for (i = 0; i < 6; i++) {window.lyricsFont.insertAdjacentHTML('beforeend', ''+headingFontLabels[i]+'');} - i = 1; window.cusTextPreview.insertAdjacentHTML('beforeend', '
'); + for (i = 0; i < 11; i++) {window.shadowLevel.insertAdjacentHTML('beforeend', ''+dropShadowLevelLabels[i]+'');} + i = 0; window.largerHighlighted.insertAdjacentHTML('beforeend', '
'); + i = 1; window.cusTextPreview.insertAdjacentHTML('beforeend', '
'); i = 2; window.downloadLyrics.insertAdjacentHTML('beforeend', '
'); i = 3; window.syncedScroll.insertAdjacentHTML('beforeend', '
'); i = 4; window.unsyncedScroll.insertAdjacentHTML('beforeend', '
'); @@ -1632,6 +1672,8 @@ i = 8; window.rowStripes.insertAdjacentHTML('beforeend', '
'); i = 9; window.lineDividers.insertAdjacentHTML('beforeend', '
'); i = 10; window.fieldWidth.insertAdjacentHTML('beforeend', '
'); + i = 11; window.npVCenter.insertAdjacentHTML('beforeend', '
'); + i = 12; window.npHCenter.insertAdjacentHTML('beforeend', '
'); } function setCustom() { @@ -1649,7 +1691,9 @@ i = 8; window.scrollbar.insertAdjacentHTML('beforeend', '
'); i = 0; window.themeBlur.insertAdjacentHTML('beforeend', '
'); for (i = 1; i < 3; i++) {window.themeBlur.insertAdjacentHTML('beforeend', '
');} - i = 3; window.smoothTransLevel.insertAdjacentHTML('beforeend', '
'); + i = 3; window.smoothTransLevel.insertAdjacentHTML('beforeend', '
'); + i = 4; window.loadDelay.insertAdjacentHTML('beforeend', '
'); + i = 5; window.loadDelay.insertAdjacentHTML('beforeend', '
'); window.serverName.insertAdjacentHTML('beforeend', '
'); } @@ -1772,7 +1816,7 @@ window.flagInfo.innerHTML = ppt['hdPos_internal'].value == 0 ? "Country codes are saved during wikipedia bio download" : "Flag display requires heading format set to left"; window.highlight_checkbox0.parentNode.disabled = window.colour_checkbox2.checked; window.highlight_checkbox1.parentNode.disabled = window.colour_checkbox3.checked; - window.highlight_checkbox2.parentNode.disabled = window.colour_checkbox9.checked; + window.highlight_checkbox2.parentNode.disabled = window.colour_checkbox9.checked || window.lines_checkbox.checked; window.highlight_checkbox4.parentNode.disabled = window.colour_checkbox7.checked; window.highlight_checkbox5.parentNode.disabled = window.colour_checkbox4.checked; window.highlight_checkbox7.parentNode.disabled = window.colour_checkbox5.checked; @@ -1789,7 +1833,8 @@ document.getElementById('ratingTextName').style.display = ppt['star_internal'].value ? "block" : "none"; window.rating3.style.display = ppt['star_internal'].value ? "block" : "none"; window.theme_checkbox5.parentNode.disabled = ppt['theme_internal'].value == 0; - window.theme_checkbox6.parentNode.disabled = ppt['theme_internal'].value == 1 || 4; + window.theme_checkbox6.parentNode.disabled = ppt['theme_internal'].value == 1 || ppt['theme_internal'].value == 4; + if (ppt['themed_internal'].value) window.themeStyle.style.display = "none"; window.whitelistInfo.style.display = window.useWhitelist1.checked ? "block" : "none"; window.lfmGenreTag.style.display = window.useWhitelist1.checked ? "block" : "none"; @@ -2016,6 +2061,7 @@ ['sbarCol_dropdown', 'sbarCol'], ['sbarIcon_dropdown', 'sbarIcon'], ['seeker_dropdown', 'seeker'], + ['shadowFontStyle_dropdown', 'shadowLevel'], ['theme_dropdown', 'themeBg'] ]; diff --git a/assets/html/confirm.html b/assets/html/confirm.html index e288df8..7d1ced6 100644 --- a/assets/html/confirm.html +++ b/assets/html/confirm.html @@ -39,11 +39,12 @@ if (parsed_args) { document.title = parsed_args[0]; window.div_text.innerText = parsed_args[1]; + if (parsed_args[5] == 'center') window.div_text.style.textAlign = "center"; var labelInnerHtml = window.btn_ok.innerHTML; window.btn_ok.innerHTML = labelInnerHtml.replace(/Yes/g, parsed_args[2]); var labelInnerHtml = window.btn_cancel.innerHTML; window.btn_cancel.innerHTML = labelInnerHtml.replace(/No/g, parsed_args[3]); - callback = parsed_args[4]; + callback = parsed_args[6]; } var window_w = 558; diff --git a/assets/images/Logo.png b/assets/images/Logo.png index cb59832b61fcf36e35f57eb6362d5941b3b1f2a2..4c53ed4a0bd10a21b8f4730428898063202efb1a 100644 GIT binary patch literal 119862 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 diff --git a/main.js b/main.js index 0b62612..0ac0186 100644 --- a/main.js +++ b/main.js @@ -1,6 +1,7 @@ 'use strict'; if (typeof my_utils === 'undefined') include('utils.js'); +//include(fb.ProfilePath + 'elements\\colourSelector.js'); // sort handling n/a standalone const loadAsync = window.GetProperty('Load Biography Asynchronously', true); @@ -17,6 +18,7 @@ const files = [ 'properties.js', 'settings.js', 'interface.js', + 'language.js', 'panel.js', 'server.js', 'allmusic.js', diff --git a/package.json b/package.json index c36fc0f..ef8cd72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "author": "WilB", - "description": "Feature rich all-in-one biography plugin\r\n\r\nDownloads and displays artist photos, biographies and reviews from last.fm, allmusic and wikipedia\r\n\r\n• main artist, similar artists, contributing artists, performers and composers etc\r\n\r\n• main album and top albums\r\n\r\n• history\r\n\r\n• lyrics\r\n\r\n• textreader\r\n\r\n• item properties\r\n\r\n• layout presets: top, left, right, bottom and overlay. Fully customisable with freestyle layouts\r\n\r\n• themes: user interface, dark, blend, light and random\r\n\r\n• custom colours and fonts\r\n\r\n• filmstrip: top, left, right, bottom or hidden\r\n\r\n• image+text, image only or text only\r\n\r\n• drag resizing of image and filmstrip (press Ctrl while panel has focus)\r\n\r\n• custom title format for field remapping and save locations\r\n\r\n• and more...", + "description": "Feature rich all-in-one biography plugin\r\n\r\nDownloads and displays artist photos, biographies and reviews from last.fm, allmusic and wikipedia\r\n\r\n• main artist, similar artists, contributing artists, performers and composers etc\r\n\r\n• main album and top albums\r\n\r\n• history\r\n\r\n• lyrics\r\n\r\n• textreader\r\n\r\n• item properties\r\n\r\n• now playing display\r\n\r\n• layout presets: top, left, right, bottom and overlay. Fully customisable with freestyle layouts\r\n\r\n• themes: user interface, dark, blend, light and random\r\n\r\n• custom colours and fonts\r\n\r\n• filmstrip: top, left, right, bottom or hidden\r\n\r\n• image+text, image only or text only\r\n\r\n• drag resizing of image and filmstrip (press Ctrl while panel has focus)\r\n\r\n• custom title format for field remapping and save locations\r\n\r\n• and more...", "enableDragDrop": false, "id": "{BA9557CE-7B4B-4E0E-9373-99F511E81252}", "name": "Biography", diff --git a/scripts/allmusic.js b/scripts/allmusic.js index 1efe21e..4b68afa 100644 --- a/scripts/allmusic.js +++ b/scripts/allmusic.js @@ -265,9 +265,11 @@ class DldAllmusicRev { $.htmlParse(div.getElementsByTagName('li'), 'id', 'microdata-rating', v => rating = v.innerText.replace(/\D+/g, '') / 2); review = '>> Album rating: ' + rating + ' << ' + review; if (review.length > 22) { - $.buildPth(this.fo_rev); - $.save(this.pth_rev, review, true); - server.res(); + if (this.fo_rev) { + $.buildPth(this.fo_rev); + $.save(this.pth_rev, review, true); + server.res(); + } } else { server.updateNotFound('Rev ' + cfg.partialMatch + ' ' + this.pth_rev); $.trace('allmusic review: ' + this.album + ' / ' + this.albumArtist + ': not found', true); @@ -310,9 +312,11 @@ class DldAllmusicRev { wiki: review, update: Date.now() }; - $.buildPth(this.fo_rev); - $.save(this.pth_rev, JSON.stringify($.sortKeys(text), null, 3), true); - + if (this.fo_rev) { + $.buildPth(this.fo_rev); + $.save(this.pth_rev, JSON.stringify($.sortKeys(text), null, 3), true); + } + if (reviewAuthor || reviewGenre || reviewMood || reviewTheme || review || songReleaseYear || composer) { server.res(); } else { @@ -385,9 +389,11 @@ class Parse { biography = biography.trim(); if (biography.length > 19) { - $.buildPth(fo_bio); - $.save(pth_bio, biography, true); - server.res(); + if (fo_bio) { + $.buildPth(fo_bio); + $.save(pth_bio, biography, true); + server.res(); + } } else { album ? server.updateNotFound('Bio ' + cfg.partialMatch + ' ' + pth_rev) : server.updateNotFound('Bio ' + cfg.partialMatch + ' ' + artist + ' - ' + title); if (!$.file(pth_bio)) $.trace('allmusic biography: ' + artist + ': not found', true); diff --git a/scripts/buttons.js b/scripts/buttons.js index 14d3f15..780ac59 100644 --- a/scripts/buttons.js +++ b/scripts/buttons.js @@ -60,7 +60,6 @@ class Buttons { bahnInstalled: utils.CheckFont('Bahnschrift SemiBold SemiConden'), col: {}, font: gdi.Font('Segoe UI Symbol', 12, 1), - fontSize: 12, h: 19, icon: false, item_w: { @@ -232,67 +231,67 @@ class Buttons { } } - createStars() { + createStars(force) { this.src.icon = ui.show.btnLabel == 2 ? 1 : 0; const hs = ui.font.heading.Size; const fs = ui.stars != 1 ? (this.src.icon ? (this.src.bahnInstalled ? 12 : 11) : 10) * $.scale : 12 * $.scale; + const srcFontSize = this.src.fontSize; this.src.fontSize = $.clamp(Math.round(hs * 0.47) + (ppt.zoomHeadBtn - 100) / 10, Math.min(fs, hs), Math.max(fs, hs)); - this.src.font = gdi.Font('Segoe UI', this.src.fontSize, 1); + if (this.src.fontSize != srcFontSize || force) this.src.font = gdi.Font('Segoe UI', this.src.fontSize, 1); $.gr(1, 1, false, g => { this.src.h = g.CalcTextHeight('allmusic', this.src.font); - }); - switch (this.src.icon) { - case 0: - this.src.amBio = cfg.amDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.amRev = cfg.amDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.lfmBio = cfg.lfmDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.lfmRev = cfg.lfmDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.wikiBio = cfg.wikiDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.wikiRev = cfg.wikiDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.txtBio = (txt.bio.subhead.txt[0] || '').toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.txtRev = (txt.rev.subhead.txt[0] || '').toLowerCase() + (!ppt.sourceAll ? '' : '... '); - if (!ui.show.btnLabel) { - this.src.amBio = ''; - this.src.amRev = ''; - this.src.lfmBio = ''; - this.src.lfmRev = ''; - this.src.wikiBio = ''; - this.src.wikiRev = ''; - this.src.txtBio = ''; - this.src.txtRev = ''; - } - $.gr(1, 1, false, g => { - ['space', 'amRev', 'lfmRev', 'wikiRev', 'txtRev', 'amBio', 'lfmBio', 'wikiBio', 'txtBio'].forEach(v => this.src.item_w[v] = g.CalcTextWidth(this.src[v], this.src.font, true)) - }); - break; - case 1: { - this.src.amBio = this.src.amRev = cfg.amDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.lfmBio = this.src.lfmRev = '\uF202' + (!ppt.sourceAll ? '' : '... '); - this.src.wikiBio = this.src.wikiRev = '\uF266' + (!ppt.sourceAll ? '' : '... '); - this.src.txtBio = (txt.bio.subhead.txt[0] || '').toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.txtRev = (txt.rev.subhead.txt[0] || '').toLowerCase() + (!ppt.sourceAll ? '' : '... '); - this.src.font = gdi.Font(this.src.bahnInstalled ? this.src.bahn : 'Segoe UI Semibold', this.src.fontSize, 0); - this.src.iconFont = gdi.Font('FontAwesome', Math.round(this.src.fontSize * (this.src.bahnInstalled ? 1.09 : 1.16)), 0); - const alt_w = []; - alt_w[9] = ' '; - const fonts = [this.src.font, this.src.font, this.src.iconFont, this.src.iconFont, this.src.font, this.src.font, this.src.iconFont, this.src.iconFont, this.src.font, this.src.iconFont]; - $.gr(1, 1, false, g => { + switch (this.src.icon) { + case 0: + this.src.amBio = cfg.amDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); + this.src.amRev = cfg.amDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); + this.src.lfmBio = cfg.lfmDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); + this.src.lfmRev = cfg.lfmDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); + this.src.wikiBio = cfg.wikiDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); + this.src.wikiRev = cfg.wikiDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); + this.src.txtBio = (txt.bio.subhead.txt[0] || '').toLowerCase() + (!ppt.sourceAll ? '' : '... '); + this.src.txtRev = (txt.rev.subhead.txt[0] || '').toLowerCase() + (!ppt.sourceAll ? '' : '... '); + if (!ui.show.btnLabel) { + this.src.amBio = ''; + this.src.amRev = ''; + this.src.lfmBio = ''; + this.src.lfmRev = ''; + this.src.wikiBio = ''; + this.src.wikiRev = ''; + this.src.txtBio = ''; + this.src.txtRev = ''; + } + $.gr(1, 1, false, g => { + ['space', 'amRev', 'lfmRev', 'wikiRev', 'txtRev', 'amBio', 'lfmBio', 'wikiBio', 'txtBio'].forEach(v => this.src.item_w[v] = g.CalcTextWidth(this.src[v], this.src.font, true)) + }); + break; + case 1: { + this.src.amBio = this.src.amRev = cfg.amDisplayName.toLowerCase() + (!ppt.sourceAll ? '' : '... '); + this.src.lfmBio = this.src.lfmRev = '\uF202' + (!ppt.sourceAll ? '' : '... '); + this.src.wikiBio = this.src.wikiRev = '\uF266' + (!ppt.sourceAll ? '' : '... '); + this.src.txtBio = (txt.bio.subhead.txt[0] || '').toLowerCase() + (!ppt.sourceAll ? '' : '... '); + this.src.txtRev = (txt.rev.subhead.txt[0] || '').toLowerCase() + (!ppt.sourceAll ? '' : '... '); + if (this.src.fontSize != srcFontSize || force) { + this.src.font = gdi.Font(this.src.bahnInstalled ? this.src.bahn : 'Segoe UI Semibold', this.src.fontSize, 0); + this.src.iconFont = gdi.Font('FontAwesome', Math.round(this.src.fontSize * (this.src.bahnInstalled ? 1.09 : 1.16)), 0); + } + const alt_w = []; + alt_w[9] = ' '; + const fonts = [this.src.font, this.src.font, this.src.iconFont, this.src.iconFont, this.src.font, this.src.font, this.src.iconFont, this.src.iconFont, this.src.font, this.src.iconFont]; ['space', 'amRev', 'lfmRev', 'wikiRev', 'txtRev', 'amBio', 'lfmBio', 'wikiBio', 'txtBio', 'spaceIconFont'].forEach((v, i) => { this.src.item_w[v] = g.CalcTextWidth(i < 9 ? this.src[v] : alt_w[i], fonts[i], true); - }) - }); - this.src.item_w.space = Math.max(this.src.item_w.space, this.src.item_w.spaceIconFont); - const n = ppt.artistView ? 'bio' : 'rev'; - this.src.y = this.src.fontSize < 12 || txt[n].loaded.ix == 2 ? 1 : 0; - break; + }); + this.src.item_w.space = Math.max(this.src.item_w.space, this.src.item_w.spaceIconFont); + const n = ppt.artistView ? 'bio' : 'rev'; + this.src.y = this.src.fontSize < 12 || txt[n].loaded.ix == 2 ? 1 : 0; + break; + } } - } + }); if (ui.stars == 1) this.setRatingImages(Math.round(this.src.h / 1.5) * 5, Math.round(this.src.h / 1.5), ui.col.starOn, ui.col.starOff, ui.col.starBor, false); else if (ui.stars == 2) { this.setRatingImages(Math.round(ui.font.main_h / 1.75) * 5, Math.round(ui.font.main_h / 1.75), ui.col.starOn, ui.col.starOff, ui.col.starBor, false); } if (ui.stars == 1 && ui.show.btnRedLastfm) this.setRatingImages(Math.round(this.src.h / 1.5) * 5, Math.round(this.src.h / 1.5), RGBA(225, 225, 245, 255), RGBA(225, 225, 245, 60), ui.col.starBor, true); - this.src.pxShift = /[gjpqy]/.test(this.src.amRev + this.src.lfmRev + this.src.wikiRev + this.src.txtRev + this.src.amBio + this.src.lfmBio + this.src.wikiBio + this.src.txtBio); } @@ -467,9 +466,14 @@ class Buttons { this.btns.lookUp = new Btn(this.lookUp.x, this.lookUp.y, this.lookUp.w, this.lookUp.h, 7, this.lookUp.p1, this.lookUp.p2, '', { normal: RGBA(this.lookUp.col[0], this.lookUp.col[1], this.lookUp.col[2], this.lookUp.pos == 2 ? 100 : 50), hover: RGBA(this.lookUp.col[0], this.lookUp.col[1], this.lookUp.col[2], this.lookUp.pos == 2 ? 200 : this.alpha[1]) - //}, !panel.id.lookUp, '', () => men.buttonMenu(this.lookUp.x + this.lookUp.p1, this.lookUp.y + this.lookUp.h), () => 'Click: look up...\r\n' + (!panel.id.lyricsSource ? 'Middle click: ' + (!panel.lock ? 'lock: stop track change updates' : 'Unlock') + '...' : 'Lock N/A with enabled lyrics source'), true, 'lookUp'); - }, !panel.id.lookUp, '', () => bMenu.load(this.lookUp.x + this.lookUp.p1, this.lookUp.y + this.lookUp.h), () => 'Click: look up...\r\n' + (!panel.id.lyricsSource ? 'Middle click: ' + (!panel.lock ? 'lock: stop track change updates' : 'Unlock') + '...' : 'Lock N/A with enabled lyrics source'), true, 'lookUp'); + }, !panel.id.lookUp, '', () => bMenu.load(this.lookUp.x + this.lookUp.p1, this.lookUp.y + this.lookUp.h), () => 'Click: look up...\r\n' + (!panel.id.lyricsSource && !panel.id.nowplayingSource ? 'Middle click: ' + (!panel.lock ? 'lock: stop track change updates' : 'Unlock') + '...' : 'Lock N/A with enabled lyrics or nowplaying sources'), true, 'lookUp'); } else delete this.btns.lookUp; + if (ppt.summaryShow) { + this.btns.summary = new Btn(panel.text.l, panel.text.t, panel.text.w, ppt.artistView ? (txt.line.h.bio * txt.bio.summaryEnd) : (txt.line.h.rev * txt.rev.summaryEnd), 8, this.lookUp.p1, this.lookUp.p2, '', { + normal: RGBA(this.lookUp.col[0], this.lookUp.col[1], this.lookUp.col[2], this.lookUp.pos == 2 ? 100 : 50), + hover: RGBA(this.lookUp.col[0], this.lookUp.col[1], this.lookUp.col[2], this.lookUp.pos == 2 ? 200 : this.alpha[1]) + }, '', '', () => {ppt.toggle('summaryCompact'); txt.refresh(1);}, '', false, 'summary'); + } else delete this.btns.summary; if (ppt.sbarShow) { switch (ui.sbar.type) { case 2: @@ -510,7 +514,7 @@ class Buttons { } resetZoom() { - txt.bio.scrollPos = {}; // avoids sometimes memory + txt.bio.scrollPos = {}; txt.rev.scrollPos = {}; ppt.zoomFont = 100; ppt.zoomHead = 115; @@ -527,7 +531,7 @@ class Buttons { this.refresh(true); txt.refresh(2); const n = ppt.artistView ? 'bio' : 'rev'; - if (txt[n].loaded.txt && txt.reader.lyrics) txt.getText(); + if (txt[n].loaded.txt && txt.reader[n].lyrics) txt.getText(); } scrollAlb() { @@ -886,6 +890,7 @@ class Btn { trace(x, y) { but.traceBtn = !this.hide && x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h; + if (this.name == 'summary' && (ppt.artistView && art_scrollbar.delta > txt.line.h.bio * txt.bio.summaryEnd || !ppt.artistView && alb_scrollbar.delta > txt.line.h.rev * txt.rev.summaryEnd)) but.traceBtn = false; return but.traceBtn; } } diff --git a/scripts/callbacks.js b/scripts/callbacks.js index 6b81b0a..6896115 100644 --- a/scripts/callbacks.js +++ b/scripts/callbacks.js @@ -15,6 +15,10 @@ function on_colours_changed() { if (ui.font.heading && ui.font.heading.Size) but.createStars(); img.clearCache(); img.getImages(); + if (panel.id.lyricsSource) { + lyrics.transBot = {} + lyrics.transTop = {} + } txt.rev.cur = ''; txt.bio.cur = ''; txt.albCalc(); @@ -207,6 +211,7 @@ function on_mouse_leave() { } function on_mouse_mbtn_up(x, y, mask) { + // hacks at default settings blocks on_mouse_mbtn_up, at least in windows; workaround configure hacks: main window > move with > caption only & ensure pseudo-caption doesn't overlap buttons switch (true) { case mask == 0x0004: panel.inactivate(); @@ -280,7 +285,7 @@ function on_mouse_wheel(step) { function on_notify_data(name, info) { let clone; - if (ui.id.local) { + if (ui.id.local && name.startsWith('opt_')) { clone = typeof info === 'string' ? String(info) : info; on_cui_notify(name, clone); } @@ -364,9 +369,6 @@ function on_notify_data(name, info) { panel.notifyTimestamp = Date.now(); window.NotifyOthers(`bio_notServer${ppt.serverName}`, panel.notifyTimestamp); break; - case 'bio_checkTimerSync': - timer.image() - break; case 'bio_refresh': window.Reload(); break; @@ -380,7 +382,7 @@ function on_notify_data(name, info) { } break; case 'bio_followSelectedTrack': - if (!panel.id.lyricsSource) { // if there is a lyricsSource enabled, panel has to be in prefer nowplaying mode + if (!panel.id.lyricsSource && !panel.id.nowplayingSource) { // if enabled, panel has to be in prefer nowplaying mode if (panel.id.focus !== info) { panel.id.focus = ppt.focus = info; panel.changed(); @@ -393,10 +395,36 @@ function on_notify_data(name, info) { ppt.panelActive = info; window.Reload(); break; + case 'bio_syncTags': + if ($.server) { + tag.force = true; + const focus = info; + server.call(focus); + } + break; case 'bio_webRequest': clone = String(info); - server.urlRequested[info] = Date.now(); // if multiServer enabled, limit URL requests for same item to one - break; + server.urlRequested[clone] = Date.now(); // if multiServer enabled, limit URL requests for same item to one + break; + case 'newThemeColours': + if (!ppt.themed) break; + ppt.theme = info.theme; + ppt.themeBgImage = info.themeBgImage; + ppt.themeColour = info.themeColour; + on_colours_changed(); + break; + case 'Sync col': + if (!ppt.themed) break; + const themeLight = ppt.themeLight; + if (themeLight != info.themeLight) { + ppt.themeLight = info.themeLight; + on_colours_changed(); + } + break; + case 'Sync image': + if (!ppt.themed) break; + sync.image(new GdiBitmap(info.image), info.id); + break } } @@ -423,7 +451,7 @@ function on_playback_dynamic_info_track() { txt.rev.wikiFallback = true; if ($.server) server.downloadDynamic(); txt.reader.lyrics3Saved = false; - txt.reader.openLyricsSaved = false; + txt.reader.ESLyricSaved = false; txt.reader.trackStartTime = fb.PlaybackTime; txt.on_playback_new_track(); img.on_playback_new_track(); @@ -431,12 +459,12 @@ function on_playback_dynamic_info_track() { function on_playback_new_track() { if (!ppt.panelActive) return; - if ($.server) server.on_playback_new_track(); + if ($.server) server.call(); if (panel.id.focus) return; txt.rev.amFallback = true; txt.rev.wikiFallback = true; txt.reader.lyrics3Saved = false; - txt.reader.openLyricsSaved = false; + txt.reader.ESLyricSaved = false; txt.reader.trackStartTime = 0; txt.on_playback_new_track(); img.on_playback_new_track(); @@ -448,12 +476,29 @@ function on_playback_pause(state) { function on_playback_seek() { if (panel.id.lyricsSource) lyrics.seek(); + if (panel.block()) return; + const n = ppt.artistView ? 'bio' : 'rev'; + if ((txt[n].loaded.txt && txt.reader[n].nowplaying || ppt.sourceAll) && txt.reader[n].perSec) { + txt.logScrollPos(); + txt.getText(); + txt.paint(); + } +} + +function on_playback_time(t) { + if (panel.block()) return; + const n = ppt.artistView ? 'bio' : 'rev'; + if ((txt[n].loaded.txt && txt.reader[n].nowplaying || ppt.sourceAll) && txt.reader[n].perSec) { + txt.logScrollPos(); + txt.getText(); + txt.paint(); + } } function on_playback_stop(reason) { if (!ppt.panelActive) return; const n = ppt.artistView ? 'bio' : 'rev'; - if (reason != 2 && txt[n].loaded.txt && txt.reader.lyrics) txt.getText(); + if (reason != 2 && txt[n].loaded.txt && txt.reader[n].lyrics) txt.getText(); if (panel.id.lyricsSource) lyrics.clear(); if (reason == 2) return; on_item_focus_change(); @@ -488,11 +533,12 @@ function on_script_unload() { txt.deactivateTooltip(); } +const windowMetricsPath = `${fb.ProfilePath}settings\\themed\\windowMetrics.json`; function on_size() { txt.repaint = false; panel.w = window.Width; panel.h = window.Height; - if (!window.IsVisible && ui.pss.installed) { + if (!window.IsVisible && ui.pss.installed && !ppt.themed) { ui.pss.checkOnSize = true; return; } @@ -500,14 +546,27 @@ function on_size() { if (!panel.w || !panel.h) return; txt.logScrollPos('bio'); txt.logScrollPos('rev'); - ui.getFont(); - panel.getLogo(); + ui.getParams(); + if (!ppt.panelActive) return; txt.deactivateTooltip(); panel.calcText = true; txt.on_size(); + + if (ppt.themed && ppt.theme) { + const themed_image = `${fb.ProfilePath}settings\\themed\\themed_image.bmp`; + if ($.file(themed_image)) sync.image(gdi.Image(themed_image)); + } img.on_size(); filmStrip.on_size(); txt.repaint = true; - img.art.displayedOtherPanel = null; + img.art.displayedOtherPanel = null; + + if (!ppt.themed) return; + const windowMetrics = $.jsonParse(windowMetricsPath, {}, 'file'); + windowMetrics[window.Name] = { + w: panel.w, + h: panel.h + } + $.save(windowMetricsPath, JSON.stringify(windowMetrics, null, 3), true); } \ No newline at end of file diff --git a/scripts/filmstrip.js b/scripts/filmstrip.js index 1c97d6b..04952cf 100644 --- a/scripts/filmstrip.js +++ b/scripts/filmstrip.js @@ -85,6 +85,14 @@ class FilmStrip { // Methods + async load_image_async(image_path) { + const image = await gdi.LoadImageAsyncV2(0, image_path); + if (!panel.style.showFilmStrip) return; + const key = this.getLoadKey(image_path); + const o = this.cache[key]; + if (o && o.img == 'called') this.cacheIt(image, key, o.style); + } + cacheIt(image, key, style) { try { if (image) { @@ -529,7 +537,14 @@ class FilmStrip { if (!panel.style.showFilmStrip) return; const key = this.getLoadKey(image_path); const o = this.cache[key]; - if (o && o.img == 'called') this.cacheIt(image, key, o.style); + if (o && o.img == 'called') { + if (image) this.cacheIt(image, key, o.style); + else { + setTimeout(() => { + this.load_image_async(image_path); // try again some dnlded can fail to load: folder temp locked? + }, 1500); + } + } } on_size() { @@ -567,6 +582,7 @@ class FilmStrip { case 1: case 2: case 3: + ppt.showFilmStrip = true; ppt.filmStripPos = i; break; case 5: { @@ -575,12 +591,13 @@ class FilmStrip { } const caption = 'Reset Filmstrip To Default Size'; const prompt = 'Continue?'; - const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', continue_confirmation) : true; + const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'Yes', 'No', '', '', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $.wshPopup(prompt, caption)); break; } } filmStrip.logScrollPos(); + img.mask.reset = true; this.clearCache(); this.setSize(); this.check(i); diff --git a/scripts/helpers.js b/scripts/helpers.js index f93aacb..4825e12 100644 --- a/scripts/helpers.js +++ b/scripts/helpers.js @@ -101,11 +101,12 @@ class Helpers { if (!n) return ''; const tfo = FbTitleFormat(n); if (panel.isRadio(focus)) return tfo.Eval(); - const handle = $.handle(focus, ignoreLock); + const handle = this.handle(focus, ignoreLock); return handle ? tfo.EvalWithMetadb(handle) : ''; } equal(arr1, arr2) { + if (!this.isArray(arr1) || !this.isArray(arr2)) return false; let i = arr1.length; if (i != arr2.length) return false; while (i--) @@ -114,11 +115,11 @@ class Helpers { } file(f) { - return fso.FileExists(f); + return typeof f === 'string' && fso.FileExists(f); } folder(fo) { - return fso.FolderExists(fo); + return typeof fo === 'string' && fso.FolderExists(fo); } getClipboardData() { @@ -367,11 +368,11 @@ class Helpers { } tfEscape(n) { - return n.replace(/'/g, "''").replace(/[()[\],%]/g, "'$&'").replace(/\$/g, '$$$$'); + return n.replace(/'/g, "''").replace(/[()[\],%]/g, "'$&'").replace(/\$/g, `'$$$$'`); } throttle(e,i,t) { - var n=!0,r=!0;if('function'!=typeof e)throw new TypeError('throttle: invalid function');return this.isObject(t)&&(n='leading'in t?!!txt.leading:n,r='trailing'in t?!!txt.trailing:r),this.debounce(e,i,{leading:n,maxWait:i,trailing:r}) + var n=!0,r=!0;if('function'!=typeof e)throw new TypeError('throttle: invalid function');return this.isObject(t)&&(n='leading'in t?!!txt.leading:n,r='trailing'in t?!!txt.trailing:r),this.debounce(e,i,{leading:n,maxWait:i,trailing:r}); } titlecase(n) { diff --git a/scripts/images.js b/scripts/images.js index 088ff36..84aa960 100644 --- a/scripts/images.js +++ b/scripts/images.js @@ -13,6 +13,7 @@ class Images { this.nh = 10; this.nw = 10; this.removed = 0; + this.themed = null; this.x = 0; this.y = 0; this.exclArr = [6467, 6473, 6500, 24104, 24121, 34738, 29520, 35875, 37235, 47700, 52526, 68626, 86884, 92172]; @@ -108,7 +109,7 @@ class Images { this.refl = { adjust: false, - gradient: ppt.overlayGradient / 10 - 1, + gradient: ppt.reflGradient / 10 - 1, size: $.clamp(ppt.reflSize / 100, 0.1, 1), strength: $.clamp(255 * ppt.reflStrength / 100, 0, 255) } @@ -254,13 +255,13 @@ class Images { this.art.folderSup = ''; let files = []; if (ppt.cycPhotoLocation == 1) { - this.art.folder = panel.cleanPth(cfg.artCusImgFolder, panel.id.focus); + this.art.folder = !panel.isRadio(panel.id.focus) ? panel.cleanPth(cfg.artCusImgFolder, panel.id.focus) : panel.cleanPth(cfg.remap.foCycPhoto, panel.id.focus, 'remap', this.artist, '', 1); files = utils.Glob(this.art.folder + '*'); } if (files.length && files.some(v => /(?:jpe?g|png|webp|gif|bmp)$/i.test(fso.GetExtensionName(v)))) { this.art.cusPhotoLocation = true; } else { - this.art.folder = panel.cleanPth(cfg.pth.foImgArt, panel.id.focus); + this.art.folder = !panel.isRadio(panel.id.focus) ? panel.cleanPth(cfg.pth.foImgArt, panel.id.focus) : panel.cleanPth(cfg.remap.foImgArt, panel.id.focus, 'remap', this.artist, '', 1); this.art.cusPhotoLocation = false; } this.clearArtCache(true); @@ -272,6 +273,20 @@ class Images { } } + async load_image_async(image_path) { + const image = await gdi.LoadImageAsyncV2(0, image_path); + const caller = this.getCallerId(image_path); + if (caller.art_id === this.art.ix) { + if (!image) { + this.art.images.splice(this.art.ix, 1); + if (this.art.images.length > 1) this.changePhoto(1); + filmStrip.check('imgUpd'); + return; + } + this.processArtImg(image, image_path); + } + } + blacklist(clean_artist) { let black_list = []; if (!$.file(this.blackList.file)) return black_list; @@ -289,7 +304,7 @@ class Images { } blurCheck() { - if (!(ppt.covBlur && ui.style.isBlur) && !ppt.imgSmoothTrans) return; + if (!(ppt.covBlur && ui.style.isBlur) && !ppt.imgSmoothTrans || ppt.themed) return; this.id.curBlur = this.id.blur; this.id.blur = name.albID(panel.id.focus, 'stnd'); this.id.blur += ppt.covType; @@ -332,7 +347,7 @@ class Images { this.cov.newBlur = false; if (this.cov.blur && !ppt.blurAutofill) this.cov.blur = this.cov.blur.Resize(panel.w, panel.h); } - if (this.covBlur() && this.cov.blur) image = this.cov.blur; + if (this.covBlur() && this.cov.blur) image = this.cov.blur.Clone(0, 0, this.cov.blur.Width, this.cov.blur.Height); // clone to stop blurring same img more than once image = ppt.blurAutofill ? this.format(image, 1, 'crop', panel.w, panel.h, 'blurAutofill', o) : this.format(image, 1, 'stretch', panel.w, panel.h, 'blurStretch', o); const i = $.gr(panel.w, panel.h, true, (g, gi) => { g.SetInterpolationMode(0); @@ -507,7 +522,7 @@ class Images { const font2 = gdi.Font('Segoe UI', 120, 1); const font3 = gdi.Font('Segoe UI', 200, 1); const font4 = gdi.Font('Segoe UI', 90, 1); - const tcol = !bg ? ui.col.text : ui.dui ? window.GetColourDUI(0) : window.GetColourCUI(0); + const tcol = ui.col.text; const sz = 600; for (let i = 0; i < 3; i++) { this.stub.default[i] = $.gr(sz, sz, true, g => { @@ -539,9 +554,14 @@ class Images { if (ppt.showFilmStrip && this.get) this.getImgFallback(); return; } - if (ui.style.isBlur && this.blur) gr.DrawImage(this.blur, 0, 0, this.blur.Width, this.blur.Height, 0, 0, this.blur.Width, this.blur.Height); + if (ui.style.isBlur) { + const bImg = !this.themed ? this.blur : this.themed; + if (bImg) gr.DrawImage(bImg, 0, 0, panel.w, panel.h, 0, 0, bImg.Width, bImg.Height); + } if (this.get) return this.getImgFallback(); - if (!ppt.text_only && this.cur) gr.DrawImage(this.cur, this.x, this.y, this.cur.Width, this.cur.Height, 0, 0, this.cur.Width, this.cur.Height, 0, this.style.alpha); + if (!ppt.text_only && this.cur) { + gr.DrawImage(this.cur, this.x, this.y, this.cur.Width, this.cur.Height, 0, 0, this.cur.Width, this.cur.Height, 0, this.style.alpha); + } } fadeMask(image, x, y, w, h) { @@ -563,6 +583,10 @@ class Images { } }); this.mask.reset = false; + if (ppt.style == 4 && panel.style.showFilmStrip) { + const rotate = [2, 3, 0, 1][ppt.filmStripPos]; + this.mask.fade.RotateFlip(rotate); + } } const mask = $.gr(w, h, true, g => g.DrawImage(this.mask.fade, xl, yl, wl, hl, 0, 0, this.mask.fade.Width, this.mask.fade.Height)); image.ApplyMask(mask); @@ -574,7 +598,7 @@ class Images { forceStnd() { const n = ppt.artistView ? 'bio' : 'rev'; - return !ppt.sourceAll && txt[n].loaded.txt && (txt.reader.props || txt.reader.lyrics) && (ppt.artistView && panel.art.ix || !ppt.artistView && panel.alb.ix); + return !ppt.sourceAll && txt[n].loaded.txt && (txt.reader[n].props || txt.reader[n].lyrics) && (ppt.artistView && panel.art.ix || !ppt.artistView && panel.alb.ix); } format(image, n, type, w, h, caller, o, blur, border, fade, reflection) { @@ -668,6 +692,7 @@ class Images { getArtImages() { let allFiles = this.art.folder ? utils.Glob(this.art.folder + '*') : []; if (!allFiles.length && this.art.folderSup) allFiles = utils.Glob(this.art.folderSup + '*'); + console.log('allFiles.length',allFiles.length,'this.art.allFilesLength',this.art.allFilesLength) if (allFiles.length == this.art.allFilesLength) return; let newArr = false; if (!this.art.images.length) { @@ -684,7 +709,6 @@ class Images { return; } } else filmStrip.logScrollPos(this.art.list); - let arr = this.art.list.slice(); if (this.filter.size) arr = arr.filter(this.sizeFilter.bind(this)); this.art.images = this.art.images.concat(arr); @@ -698,8 +722,9 @@ class Images { getArtImg(update, bypass) { if (!ppt.artistView || ppt.text_only && !ui.style.isBlur && !ppt.showFilmStrip) return; - if (!bypass && (panel.id.lyricsSource || panel.id.propsSource)) { + if (!bypass && (panel.id.lyricsSource || panel.id.nowplayingSource || panel.id.propsSource)) { this.getItem(panel.art.ix, panel.alb.ix); + this.init = false; } if (!this.art.done || update) { this.art.done = true; @@ -892,7 +917,7 @@ class Images { if (ppt.cycPhoto) { if (new_artist) { this.counter = 0; - this.art.folder = panel.lock ? panel.cleanPth(cfg.remap.foImgArt, panel.id.focus, 'remap', this.artist, '', 1) : stndBio ? panel.cleanPth(cfg.pth.foImgArt, panel.id.focus) : panel.cleanPth(cfg.remap.foImgArt, panel.id.focus, 'remap', this.artist, '', 1); + this.art.folder = panel.lock || panel.isRadio(panel.id.focus) ? panel.cleanPth(cfg.remap.foImgArt, panel.id.focus, 'remap', this.artist, '', 1) : stndBio ? panel.cleanPth(cfg.pth.foImgArt, panel.id.focus) : panel.cleanPth(cfg.remap.foImgArt, panel.id.focus, 'remap', this.artist, '', 1); this.art.folderSup = ''; if (!stndBio && cfg.supCache && !$.folder(this.art.folder)) this.art.folderSup = panel.cleanPth(cfg.sup.foImgArt, panel.id.focus, 'remap', this.artist, '', 1); this.clearArtCache(true); @@ -1001,7 +1026,7 @@ class Images { freqTot += v.freq; }); const avgCol = [$.clamp(Math.round(Math.sqrt(rTot / freqTot)), 0, 255), $.clamp(Math.round(Math.sqrt(gTot / freqTot)), 0, 255), $.clamp(Math.round(Math.sqrt(bTot / freqTot)), 0, 255)]; - return ui.getSelCol(avgCol, true, true) == 50 ? true : false; + return ui.isLightCol(avgCol, true) ? true : false; } isType(n, image) { @@ -1013,10 +1038,11 @@ class Images { case 'AnyBor': return ['artBorderImgOnly', 'artBorderDual', 'covBorderImgOnly', 'covBorderDual'].some(v => ppt[v]); case 'Fade': - return !ppt.typeOverlay && ppt.style > 3 && !ppt.img_only; + return (!ppt.typeOverlay || ppt.style == 4 && !ppt.typeOverlay) && ppt.style > 3 && !ppt.img_only; case 'Overlay': return ppt.style > 3 && ppt.alignAuto && !ppt.img_only; case 'Circ': + if (ppt.style == 4) return false; switch (ppt.artistView) { case true: return !ppt.img_only ? ppt.artStyleDual == 2 : ppt.artStyleImgOnly == 2; @@ -1240,20 +1266,13 @@ class Images { const caller = this.getCallerId(image_path); if (caller.art_id === this.art.ix) { if (!image) { - this.art.images.splice(this.art.ix, 1); - if (this.art.images.length > 1) this.changePhoto(1); - filmStrip.check('imgUpd'); + setTimeout(() => { + this.load_image_async(image_path); // try again in case dnlded fails to load: folder temp locked? + }, 1000); return; } - if (this.filter.size && (!ppt.imgFilterBothPx ? image.Width < this.filter.minPx && image.Height < this.filter.minPx : image.Width < this.filter.minPx || image.Height < this.filter.minPx) && this.art.images.length > this.filter.minNo) { - this.art.images.splice(this.art.ix, 1); - seeker.upd(false, false, true); - if (ppt.showFilmStrip) filmStrip.trimCache(image_path); - this.changePhoto(1); - filmStrip.check('imgUpd'); - return; - } - art.cacheIt(image, image_path); + + this.processArtImg(image, image_path); } if (caller.cov_id === this.cov.ix) { if (!txt.rev.lookUp && !this.cov.cycle) this.clearCovCache(); @@ -1270,6 +1289,21 @@ class Images { cov.cacheIt(image, image_path); } } + + processArtImg(image, image_path) { + const caller = this.getCallerId(image_path); + if (caller.art_id === this.art.ix) { + if (this.filter.size && (!ppt.imgFilterBothPx ? image.Width < this.filter.minPx && image.Height < this.filter.minPx : image.Width < this.filter.minPx || image.Height < this.filter.minPx) && this.art.images.length > this.filter.minNo) { + this.art.images.splice(this.art.ix, 1); + seeker.upd(false, false, true); + if (ppt.showFilmStrip) filmStrip.trimCache(image_path); + this.changePhoto(1); + filmStrip.check('imgUpd'); + return; + } + art.cacheIt(image, image_path); + } + } on_playback_new_track(force) { this.resetCounters(); @@ -1360,7 +1394,7 @@ class Images { this.im.h = this.nh; break; } - return this.format(image, n, type, this.im.w, this.im.h, 'img', o, this.style.blur, this.style.border, this.style.fade, this.style.reflection); + return this.format(image, n, type, this.im.w, this.im.h, 'img', o, !ppt.themed ? this.style.blur : false, this.style.border, this.style.fade, this.style.reflection); } processSizeFilter() { @@ -1558,7 +1592,7 @@ class Images { setCrop(sz) { const imgRefresh = ppt.img_only; this.style.crop = this.isType('Circ') ? false : - ppt.artistView && (ppt.artStyleImgOnly == 1 && imgRefresh || ppt.artStyleDual == 1 && !ppt.img_only) || !ppt.artistView && (ppt.covStyleImgOnly == 1 && imgRefresh || ppt.covStyleDual == 1 && !ppt.img_only); + ppt.artistView && (ppt.artStyleImgOnly == 1 && imgRefresh || (ppt.artStyleDual == 1 || ppt.style == 4) && !ppt.img_only) || !ppt.artistView && (ppt.covStyleImgOnly == 1 && imgRefresh || (ppt.covStyleDual == 1 || ppt.style == 4) && !ppt.img_only); panel.setBorder(imgRefresh && this.style.crop, this.isType('Border'), this.isType('Refl')); if (sz) { panel.setStyle(); diff --git a/scripts/interface.js b/scripts/interface.js index 36c7a1f..6d65f2b 100644 --- a/scripts/interface.js +++ b/scripts/interface.js @@ -32,15 +32,8 @@ class UserInterface { headingStyle: 1, items: [['lyricsFontStyle', 'lyrics'], ['sourceStyle', 'subHeadSource'], ['summaryStyle', 'summary'], ['trackStyle', 'subHeadTrack'], ['wikiStyle', 'subHeadWiki']], lyrics: gdi.Font('Segoe UI', 16, 3), - main: gdi.Font('Segoe UI', 16, 0), main_h: 21, - message: gdi.Font('Segoe UI', 16, 0), - small: gdi.Font('Segoe UI', 10, 0), small_h: 8, - subHeadSource: gdi.Font('Segoe UI', 16, 0), - subHeadTrack: gdi.Font('Segoe UI', 16, 0), - subHeadWiki: gdi.Font('Segoe UI', 16, 0), - summary: gdi.Font('Segoe UI', 16, 0), zoomSize: 16 } @@ -100,12 +93,15 @@ class UserInterface { trans: false } + this.themeColour = {} + if (ppt.narrowSbarWidth != 0) ppt.narrowSbarWidth = $.clamp(ppt.narrowSbarWidth, 2, 10); ppt.hdLine = $.value(ppt.hdLine, 1, 2); if (ppt.headFontStyle < 0 || ppt.headFontStyle > 5) ppt.headFontStyle = 2; this.id.c_c = this.id.local && typeof opt_c_c !== 'undefined'; this.getColours(); + this.getFont(true); this.refresh = $.debounce(() => { txt.refresh(3); @@ -206,7 +202,7 @@ class UserInterface { const c = ppt.text_h.replace(/[^0-9.,-]/g, '').split(/[,-]/); if (c.length == 3 || c.length == 4) valid = true; } - this.col.accent = !this.blur.dark || ppt.text_hUse && valid ? this.col.text_h : RGB(128, 228, 27); + this.col.accent = !this.blur.dark || ppt.text_hUse && valid ? this.col.text_h : ppt.themed && ppt.theme == 9 ? RGB(104, 225, 255) : RGB(128, 228, 27); } getBlend(c1, c2, f) { @@ -222,50 +218,91 @@ class UserInterface { } getBlurColours() { - this.style.isBlur = ppt.theme > 0; - this.blur = { - alpha: $.clamp(ppt.blurAlpha, 0, 100) / 30, - blend: ppt.theme == 2, - blendAlpha: $.clamp($.clamp(ppt.blurAlpha, 0, 100) * 105 / 30, 0, 255), - dark: ppt.theme == 1 || ppt.theme == 4, - level: ppt.theme == 2 ? 91.05 - $.clamp(ppt.blurTemp, 1.05, 90) : $.clamp(ppt.blurTemp * 2, 0, 254), - light: ppt.theme == 3 - } - if (this.blur.dark) { - this.col.bg_light = RGBA(0, 0, 0, Math.min(160 / this.blur.alpha, 255)); - this.col.bg_dark = RGBA(0, 0, 0, Math.min(80 / this.blur.alpha, 255)); - if (ppt.typeOverlay) { - if (!ppt.rectOvUse) this.col.rectOv = RGBA(0, 0, 0, 255 - this.overlay.strength); - } - } - if (this.blur.light) { - this.col.bg_light = RGBA(255, 255, 255, Math.min(160 / this.blur.alpha, 255)); - this.col.bg_dark = RGBA(255, 255, 255, Math.min(205 / this.blur.alpha, 255)); - if (ppt.typeOverlay) { - if (!ppt.rectOvUse) this.col.rectOv = RGBA(255, 255, 255, 255 - this.overlay.strength); - } + switch (true) { + case !ppt.themed: // has to be able to create image: uses original themes + if (ppt.theme > 4) ppt.theme = 0; // reset if coming from themed & out of bounds + this.style.isBlur = ppt.theme > 0; + this.blur = { + alpha: $.clamp(ppt.blurAlpha, 0, 100) / 30, + blend: ppt.theme == 2, + blendAlpha: $.clamp($.clamp(ppt.blurAlpha, 0, 100) * 105 / 30, 0, 255), + dark: ppt.theme == 1 || ppt.theme == 4, + level: ppt.theme == 2 ? 91.05 - $.clamp(ppt.blurTemp, 1.05, 90) : $.clamp(ppt.blurTemp * 2, 0, 254), + light: ppt.theme == 3 + } + if (this.blur.dark) { + this.col.bg_light = RGBA(0, 0, 0, Math.min(160 / this.blur.alpha, 255)); + this.col.bg_dark = RGBA(0, 0, 0, Math.min(80 / this.blur.alpha, 255)); + if (ppt.typeOverlay) { + if (!ppt.rectOvUse) this.col.rectOv = RGBA(0, 0, 0, 255 - this.overlay.strength); + } + } + if (this.blur.light) { + this.col.bg_light = RGBA(255, 255, 255, Math.min(160 / this.blur.alpha, 255)); + this.col.bg_dark = RGBA(255, 255, 255, Math.min(205 / this.blur.alpha, 255)); + if (ppt.typeOverlay) { + if (!ppt.rectOvUse) this.col.rectOv = RGBA(255, 255, 255, 255 - this.overlay.strength); + } + } + break; + case ppt.themed: // sent image + this.style.isBlur = ppt.theme || ppt.themeBgImage; + this.blur.blend = ppt.theme == 6 || ppt.theme == 7; + this.blur.dark = ppt.theme == 1 && !ppt.themeLight || ppt.theme == 2 && !ppt.themeLight || ppt.theme == 3 || ppt.theme == 4 || ppt.theme == 5 || ppt.theme == 9; + this.blur.light = ppt.theme == 1 && ppt.themeLight || ppt.theme == 2 && ppt.themeLight || ppt.theme == 8; + break; } } + getButCol(c, bypass) { + return this.getLuminance(c, bypass) > 0.35 ? 50 : 200; + } + getColours() { this.assignColours(); this.getBlurColours(); this.setStarType(); this.getUIColours(); this.getItemColours(); + if (ppt.themed) { + if ((ppt.theme == 0 || ppt.theme == 6 || ppt.theme == 7) && this.themeColour && ppt.themeColour) { + // nothing to do + } else { + this.themeColour = { + name: 'User interface', + text: this.col.text, + background: this.col.bg, + selection: this.col.bgSel, + highlight: this.col.text_h, + bar: RGBA(0, 0, 0, 63) + } + } + } this.getAccentColour(); } - getFont() { - const RunningWine = () => utils.IsFile('Z:\\bin\\bash' || 'Z:\\bin\\ls' || 'Z:\\bin\\sh' || 'Z:\\etc\\fstab'); /* detects if user is running Wine on Linux or MacOs, default Wine mount point is Z:\ */ + getColSat(c) { + c = $.toRGB(c); + return c[0] + c[1] + c[2]; + } + getFont(init) { + const DetectWine = () => { /*Detects if user is running Wine on Linux or MacOs, default Wine mount point is Z:\*/ + const diskLetters = Array.from(Array(26)).map((e, i) => i + 65).map((x) => `${String.fromCharCode(x)}:\\`); + const paths = ['bin\\bash', 'bin\\ls', 'bin\\sh', 'etc\\fstab']; + return diskLetters.some(d => { + try { // Needed when permission error occurs and current SMP implementation is broken for some devices.... + return utils.IsDirectory(d) ? paths.some(p => utils.IsFile(d + p)) : false; + } catch (e) {return false;} + }); + } if (ppt.custFontUse && ppt.custFont.length) { const custFont = $.split(ppt.custFont, 1); this.font.main = gdi.Font(custFont[0], Math.max(Math.round($.value(custFont[1], 16, 0)), 1), Math.round($.value(custFont[2], 0, 0))); } else if (this.dui) this.font.main = window.GetFontDUI(3); else this.font.main = window.GetFontCUI(0); - if (!this.font.main || /tahoma/i.test(this.font.main.Name) && RunningWine()) { // Windows: check still needed (test MS Serif or Modern, neither can be used); Wine: tahoma is default system font, but bold and some unicode characters don't work: if Wine + tahoma detected changed to Segoe UI (if that's not installed, tahoma is still used) + if (!this.font.main || /tahoma/i.test(this.font.main.Name) && DetectWine()) { // Windows: check still needed (test MS Serif or Modern, neither can be used); Wine: tahoma is default system font, but bold and some unicode characters don't work: if Wine + tahoma detected changed to Segoe UI (if that's not installed, tahoma is still used) this.font.main = gdi.Font('Segoe UI', 16, 0); $.trace('Spider Monkey Panel is unable to use your default font. Using Segoe UI at default size & style instead'); } @@ -316,17 +353,12 @@ class UserInterface { } } - this.calcText(); - panel.setStyle(); - but.createStars(); - but.setTooltipFont(); - txt.getSubHeadWidths(); - txt.artCalc(); - txt.albCalc(); + if (init) return; + this.getParams(); } getItemColours() { - const lightBg = this.getSelCol(this.col.bg == 0 ? 0xff000000 : this.col.bg, true) == 50; + const lightBg = this.isLightBackground(); let customColText = false; let customColText_h = false; @@ -335,7 +367,7 @@ class UserInterface { this.col.txt = this.col.text; customColText = true; } - if (this.col.text_h === '') this.col.txt_h = this.blur.blend ? this.setBrightness(this.col.txt_h, lightBg ? -10 : 10) : this.blur.dark ? RGB(255, 255, 255) : this.blur.light ? RGB(71, 129, 183) : this.col.txt_h; + if (this.col.text_h === '') this.col.txt_h = ppt.themed && ppt.theme == 9 ? RGB(104, 225, 255) : this.blur.blend ? this.setBrightness(this.col.txt_h, lightBg ? -10 : 10) : this.blur.dark ? RGB(255, 255, 255) : this.blur.light ? (ppt.themed && (ppt.theme == 1 || ppt.themed == 2) ? RGB(25, 25, 25) : RGB(71, 129, 183)) : this.col.txt_h; else { this.col.txt_h = this.col.text_h; customColText_h = true; @@ -349,8 +381,8 @@ class UserInterface { if (this.id.local) { this.style.trans = c_trans; this.col.bg = c_backcol; - this.col.txt = this.blur.blend ? this.setBrightness(c_textcol, this.getSelCol(c_backcol == 0 ? 0xff000000 : c_backcol, true) == 50 ? -10 : 10) : this.blur.dark ? RGB(255, 255, 255) : this.blur.light ? RGB(50, 50, 50) : c_textcol; - this.col.txt_h = this.blur.blend ? this.setBrightness(c_textcol_h, this.getSelCol(c_backcol == 0 ? 0xff000000 : c_backcol, true) == 50 ? -10 : 10) : this.blur.dark || !this.style.bg && this.style.trans && !this.blur.light ? RGB(255, 255, 255) : this.blur.light ? RGB(71, 129, 183) : c_textcol_h; + this.col.txt = this.blur.blend ? this.setBrightness(c_textcol, this.isLightCol(c_backcol == 0 ? 0xff000000 : c_backcol) ? -10 : 10) : this.blur.dark ? RGB(255, 255, 255) : this.blur.light ? RGB(50, 50, 50) : c_textcol; + this.col.txt_h = this.blur.blend ? this.setBrightness(c_textcol_h, this.isLightCol(c_backcol == 0 ? 0xff000000 : c_backcol) ? -10 : 10) : this.blur.dark || !this.style.bg && this.style.trans && !this.blur.light ? RGB(255, 255, 255) : this.blur.light ? RGB(71, 129, 183) : c_textcol_h; } this.col.bg1 = lightBg ? 0x08000000 : 0x08ffffff; @@ -365,9 +397,9 @@ class UserInterface { if (!customColText || ppt.swapCol) this.col.text = !ppt.highlightText ? this.col.txt : this.col.txt_h; if (!customColText_h || ppt.swapCol) this.col.text_h = !ppt.highlightText ? this.col.txt_h : this.col.txt; - this.col.shadow = this.getSelCol(this.col.text_h, false); + this.col.shadow = this.getSelTextCol(this.col.text_h); if (this.col.summary === '') this.col.summary = !ppt.highlightSummary ? this.col.txt : this.col.txt_h; - this.col.t = this.style.bg ? this.getSelCol(this.col.bg, true) : 200; + this.col.t = this.style.bg ? this.getButCol(this.col.bg) : 200; if (this.stars) { ['starOn', 'starOff', 'starBor'].forEach((v, i) => { @@ -378,11 +410,12 @@ class UserInterface { this.col.bottomLine = this.getLineCol('bottom'); this.col.centerLine = this.getLineCol('center'); - this.col.sectionLine = this.col.line === '' ? (ppt.highlightHdLine ? this.col.text_h : this.col.text) & 0x40ffffff : $.RGBAtoRGB(this.col.bottomLine, this.col.bg == 0 ? 0xff000000 : this.col.bg) & 0x56ffffff; + this.col.sectionLine = this.col.line === '' && !ppt.colLineDark ? (ppt.highlightHdLine ? this.col.text_h : this.col.text) & 0x40ffffff : $.RGBAtoRGB(this.col.bottomLine, this.col.bg == 0 ? 0xff000000 : this.col.bg) & 0x56ffffff; this.col.edBg = (this.blur.dark ? RGB(0, 0, 0) : this.blur.light ? RGB(255, 255, 255) : this.col.bg) & 0x99ffffff; this.col.imgBor = this.col.text & 0x25ffffff; const col_txt = this.col.txt == -1 ? RGB(240, 240, 240) : this.col.txt; const col_txt_h = this.col.txt_h == -1 ? RGB(240, 240, 240) : this.col.txt_h; + this.col.dropShadow = RGB(18, 26, 46); this.col.source = this.blur.dark ? (ppt.highlightSubHd ? col_txt_h : col_txt) : !this.blur.light && (ppt.sourceStyle == 1 || ppt.sourceStyle == 3) && (ppt.headFontStyle != 1 && ppt.headFontStyle != 3) ? this.dim(ppt.highlightSubHd ? this.col.txt_h : this.col.txt, !window.IsTransparent ? this.col.bg : 0xff000000, 240) : ppt.highlightSubHd ? this.col.txt_h : this.col.txt; this.col.track = this.blur.dark ? (ppt.highlightSubHd ? col_txt_h : col_txt) : !this.blur.light && (ppt.trackStyle == 1 || ppt.trackStyle == 3) && (ppt.headFontStyle != 1 && ppt.headFontStyle != 3) ? this.dim(ppt.highlightSubHd ? this.col.txt_h : this.col.txt, !window.IsTransparent ? this.col.bg : 0xff000000, 240) : ppt.highlightSubHd ? this.col.txt_h : this.col.txt; @@ -410,37 +443,78 @@ class UserInterface { } getLineCol(type) { - return this.col.line === '' ? this.getBlend(this.blur.dark ? RGB(0, 0, 0) : this.blur.light ? RGB(255, 255, 255) : this.col.bg == 0 ? 0xff000000 : this.col.bg, ppt.highlightHdLine ? this.col.txt_h : this.col.txt, type == 'bottom' || this.style.isBlur ? 0.25 : 0.5) : this.col.line; + if (!ppt.colLineDark) return this.col.line === '' ? this.getBlend(this.blur.dark ? RGB(0, 0, 0) : this.blur.light ? RGB(255, 255, 255) : this.col.bg == 0 ? 0xff000000 : this.col.bg, ppt.highlightHdLine ? this.col.txt_h : this.col.txt, type == 'bottom' || this.style.isBlur ? 0.25 : 0.5) : this.col.line; + const lightBg = this.isLightBackground(); + const isLightBg = !this.blur.dark && lightBg; + const nearBlack = ((ppt.theme == 1 || ppt.theme == 2) && !this.col.themeLight || (ppt.theme == 0 || ppt.theme == 6 || ppt.theme == 7) && !lightBg) && this.getColSat(this.col.bg) < 45; + const alpha = !lightBg ? nearBlack ? 0x20ffffff : 0x50000000 : 0x30000000; + return this.col.text & alpha; } - getSelCol(c, n, bypass) { + getLuminance(c, bypass) { if (!bypass) c = $.toRGB(c); const cc = c.map(v => { v /= 255; - return v <= 0.03928 ? v /= 12.92 : Math.pow(((v + 0.055) / 1.055), 2.4); + return v <= 0.04045 ? v /= 12.92 : Math.pow(((v + 0.055) / 1.055), 2.4); }); - const L = 0.2126 * cc[0] + 0.7152 * cc[1] + 0.0722 * cc[2]; - if (L > 0.31) return n ? 50 : RGB(0, 0, 0); - else return n ? 200 : RGB(255, 255, 255); + return 0.2126 * cc[0] + 0.7152 * cc[1] + 0.0722 * cc[2]; + } + + getParams() { + this.calcText(); + panel.setStyle(); + but.createStars(); + but.setTooltipFont(); + txt.getSubHeadWidths(); + txt.artCalc(); + txt.albCalc(); + } + + getSelTextCol(c, bypass) { + return this.getLuminance(c, bypass) > 0.35 ? RGB(0, 0, 0) : RGB(255, 255, 255); } getUIColours() { - switch (this.dui) { - case 0: - if (this.col.bg === '') this.col.bg = window.GetColourCUI(3); - this.col.bgSel = this.blur.dark ? RGBA(255, 255, 255, 36) : this.blur.light ? RGBA(50, 50, 50, 36) : window.GetColourCUI(4); - this.col.txt = window.GetColourCUI(0); - this.col.txt_h = window.GetColourCUI(2); - break; - case 1: - if (this.col.bg === '') this.col.bg = window.GetColourDUI(1); - this.col.bgSel = this.blur.dark ? RGBA(255, 255, 255, 36) : this.blur.light ? RGBA(50, 50, 50, 36) : window.GetColourDUI(3); - this.col.txt = window.GetColourDUI(0); - this.col.txt_h = window.GetColourDUI(2); - break; + const colours = Object.keys(colourSelector); + this.themeColour = ppt.themeColour && colours.length ? colourSelector[colours[ppt.themeColour]] : null; + if (ppt.themed && (ppt.theme == 0 || ppt.theme == 6 || ppt.theme == 7) && this.themeColour && ppt.themeColour) { + this.col.txt = this.themeColour.text; + if (this.col.bg === '') this.col.bg = this.themeColour.background; + if (this.col.bgSel === '') this.col.bgSel = this.img.blurDark ? RGBA(255, 255, 255, 36) : this.img.blurLight ? RGBA(50, 50, 50, 36) : this.themeColour.selection; + this.col.txt_h = this.themeColour.highlight; + } else { + switch (this.dui) { + case 0: + if (this.col.bg === '') this.col.bg = this.blur.light ? RGB(245, 247, 255) : window.GetColourCUI(3); + this.col.bgSel = this.blur.dark ? RGBA(255, 255, 255, 36) : this.blur.light ? RGBA(50, 50, 50, 36) : window.GetColourCUI(4); + this.col.txt = window.GetColourCUI(0); + this.col.txt_h = window.GetColourCUI(2); + break; + case 1: + if (this.col.bg === '') this.col.bg = this.blur.light ? RGB(245, 247, 255) : window.GetColourDUI(1); + this.col.bgSel = this.blur.dark ? RGBA(255, 255, 255, 36) : this.blur.light ? RGBA(50, 50, 50, 36) : window.GetColourDUI(3); + this.col.txt = window.GetColourDUI(0); + this.col.txt_h = window.GetColourDUI(2); + break; + } } } + isLightBackground() { + let isLightBg = this.isLightCol(this.col.bg == 0 ? 0xff000000 : this.col.bg); + if (ppt.themed && (ppt.theme == 0 || ppt.theme == 6 || ppt.theme == 7) && this.themeColour && ppt.themeColour) { + // do nothing + } else { + if (this.blur.light) isLightBg = true; + else if (this.blur.dark) isLightBg = false; + } + return isLightBg; + } + + isLightCol(c, bypass) { + return this.getLuminance(c, bypass) > 0.35; + } + lines(gr) { if (!this.id.c_c) return; if (ppt.artistView && !ppt.img_only || !ppt.artistView && !ppt.img_only) { @@ -457,14 +531,23 @@ class UserInterface { panel.style.fullWidthHeading = ppt.heading && ppt.fullWidthHeading; panel.id.focus = ppt.focus; panel.id.lyricsSource = false; + panel.id.nowplayingSource = false; panel.id.propsSource = false; for (let i = 0; i < 8; i++) { - if (ppt.txtReaderEnable && ppt[`useTxtReader${i}`] && ppt[`pthTxtReader${i}`] && ppt[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1])) { + if (ppt.txtReaderEnable && ppt[`useTxtReader${i}`] && ppt[`pthTxtReader${i}`] && ppt[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1]) && !/nowplaying/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1])) { panel.id.lyricsSource = true; panel.id.focus = false; break; } } + + for (let i = 0; i < 8; i++) { + if (ppt.txtReaderEnable && ppt[`useTxtReader${i}`] && /nowplaying/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1])) { + panel.id.nowplayingSource = true; + panel.id.focus = false; + break; + } + } for (let i = 0; i < 8; i++) { if (ppt.txtReaderEnable && ppt[`useTxtReader${i}`] && /item_properties/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1])) { panel.id.propsSource = true; @@ -481,7 +564,8 @@ class UserInterface { headingText: ppt.hdShowTitle }; if (this.show.btnRedLastfm) this.show.btnBg = 1; - + + panel.checkRefreshRates(); panel.setSummary(); if (ppt.typeOverlay > 4 || ppt.typeOverlay < 0) ppt.typeOverlay = 0; @@ -538,7 +622,7 @@ class UserInterface { panel.art.ix = 0; img.id.albCyc = img.id.curAlbCyc = txt.id.curAlb = txt.id.alb = ''; - but.createStars(); + but.createStars(true); txt.artistReset(true); txt.albumReset(true); @@ -586,7 +670,7 @@ class UserInterface { if (ppt.showFilmStrip && ppt.autoFilm) txt.getScrollPos(); if (ppt.filmStripOverlay) filmStrip.set(ppt.filmStripPos); - if (ppt.text_only && !ui.style.isBlur) txt.paint(); + if (ppt.text_only && !this.style.isBlur) txt.paint(); } setBrightness(c, percent) { @@ -728,4 +812,9 @@ class Vkeys { return utils.IsKeyPressed(0x12); } } -} \ No newline at end of file +} + +let colourSelector = {} +let sync = {image: () => {}} +const syncer = fb.ProfilePath + 'settings\\themed\\bioSyncTheme.js'; +if (ppt.themed && $.file(syncer)) include(syncer); \ No newline at end of file diff --git a/scripts/language.js b/scripts/language.js new file mode 100644 index 0000000..ba5999a --- /dev/null +++ b/scripts/language.js @@ -0,0 +1,484 @@ +// language applies to menu only +// choose in panel properties (shift + windows key): Language [Menu] .... +// English, simplifed Chinese and traditional Chinese are currently available (translated by always.beta and SyaoranChang) + +'use strict'; + +const English = { + " - Remove from black list (click name): ": " - Remove from black list (click name): ", + " biography": " biography", + " first": " first", + " image file": " image file", + " main image": " main image", + " N/A no tags enabled": " N/A no tags enabled", + " review": " review", + " to black list: ": " to black list: ", + " WARNING: last.fm genre whitelist not found or invalid [try force update - needs internet connection]": " WARNING: last.fm genre whitelist not found or invalid [try force update - needs internet connection]", + ": N/A no playlist tracks selected": ": N/A no playlist tracks selected", + ": similar artists...": ": similar artists...", + ": top albums...": ": top albums...", + "...": "...", + "+ Add": "+ Add", + "+ Add to black list: ": "+ Add to black list: ", + "Activate biography": "Activate biography", + "Album": "Album", + "Album History:": "Album History:", + "album look-up": "album look-up", + "Album review [allmusic]": "Album review [allmusic]", + "Album review [last.fm]": "Album review [last.fm]", + "Album review [wikipedia]": "Album review [wikipedia]", + "Album Unknown": "Album Unknown", + "Album view": "Album view", + "Align with text": "Align with text", + "Alignment": "Alignment", + "Alignment horizontal": "Alignment horizontal", + "Alignment vertical": "Alignment vertical", + "All tagger settings...": "All tagger settings...", + "Allmusic": "Allmusic", + "Allmusic:": "Allmusic:", + "Allmusic: ": "Allmusic: ", + "Alt+Click": "Alt+Click", + "Alt+Middle click": "Alt+Middle click", + "Amalgamate": "Amalgamate", + "Artist": "Artist", + "Artist (single image [fb2k: display])": "Artist (single image [fb2k: display])", + "Artist History:": "Artist History:", + "artist look-up": "artist look-up", + "Artist Unknown": "Artist Unknown", + "Artist view": "Artist view", + "Auto": "Auto", + "Auto cycle": "Auto cycle", + "Auto cycle items": "Auto cycle items", + "Auto lock": "Auto lock", + "Auto-fallback": "Auto-fallback", + "Back": "Back", + "Biography [allmusic location]": "Biography [allmusic location]", + "Biography [allmusic]": "Biography [allmusic]", + "Biography [last.fm location]": "Biography [last.fm location]", + "Biography [last.fm]": "Biography [last.fm]", + "Biography [wikipedia location]": "Biography [wikipedia location]", + "Biography [wikipedia]": "Biography [wikipedia]", + "Black list": "Black list", + "Bottom": "Bottom", + "Cancel": "Cancel", + "Centre": "Centre", + "Click": "Click", + "composition loaded": "composition loaded", + "Composition Unknown": "Composition Unknown", + "Configure...": "Configure...", + "Cover": "Cover", + "Create && manage styles": "Create && manage styles", + "Create new style...": "Create new style...", + "Cycle above": "Cycle above", + "Cycle from custom folder [fallback to above]": "Cycle from custom folder [fallback to above]", + "Cycle from download folder": "Cycle from download folder", + "Cycle from folder": "Cycle from folder", + "Delete custom style...": "Delete custom style...", + "Disc": "Disc", + "Display": "Display", + "Double click": "Double click", + "Export custom style...": "Export custom style...", + "Filmstrip": "Filmstrip", + "Follow selected track (playlist)": "Follow selected track (playlist)", + "Follow selected track: N/A lyrics or nowplaying enabled": "Follow selected track: N/A lyrics or nowplaying enabled", + "Force update": "Force update", + "Freestyle": "Freestyle", + "Front": "Front", + "Full overlay": "Full overlay", + "Heading": "Heading", + "Icon": "Icon", + "Image": "Image", + "Image ": "Image ", + "Image+text": "Image+text", + "Inactivate": "Inactivate", + "Last.fm": "Last.fm", + "Last.fm Top Albums: ": "Last.fm Top Albums: ", + "Last.fm:": "Last.fm:", + "Last.fm: ": "Last.fm: ", + "Layout": "Layout", + "Left": "Left", + "Load": "Load", + "Manual cycle: wheel over button": "Manual cycle: wheel over button", + "Middle click": "Middle click", + "Missing data": "Missing data", + "Mode: ": "Mode: ", + "More Tags:": "More Tags:", + "More...": "More...", + "N/A - no": "N/A - no", + "N/A - requires last.fm photo. Selected image: ": "N/A - requires last.fm photo. Selected image: ", + "No black listed images for current artist": "No black listed images for current artist", + "Open file location": "Open file location", + "Open last edited": "Open last edited", + "Options: see console": "Options: see console", + "Options...": "Options...", + "Overlay image area": "Overlay image area", + "Part overlay": "Part overlay", + "Paste text from clipboard": "Paste text from clipboard", + "Photo": "Photo", + "Photos [last.fm]": "Photos [last.fm]", + "Playlists": "Playlists", + "Prefer both": "Prefer both", + "Prefer composition reviews (allmusic && wikipedia)": "Prefer composition reviews (allmusic && wikipedia)", + "Prefer nowplaying": "Prefer nowplaying", + "Reload": "Reload", + "Rename custom style...": "Rename custom style...", + "Reset album history...": "Reset album history...", + "Reset artist history...": "Reset artist history...", + "Reset style...": "Reset style...", + "Reset to default size...": "Reset to default size...", + "Reset zoom": "Reset zoom", + "Review [allmusic location]": "Review [allmusic location]", + "Review [allmusic]": "Review [allmusic]", + "Review [last.fm location]": "Review [last.fm location]", + "Review [last.fm]": "Review [last.fm]", + "Review [wikipedia location]": "Review [wikipedia location]", + "Review [wikipedia]": "Review [wikipedia]", + "Right": "Right", + "Show album history": "Show album history", + "Show artist history": "Show artist history", + "Show more tags (circle button if present)": "Show more tags (circle button if present)", + "Show similar artists": "Show similar artists", + "Show top albums": "Show top albums", + "Show track review options on load menu": "Show track review options on load menu", + "Similar Artists:": "Similar Artists:", + "Sources": "Sources", + "Static": "Static", + "Summary": "Summary", + "Summary compact": "Summary compact", + "Summary expand": "Summary expand", + "Tag": "Tag", + "Tag files...": "Tag files...", + "Tagger": "Tagger", + "Text": "Text", + "Top": "Top", + "track": "track", + "Track": "Track", + "tracks": "tracks", + "Tracks [allmusic]": "Tracks [allmusic]", + "Tracks [last.fm]": "Tracks [last.fm]", + "Tracks [wikipedia]": "Tracks [wikipedia]", + "Type:": "Type:", + "Undo": "Undo", + "Wikipedia": "Wikipedia", + "Write existing file info to tags: ": "Write existing file info to tags: " +} + +const simplifiedChinese = { + " - Remove from black list (click name): ": " - 从黑名单中删除(点击名称):", + " biography": " 简介", + " first": " 首选", + " image file": " 图像文件", + " main image": " 主图像", + " N/A no tags enabled": " 不可用,未启用标签", + " review": " 评论", + " to black list: ": "到黑名单:", + " WARNING: last.fm genre whitelist not found or invalid [try force update - needs internet connection]": " 警告:last.fm 流派白名单未找到或无效 [尝试强制更新 - 需要互联网连接]", + ": N/A no playlist tracks selected": ":不可用,播放列表内未选择音轨", + ": similar artists...": ":相似艺术家...", + ": top albums...": ":热门专辑...", + "...": "...", + "+ Add": "+ 添加", + "+ Add to black list: ": "+ 添加到黑名单: ", + "Activate biography": "激活简介", + "Album": "专辑", + "Album History:": "专辑历史记录:", + "album look-up": "查找专辑", + "Album review [allmusic]": "专辑评论 [allMusic]", + "Album review [last.fm]": "专辑评论 [last.fm]", + "Album review [wikipedia]": "专辑评论 [维基百科]", + "Album Unknown": "未知专辑", + "Album view": "专辑视图", + "Align with text": "对齐文本", + "Alignment": "对齐方式", + "Alignment horizontal": "水平对齐", + "Alignment vertical": "垂直对齐", + "All tagger settings...": "标签设置...", + "Allmusic": "Allmusic", + "Allmusic:": "Allmusic:", + "Allmusic: ": "Allmusic:", + "Alt+Click": "Alt+单击", + "Alt+Middle click": "Alt+中键单击", + "Amalgamate": "合并", + "Artist": "艺术家", + "Artist (single image [fb2k: display])": "艺术家(单张图片 [fb2k>参数选项>显示])", + "Artist History:": "艺术家历史记录:", + "artist look-up": "查找艺术家", + "Artist Unknown": "未知艺术家", + "Artist view": "艺术家视图", + "Auto": "自动", + "Auto cycle": "自动循环", + "Auto cycle items": "自动循环项目", + "Auto lock": "自动锁定", + "Auto-fallback": "自动回退", + "Back": "后退", + "Biography [allmusic location]": "简介 [allMusic 位置]", + "Biography [allmusic]": "简介 [allmusic]", + "Biography [last.fm location]": "简介 [last.fm 位置]", + "Biography [last.fm]": "简介 [last.fm]", + "Biography [wikipedia location]": "简介 [维基百科位置]", + "Biography [wikipedia]": "简介 [维基百科]", + "Black list": "黑名单", + "Bottom": "下", + "Cancel": "取消", + "Centre": "中", + "Click": "单击", + "composition loaded": "加载作品", + "Composition Unknown": "未知作品", + "Configure...": "配置...", + "Cover": "封面", + "Create && manage styles": "创建和管理样式", + "Create new style...": "创建新样式...", + "Cycle above": "以上循环", + "Cycle from custom folder [fallback to above]": "从自定义文件夹循环 [回退到上面]", + "Cycle from download folder": "从下载文件夹循环", + "Cycle from folder": "从文件夹循环", + "Delete custom style...": "删除自定义样式...", + "Disc": "碟片", + "Display": "显示", + "Double click": "双击", + "Export custom style...": "导出自定义样式...", + "Filmstrip": "幻灯片", + "Follow selected track (playlist)": "跟随所选音轨(播放列表)", + "Follow selected track: N/A lyrics or nowplaying enabled": "跟随所选音轨:歌词来源启用或正在播放时不可用", + "Force update": "强制更新", + "Freestyle": "自由式", + "Front": "封面", + "Full overlay": "全叠加", + "Heading": "标题", + "Icon": "图标", + "Image": "图像", + "Image ": "图像", + "Image+text": "图像+文本", + "Inactivate": "停用", + "Last.fm": "Last.fm", + "Last.fm Top Albums: ": "Last.fm 热门专辑: ", + "Last.fm:": "Last.fm:", + "Last.fm: ": "Last.fm:", + "Layout": "布局", + "Left": "左", + "Load": "加载", + "Manual cycle: wheel over button": "手动循环:滚动滚轮", + "Middle click": "中键单击", + "Missing data": "缺少数据", + "Mode: ": "模式:", + "More Tags:": "更多标签:", + "More...": "更多...", + "N/A - no": "不可用 - 无", + "N/A - requires last.fm photo. Selected image: ": "不可用 - 需要 last.fm 照片。所选图像: ", + "No black listed images for current artist": "当前艺术家没有列入黑名单的图像", + "Open file location": "打开文件位置", + "Open last edited": "打开上次编辑的项目", + "Options: see console": "选项:请查看控制台", + "Options...": "选项...", + "Overlay image area": "叠加图像区域", + "Part overlay": "零件覆盖", + "Paste text from clipboard": "从剪贴板粘贴文本", + "Photo": "照片", + "Photos [last.fm]": "照片 [last.fm]", + "Playlists": "播放列表", + "Prefer both": "首选两个", + "Prefer composition reviews (allmusic && wikipedia)": "首选作品评论(allmusic 和 维基百科)", + "Prefer nowplaying": "首选正在播放项", + "Reload": "重新加载", + "Rename custom style...": "重命名自定义样式...", + "Reset album history...": "重置专辑历史记录...", + "Reset artist history...": "重置艺术家历史记录...", + "Reset style...": "重置样式...", + "Reset to default size...": "重置为默认大小...", + "Reset zoom": "重置缩放", + "Review [allmusic location]": "评论 [allMusic 位置]", + "Review [allmusic]": "评论 [allmusic]", + "Review [last.fm location]": "评论 [last.fm 位置]", + "Review [last.fm]": "评论 [last.fm]", + "Review [wikipedia location]": "评论 [维基百科位置]", + "Review [wikipedia]": "评论 [维基百科]", + "Right": "正确的", + "Show album history": "显示专辑历史记录", + "Show artist history": "显示艺术家历史记录", + "Show more tags (circle button if present)": "显示更多标签(如果存在,则为圆圈按钮)", + "Show similar artists": "显示相似艺术家", + "Show top albums": "显示热门专辑", + "Show track review options on load menu": "在加载菜单上显示音轨评论选项", + "Similar Artists:": "相似艺术家:", + "Sources": "来源", + "Static": "静态", + "Summary": "摘要", + "Summary compact": "总结紧凑", + "Summary expand": "摘要展开", + "Tag": "标签", + "Tag files...": "写入文件标签...", + "Tagger": "标签", + "Text": "文本", + "Top": "上", + "track": "首", + "Track": "首", + "tracks": "首", + "Tracks [allmusic]": "音轨 [allmusic]", + "Tracks [last.fm]": "音轨 [last.fm]", + "Tracks [wikipedia]": "音轨 [维基百科]", + "Type:": "样式:", + "Undo": "撤消", + "Wikipedia": "维基百科", + "Write existing file info to tags: ": "将现有文件信息写入标签:" +} + +const traditionalChinese = { + " - Remove from black list (click name): ": " - 從黑名單中刪除(點擊名稱): ", + " biography": " 傳記", + " first": " 第一的", + " image file": " 圖片檔案", + " main image": " 主要圖像", + " N/A no tags enabled": " N/A 未啟用標籤", + " review": " 評論", + " to black list: ": " 加入黑名單: ", + " WARNING: last.fm genre whitelist not found or invalid [try force update - needs internet connection]": " 警告:未找到 last.fm 類型白名單或無效 [嘗試強制更新 - 需要網路連接]", + ": N/A no playlist tracks selected": ": N/A 未選擇播放列表的曲目", + ": similar artists...": ":相似的表演者...", + ": top albums...": ":熱門專輯...", + "...": "...", + "+ Add": "+ 增加", + "+ Add to black list: ": "+ 加入到黑名單: ", + "Activate biography": "啟用傳記", + "Album": "專輯", + "Album History:": "專輯歷史紀錄:", + "album look-up": "專輯查詢", + "Album review [allmusic]": "專輯評論 [allmusic]", + "Album review [last.fm]": "專輯評論 [last.fm]", + "Album review [wikipedia]": "專輯評論 [維基百科]", + "Album Unknown": "專輯不詳", + "Album view": "專輯檢視", + "Align with text": "與文字對齊", + "Alignment": "對齊方式", + "Alignment horizontal": "水平對齊", + "Alignment vertical": "垂直對齊", + "All tagger settings...": "所有標籤編輯器設置...", + "Allmusic": "Allmusic", + "Allmusic:": "Allmusic:", + "Allmusic: ": "Allmusic: ", + "Alt+Click": "Alt+點擊", + "Alt+Middle click": "Alt+點擊中鍵", + "Amalgamate": "合併", + "Artist": "表演者", + "Artist (single image [fb2k: display])": "表演者(單一圖片 [fb2k: 顯示])", + "Artist History:": "表演者歷史紀錄:", + "artist look-up": "表演者查詢", + "Artist Unknown": "表演者不詳", + "Artist view": "表演者檢視", + "Auto": "自動", + "Auto cycle": "自動循環", + "Auto cycle items": "自動循環項目", + "Auto lock": "自動鎖", + "Auto-fallback": "自動回退", + "Back": "後面", + "Biography [allmusic location]": "傳記 [allmusic 位置]", + "Biography [allmusic]": "傳記 [allmusic]", + "Biography [last.fm location]": "傳記 [last.fm 位置]", + "Biography [last.fm]": "傳記 [last.fm]", + "Biography [wikipedia location]": "傳記 [維基百科位置]", + "Biography [wikipedia]": "傳記 [維基百科]", + "Black list": "黑名單", + "Bottom": "底部", + "Cancel": "取消", + "Centre": "中心", + "Click": "點擊", + "composition loaded": "作品載入", + "Composition Unknown": "作品不詳", + "Configure...": "設置...", + "Cover": "封面", + "Create && manage styles": "建立和管理樣式", + "Create new style...": "建立新樣式...", + "Cycle above": "循環以上", + "Cycle from custom folder [fallback to above]": "從自訂文件夾循環 [自動退回上面]", + "Cycle from download folder": "從下載資料夾循環", + "Cycle from folder": "從資料夾循環", + "Delete custom style...": "刪除自定義的樣式...", + "Disc": "光碟", + "Display": "顯示", + "Double click": "雙擊", + "Export custom style...": "匯出自定義的樣式...", + "Filmstrip": "幻燈片", + "Follow selected track (playlist)": "跟隨選擇的曲目(播放列表)", + "Follow selected track: N/A lyrics or nowplaying enabled": "跟隨所選曲目:N/A 歌詞或正在播放已啟用", + "Force update": "強制更新", + "Freestyle": "自由風格", + "Front": "前面", + "Full overlay": "全疊加", + "Heading": "標題", + "Icon": "圖示", + "Image": "圖片", + "Image ": "圖片 ", + "Image+text": "圖片+文字", + "Inactivate": "停用", + "Last.fm": "Last.fm", + "Last.fm Top Albums: ": "Last.fm 熱門專輯: ", + "Last.fm:": "Last.fm:", + "Last.fm: ": "Last.fm: ", + "Layout": "佈局", + "Left": "左側", + "Load": "載入", + "Manual cycle: wheel over button": "手動循環: 滑鼠滾輪按鈕", + "Middle click": "點擊中鍵", + "Missing data": "缺失數據", + "Mode: ": "模式:", + "More Tags:": "更多標籤:", + "More...": "更多...", + "N/A - no": "N/A - 無", + "N/A - requires last.fm photo. Selected image: ": "N/A - 需要 last.fm 照片。 選擇的圖片: ", + "No black listed images for current artist": "目前的表演者沒有黑名單的圖片", + "Open file location": "開啟所在資料夾", + "Open last edited": "開啟上次編輯", + "Options: see console": "設定: 查看主控台", + "Options...": "設定...", + "Overlay image area": "疊加圖像區域", + "Part overlay": "零件覆蓋", + "Paste text from clipboard": "從剪貼簿貼上文字", + "Photo": "照片", + "Photos [last.fm]": "照片 [last.fm]", + "Playlists": "播放列表", + "Prefer both": "兩者都喜歡", + "Prefer composition reviews (allmusic && wikipedia)": "比較喜歡作品評論(allmusic 和 維基百科)", + "Prefer nowplaying": "比較喜歡現在播放", + "Reload": "重新載入", + "Rename custom style...": "重新命名自定義的樣式...", + "Reset album history...": "重置專輯歷史紀錄...", + "Reset artist history...": "重置表演者歷史紀錄...", + "Reset style...": "重置樣式...", + "Reset to default size...": "重置為預設大小...", + "Reset zoom": "重置縮放", + "Review [allmusic location]": "評論 [allmusic 位置]", + "Review [allmusic]": "評論 [allmusic]", + "Review [last.fm location]": "評論 [last.fm 位置]", + "Review [last.fm]": "評論 [last.fm]", + "Review [wikipedia location]": "評論 [維基百科位置]", + "Review [wikipedia]": "評論 [維基百科]", + "Right": "右側", + "Show album history": "顯示專輯歷史紀錄", + "Show artist history": "顯示表演者歷史紀錄", + "Show more tags (circle button if present)": "顯示更多標籤(如果有圓圈按鈕)", + "Show similar artists": "顯示相似的表演者", + "Show top albums": "顯示熱門專輯", + "Show track review options on load menu": "在載入選單上顯示曲目評論選項", + "Similar Artists:": "相似的表演者:", + "Sources": "來源", + "Static": "靜態", + "Summary": "摘要", + "Summary compact": "總結緊湊", + "Summary expand": "摘要展開", + "Tag": "標籤", + "Tag files...": "標籤檔案...", + "Tagger": "標籤編輯器", + "Text": "文字", + "Top": "頂部", + "track": "曲目", + "Track": "曲目", + "tracks": "曲目", + "Tracks [allmusic]": "曲目 [allmusic]", + "Tracks [last.fm]": "曲目 [last.fm]", + "Tracks [wikipedia]": "曲目 [維基百科]", + "Type:": "類型:", + "Undo": "還原", + "Wikipedia": "維基百科", + "Write existing file info to tags: ": "將現有文件信息寫入標籤:" +} + +const lg = [English, simplifiedChinese, traditionalChinese][ppt.menuLanguage]; \ No newline at end of file diff --git a/scripts/lastfm.js b/scripts/lastfm.js index b3e352c..4e10e19 100644 --- a/scripts/lastfm.js +++ b/scripts/lastfm.js @@ -199,6 +199,7 @@ class DldLastfmBio { $.trace('last.fm biography: ' + this.artist + ': not found', true); return; } + if (!this.fo_bio) return; $.buildPth(this.fo_bio); $.save(this.pth_bio, this.con, true); server.res(); @@ -247,7 +248,7 @@ class DldArtImages { run(dl_ar, force, art, p_stndBio, p_supCache) { if (!$.file(`${cfg.storageFolder}foo_lastfm_img.vbs`)) return; - let img_folder = p_stndBio ? panel.cleanPth(cfg.pth.foImgArt, art.focus, 'server') : panel.cleanPth(cfg.remap.foImgArt, art.focus, 'remap', dl_ar, '', 1); + let img_folder = p_stndBio && !panel.isRadio(art.focus) ? panel.cleanPth(cfg.pth.foImgArt, art.focus, 'server') : panel.cleanPth(cfg.remap.foImgArt, art.focus, 'remap', dl_ar, '', 1); if (p_supCache && !$.folder(img_folder)) img_folder = panel.cleanPth(cfg.sup.foImgArt, art.focus, 'remap', dl_ar, '', 1); const getNo = this.img_exp(dl_ar, img_folder, !force ? server.exp : 0); if (!getNo[0]) return; @@ -454,9 +455,11 @@ class LfmAlbum { } wiki = wiki ? wiki + this.tags + this.stats : this.tags + this.stats; wiki = wiki.trim(); - $.buildPth(this.fo); - $.save(this.pth, wiki, true); - server.res(); + if (this.fo) { + $.buildPth(this.fo); + $.save(this.pth, wiki, true); + server.res(); + } } else if (this.rev) { doc.open(); const counts = ['', '', '']; @@ -635,7 +638,11 @@ class LfmTrack { this.timer = null; }, 30000); } - this.xmlhttp.send(); + try { + this.xmlhttp.send(); + } catch (e) { + this.revSave(); + } } analyse() { @@ -783,8 +790,10 @@ class LfmTrack { lang: this.retry ? 'EN' : cfg.language, update: Date.now() }; - $.buildPth(this.fo); - $.save(this.pth, JSON.stringify($.sortKeys(this.text), null, 3), true); + if (this.fo) { + $.buildPth(this.fo); + $.save(this.pth, JSON.stringify($.sortKeys(this.text), null, 3), true); + } if (ret) return $.trace('last.fm track review: ' + $.titlecase(this.track) + ' / ' + this.artist + ': not found', true); server.res(); } @@ -855,6 +864,7 @@ class LfmSimilarArtists { name: this.artist, score: 100 }); + if (!this.pth_sim) break; $.buildPth(this.pth_sim); $.save(this.fn_sim, JSON.stringify(list), true); if (cfg.lfmSim) { diff --git a/scripts/library.js b/scripts/library.js index fd9407e..7587c82 100644 --- a/scripts/library.js +++ b/scripts/library.js @@ -16,6 +16,8 @@ class Library { } inLibrary(type, a, l) { + if (a) a = a.toLowerCase(); + if (l) l = l.toLowerCase(); switch (type) { case 1: { let q = ''; diff --git a/scripts/lyrics.js b/scripts/lyrics.js index 42f9a8c..be41ee5 100644 --- a/scripts/lyrics.js +++ b/scripts/lyrics.js @@ -58,16 +58,16 @@ class Lyrics { const blendIn = this.type.synced ? ui.getBlend(ui.col.accent, ui.col.text, transition_factor_in) : ui.col.text; const blendOut = this.type.synced ? ui.getBlend(ui.col.text, ui.col.accent, transition_factor_out) : ui.col.text; const y = this.y + this.scroll; - + let col = ui.col.text; - let fadeBot = this.transBot[transition_factor]; + let fadeBot = !this.shadowEffect ? this.transBot[transition_factor] : col; if (!fadeBot) { fadeBot = $.RGBtoRGBA(col, alpha); this.transBot[transition_factor] = fadeBot; } - let fadeTop = this.transTop[transition_factor]; + let fadeTop = !this.shadowEffect ? this.transTop[transition_factor] : col; if (!fadeTop) { fadeTop = $.RGBtoRGBA(col, 255 - alpha); this.transTop[transition_factor] = fadeTop; @@ -80,12 +80,21 @@ class Lyrics { const line_y = Math.round(y - top + lyric_y); const bottomLine = line_y > this.bot; if (this.showlyric(lyric_y, top)) { + const font = !lyric.highlight ? ui.font.lyrics : this.font.lyrics; + if (this.shadowEffect) { + if (this.dropNegativeShadowLevel) { + gr.DrawString(lyric.content, font, ui.col.dropShadow, this.x - this.dropNegativeShadowLevel, line_y, this.w + 1, this.lineHeight + 1, this.alignCenter); + gr.DrawString(lyric.content, font, ui.col.dropShadow, this.x, line_y - this.dropNegativeShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); + } + + gr.DrawString(lyric.content, font, ui.col.dropShadow, this.x + this.dropShadowLevel, line_y + this.dropShadowLevel, this.w + 1, this.lineHeight + 1, this.alignCenter); + } col = line_y >= this.top ? lyric.highlight ? blendIn : i == this.locus - 1 ? blendOut : bottomLine ? fadeBot : ui.col.text : fadeTop; - gr.DrawString(lyric.content, ui.font.lyrics, col, this.x, line_y, this.w + 1, this.lineHeight + 1, this.alignCenter); + gr.DrawString(lyric.content, font, col, this.x, line_y, this.w + 1, this.lineHeight + 1, this.alignCenter); } }); if (this.showOffset) { - gr.DrawString(`Offset: ${this.userOffset / 1000}s`, ui.font.main, ui.col.accent, this.x, this.top, this.w, this.lineHeight + 1, this.alignRight); + gr.DrawString(`Offset: ${this.userOffset / 1000}s`, ui.font.main, ui.col.lyrAccent, this.x, this.top, this.w, this.lineHeight + 1, this.alignRight); } } @@ -94,7 +103,7 @@ class Lyrics { if (isSynced && lyrics[0].content && lyrics[0].timestamp > this.durationScroll) lyrics.unshift({timestamp: 0, content: ''}); $.gr(1, 1, false, g => { for (let i = 0; i < lyrics.length; i++) { - const l = g.EstimateLineWrap(lyrics[i].content, ui.font.lyrics, this.w - 10); + const l = g.EstimateLineWrap(lyrics[i].content, this.font.lyrics, this.w - 10); if (l[1] > this.maxLyrWidth) this.maxLyrWidth = l[1]; if (l.length > 2) { const numLines = l.length / 2; @@ -168,10 +177,13 @@ class Lyrics { this.lyr = lyr; this.userOffset = 0; } + this.font = { + lyrics: !ppt.largerSyncLyricLine ? ui.font.lyrics : gdi.Font(ui.font.main.Name, ui.font.zoomSize * 1.33, ui.font.lyrics.Style) + } this.alignCenter = StringFormat(1, 1); this.alignRight = StringFormat(2, 1); this.init = true; - this.lineHeight = ui.font.lyrics_h + 4 * $.scale; + this.lineHeight = !ppt.largerSyncLyricLine ? ui.font.lyrics_h + 4 * $.scale : ui.font.lyrics_h * 1.33; ppt.lyricsScrollTimeMax = $.clamp(Math.round(ppt.lyricsScrollTimeMax), 0, 3000); ppt.lyricsScrollTimeAvg = $.clamp(Math.round(ppt.lyricsScrollTimeAvg), 0, 3000); this.durationScroll = ppt.lyricsScrollMaxMethod ? ppt.lyricsScrollTimeMax : Math.round(ppt.lyricsScrollTimeAvg * 2 / 3); @@ -184,6 +196,9 @@ class Lyrics { this.minDurationScroll = Math.min(this.durationScroll, 250); this.newHighlighted = false; this.scroll = 0; + this.shadowEffect = ppt.dropShadowLevel > 0; + this.dropShadowLevel = ppt.dropShadowLevel; + this.dropNegativeShadowLevel = this.dropShadowLevel > 1 ? Math.floor(this.dropShadowLevel / 2) : 0; this.showOffsetTimer = null; this.timer = null; this.trackLength = parseInt(this.tfLength.Eval(true)); @@ -193,13 +208,13 @@ class Lyrics { ppt.lyricsFadeHeight = $.clamp(ppt.lyricsFadeHeight, -1, 2) const fadeHeight = this.lineHeight * ppt.lyricsFadeHeight; this.x = panel.text.l; - this.y = panel.text.t - this.lineHeight + fadeHeight; + this.y = panel.text.t - (!this.shadowEffect ? this.lineHeight + fadeHeight : 0); this.w = panel.text.w; - this.h = panel.lines_drawn * ui.font.main_h + this.lineHeight * 2 - fadeHeight * 2; - + this.h = panel.lines_drawn * ui.font.main_h + (!this.shadowEffect ? this.lineHeight * 2 - fadeHeight * 2 : 0); const linesDrawn = Math.floor(this.h / this.lineHeight); const oddNumLines = linesDrawn % 2; - + const realHeight = this.lineHeight * linesDrawn; + this.locusOffset = this.h / 2 - (oddNumLines ? this.lineHeight / 2 : this.lineHeight); this.top = this.locusOffset - this.lineHeight * (Math.floor(linesDrawn / 2) - (oddNumLines ? 1 : 2)) + this.y; this.bot = this.top + this.lineHeight * (linesDrawn - 3); @@ -318,7 +333,7 @@ class Lyrics { } showlyric(y, top) { - return y >= top && y + this.lineHeight * 2 <= this.h + top; + return y >= top && y + this.lineHeight * (!this.shadowEffect ? 2 : 1) <= this.h + top; } smoothScroll() { diff --git a/scripts/menu.js b/scripts/menu.js index afa850b..11b5e5f 100644 --- a/scripts/menu.js +++ b/scripts/menu.js @@ -141,7 +141,7 @@ class MenuItems { artistClean: '', blacklist: [], blacklistStr: [], - covType: ['Front', 'Back', 'Disc', 'Icon', 'Artist', 'Cycle above', 'Cycle from folder'], + covType: [lg['Front'], lg['Back'], lg['Disc'], lg['Icon'], lg['Artist'], lg['Cycle above'], lg['Cycle from folder']], isLfm: true, list: [], name: '' @@ -194,10 +194,10 @@ class MenuItems { }); bMenu.newMenu({ - menuName: 'More...' + menuName: lg['More...'] }); for (let i = 0; i < 8; i++) bMenu.newItem({ - menuName: 'More...', + menuName: lg['More...'], str: this.getlookUpStr(i, 1, artist), func: () => this.lookUpArtistItems(i), checkItem: i < 4 && [ppt.showSimilarArtists, ppt.showMoreTags, ppt.showArtistHistory, ppt.autoLock][i], @@ -208,7 +208,7 @@ class MenuItems { panel.alb.list.forEach((v, i) => bMenu.newItem({ str: ((!i || v.type.includes('history') ? v.artist.replace(/&/g, '&&') + ' - ' + v.album.replace(/&/g, '&&') : v.album.replace(/&/g, '&&')) + (!v.composition ? '' : ' [composition]')).replace(/^\s-\s/, ''), func: () => this.lookUpAlbum(i), - flags: v.type != 'label' && v.album != 'Album History:' ? MF_STRING : MF_GRAYED, + flags: v.type != 'label' && v.album != lg['Album History:'] ? MF_STRING : MF_GRAYED, checkRadio: i == panel.alb.ix, separator: !i || v.type == 'albumend' || v.type == 'label' || v.type == 'historyend' })); @@ -221,10 +221,10 @@ class MenuItems { }); bMenu.newMenu({ - menuName: 'More...' + menuName: lg['More...'] }); for (let i = 0; i < 8; i++) bMenu.newItem({ - menuName: 'More...', + menuName: lg['More...'], str: this.getlookUpStr(i, 2, artist), func: () => this.lookUpAlbumItems(i), checkItem: i < 3 && [ppt.showTopAlbums, ppt.showAlbumHistory, ppt.autoLock][i], @@ -244,7 +244,7 @@ class MenuItems { }); const b = ppt.artistView ? 'Bio' : 'Rev'; - const loadName = 'Load' + (!ppt.sourceAll ? '' : ' first'); + const loadName = lg['Load'] + (!ppt.sourceAll ? '' : lg[' first']); const n = b.toLowerCase(); const separator = !ppt.artistView && (ppt.showTrackRevOptions || txt.isCompositionLoaded()) || !panel.stndItem(); @@ -265,11 +265,11 @@ class MenuItems { if (ppt.showTrackRevOptions && !ppt.artistView && panel.stndItem() && !txt.isCompositionLoaded()) { menu.newItem({ menuName: loadName, - str: 'Type:', + str: lg['Type:'], flags: MF_GRAYED, separator: true }); - ['Album', 'Track', 'Prefer both'].forEach((v, i) => menu.newItem({ + [lg['Album'], lg['Track'], lg['Prefer both']].forEach((v, i) => menu.newItem({ menuName: loadName, str: v, func: () => this.setReviewType(i), @@ -281,7 +281,7 @@ class MenuItems { if (!panel.stndItem() || txt.isCompositionLoaded()) { menu.newItem({ menuName: loadName, - str: 'Mode: ' + (ppt.artistView ? 'artist look-up' : (txt.isCompositionLoaded() ? 'composition loaded' : 'album look-up')), + str: lg['Mode: '] + (ppt.artistView ? lg['artist look-up'] : (txt.isCompositionLoaded() ? lg['composition loaded'] : lg['album look-up'])), flags: MF_GRAYED }); } @@ -289,33 +289,33 @@ class MenuItems { menu.addSeparator({separator: !ppt.img_only ? true : false}); menu.newMenu({ - menuName: 'Display' + menuName: lg['Display'] }); - for (let i = 0; i < 10; i++) menu.newItem({ - menuName: 'Display', + for (let i = 0; i < 11; i++) menu.newItem({ + menuName: lg['Display'], str: this.display.str[i], func: () => this.setDisplay(i), - flags: i == 1 && ppt.autoEnlarge || i == 9 && panel.id.lyricsSource ? MF_GRAYED : MF_STRING, + flags: i == 1 && ppt.autoEnlarge || i == 6 && !ppt.summaryShow || i == 10&& (panel.id.lyricsSource || panel.id.nowplayingSource) ? MF_GRAYED : MF_STRING, checkItem: (i > 2 && i < 6) && this.display.check[i], - checkRadio: (i < 3 || i > 5 && i < 8 || i > 7) && this.display.check[i], - separator: i == 2 || i == 5 || i == 7 + checkRadio: (i < 3 || i > 6 && i < 9 || i > 8) && this.display.check[i], + separator: i == 2 || i == 5 || i == 6 || i == 8 }); menu.addSeparator({}); menu.newMenu({ - menuName: 'Sources' + menuName: lg['Sources'] }); menu.newMenu({ - menuName: 'Text', - appendTo: 'Sources' + menuName: lg['Text'], + appendTo: lg['Sources'] }); for (let i = 0; i < 5; i++) menu.newItem({ - menuName: 'Text', - str: ['Auto-fallback', 'Static', 'Amalgamate', 'Show track review options on load menu', 'Prefer composition reviews (allmusic && wikipedia)'][i], + menuName: lg['Text'], + str: [lg['Auto-fallback'], lg['Static'], lg['Amalgamate'], lg['Show track review options on load menu'], lg['Prefer composition reviews (allmusic && wikipedia)']][i], func: () => this.setTextType(i, b), flags: !i && ppt.sourceAll || i == 1 && ppt.sourceAll ? MF_GRAYED : MF_STRING, checkItem: i == 2 && ppt.sourceAll || i == 3 && ppt.showTrackRevOptions || i == 4 && ppt.classicalMusicMode, @@ -324,15 +324,15 @@ class MenuItems { hide: i == 4 && !cfg.classicalModeEnable }); - menu.addSeparator({menuName: 'Sources'}); + menu.addSeparator({menuName: lg['Sources']}); menu.newMenu({ - menuName: 'Photo', - appendTo: 'Sources' + menuName: lg['Photo'], + appendTo: lg['Sources'] }); - ['Cycle from download folder', 'Cycle from custom folder [fallback to above]', 'Artist (single image [fb2k: display])'].forEach((v, i) => menu.newItem({ - menuName: 'Photo', + [lg['Cycle from download folder'], lg['Cycle from custom folder [fallback to above]'], lg['Artist (single image [fb2k: display])']].forEach((v, i) => menu.newItem({ + menuName: lg['Photo'], str: v, func: () => this.setPhotoType(i), checkRadio: ppt.cycPhotoLocation == i, @@ -340,14 +340,14 @@ class MenuItems { })); menu.newMenu({ - menuName: 'Cover', - str: 'Cover', - appendTo: 'Sources', + menuName: lg['Cover'], + str: lg['Cover'], + appendTo: lg['Sources'], flags: !panel.alb.ix || ppt.artistView ? MF_STRING : MF_GRAYED }); this.img.covType.forEach((v, i) => menu.newItem({ - menuName: 'Cover', + menuName: lg['Cover'], str: v, func: () => this.setCover(i), flags: ppt.loadCovFolder && !ppt.loadCovAllFb && i < 5 ? MF_GRAYED : MF_STRING, @@ -356,16 +356,16 @@ class MenuItems { separator: i == 4 })); - menu.addSeparator({menuName: 'Sources'}); + menu.addSeparator({menuName: lg['Sources']}); menu.newMenu({ - menuName: 'Open file location', - appendTo: 'Sources', + menuName: lg['Open file location'], + appendTo: lg['Sources'], flags: this.getOpenFlag() }); for (let i = 0; i < 8; i++) menu.newItem({ - menuName: 'Open file location', + menuName: lg['Open file location'], str: this.openName[i], func: () => $.browser('explorer /select,' + '"' + this.path.open[i] + '"', false), flags: this.getOpenFlag(), @@ -373,17 +373,17 @@ class MenuItems { hide: !this.openName[i] }); - menu.addSeparator({menuName: 'Sources'}); + menu.addSeparator({menuName: lg['Sources']}); if (ppt.menuShowPaste == 2 || ppt.menuShowPaste && this.shift) { menu.newMenu({ - menuName: 'Paste text from clipboard', - appendTo: 'Sources', + menuName: lg['Paste text from clipboard'], + appendTo: lg['Sources'], separator: ppt.menuShowPaste == 2 || ppt.menuShowPaste && this.shift }); for (let i = 0; i < 5; i++) menu.newItem({ - menuName: 'Paste text from clipboard', - str: [ppt.artistView ? 'Biography [allmusic location]' : 'Review [allmusic location]', ppt.artistView ? 'Biography [last.fm location]' : 'Review [last.fm location]', ppt.artistView ? 'Biography [wikipedia location]' : 'Review [wikipedia location]', 'Open last edited', 'Undo'][i], + menuName: lg['Paste text from clipboard'], + str: [ppt.artistView ? lg['Biography [allmusic location]'] : lg['Review [allmusic location]'], ppt.artistView ? lg['Biography [last.fm location]'] : lg['Review [last.fm location]'], ppt.artistView ? lg['Biography [wikipedia location]'] : lg['Review [wikipedia location]'], lg['Open last edited'], lg['Undo']][i], func: () => this.setPaste(i), flags: !i && !this.path.am[2] || i == 1 && !this.path.lfm[2] || i == 2 && !this.path.wiki[2] || i == 3 && !this.undo.path || i == 4 && this.undo.text == '#!#' ? MF_GRAYED : MF_STRING, separator: i == 2 || i == 3 @@ -391,72 +391,73 @@ class MenuItems { } menu.newItem({ - menuName: 'Sources', - str: 'Force update', + menuName: lg['Sources'], + str: lg['Force update'], func: () => panel.callServer(1, panel.id.focus, 'bio_forceUpdate', 0) }); const style_arr = panel.style.name.slice(); menu.newMenu({ - menuName: 'Layout' + menuName: lg['Layout'] }); const style = ppt.sameStyle ? ppt.style : ppt.artistView ? ppt.bioStyle : ppt.revStyle style_arr.forEach((v, i) => menu.newItem({ - menuName: 'Layout', + menuName: lg['Layout'], str: v, func: () => this.setStyle(i), checkRadio: style <= style_arr.length - 1 && i == style, - separator: i == 4 || style_arr.length > 5 && i == style_arr.length - 1 + separator: i == 3 || style_arr.length > 5 && i == style_arr.length - 1 })); menu.newMenu({ - menuName: 'Create && manage styles', - appendTo: 'Layout' + menuName: lg['Create && manage styles'], + appendTo: lg['Layout'] }); - ['Create new style...', 'Rename custom style...', 'Delete custom style...', 'Export custom style...', 'Reset style...'].forEach((v, i) => menu.newItem({ - menuName: 'Create && manage styles', + [lg['Create new style...'], lg['Rename custom style...'], lg['Delete custom style...'], lg['Export custom style...'], lg['Reset style...']].forEach((v, i) => menu.newItem({ + menuName: lg['Create && manage styles'], str: v, func: () => this.setStyles(i), flags: !i || ppt.style > 4 || i == 4 ? MF_STRING : MF_GRAYED, separator: !i })); - menu.addSeparator({menuName: 'Layout'}); + menu.addSeparator({menuName: lg['Layout']}); menu.newMenu({ - menuName: 'Filmstrip', - appendTo: 'Layout' + menuName: lg['Filmstrip'], + appendTo: lg['Layout'] }); - ['Top', 'Right', 'Bottom', 'Left', 'Overlay image area', 'Reset to default size...'].forEach((v, i) => menu.newItem({ - menuName: 'Filmstrip', + [lg['Top'], lg['Right'], lg['Bottom'], lg['Left'], lg['Overlay image area'], lg['Reset to default size...']].forEach((v, i) => menu.newItem({ + menuName: lg['Filmstrip'], str: v, func: () => { if (i == 4) ppt.toggle('filmStripOverlay'); filmStrip.set(i == 4 ? ppt.filmStripPos : i) }, - checkItem: i == 4 && ppt.filmStripOverlay, + flags: i != 4 || ppt.style != 4 ? MF_STRING : MF_GRAYED, + checkItem: i == 4 && (ppt.filmStripOverlay || (ppt.style == 4 && !ppt.text_only && !ppt.img_only)), checkRadio: i < 4 && i == ppt.filmStripPos, separator: i == 3 || i == 4 })); - menu.addSeparator({menuName: 'Layout'}); + menu.addSeparator({menuName: lg['Layout']}); - ['Reset zoom', 'Reload'].forEach((v, i) => menu.newItem({ - menuName: 'Layout', + [lg['Reset zoom'], lg['Reload']].forEach((v, i) => menu.newItem({ + menuName: lg['Layout'], str: v, func: () => !i ? but.resetZoom() : window.Reload(), })); menu.newMenu({ - menuName: 'Image' + menuName: lg['Image'] }); menu.newItem({ - menuName: 'Image', - str: 'Auto cycle', + menuName: lg['Image'], + str: lg['Auto cycle'], func: () => ppt.toggle('cycPic'), checkItem: ppt.cycPic, separator: true @@ -464,12 +465,12 @@ class MenuItems { if (ppt.style < 4) { menu.newMenu({ - menuName: 'Alignment', - appendTo: 'Image' + menuName: lg['Alignment'], + appendTo: lg['Image'] }); for (let i = 0; i < 4; i++) menu.newItem({ - menuName: 'Alignment', - str: ppt.style == 0 || ppt.style == 2 ? ['Left', 'Centre', 'Right', 'Align with text'][i] : ['Top', 'Centre', 'Bottom', 'Align with text'][i], + menuName: lg['Alignment'], + str: ppt.style == 0 || ppt.style == 2 ? [lg['Left'], lg['Centre'], lg['Right'], lg['Align with text']][i] : [lg['Top'], lg['Centre'], lg['Bottom'], lg['Align with text']][i], func: () => this.setImageAlignnment(i, 'standard'), checkItem: i == 3 && ppt.textAlign, checkRadio: i == (ppt.style == 0 || ppt.style == 2 ? ppt.alignH : ppt.alignV), @@ -479,21 +480,21 @@ class MenuItems { if (ppt.style > 3) { menu.newMenu({ - menuName: 'Alignment horizontal', - appendTo: 'Image' + menuName: lg['Alignment horizontal'], + appendTo: lg['Image'] }); - ['Left', 'Centre', 'Right'].forEach((v, i) => menu.newItem({ - menuName: 'Alignment horizontal', + [lg['Left'], lg['Centre'], lg['Right']].forEach((v, i) => menu.newItem({ + menuName: lg['Alignment horizontal'], str: v, func: () => this.setImageAlignnment(i, 'horizontal'), checkRadio: i == ppt.alignH })); menu.newMenu({ - menuName: 'Alignment vertical', - appendTo: 'Image' + menuName: lg['Alignment vertical'], + appendTo: lg['Image'] }); - ['Top', 'Centre', 'Bottom', 'Auto'].forEach((v, i) => menu.newItem({ - menuName: 'Alignment vertical', + [lg['Top'], lg['Centre'], lg['Bottom'], lg['Auto']].forEach((v, i) => menu.newItem({ + menuName: lg['Alignment vertical'], str: v, func: () => this.setImageAlignnment(i, 'vertical'), checkRadio: [!ppt.alignV && !ppt.alignAuto, ppt.alignV == 1 && !ppt.alignAuto, ppt.alignV == 2 && !ppt.alignAuto, ppt.alignAuto][i], @@ -501,15 +502,15 @@ class MenuItems { })); } - menu.addSeparator({menuName: 'Image'}); + menu.addSeparator({menuName: lg['Image']}); menu.newMenu({ - menuName: 'Black list', - appendTo: 'Image' + menuName: lg['Black list'], + appendTo: lg['Image'] }); for (let i = 0; i < 3; i++) menu.newItem({ - menuName: 'Black list', + menuName: lg['Black list'], str: this.img.blacklistStr[i], func: () => this.setImageBlacklist(i), flags: !i && this.img.isLfm || i == 2 ? MF_STRING : MF_GRAYED, @@ -517,7 +518,7 @@ class MenuItems { }); this.img.blacklist.forEach((v, i) => menu.newItem({ - menuName: 'Black list', + menuName: lg['Black list'], str: (this.img.artist + '_' + v).replace(/&/g, '&&'), func: () => this.setImageBlacklist(i + (img.blackList.undo[0] == this.img.artistClean ? 3 : 2)), })); @@ -527,14 +528,14 @@ class MenuItems { if (ppt.menuShowPlaylists == 2 || ppt.menuShowPlaylists && this.shift) { const pl_no = Math.ceil(this.playlist.menu.length / 30); menu.newMenu({ - menuName: 'Playlists', + menuName: lg['Playlists'], separator: ppt.menuShowPlaylists == 2 || ppt.menuShowPlaylists && this.shift }); for (let j = 0; j < pl_no; j++) { const n = '# ' + (j * 30 + 1 + ' - ' + Math.min(this.playlist.menu.length, 30 + j * 30) + (30 + j * 30 > plman.ActivePlaylist && ((j * 30) - 1) < plman.ActivePlaylist ? ' >>>' : '')); menu.newMenu({ menuName: n, - appendTo: 'Playlists' + appendTo: lg['Playlists'] }); for (let i = j * 30; i < Math.min(this.playlist.menu.length, 30 + j * 30); i++) { menu.newItem({ @@ -549,13 +550,13 @@ class MenuItems { if (ppt.menuShowTagger == 2 || ppt.menuShowTagger && this.shift) { menu.newMenu({ - menuName: 'Tagger', - str: 'Tagger' + (this.handles.Count ? '' : ': N/A no playlist tracks selected'), + menuName: lg['Tagger'], + str: lg['Tagger'] + (this.handles.Count ? '' : lg[': N/A no playlist tracks selected']), flags: this.handles.Count ? MF_STRING : MF_GRAYED, separator: ppt.menuShowTagger == 2 || ppt.menuShowTagger && this.shift }); for (let i = 0; i < 13 + 4; i++) menu.newItem({ - menuName: 'Tagger', + menuName: lg['Tagger'], str: this.getTaggerStr(i), func: () => cfg.setTag(i, this.handles), flags: !i || i == 13 + 1 && !this.tags ? MF_GRAYED : MF_STRING, @@ -566,11 +567,11 @@ class MenuItems { if (ppt.menuShowMissingData == 2 || ppt.menuShowMissingData && this.shift) { menu.newMenu({ - menuName: 'Missing data', + menuName: lg['Missing data'], separator: ppt.menuShowMissingData == 2 || ppt.menuShowMissingData && this.shift }); - ['Album review [allmusic]', 'Album review [last.fm]', 'Album review [wikipedia]', 'Biography [allmusic]', 'Biography [last.fm]', 'Biography [wikipedia]', 'Photos [last.fm]'].forEach((v, i) => menu.newItem({ - menuName: 'Missing data', + [lg['Album review [allmusic]'], lg['Album review [last.fm]'], lg['Album review [wikipedia]'], lg['Biography [allmusic]'], lg['Biography [last.fm]'], lg['Biography [wikipedia]'], lg['Photos [last.fm]']].forEach((v, i) => menu.newItem({ + menuName: lg['Missing data'], str: v, func: () => this.checkMissingData(i), separator: i == 2 || i == 5 @@ -579,14 +580,14 @@ class MenuItems { if (ppt.menuShowInactivate == 2 || ppt.menuShowInactivate && this.shift) { menu.newItem({ - str: ppt.panelActive ? 'Inactivate' : 'Activate biography', + str: ppt.panelActive ? lg['Inactivate'] : lg['Activate biography'], func: () => panel.inactivate(), separator: true }); } for (let i = 0; i < 2; i++) menu.newItem({ - str: [popUpBox.ok ? 'Options...' : 'Options: see console', 'Configure...'][i], + str: [popUpBox.ok ? lg['Options...'] : lg['Options: see console'], lg['Configure...']][i], func: () => !i ? cfg.open('PanelCfg') : window.EditScript(), separator: !i && this.shift, hide: i && !this.shift @@ -655,22 +656,22 @@ class MenuItems { this.img.blacklist = this.img.list.blacklist[this.img.artistClean] || []; } - this.img.blacklistStr = [this.img.isLfm ? '+ Add' + (!panel.style.showFilmStrip ? '' : ' main image') + ' to black list: ' + this.img.artist + '_' + this.img.name : '+ Add to black list: ' + (this.img.name ? 'N/A - requires last.fm photo. Selected image : ' + this.img.name : 'N/A - no' + (!panel.style.showFilmStrip ? '' : '') + ' image file'), this.img.blacklist.length ? ' - Remove from black list (click name): ' : 'No black listed images for current artist', 'Undo']; + this.img.blacklistStr = [this.img.isLfm ? lg['+ Add'] + (!panel.style.showFilmStrip ? '' : lg[' main image']) + lg[' to black list: '] + this.img.artist + '_' + this.img.name : lg['+ Add to black list: '] + (this.img.name ? lg['N/A - requires last.fm photo. Selected image: '] + this.img.name : lg['N/A - no'] + (!panel.style.showFilmStrip ? '' : '') + lg[' image file']), this.img.blacklist.length ? lg[' - Remove from black list (click name): '] : lg['No black listed images for current artist'], lg['Undo']]; } getDisplayStr() { const m = ppt.artistView ? ppt.bioMode : ppt.revMode; - this.display.check = [ppt.sameStyle ? !ppt.img_only && !ppt.text_only : m == 0, ppt.sameStyle ? ppt.img_only : m == 1, ppt.sameStyle ? ppt.text_only : m == 2, ppt.showFilmStrip, ppt.heading, ppt.summaryShow, ppt.artistView, !ppt.artistView, !panel.id.focus, panel.id.focus]; - const n = ['Image+text', 'Image', 'Text', 'Filmstrip', 'Heading', 'Summary', 'Artist view', 'Album view', 'Prefer nowplaying', !panel.id.lyricsSource ? 'Follow selected track (playlist)' : 'Follow selected track: N/A lyrics source enabled']; - const click = [!this.display.check[0] ? '\tMiddle click' : '', !this.display.check[1] && !ppt.text_only && !ppt.img_only ? '\tMiddle click' : '', !this.display.check[2] && !ppt.img_only ? '\tMiddle click' : '', '\tAlt+Middle click', '', '', !ppt.artistView ? (!ppt.dblClickToggle ? '\tClick' : '\tDouble click') : '', ppt.artistView ? (!ppt.dblClickToggle ? '\tClick' : '\tDouble click') : '', '', '']; + this.display.check = [ppt.sameStyle ? !ppt.img_only && !ppt.text_only : m == 0, ppt.sameStyle ? ppt.img_only : m == 1, ppt.sameStyle ? ppt.text_only : m == 2, ppt.showFilmStrip, ppt.heading, ppt.summaryShow, false, ppt.artistView, !ppt.artistView, !panel.id.focus, panel.id.focus]; + const n = [lg['Image+text'], lg['Image'], lg['Text'], lg['Filmstrip'], lg['Heading'], lg['Summary'], ppt.summaryCompact ? lg['Summary expand'] : lg['Summary compact'], lg['Artist view'], lg['Album view'], lg['Prefer nowplaying'], !panel.id.lyricsSource && !panel.id.nowplayingSource ? lg['Follow selected track (playlist)'] : lg['Follow selected track: N/A lyrics or nowplaying enabled']]; + const click = [!this.display.check[0] ? '\t' + lg['Middle click'] : '', !this.display.check[1] && !ppt.text_only && !ppt.img_only ? '\t' + lg['Middle click'] : '', !this.display.check[2] && !ppt.img_only ? '\t' + lg['Middle click'] : '', '\t' + lg['Alt+Middle click'], '', '', '\t' + lg['Click'], !ppt.artistView ? (!ppt.dblClickToggle ? '\t' + lg['Click'] : '\t' + lg['Double click']) : '', ppt.artistView ? (!ppt.dblClickToggle ? '\t' + lg['Click'] : '\t' + lg['Double click']) : '', '', '']; this.display.str = n.map((v, i) => v + click[i]) } getlookUpStr(i, j, artist) { return [ - ['Manual cycle: wheel over button', 'Auto cycle items', popUpBox.ok ? 'Options...' : 'Options: see console', 'Reload'][i], - ['Show similar artists', 'Show more tags' + ' (circle button if present)', 'Show artist history', 'Auto lock', 'Reset artist history...', 'Last.fm: ' + artist + '...', 'Last.fm: ' + artist + ': similar artists...', 'Last.fm: ' + artist + ': top albums...', 'AllMusic: ' + artist + '...'][i], - ['Show top albums', 'Show album history', 'Auto lock', 'Reset album history...', 'Last.fm: ' + artist + '...', 'Last.fm: ' + artist + ': similar artists...', 'Last.fm: ' + artist + ': top albums...', 'AllMusic: ' + artist + '...'][i] + [lg['Manual cycle: wheel over button'], lg['Auto cycle items'], popUpBox.ok ? lg['Options...'] : lg['Options: see console'], lg['Reload']][i], + [lg['Show similar artists'], lg['Show more tags (circle button if present)'], lg['Show artist history'], lg['Auto lock'], lg['Reset artist history...'], lg['Last.fm: '] + artist + lg['...'], lg['Last.fm: '] + artist + lg[': similar artists...'], lg['Last.fm: '] + artist + lg[': top albums...'], lg['Allmusic: '] + artist + lg['...']][i], + [lg['Show top albums'], lg['Show album history'], lg['Auto lock'], lg['Reset album history...'], lg['Last.fm: '] + artist + lg['...'], lg['Last.fm: '] + artist + lg[': similar artists...'], lg['Last.fm: '] + artist + lg[': top albums...'], lg['Allmusic: '] + artist + lg['...']][i] ][j]; } @@ -680,7 +681,7 @@ class MenuItems { getOpenName() { const fo = [this.path.img, this.path.am[3], this.path.lfm[3], this.path.wiki[3], this.path.tracksAm[3], this.path.tracksLfm[3], this.path.tracksWiki[3], this.path.txt[3]]; - this.openName = ['Image \tAlt+Click', ppt.artistView ? 'Biography [allmusic]' : 'Review [allmusic]', ppt.artistView ? 'Biography [last.fm]' : 'Review [last.fm]', ppt.artistView ? 'Biography [wikipedia]' : 'Review [wikipedia]', ppt.artistView ? '' : 'Tracks [allmusic]', ppt.artistView ? '' : 'Tracks [last.fm]', ppt.artistView ? '' : 'Tracks [wikipedia]', ppt.artistView ? txt.bio.subhead.txt[0] : txt.rev.subhead.txt[0]]; + this.openName = [lg['Image '] + '\t' + lg['Alt+Click'], ppt.artistView ? lg['Biography [allmusic]'] : lg['Review [allmusic]'], ppt.artistView ? lg['Biography [last.fm]'] : lg['Review [last.fm]'], ppt.artistView ? lg['Biography [wikipedia]'] : lg['Review [wikipedia]'], ppt.artistView ? '' : lg['Tracks [allmusic]'], ppt.artistView ? '' : lg['Tracks [last.fm]'], ppt.artistView ? '' : lg['Tracks [wikipedia]'], ppt.artistView ? txt.bio.subhead.txt[0] : txt.rev.subhead.txt[0]]; let i = this.openName.length; while (i--) if (!fo[i]) { @@ -694,14 +695,14 @@ class MenuItems { const b = ppt.artistView ? 'Bio' : 'Rev'; const n = b.toLowerCase(); this.types = !txt[n].reader ? $.source.amLfmWiki : $.source.amLfmWikiTxt; - this.sources = ['Allmusic', 'Last.fm', 'Wikipedia']; + this.sources = [lg['Allmusic'], lg['Last.fm'], lg['Wikipedia']]; this.sources = this.sources.map(v => v + (ppt.artistView ? ' biography' : ' review')); if (txt[n].reader) this.sources.push(txt[n].subhead.txt[0] || ''); - if (!panel.stndItem() && (txt.reader.lyrics || txt.reader.props)) this.sources[3] += ' // current track'; + if (!panel.stndItem() && (txt.reader[n].lyrics || txt.reader[n].props)) this.sources[3] += ' // current track'; } getTaggerStr(i) { - return !i ? 'Write existing file info to tags: ' : i == 13 + 1 ? 'All tagger settings...' : i == 13 + 2 ? (cfg.taggerConfirm ? 'Tag files...' : `Tag ${this.handles.Count} ${this.handles.Count > 1 ? 'tracks' : 'track'}...`) + (this.tags ? '' : ' N/A no tags enabled') + (cfg.tagEnabled5 || cfg.tagEnabled7 ? tag.genres.length > 700 || !cfg.useWhitelist ? '' : ' WARNING: last.fm genre whitelist not found or invalid [try force update - needs internet connection]' : '') : i == 13 + 3 ? 'Cancel' : i == 11 ? cfg[`tagName${i - 1}`] + (cfg[`tagEnabled${i - 1}`] ? ' (' + cfg[`tagEnabled${i + 2}`] + ')' : '') : cfg[`tagName${i - 1}`]; + return !i ? lg['Write existing file info to tags: '] : i == 13 + 1 ? lg['All tagger settings...'] : i == 13 + 2 ? (cfg.taggerConfirm ? lg['Tag files...'] : `${lg['Tag']} ${this.handles.Count} ${this.handles.Count > 1 ? lg['tracks'] : lg['track']}...`) + (this.tags ? '' : lg[' N/A no tags enabled']) + (cfg.tagEnabled5 || cfg.tagEnabled7 ? tag.genres.length > 700 || !cfg.useWhitelist ? '' : lg[' WARNING: last.fm genre whitelist not found or invalid [try force update - needs internet connection]'] : '') : i == 13 + 3 ? lg['Cancel'] : i == 11 ? cfg[`tagName${i - 1}`] + (cfg[`tagEnabled${i - 1}`] ? ' (' + cfg[`tagEnabled${i + 2}`] + ')' : '') : cfg[`tagName${i - 1}`]; } images(v) { @@ -930,7 +931,7 @@ class MenuItems { } const caption = this.popUpTitle; const prompt = this.popUpText(n2, n3); - const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', continue_confirmation) : true; + const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', '', '', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $.wshPopup(prompt, caption)); } @@ -963,7 +964,7 @@ class MenuItems { } const caption = this.popUpTitle; const prompt = this.popUpText(n2, n3); - const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', continue_confirmation) : true; + const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', '', '', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $.wshPopup(prompt, caption)); } @@ -1007,7 +1008,7 @@ class MenuItems { } const caption = this.popUpTitle; const prompt = this.popUpText(n2, n3); - const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', continue_confirmation) : true; + const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', '', '', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $.wshPopup(prompt, caption)); } @@ -1106,17 +1107,18 @@ class MenuItems { txt.refresh(1); break; case 5: + case 6: txt.bio.scrollPos = {}; txt.rev.scrollPos = {}; - ppt.toggle('summaryShow'); + ppt.toggle(i == 5 ? 'summaryShow' : 'summaryCompact'); panel.setSummary(); txt.refresh(1); break; - case 6: case 7: + case 8: panel.click('', '', true); break; - case 8: case 9: + case 10: ppt.toggle('focus'); panel.id.focus = ppt.focus; panel.changed(); @@ -1283,6 +1285,8 @@ class MenuItems { setStyle(i) { const prop = ppt.sameStyle ? 'style' : ppt.artistView ? 'bioStyle' : 'revStyle'; ppt[prop] = i; + img.mask.reset = true; + ppt.img_only = false; ppt.text_only = false; txt.refresh(0); if (ppt.filmStripOverlay) filmStrip.set(ppt.filmStripPos); } @@ -1311,7 +1315,7 @@ class MenuItems { switch (i) { case 0: case 1: this.toggle(4, b); break; - case 2: txt.bio.scrollPos = {}; txt.rev.scrollPos = {}; ppt.toggle('sourceAll'); txt.refresh(1); break; // want rest? + case 2: txt.bio.scrollPos = {}; txt.rev.scrollPos = {}; ppt.toggle('sourceAll'); txt.refresh(1); break; case 3: ppt.toggle('showTrackRevOptions'); txt.logScrollPos(); diff --git a/scripts/names.js b/scripts/names.js index a4b5410..bb74424 100644 --- a/scripts/names.js +++ b/scripts/names.js @@ -1,5 +1,7 @@ 'use strict'; +include(cfg.expandPath('%storage_folder%\\advanced_radio_stream_parser.js')); + class Names { constructor() { this.cur_artist = ''; @@ -47,7 +49,9 @@ class Names { } artist(focus, ignoreLock) { - return $.eval('[$trim(' + cfg.tf.artist + ')]', focus, ignoreLock); + let artist = $.eval('[$trim(' + cfg.tf.artist + ')]', focus, ignoreLock); + const radioTrackArtist = !panel.isRadio(focus) ? '' : radioStreamParser.getStreamInfo(focus, ignoreLock).artist; + return radioTrackArtist || artist; } composition(focus, ignoreLock) { @@ -65,18 +69,9 @@ class Names { } title(focus, ignoreLock) { - let n = $.eval('[$trim(' + cfg.tf.title + ')]', focus, ignoreLock); - if (cfg.local && panel.isRadio(focus)) { - const kw = '(-\\s*|\\s+)\\d\\d\\d\\d'; - let ix = -1; - let yr = n.match(RegExp(kw)); - if (yr) { - yr = yr[0].toString(); - ix = n.indexOf(yr); - } - if (ix != -1) n = n.slice(0, ix).trim(); - } - return n; + let title = $.eval('[$trim(' + cfg.tf.title + ')]', focus, ignoreLock); + const radioTrackTitle = !panel.isRadio(focus) ? '' : radioStreamParser.getStreamInfo(focus, ignoreLock).title; + return radioTrackTitle || title; } trackID(focus) { diff --git a/scripts/panel.js b/scripts/panel.js index bd5b4e3..af54837 100644 --- a/scripts/panel.js +++ b/scripts/panel.js @@ -83,6 +83,7 @@ class Panel { lockArt: '', loadTimestamp: Date.now(), lyricsSource: false, + nowplayingSource: false, numServersChecked: false, propsSource: false, tr: '', @@ -90,12 +91,20 @@ class Panel { } for (let i = 0; i < 8; i++) { - if (ppt.txtReaderEnable && ppt[`useTxtReader${i}`] && ppt[`pthTxtReader${i}`] && ppt[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1])) { + if (ppt.txtReaderEnable && ppt[`useTxtReader${i}`] && ppt[`pthTxtReader${i}`] && ppt[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1]) && !/nowplaying/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1])) { this.id.lyricsSource = true; this.id.focus = false; break; } } + + for (let i = 0; i < 8; i++) { + if (ppt.txtReaderEnable && ppt[`useTxtReader${i}`] && /nowplaying/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1])) { + this.id.nowplayingSource = true; + this.id.focus = false; + break; + } + } for (let i = 0; i < 8; i++) { if (ppt.txtReaderEnable && ppt[`useTxtReader${i}`] && /item_properties/i.test(utils.SplitFilePath(ppt[`pthTxtReader${i}`])[1])) { @@ -121,7 +130,7 @@ class Panel { } this.logo = { - img: null + img: my_utils.getImageAsset('Logo.png') } this.repaint = { @@ -185,29 +194,11 @@ class Panel { l: 0 } + this.checkRefreshRates(); this.setSummary(); this.similarArtistsKey = 'Similar Artists: |\\u00c4hnliche K\\u00fcnstler: |Artistas Similares: |Artistes Similaires: |Artisti Simili: |\\u4f3c\\u3066\\u3044\\u308b\\u30a2\\u30fc\\u30c6\\u30a3\\u30b9\\u30c8: |Podobni Wykonawcy: |Artistas Parecidos: |\\u041f\\u043e\\u0445\\u043e\\u0436\\u0438\\u0435 \\u0438\\u0441\\u043f\\u043e\\u043b\\u043d\\u0438\\u0442\\u0435\\u043b\\u0438: |Liknande Artister: |Benzer Sanat\\u00e7\\u0131lar: |\\u76f8\\u4f3c\\u827a\\u672f\\u5bb6: '; this.d = parseFloat(this.q('0000029142')); this.lfm = this.q($.s); this.topAlbumsKey = 'Top Albums: |Top-Alben: |\\u00c1lbumes M\\u00e1s Escuchados: |Top Albums: |Album Pi\\u00f9 Ascoltati: |\\u4eba\\u6c17\\u30a2\\u30eb\\u30d0\\u30e0: |Najpopularniejsze Albumy: |\\u00c1lbuns Principais: |\\u041f\\u043e\\u043f\\u0443\\u043b\\u044f\\u0440\\u043d\\u044b\\u0435 \\u0430\\u043b\\u044c\\u0431\\u043e\\u043c\\u044b: |Toppalbum: |En Sevilen Alb\\u00fcmler: |\\u6700\\u4f73\\u4e13\\u8f91: '; - this.focusLoad = $.debounce(() => { - if (!ppt.img_only) txt.on_playback_new_track(); - if (!ppt.text_only || ui.style.isBlur || ppt.showFilmStrip) img.on_playback_new_track(); - }, ppt.focusLoadRate, { - 'leading': true, - 'trailing': true - }); - - this.focusServer = $.debounce(() => { - this.changed(); - }, ppt.focusServerRate, { - 'leading': true, - 'trailing': true - }); - - this.lookUpServer = $.debounce(() => { - this.callServer(false, panel.id.focus, 'bio_lookUpItem', 0); - }, ppt.lookUpServerRate); - if (!this.style.free || !$.isArray(this.style.free)) { ppt.set('SYSTEM.Freestyle Custom BackUp', ppt.styleFree); this.style.free = []; @@ -301,8 +292,46 @@ class Panel { } changed() { - if (panel.id.focus || !fb.IsPlaying) this.callServer(false, panel.id.focus, 'bio_lookUpItem', 0); - else if ($.server) this.callServer(false, panel.id.focus, '', 1); + if (this.id.focus || !fb.IsPlaying) this.callServer(false, this.id.focus, 'bio_lookUpItem', 0); + else if ($.server) this.callServer(false, this.id.focus, '', 1); + } + + checkRefreshRates() { + [ + {key: 'focusLoadRate', descr: 'Panel Focus Load Refresh Rate', min: 200, max: 3000, oldDef: 250, newDef: 250}, + {key: 'focusServerRate', descr: 'Panel Focus Server Refresh Rate', min: 1500, max: 15000, oldDef: 5000, newDef: 5000}, + {key: 'lookUpServerRate', descr: 'Panel Lookup Refresh Rate', min: 1500, max: 15000, oldDef: 1500, newDef: 1500} + ].forEach((rate) => { + const name = `${rate.descr} ${rate.min}-${rate.max} msec (Max)`; + const value = ppt.get(name, null); + if (value === null) {throw ('property_name: ' + name + '\nPanel\'s rate property name does not match range checked');} + else { + if (ppt[rate.key] === rate.oldDef && ppt[rate.key] !== rate.newDef) {ppt[rate.key] = rate.newDef;} + ppt[rate.key] = $.clamp(ppt[rate.key], rate.min, rate.max); + if (ppt[rate.key] !== Number(value)) { + ppt.set(name, ppt[rate.key]); + } + } + }); + + this.focusLoad = $.debounce(() => { + if (!ppt.img_only) txt.on_playback_new_track(); + if (!ppt.text_only || ui.style.isBlur || ppt.showFilmStrip) img.on_playback_new_track(); + }, ppt.focusLoadRate, { + 'leading': ppt.focusLoadImmediate, + 'trailing': true + }); + + this.focusServer = $.debounce(() => { + this.changed(); + }, !ppt.focusLoadImmediate && ppt.focusServerRate == 5000 ? 2000 : ppt.focusServerRate, { // auto-adjust if !immediate & default value + 'leading': ppt.focusLoadImmediate, + 'trailing': true + }); + + this.lookUpServer = $.debounce(() => { + this.callServer(false, this.id.focus, 'bio_lookUpItem', 0); + }, ppt.lookUpServerRate); } checkNumServers() { @@ -408,7 +437,7 @@ class Panel { break; } if (ppt.img_only) img.setCrop(true); - sameStyle & !ui.show.btnRedLastfm ? but.check() : but.refresh(true); + but.refresh(true); if (!sameStyle && ppt.filmStripOverlay) filmStrip.set(ppt.filmStripPos); if (!ppt.artistView) img.setCheckArr(null); this.move(x, y, true); @@ -423,8 +452,8 @@ class Panel { ns = input; } } - const caption = 'Create New Freestyle Layout'; - const prompt = 'Enter new style name\n\nFreestyle layouts offer drag style positioning of image & text boxes + text overlay\n\nContinue?'; + const caption = 'Create New Layout'; + const prompt = 'This copies the current layout style & saves it to the entered name\n\nThe copy is in freestyle format that offers fully flexible drag style positioning of image & text boxes + overlay effects\n\nContinue?'; const fallback = popUpBox.isHtmlDialogSupported() ? popUpBox.input(caption, prompt, ok_callback, '', 'My Style') : true; if (fallback) { try { @@ -501,8 +530,8 @@ class Panel { if (v.name == ns) ns = ns + ' New'; }); if (ppt.style > 3 && (ppt.img_only || ppt.text_only)) { - if (ppt.style - 5 >= this.style.free.length) this.getStyleFallback(); - const obj = ppt.style == 4 ? this.style.overlay : this.style.free[ppt.style - 5]; + if (ppt.style - 6 >= this.style.free.length) this.getStyleFallback(); + const obj = ppt.style == 4 || ppt.style == 5 ? this.style.overlay : this.style.free[ppt.style - 6]; this.im.l = $.clamp(obj.imL, 0, 1); this.im.r = $.clamp(obj.imR, 0, 1); this.im.t = $.clamp(obj.imT, 0, 1); @@ -527,9 +556,9 @@ class Panel { ppt.styleFree = JSON.stringify(this.style.free); this.style.free.some((v, i) => { if (v.name == ns) { - if (ppt.sameStyle) ppt.style = i + 5; - else if (ppt.artistView) ppt.bioStyle = i + 5; - else ppt.revStyle = i + 5; + if (ppt.sameStyle) ppt.style = i + 6; + else if (ppt.artistView) ppt.bioStyle = i + 6; + else ppt.revStyle = i + 6; return true; } }) @@ -550,7 +579,7 @@ class Panel { deleteStyle(n) { const continue_confirmation = (status, confirmed) => { if (confirmed) { - this.style.free.splice(n - 5, 1); + this.style.free.splice(n - 6, 1); ppt.styleFree = JSON.stringify(this.style.free); ppt.style = 0; if (!ppt.sameStyle) { @@ -558,23 +587,24 @@ class Panel { else ppt.revStyle = 0; } this.getStyleNames(); - txt.refresh(0); + if (!ppt.showFilmStrip) txt.refresh(0); + else filmStrip.set(ppt.filmStripPos); } } const caption = 'Delete Current Style'; const prompt = 'Delete: ' + this.style.name[n] + '\n\nStyle will be set to top'; - const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', continue_confirmation) : true; + const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', '', '', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $.wshPopup(prompt, caption)); } draw(gr) { let font = ui.font.main; - let str = 'POWERED by allmusic and last.fm.\r\n\r\nShift+middle click to activate / inactivate.'; + let str = 'POWERED by allmusic, last.fm & Wikipedia.\r\n\r\nShift+middle click to activate / inactivate.'; let textHeight2; const textHeight1 = Math.round(gr.MeasureString(str, font, 10, 0, this.w - 20, 1000, StringFormat(1, 1)).Height); const version = ` ${window.ScriptInfo.Name}: v${window.ScriptInfo.Version}`; - const versionHeight = gr.CalcTextHeight(version, ui.font.small) + const versionHeight = gr.CalcTextHeight(version, ui.font.small); const txtSp = this.h * 0.37; if (textHeight1 > txtSp) { @@ -592,9 +622,16 @@ class Panel { textCol = ui.dui ? window.GetColourDUI(0) : window.GetColourCUI(0); textCol_h = ui.dui ? window.GetColourDUI(2) : window.GetColourCUI(2); } + const hAvail = (this.h - txtSp - versionHeight) * 0.9; + const wAvail = this.w * 0.9; + let scale = this.getScale(this.logo.img, wAvail, hAvail); + this.logo.w = scale[0]; + this.logo.h = scale[1]; + this.logo.x = (this.w - this.logo.w) / 2; + this.logo.y = hAvail - this.logo.h + versionHeight + hAvail * 0.145; gr.SetInterpolationMode(7); - if (this.logo.img) gr.DrawImage(this.logo.img, this.logo.x, Math.max(this.logo.y, versionHeight), this.logo.w, this.logo.h, 0, 0, this.logo.img.Width, this.logo.img.Height); + if (this.logo.img) gr.DrawImage(this.logo.img, this.logo.x, this.logo.y, this.logo.w, this.logo.h, 0, 0, this.logo.img.Width, this.logo.img.Height); gr.SetInterpolationMode(0); gr.GdiDrawText(version, ui.font.small, textCol_h, 0, 0, this.w, this.h, 0x00000800); gr.GdiDrawText(str, font, textCol, 10, this.h - txtSp, this.w - 20, txtSp, txt.ncc); @@ -603,12 +640,12 @@ class Panel { exportStyle(n) { const continue_confirmation = (status, confirmed) => { if (confirmed) { - window.NotifyOthers('bio_customStyle', JSON.stringify(this.style.free[n - 5])); + window.NotifyOthers('bio_customStyle', JSON.stringify(this.style.free[n - 6])); } } const caption = 'Export Current Style To Other Biography Panels'; const prompt = 'Export: ' + this.style.name[n]; - const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', continue_confirmation) : true; + const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', '', '', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $.wshPopup(prompt, caption)); } @@ -620,10 +657,10 @@ class Panel { getList(p_clear, isAlbum) { if (!this.id.lookUp) return; - const artist = name.artist(panel.id.focus, true) || 'Artist Unknown'; - const albumArtist = name.albumArtist(panel.id.focus, true) || 'Artist Unknown'; + const artist = name.artist(this.id.focus, true) || lg['Artist Unknown']; + const albumArtist = (!panel.isRadio(this.id.focus) ? name.albumArtist(this.id.focus, true) : artist) || lg['Artist Unknown']; const composition = isAlbum ? false : ppt.classicalMusicMode && (txt.rev.loaded.am && !txt.rev.amFallback || txt.rev.loaded.wiki && !txt.rev.wikiFallback); - const album = !composition ? name.album(panel.id.focus, true) || 'Album Unknown' : name.composition(panel.id.focus, true) || 'Composition Unknown'; + const album = !composition ? name.album(this.id.focus, true) || lg['Album Unknown'] : name.composition(this.id.focus, true) || lg['Composition Unknown']; if (this.lock) { this.logArtistHistory(artist); this.logAlbumHistory(albumArtist, album, composition); @@ -631,9 +668,9 @@ class Panel { } let k = 0; - const lfmBio = this.cleanPth(cfg.pth.foLfmBio, panel.id.focus) + $.clean(artist) + cfg.suffix.foLfmBio + '.txt'; + const lfmBio = (!panel.isRadio(this.id.focus) ? this.cleanPth(cfg.pth.foLfmBio, this.id.focus) : this.cleanPth(cfg.remap.foLfmBio, this.id.focus, 'remap', artist, '', 1)) + $.clean(artist) + cfg.suffix.foLfmBio + '.txt'; const lBio = $.open(lfmBio); - const lfmSim = this.cleanPth(cfg.pth.foLfmSim, panel.id.focus) + $.clean(artist) + ' And Similar Artists.json'; + const lfmSim = (!panel.isRadio(this.id.focus) ? this.cleanPth(cfg.pth.foLfmSim, this.id.focus) : this.cleanPth(cfg.remap.foLfmSim, this.id.focus, 'remap', artist, '', 1)) + $.clean(artist) + ' And Similar Artists.json'; const mult_arr = []; let mn = ''; let nm = ''; @@ -658,7 +695,7 @@ class Panel { $.take(lSim, cfg.menuSimilarNum); if (lSim.length) { this.art.list.push({ - name: 'Similar Artists:', + name: lg['Similar Artists:'], field: '', type: 'label' }); @@ -691,7 +728,7 @@ class Panel { } if (found && $.isArray(sa) && sa.length) { this.art.list.push({ - name: 'Similar Artists:', + name: lg['Similar Artists:'], field: '', type: 'label' }); @@ -709,9 +746,9 @@ class Panel { this.style.moreTags = false; this.art.fields.forEach(v => { nm = v.replace(/%/g, ''); - for (let h = 0; h < $.eval('$meta_num(' + nm + ')', panel.id.focus); h++) { + for (let h = 0; h < $.eval('$meta_num(' + nm + ')', this.id.focus); h++) { mn = '$trim($meta(' + nm + ',' + h + '))'; - const name = $.eval(mn, panel.id.focus); + const name = $.eval(mn, this.id.focus); if (this.art.list.every(v => v.name !== name) && name.toLowerCase() != cfg.va.toLowerCase()) mult_arr.push({ name: name, field: ' ~ ' + $.titlecase(nm), @@ -731,7 +768,7 @@ class Panel { if (mult_arr.length) { this.style.moreTags = true; this.art.list.push({ - name: 'More Tags:', + name: lg['More Tags:'], field: '', type: 'label' }); @@ -753,7 +790,7 @@ class Panel { if (this.art.history.length && ppt.showArtistHistory) { this.art.list.push({ - name: 'Artist History:', + name: lg['Artist History:'], field: '', type: 'label' }); @@ -790,8 +827,8 @@ class Panel { }); if (found && $.isArray(ta) && ta.length) { this.alb.list.push({ - artist: 'Last.fm Top Albums: ' + artist + ':', - album: 'Last.fm Top Albums: ' + artist + ':', + artist: lg['Last.fm Top Albums: '] + artist + ':', + album: lg['Last.fm Top Albums: '] + artist + ':', type: 'label' }); ta.forEach((v, i) => this.alb.list.push({ @@ -813,7 +850,7 @@ class Panel { if (this.alb.history.length && ppt.showAlbumHistory) { this.alb.list.push({ artist: '', - album: 'Album History:', + album: lg['Album History:'], type: 'label' }); for (let h = 0; h < this.alb.history.length; h++) { @@ -829,21 +866,11 @@ class Panel { if (!this.albumsSame() && p_clear) this.alb.ix = 0; } - getLogo() { - this.logo.img = my_utils.getImageAsset('Logo.png'); - if (!this.logo.img) return; - let scale = this.getScale(this.logo.img, this.w * Math.min(this.h * 0.8 / this.w, 0.9), this.h * 0.34); - this.logo.w = scale[0]; - this.logo.h = scale[1]; - this.logo.x = (this.w - this.logo.w) / 2; - this.logo.y = this.h * 0.05 + (this.h * 0.66 - this.logo.h) / 2; - } - getPth(sw, focus, artist, album, stnd, supCache, cleanArtist, cleanAlbumArtist, cleanAlbum, folder, basic, server) { let fo, pth; switch (sw) { case 'bio': - if (stnd === '') stnd = this.stnd(this.art.ix, this.art.list); + if (stnd === '' || panel.isRadio(ppt.focus)) stnd = this.stnd(this.art.ix, this.art.list); if (server) fo = stnd ? this.cleanPth(cfg.pth[folder], focus, 'server') : this.cleanPth(cfg.remap[folder], focus, 'remap', artist, '', 1); else fo = stnd && !this.lock ? this.cleanPth(cfg.pth[folder], focus) : this.cleanPth(cfg.remap[folder], focus, 'remap', artist, '', 1); pth = fo + cleanArtist + cfg.suffix[folder] + '.txt'; @@ -941,19 +968,10 @@ class Panel { } getStyleNames() { - this.style.name = ['Top', 'Right', 'Bottom', 'Left', 'Overlay']; + this.style.name = [lg['Top'], lg['Right'], lg['Bottom'], lg['Left'], lg['Full overlay'], lg['Part overlay']]; this.style.free.forEach(v => this.style.name.push(v.name)); } - isRadioFocused() { - if (this.lock) return true; - const fid = plman.ActivePlaylist.toString() + plman.GetPlaylistFocusItemIndex(plman.ActivePlaylist).toString(); - const np = plman.GetPlayingItemLocation(); - let pid = -2; - if (np.IsValid) pid = plman.PlayingPlaylist.toString() + np.PlaylistItemIndex.toString(); - return fid == pid; - } - imgBoxTrace(x, y) { if (this.trace.film || this.m.y == -1) return false; if (ppt.img_only) return true; @@ -979,6 +997,15 @@ class Panel { return fb.IsPlaying && fb.PlaybackLength <= 0 && (!focus || this.isRadioFocused()); } + isRadioFocused() { + if (this.lock) return true; + const fid = plman.ActivePlaylist.toString() + plman.GetPlaylistFocusItemIndex(plman.ActivePlaylist).toString(); + const np = plman.GetPlayingItemLocation(); + let pid = -2; + if (np.IsValid) pid = plman.PlayingPlaylist.toString() + np.PlaylistItemIndex.toString(); + return fid == pid; + } + isTouchEvent(x, y) { return ppt.touchControl && Math.sqrt((Math.pow(this.id.last_pressed_coord.x - x, 2) + Math.pow(this.id.last_pressed_coord.y - y, 2))) > 3 * $.scale; } @@ -992,7 +1019,7 @@ class Panel { } logAlbumHistory(albumArtist, album, composition) { - if (albumArtist != 'Artist Unknown' && album != 'Album Unknown') this.alb.history.unshift({ + if (albumArtist != lg['Artist Unknown'] && album != lg['Album Unknown']) this.alb.history.unshift({ artist: albumArtist, album: album, composition: composition, @@ -1004,7 +1031,7 @@ class Panel { } logArtistHistory(artist) { - if (artist != 'Artist Unknown') this.art.history.unshift({ + if (artist != lg['Artist Unknown']) this.art.history.unshift({ name: artist, field: '', type: 'history' @@ -1017,22 +1044,22 @@ class Panel { mbtn_up(x, y, menuLock, bypass) { if ((x < 0 || y < 0 || x > this.w || y > this.h) && !bypass) return; if (this.id.lookUp && (but.btns['lookUp'].trace(x, y) || menuLock || bypass)) { - if (panel.id.lyricsSource) { + if (this.id.lyricsSource || this.id.nowplayingSource) { this.lock = 0; return; } let mArtist = ppt.artistView && this.art.ix; if (!this.lock && !mArtist) img.artistReset(); if (!this.lock) { - this.id.lockArt = $.eval(this.art.fields, panel.id.focus); - this.id.lockAlb = name.albID(panel.id.focus, 'full') + (this.style.inclTrackRev ? name.trackID(panel.id.focus) : ''); - this.lockHandle = $.handle(panel.id.focus); + this.id.lockArt = $.eval(this.art.fields, this.id.focus); + this.id.lockAlb = name.albID(this.id.focus, 'full') + (this.style.inclTrackRev ? name.trackID(this.id.focus) : ''); + this.lockHandle = $.handle(this.id.focus); img.setAlbID(); - img.cov.folder = panel.cleanPth(cfg.albCovFolder, panel.id.focus); + img.cov.folder = this.cleanPth(cfg.albCovFolder, this.id.focus); } if (!bypass) this.lock = this.lock == 0 || menuLock ? 1 : 0; txt.curHeadingID = this.lock ? txt.headingID() : ''; - if (!this.lock && (ppt.artistView && this.id.lockArt != $.eval(this.art.fields, panel.id.focus) || !ppt.artistView && this.id.lockAlb != name.albID(panel.id.focus, 'full') + (this.style.inclTrackRev ? name.trackID(panel.id.focus) : ''))) { + if (!this.lock && (ppt.artistView && this.id.lockArt != $.eval(this.art.fields, this.id.focus) || !ppt.artistView && this.id.lockAlb != name.albID(this.id.focus, 'full') + (this.style.inclTrackRev ? name.trackID(this.id.focus) : ''))) { txt.on_playback_new_track(true); img.on_playback_new_track(true); } @@ -1161,7 +1188,7 @@ class Panel { this.style.free.forEach(v => { if (v.name == input) input = input + ' New'; }); - this.style.free[n - 5].name = input; + this.style.free[n - 6].name = input; this.sort(this.style.free, 'name'); ppt.styleFree = JSON.stringify(this.style.free); this.style.free.some((v, i) => { @@ -1212,7 +1239,7 @@ class Panel { if (confirmed) { if (ppt.style < 4) ppt.rel_imgs = 0.65; else { - const obj = ppt.style == 4 ? this.style.overlay : this.style.free[ppt.style - 5]; + const obj = ppt.style == 4 || ppt.style == 5 ? this.style.overlay : this.style.free[ppt.style - 6]; obj.name = this.style.name[n]; obj.imL = 0; obj.imR = 0; @@ -1222,14 +1249,14 @@ class Panel { obj.txR = 0; obj.txT = 0.632; obj.txB = 0; - ppt.style == 4 ? ppt.styleOverlay = JSON.stringify(this.style.overlay) : ppt.styleFree = JSON.stringify(this.style.free); + ppt.style == 4 || ppt.style == 5 ? ppt.styleOverlay = JSON.stringify(this.style.overlay) : ppt.styleFree = JSON.stringify(this.style.free); } txt.refresh(3); } } const caption = 'Reset Current Style'; - const prompt = 'Reset to Default ' + (ppt.style < 4 ? this.style.name[n] : 'Overlay') + ' Style.\n\nContinue?' - const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', continue_confirmation) : true; + const prompt = 'Reset to Default ' + (ppt.style < 5 ? this.style.name[n] : 'Overlay') + ' Style.\n\nContinue?' + const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', '', '', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $.wshPopup(prompt, caption)); } @@ -1305,6 +1332,7 @@ class Panel { this.text.r = (ppt.sbarShow ? Math.max(ppt.textR, ui.sbar.sp + sp2) : ppt.textR) + this.filmStripSize.r; this.text.t = !ppt.topAlign ? ppt.textT + (this.h - ppt.textT + this.filmStripSize.t - ppt.textB - this.filmStripSize.b - this.lines_drawn * ui.font.main_h + ui.heading.h) / 2 : ppt.textT + ui.heading.h + this.filmStripSize.t; this.text.w = this.w - this.text.l - this.text.r; + this.text.h = this.lines_drawn * ui.font.main_h; this.heading.x = !this.style.fullWidthHeading ? this.text.l : ppt.textL; this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - this.heading.x - ppt.textR; if (ppt.sbarShow) { @@ -1322,14 +1350,15 @@ class Panel { $.key.forEach(v => this.img[v] = this.bor[v] + (v != 'b' ? (!ppt.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); let txt_h = Math.round((this.h - this.img.t - ppt.textB - (!ppt.filmStripOverlay ? this.filmStripSize.b : 0)) * (1 - ppt.rel_imgs)); this.lines_drawn = Math.max(Math.floor((txt_h - ui.heading.h) / ui.font.main_h), 0); - txt_h = this.lines_drawn * ui.font.main_h + this.style.gap; + this.text.h = this.lines_drawn * ui.font.main_h; + txt_h = this.text.h + this.style.gap; this.style.imgSize = Math.max(this.h - txt_h - this.img.t - (!ppt.filmStripOverlay ? this.filmStripSize.b : 0) - ppt.textB - ui.heading.h, 10); this.text.l = ppt.textL + (!ppt.filmStripOverlay ? this.filmStripSize.l : 0); this.text.r = (ppt.sbarShow ? Math.max(ppt.textR, ui.sbar.sp + sp2) : ppt.textR) + (!ppt.filmStripOverlay ? this.filmStripSize.r : 0); this.text.t = this.img.t + this.style.imgSize + this.style.gap + ui.heading.h; this.text.w = this.w - this.text.l - this.text.r; this.heading.x = (!this.style.fullWidthHeading ? this.text.l : ppt.textL); - this.heading.w = !this.style.fullWidthHeading ? this.text.w : panel.w - ppt.textL - ppt.textR; + this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - ppt.textL - ppt.textR; if (!this.filmStripSize.r) this.sbar.x = this.w - ui.sbar.sp; else this.sbar.x = this.text.l + this.text.w + sp1; this.sbar.y = (ui.sbar.type < this.sbar.style || ppt.heading || this.filmStripSize.b ? this.text.t : this.img.t + this.style.imgSize) + this.sbar.top_corr; @@ -1370,7 +1399,8 @@ class Panel { $.key.forEach(v => this.img[v] = this.bor[v] + (v != 't' && v != 'b' ? (!ppt.filmStripOverlay ? this.filmStripSize[v] : 0) : 0)); let txt_h = Math.round((this.h - ppt.textT - this.img.b - (!ppt.filmStripOverlay ? this.filmStripSize.t : 0) - (!ppt.filmStripOverlay ? this.filmStripSize.b : 0)) * (1 - ppt.rel_imgs)); this.lines_drawn = Math.max(Math.floor((txt_h - ui.heading.h) / ui.font.main_h), 0); - txt_h = this.lines_drawn * ui.font.main_h + this.style.gap; + this.text.h = this.lines_drawn * ui.font.main_h; + txt_h = this.text.h + this.style.gap; this.style.imgSize = Math.max(this.h - txt_h - ppt.textT - this.img.b - (!ppt.filmStripOverlay ? this.filmStripSize.t : 0) - (!ppt.filmStripOverlay ? this.filmStripSize.b : 0) - ui.heading.h, 10); this.img.t = this.h - this.bor.b - this.style.imgSize - (!ppt.filmStripOverlay ? this.filmStripSize.b : 0); this.text.l = ppt.textL + (!ppt.filmStripOverlay ? this.filmStripSize.l : 0); @@ -1378,7 +1408,7 @@ class Panel { this.text.t = this.img.t - txt_h; this.text.w = this.w - this.text.l - this.text.r; this.heading.x = (!this.style.fullWidthHeading ? this.text.l : ppt.textL); - this.heading.w = !this.style.fullWidthHeading ? this.text.w : panel.w - ppt.textL - ppt.textR; + this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - ppt.textL - ppt.textR; if (!this.filmStripSize.r) this.sbar.x = this.w - ui.sbar.sp; else this.sbar.x = this.text.l + this.text.w + sp1; this.sbar.y = (ui.sbar.type < this.sbar.style || ppt.heading ? this.text.t : 0) + this.sbar.top_corr; @@ -1416,8 +1446,8 @@ class Panel { } case ppt.style > 3: { - if (ppt.style - 5 >= this.style.free.length) this.getStyleFallback(); - const obj = ppt.style == 4 ? this.style.overlay : this.style.free[ppt.style - 5]; + if (ppt.style - 6 >= this.style.free.length) this.getStyleFallback(); + const obj = ppt.style == 4 || ppt.style == 5 ? this.style.overlay : this.style.free[ppt.style - 6]; if (!bypass) { this.im.l = $.clamp(obj.imL, 0, 1); this.im.r = $.clamp(obj.imR, 0, 1); @@ -1432,24 +1462,23 @@ class Panel { const imR = Math.round(this.im.r * this.w) + (!ppt.filmStripOverlay ? this.filmStripSize.r : 0); const imT = Math.round(this.im.t * this.h) + (!ppt.filmStripOverlay ? this.filmStripSize.t : 0); const imB = Math.round(this.im.b * this.h) + (!ppt.filmStripOverlay ? this.filmStripSize.b : 0); - const txL = Math.round(this.tx.l * this.w) + (!ppt.filmStripOverlay ? this.filmStripSize.l : 0); - const txR = Math.round(this.tx.r * this.w) + (!ppt.filmStripOverlay ? this.filmStripSize.r : 0); - const txT = Math.round(this.tx.t * this.h) + (!ppt.filmStripOverlay ? this.filmStripSize.t : 0); - const txB = Math.round(this.tx.b * this.h) + (!ppt.filmStripOverlay ? this.filmStripSize.b : 0); + const txL = ppt.style == 4 ? 0 : Math.round(this.tx.l * this.w) + (!ppt.filmStripOverlay ? this.filmStripSize.l : 0); + const txR = ppt.style == 4 ? 0 : Math.round(this.tx.r * this.w) + (!ppt.filmStripOverlay ? this.filmStripSize.r : 0); + const txT = ppt.style == 4 ? 0 : Math.round(this.tx.t * this.h) + (!ppt.filmStripOverlay ? this.filmStripSize.t : 0); + const txB = ppt.style == 4 ? 0 : Math.round(this.tx.b * this.h) + (!ppt.filmStripOverlay ? this.filmStripSize.b : 0); this.ibox.l = Math.max(imL, 0); this.ibox.t = Math.max(imT, 0); this.ibox.w = this.w - imL - imR; this.ibox.h = this.h - imT - imB; - this.img.l = imL + this.bor.l; - this.img.r = imR + this.bor.r; - this.img.t = imT + this.bor.t; - this.img.b = imB + this.bor.b; - - const t_l = ppt.textL + ui.overlay.borderWidth; - const t_t = ppt.textT + ui.overlay.borderWidth; - let t_r = ppt.textR + ui.overlay.borderWidth; - let t_b = ppt.textB + ui.overlay.borderWidth; - if (ppt.typeOverlay == 2 || ppt.typeOverlay == 4) { + this.img.l = ppt.style == 4 ? 0 : imL + this.bor.l; + this.img.r = ppt.style == 4 ? 0 : imR + this.bor.r; + this.img.t = ppt.style == 4 ? 0 : imT + this.bor.t; + this.img.b = ppt.style == 4 ? 0 : imB + this.bor.b; + const t_l = (ppt.style == 4 ? this.filmStripSize.l : 0) + ppt.textL + ui.overlay.borderWidth; + const t_t = (ppt.style == 4 ? this.filmStripSize.t : 0) + ppt.textT + ui.overlay.borderWidth; + let t_r = (ppt.style == 4 ? this.filmStripSize.r : 0) + ppt.textR + ui.overlay.borderWidth; + let t_b = (ppt.style == 4 ? this.filmStripSize.b : 0) + ppt.textB + ui.overlay.borderWidth; + if ((ppt.typeOverlay == 2 || ppt.typeOverlay == 4) && ppt.style != 4) { t_r += 1; t_b += 1; } @@ -1460,15 +1489,16 @@ class Panel { this.text.r = txR + (ppt.sbarShow ? Math.max(t_r, ui.sbar.sp + sp1) : t_r); this.text.t = txT + ui.heading.h + t_t; this.text.w = this.w - this.text.l - this.text.r; - this.heading.x = !this.style.fullWidthHeading ? this.text.l : Math.min(this.img.l, this.text.l, (!ppt.filmStripOverlay ? this.filmStripSize.l : 0) ? filmStrip.x : this.w); - this.heading.w = !this.style.fullWidthHeading ? this.text.w : this.w - this.heading.x - Math.min(this.img.r, this.text.r, (!ppt.filmStripOverlay ? this.filmStripSize.r : 0) ? this.w - filmStrip.x - filmStrip.w : this.w); + this.text.h = this.lines_drawn * ui.font.main_h; + this.heading.x = !this.style.fullWidthHeading ? this.text.l : ppt.style == 4 ? ppt.textL : Math.min(this.img.l, this.text.l, (!ppt.filmStripOverlay ? this.filmStripSize.l : 0) ? filmStrip.x : this.w); + this.heading.w = !this.style.fullWidthHeading ? this.text.w : ppt.style == 4 ? this.w - this.heading.x * 2 : this.w - this.heading.x - Math.min(this.img.r, this.text.r, (!ppt.filmStripOverlay ? this.filmStripSize.r : 0) ? this.w - filmStrip.x - filmStrip.w : this.w); this.tbox.l = Math.max(txL, 0); this.tbox.t = Math.max(txT, 0); this.tbox.w = this.w - Math.max(txL, 0) - Math.max(txR, 0); this.tbox.h = this.h - Math.max(txT, 0) - Math.max(txB, 0); this.style.minH = ui.font.main_h + ui.heading.h + t_t + t_b; - if (ppt.typeOverlay == 2) ui.overlay.borderWidth = Math.max(Math.min(ui.overlay.borderWidth, this.tbox.w / 3, this.tbox.h / 3), 1); - if (ppt.typeOverlay) this.arc = Math.max(Math.min(ui.font.main_h / 1.5, this.tbox.w / 3, this.tbox.h / 3), 1); + if (ppt.typeOverlay == 2 && ppt.style != 4) ui.overlay.borderWidth = Math.max(Math.min(ui.overlay.borderWidth, this.tbox.w / 3, this.tbox.h / 3), 1); + if (ppt.typeOverlay && ppt.style != 4) this.arc = Math.max(Math.min(ui.font.main_h / 1.5, this.tbox.w / 3, this.tbox.h / 3), 1); this.clip = this.ibox.t + 100 < this.tbox.t && this.tbox.t < this.ibox.t + this.ibox.h && (this.tbox.l < this.ibox.l + this.ibox.w || this.tbox.l + this.tbox.w < this.ibox.l + this.ibox.w); this.style.imgSize = this.clip ? this.tbox.t - this.ibox.t : Math.min(this.h - imT - imB - this.bor.t - this.bor.b, this.w - imL - imR - this.bor.l - this.bor.r); this.sbar.x = this.tbox.l + this.tbox.w - ui.sbar.sp - ui.overlay.borderWidth; @@ -1570,15 +1600,15 @@ class Panel { switch (true) { case ppt.artistView: this.id.curArtist = this.id.artist; - this.id.artist = $.eval(this.art.fields, panel.id.focus); + this.id.artist = $.eval(this.art.fields, this.id.focus); if (!this.id.lookUp) return true; else return this.id.artist != this.id.curArtist || !this.art.list.length || !this.art.ix; case !ppt.artistView: this.id.curAlb = this.id.alb; - this.id.alb = name.albID(panel.id.focus, 'simple'); + this.id.alb = name.albID(this.id.focus, 'simple'); if (this.style.inclTrackRev) { this.id.curTr = this.id.tr; - this.id.tr = name.trackID(panel.id.focus); + this.id.tr = name.trackID(this.id.focus); } else this.id.curTr = this.id.tr = ''; if (!this.id.lookUp) return true; else return this.id.alb != this.id.curAlb || this.id.tr != this.id.curTr || !this.alb.list.length || !this.alb.ix; diff --git a/scripts/popupbox.js b/scripts/popupbox.js index d4fd796..d18a678 100644 --- a/scripts/popupbox.js +++ b/scripts/popupbox.js @@ -9,9 +9,9 @@ class PopUpBox { // Methods - confirm(msg_title, msg_content, btn_yes_label, btn_no_label, height_adjust, confirm_callback) { + confirm(msg_title, msg_content, btn_yes_label, btn_no_label, height_adjust, h_center, confirm_callback) { utils.ShowHtmlDialog(0, this.confirmHtmlCode, { - data: [msg_title, msg_content, btn_yes_label, btn_no_label, height_adjust, confirm_callback] + data: [msg_title, msg_content, btn_yes_label, btn_no_label, height_adjust, h_center, confirm_callback] }); } @@ -86,7 +86,7 @@ class PopUpBox { this.soFeat.clipboard = false; } - ppt.isHtmlDialogSupported = this.soFeat.gecko && this.soFeat.clipboard ? 1 : 0; + ppt.isHtmlDialogSupported = this.soFeat.gecko && this.soFeat.clipboard || this.isIEInstalled() ? 1 : 0; if (!ppt.isHtmlDialogSupported) { const caption = 'Show HTML Dialog'; const prompt = @@ -115,6 +115,16 @@ Supported-1; unsupported-0`; return ppt.isHtmlDialogSupported; } + isIEInstalled() { + const diskLetters = Array.from(Array(26)).map((e, i) => i + 65).map((x) => `${String.fromCharCode(x)}:\\`); + const paths = ['Program Files\\Internet Explorer\\ieinstal.exe', 'Program Files (x86)\\Internet Explorer\\ieinstal.exe']; + return diskLetters.some(d => { + try { // Needed when permission error occurs and current SMP implementation is broken for some devices.... + return utils.IsDirectory(d) ? paths.some(p => utils.IsFile(d + p)) : false; + } catch (e) {return false;} + }); + } + tf_callback(tf, tfAll, type, sFind, sReplace) { return cfg.preview(tf, tfAll, type, sFind, sReplace); } diff --git a/scripts/properties.js b/scripts/properties.js index 5636d7a..d335dda 100644 --- a/scripts/properties.js +++ b/scripts/properties.js @@ -96,6 +96,7 @@ let properties = [ ['Classical Music Mode', false, 'classicalMusicMode'], ['Classical Music Mode Album Fallback', false, 'classicalAlbFallback'], + ['Colour Line Dark', false, 'colLineDark'], ['Colour Swap', false, 'swapCol'], ['Cover Border [Dual Mode]', false, 'covBorderDual'], @@ -232,6 +233,7 @@ let properties = [ ['Image Smooth Transition', false, 'imgSmoothTrans'], ['Image Smooth Transition Level (%)', 92, 'transLevel'], + ['Language [Menu] EN-0 ZH-CN-1 ZH-TW-2', 0, 'menuLanguage'], ['Layout', 0, 'style'], ['Layout Bio Mode', 0, 'bioMode'], ['Layout Bio', 0, 'bioStyle'], @@ -275,10 +277,11 @@ let properties = [ ['Overlay Gradient (%)', 10, 'overlayGradient'], ['Overlay Strength (%)', 84.5, 'overlayStrength'], ['Overlay Type', 0, 'typeOverlay'], + ['Panel Active', true, 'panelActive'], - - ['Panel Selection Refresh Rate 200-3000 msec (Max)', 250, 'focusLoadRate'], - ['Panel Focus Refresh Rate 5000-15000 msec (Max)', 5000, 'focusServerRate'], + ['Panel Focus Load Immediate', false, 'focusLoadImmediate'], + ['Panel Focus Load Refresh Rate 200-3000 msec (Max)', 250, 'focusLoadRate'], + ['Panel Focus Server Refresh Rate 1500-15000 msec (Max)', 5000, 'focusServerRate'], ['Panel Lookup Refresh Rate 1500-15000 msec (Max)', 1500, 'lookUpServerRate'], ['Photo Border [Dual Mode]', false, 'artBorderDual'], @@ -302,7 +305,8 @@ let properties = [ ['Reflection Size (%)', 100, 'reflSize'], ['Reflection Strength (%)', 14.5, 'reflStrength'], - ['Scrollbar Height Prefer Full', false, 'sbarFullHeight'], + ['Scroll Position Bio', JSON.stringify({}), 'bioScrollPos'], + ['Scroll Position Rev', JSON.stringify({}), 'revScrollPos'], ['Scroll Step 0-10 (0 = Page)', 3, 'scrollStep'], ['Scroll Smooth Duration 0-5000 msec (Max)', 500, 'durationScroll'], ['Scroll Touch Flick Duration 0-5000 msec (Max)', 3000, 'durationTouchFlick'], @@ -314,6 +318,7 @@ let properties = [ ['Scrollbar Button Type', 0, 'sbarButType'], ['Scrollbar Colour Grey-0 Blend-1', 1, 'sbarCol'], ['Scrollbar Grip MinHeight', Math.round(20 * $.scale), 'sbarGripHeight'], + ['Scrollbar Height Prefer Full', false, 'sbarFullHeight'], ['Scrollbar Padding', 0, 'sbarPad'], ['Scrollbar Narrow Bar Width (0 = Auto)', 0, 'narrowSbarWidth'], ['Scrollbar Show', 1, 'sbarShow'], @@ -350,6 +355,7 @@ let properties = [ ['Subheading Track Style', 4, 'trackStyle'], ['Subheading Wikipedia Style', 5, 'wikiStyle'], + ['Summary Compact', true, 'summaryCompact'], ['Summary Dates', true, 'summaryDate'], ['Summary Genres', true, 'summaryGenre'], ['Summary Locale', true, 'summaryLocale'], @@ -366,6 +372,8 @@ let properties = [ ['Text Reader Item Properties: Field Width', 0, 'fieldWidth'], ['Text Reader Item Properties: Show Line Dividers', true, 'lineDividers'], ['Text Reader Item Properties: Show Row Stripes', true, 'rowStripes'], + ['Text Reader Nowplaying: Vertical Center', false, 'vCenter'], + ['Text Reader Nowplaying: Horizontal Center', true, 'hCenter'], ['Text Reader 1 Use', true, 'useTxtReader0'], ['Text Reader 2 Use', true, 'useTxtReader1'], ['Text Reader 3 Use', true, 'useTxtReader2'], @@ -374,6 +382,7 @@ let properties = [ ['Text Reader 6 Use', true, 'useTxtReader5'], ['Text Reader 7 Use', true, 'useTxtReader6'], ['Text Reader 8 Use', true, 'useTxtReader7'], + ['Text Reader Larger Sync Line', 0, 'largerSyncLyricLine'], ['Text Reader Lyrics Fade Height', 0, 'lyricsFadeHeight'], ['Text Reader Lyrics Font Style', 1, 'lyricsFontStyle'], ['Text Reader Lyrics Scroll Max Method', 0, 'lyricsScrollMaxMethod'], @@ -384,7 +393,7 @@ let properties = [ ['Text Reader 1 Name', 'lyrics', 'nmTxtReader0'], ['Text Reader 2 Name', 'lyrics', 'nmTxtReader1'], ['Text Reader 3 Name', 'lyrics', 'nmTxtReader2'], - ['Text Reader 4 Name', '', 'nmTxtReader3'], + ['Text Reader 4 Name', 'nowplaying', 'nmTxtReader3'], ['Text Reader 5 Name', 'lyrics', 'nmTxtReader4'], ['Text Reader 6 Name', 'lyrics', 'nmTxtReader5'], ['Text Reader 7 Name', 'lyrics', 'nmTxtReader6'], @@ -392,7 +401,7 @@ let properties = [ ['Text Reader 1 Item (field or full path)', '%profile%\\lyrics\\%BIO_ARTIST% - %BIO_TITLE%.lrc', 'pthTxtReader0'], ['Text Reader 2 Item (field or full path)', '$if3(%lyrics%,%syncedlyrics%,%unsynced lyrics%,%unsyncedlyrics%)', 'pthTxtReader1'], ['Text Reader 3 Item (field or full path)', '%profile%\\lyrics\\%BIO_ARTIST% - %BIO_TITLE%.txt', 'pthTxtReader2'], - ['Text Reader 4 Item (field or full path)', '', 'pthTxtReader3'], + ['Text Reader 4 Item (field or full path)', '%storage_folder%\\nowplaying.txt', 'pthTxtReader3'], ['Text Reader 5 Item (field or full path)', '%profile%\\lyrics\\%BIO_ARTIST% - %BIO_TITLE%.lrc', 'pthTxtReader4'], ['Text Reader 6 Item (field or full path)', '$if3(%lyrics%,%syncedlyrics%,%unsynced lyrics%,%unsyncedlyrics%)', 'pthTxtReader5'], ['Text Reader 7 Item (field or full path)', '%profile%\\lyrics\\%BIO_ARTIST% - %BIO_TITLE%.txt', 'pthTxtReader6'], @@ -405,9 +414,14 @@ let properties = [ ['Text Reader 6 Lyrics', true, 'lyricsTxtReader5'], ['Text Reader 7 Lyrics', true, 'lyricsTxtReader6'], ['Text Reader 8 Lyrics', false, 'lyricsTxtReader7'], + ['Text Reader Lyrics/Nowplaying Drop Shadow Level', 0, 'dropShadowLevel'], ['Text Reader Synchronise With Lyrics Download', false, 'syncTxtReaderLyrics'], ['Theme', 0, 'theme'], + ['Theme Background Image', false, 'themeBgImage'], + ['Theme Colour', 3, 'themeColour'], + ['Theme Light', false, 'themeLight'], + ['Themed', false, 'themed'], // reserved: don't enable ['Touch Control', false, 'touchControl'], ['Track Review', 0, 'inclTrackRev'], ['Track Review Show Options', false, 'showTrackRevOptions'], @@ -423,7 +437,7 @@ const ppt = new PanelProperties; ppt.init('auto', properties); properties = undefined; -if (ppt.get('Update Properties', true)) { +if (ppt.get('Update Properties', true)) { // ~22.7.22 ppt.nmTxtReader7 = 'item properties'; ppt.pthTxtReader7 = '%storage_folder%\\item_properties.json'; ppt.lyricsTxtReader7 = false; @@ -444,21 +458,4 @@ if (ppt.get('Remove Old Properties', true)) { const oldProperties = ['Allmusic Alb', 'Allmusic Bio', 'Both Bio', 'Both Rev', 'Heading', 'Heading BtnName Biography [AllMusic]', 'Heading BtnName Biography [Last.fm]', 'Heading BtnName Biography [Wikipedia]', 'Heading BtnName Review [AllMusic]', 'Heading BtnName Review [Last.fm]', 'Heading BtnName Review [Wikipedia]', 'Heading Title Format Album Review [AllMusic]', 'Heading Title Format Album Review [Last.fm]', 'Heading Title Format Biography [AllMusic]', 'Heading Title Format Biography [Last.fm]', 'Heading Title Format Track Review [AllMusic]', 'Heading Title Format Track Review [Last.fm]', 'Layout Dual Image+Text', 'Image Seeker Dots', 'Subheading [Source] Text Biography [AllMusic]: Heading|No Heading', 'Subheading [Source] Text Biography [Last.fm]: Heading|No Heading', 'Subheading [Source] Text Biography [Wikipedia]: Heading|No Heading', 'Subheading [Source] Text Review [AllMusic]: Heading|No Heading', 'Subheading [Source] Text Review [Last.fm]: Heading|No Heading', 'Subheading Source Hide-0 Show-1', 'Subheading [Source] Text Review [Wikipedia]: Heading|No Heading', 'Subheading [Track Review] Title Format [AllMusic]', 'Subheading [Track Review] Title Format [Last.fm]', 'Summary First', 'Tagger Last.fm Genre Find>Replace', 'Tagger Last.fm Genre Number Clean Up', 'Tagger Last.fm Genre Run Find>Replace', 'Tagger Last.fm Genre Strip Artist+Album Names', 'Text Album + Track Auto Optimise', 'Text Reader Source 0 Name', 'Text Reader Source 1 Name', 'Text Reader Source 2 Name', 'Text Reader Source 3 Name', 'Text Reader Source 4 Name', 'Text Reader Source 5 Name', 'Text Reader Source 6 Name', 'Text Reader Source 7 Name', 'Text Reader Source 1 (field or full path)', 'Text Reader Source 2 (field or full path)', 'Text Reader Source 3 (field or full path)', 'Text Reader Source 4 (field or full path)', 'Text Reader Source 5 (field or full path)', 'Text Reader Source 6 (field or full path)', 'Text Reader Source 7 (field or full path)', 'Text Reader Source 8 (field or full path)', 'Text Reader Source 1 Lyrics', 'Text Reader Source 2 Lyrics', 'Text Reader Source 3 Lyrics', 'Text Reader Source 4 Lyrics', 'Text Reader Source 5 Lyrics', 'Text Reader Source 6 Lyrics','Text Reader Source 7 Lyrics', 'Text Reader Source 8 Lyrics']; oldProperties.forEach(v => window.SetProperty(v, null)); ppt.set('Remove Old Properties', false); -} - -[ - {key: 'focusLoadRate', descr: 'Panel Selection Refresh Rate', min: 200, max: 3000, oldDef: 250, newDef: 250}, - {key: 'focusServerRate', descr: 'Panel Focus Refresh Rate', min: 5000, max: 15000, oldDef: 5000, newDef: 5000}, - {key: 'lookUpServerRate', descr: 'Panel Lookup Refresh Rate', min: 1500, max: 15000, oldDef: 1500, newDef: 1500} -].forEach((rate) => { - const name = `${rate.descr} ${rate.min}-${rate.max} msec (Max)`; - const value = ppt.get(name, null); - if (value === null) {throw ('property_name: ' + name + '\nPanel\'s rate property name does not match range checked');} - else { - if (ppt[rate.key] === rate.oldDef && ppt[rate.key] !== rate.newDef) {ppt[rate.key] = rate.newDef;} - ppt[rate.key] = $.clamp(ppt[rate.key], rate.min, rate.max); - if (ppt[rate.key] !== Number(value)) { - ppt.set(name, ppt[rate.key]); - } - } -}); \ No newline at end of file +} \ No newline at end of file diff --git a/scripts/resize.js b/scripts/resize.js index 6584f32..7521df0 100644 --- a/scripts/resize.js +++ b/scripts/resize.js @@ -37,7 +37,7 @@ class ResizeHandler { const ed = gr.MeasureString(this.editText(), this.editorFont, 15, 15, panel.w - 15, panel.h - 15, this.lc); gr.FillSolidRect(10, 10, ed.Width + 10, ed.Height + 10, ui.col.edBg); if (!ppt.text_only && !ppt.img_only) { - if (ppt.style > 3) { + if (ppt.style > 4) { if (!vk.k('shift')) gr.DrawRect(panel.ibox.l + 2, panel.ibox.t + 2, panel.ibox.w - 4, panel.ibox.h - 4, 5, RGB(0, 255, 0)); if (!vk.k('alt')) gr.DrawRect(panel.tbox.l + 2, panel.tbox.t + 2, panel.tbox.w - 4, panel.tbox.h - 4, 5, RGB(255, 0, 0)); } else if (ppt.style < 4) { @@ -80,7 +80,7 @@ class ResizeHandler { } editText() { - return (ppt.text_only ? 'Type: Text Only' + (panel.style.showFilmStrip ? '\n - Layout Adjust: Drag Line' : '') : (ppt.img_only ? 'Type: Image Only' + (panel.style.showFilmStrip ? '\n - Layout Adjust: Drag Line' : '') : 'Name: ' + panel.style.name[ppt.style] + (ppt.style < 4 ? '\n\nType: Auto\n - Layout Adjust: Drag Line' + (panel.style.showFilmStrip && !ppt.img_only ? 's' : '') : '\n\nType: Freestyle\n - Layout Adjust: Drag Lines or Boxes: Ctrl (Any), Ctrl + Alt (Image), Ctrl + Shift (Text) or Ctrl + Alt + Shift (Filmstrip)\n - Overlay Strength: Shift + Wheel Over Text'))) + (img.isType('Refl') && !ppt.text_only ? '\n - Reflection Strength: Shift + Wheel Over Main Image' : '') + (!ppt.img_only ? '\n - Text Size: Ctrl + Wheel Over Text' : '') + '\n - Padding: Display Tab'; + return (ppt.text_only ? 'Type: Text Only' + (panel.style.showFilmStrip ? '\n - Layout Adjust: Drag Line' : '') : (ppt.img_only ? 'Type: Image Only' + (panel.style.showFilmStrip ? '\n - Layout Adjust: Drag Line' : '') : 'Name: ' + panel.style.name[ppt.style] + (ppt.style < 4 ? '\n\nType: Auto\n - Layout Adjust: Drag Line' + (panel.style.showFilmStrip && !ppt.img_only && ppt.style != 4 ? 's' : '') + '\n - Image Strength: Shift + Wheel Over Text' : ppt.style == 4 ? '\n\nType: Auto' + (panel.style.showFilmStrip && !ppt.img_only ? '\n - Layout Adjust: Drag Line' : '') + '\n - Image Strength: Shift + Wheel Over Text' : '\n\nType: Freestyle\n - Layout Adjust: Drag Lines or Boxes: Ctrl (Any), Ctrl + Alt (Image), Ctrl + Shift (Text) or Ctrl + Alt + Shift (Filmstrip)\n - Overlay Strength: Shift + Wheel Over Text'))) + (img.isType('Refl') && !ppt.text_only && ppt.style != 4 ? '\n - Reflection Strength: Shift + Wheel Over Main Image' : '') + (!ppt.img_only ? '\n - Text Size: Ctrl + Wheel Over Text' : '') + '\n - ' + (ppt.style != 4 ? '' : 'Text ') +'Padding: Display Tab'; } filmMove(x, y) { @@ -151,7 +151,7 @@ class ResizeHandler { this.down = false; img.mask.reset = true; if (ppt.style > 3) { - const obj = ppt.style == 4 ? panel.style.overlay : panel.style.free[ppt.style - 5]; + const obj = ppt.style == 4 || ppt.style == 5 ? panel.style.overlay : panel.style.free[ppt.style - 6]; const imL = Math.round(panel.im.l * panel.w); const imR = Math.round(panel.im.r * panel.w); const imT = Math.round(panel.im.t * panel.h); @@ -195,7 +195,7 @@ class ResizeHandler { } imgMove(x, y) { - if (!this.focus || ppt.img_only || ppt.text_only) return; + if (!this.focus || ppt.img_only || ppt.text_only || ppt.style == 4) return; switch (true) { case ppt.style > 3: { if (!vk.k('ctrl') || vk.k('shift')) break; @@ -329,7 +329,7 @@ class ResizeHandler { } move(x, y) { - if (ppt.style < 4 || ppt.img_only || ppt.text_only || !vk.k('ctrl') || vk.k('alt') || !this.focus) return; + if (ppt.style < 4 || ppt.img_only || ppt.text_only || !vk.k('ctrl') || vk.k('alt') || !this.focus || ppt.style == 4) return; if (!this.down) { this.st = y > panel.tbox.t - 5 && y < panel.tbox.t + 5 && x > panel.tbox.l + 10 && x < panel.tbox.l + panel.tbox.w - 10 ? 'top' : y > panel.tbox.t - 5 && y < panel.tbox.t + 15 && x > panel.tbox.l && x < panel.tbox.l + 10 ? 'nw' : diff --git a/scripts/scrollbar.js b/scripts/scrollbar.js index dbf7f25..d6ed9ab 100644 --- a/scripts/scrollbar.js +++ b/scripts/scrollbar.js @@ -381,6 +381,7 @@ class Scrollbar { if (!this.draw_timer) return; this.delta = this.scroll; this.scrollTo(); + txt.logScrollPos(); filmStrip.logScrollPos(); clearTimeout(this.draw_timer); this.draw_timer = null; diff --git a/scripts/server.js b/scripts/server.js index 44dd738..e4ba268 100644 --- a/scripts/server.js +++ b/scripts/server.js @@ -43,15 +43,15 @@ class Server { wikisearch: 'https://www.wikidata.org/w/api.php?action=query&list=search&srprop=snippet|titlesnippet&srlimit=100&utf8&format=json&srsearch=' } - this.on_playback_new_track = $.debounce(() => { + this.call = $.debounce(focus => { this.download(false, { ix: 0, - focus: false, + focus: focus || false, arr: [] }, { ix: 0, - focus: false, + focus: focus || false, arr: [] }); }, 2000, { @@ -275,7 +275,7 @@ class Server { } else timer.decelerating(); if (cfg.lfmSim && stndBio) { - const fo_sim = panel.cleanPth(cfg.pth.foLfmSim, art.focus, 'server'); + const fo_sim = !panel.isRadio(art.focus) ? panel.cleanPth(cfg.pth.foLfmSim, art.focus, 'server') : panel.cleanPth(cfg.remap.foLfmSim, art.focus, 'remap', this.artist, '', 1); const pth_sim = fo_sim + $.clean(this.artist) + ' And Similar Artists.json'; let len = 0; let valid = false; diff --git a/scripts/settings.js b/scripts/settings.js index f306c95..d3d20b5 100644 --- a/scripts/settings.js +++ b/scripts/settings.js @@ -48,6 +48,8 @@ class Settings { this.bio = `${this.storageFolder + this.cfgBaseName}.cfg`; this.item_properties = `${this.storageFolder}item_properties.json`; this.item_properties_alternative_grouping = `${this.storageFolder}item_properties_alternative_grouping.json`; + this.nowplaying = `${this.storageFolder}nowplaying.txt`; + this.radioParser = `${this.storageFolder}advanced_radio_stream_parser.js`; this.cacheTime = 0; this.cfg = $.jsonParse(this.bio, {}, 'file'); @@ -60,6 +62,7 @@ class Settings { } this.local = $.file('C:\\check_local\\1450343922.txt'); + if (!this.local) ppt.themed = false; this.remap = {} this.pth = {} this.sup = {} @@ -174,6 +177,8 @@ class Settings { checkCfg() { if (!$.file(this.item_properties)) $.save(this.item_properties, item_properties, true); if (!$.file(this.item_properties_alternative_grouping)) $.save(this.item_properties_alternative_grouping, item_properties_alternative_grouping, true); + if (!$.file(this.nowplaying)) $.save(this.nowplaying, nowplaying, true); + if (!$.file(this.radioParser)) $.save(this.radioParser, radioParser, true); if ($.file(this.bio)) return; const orig_cfg = `${fb.ProfilePath}yttm\\biography.cfg`; const orig_cfg_copied = $.file(`${cfg.storageFolder}foo_lastfm_img.vbs`); @@ -368,7 +373,7 @@ class Settings { break; } } - const dialogWindow = ppt.get('Biography Dialog Box', JSON.stringify({ + let dialogWindow = ppt.get('Biography Dialog Box', JSON.stringify({ w: 85, h: 60, def_w: 85, @@ -376,8 +381,12 @@ class Settings { tab: 'PanelCfg', panelPage: 'display', serverPage: 'download', - displaySaveFolders: false + displaySaveFolders: false, + version: `v${window.ScriptInfo.Version}` })); + dialogWindow = $.jsonParse(dialogWindow); + dialogWindow.version = `v${window.ScriptInfo.Version}`; + dialogWindow = JSON.stringify(dialogWindow); if (!panel.id.numServersChecked) panel.checkNumServers(); if (popUpBox.isHtmlDialogSupported()) popUpBox.config(JSON.stringify(ppt), JSON.stringify(cfg), dialogWindow, ok_callback, this.lang.ix, $.folder(cfg.photoRecycler)); else { @@ -648,7 +657,7 @@ class Settings { } const caption = 'Tag Files:'; const prompt = handles.Count < 2000 ? `Update ${handles.Count} ${handles.Count > 1 ? 'tracks' : 'track'}.\n\nContinue?` : `Update ${handles.Count} tracks.\n\nADVISORY: This operation analyses a lot of data.\n\nContinue?`; - const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', continue_confirmation) : true; + const wsh = popUpBox.isHtmlDialogSupported() ? popUpBox.confirm(caption, prompt, 'OK', 'Cancel', '', '', continue_confirmation) : true; if (wsh) continue_confirmation('ok', $.wshPopup(prompt, caption)); break; } @@ -912,7 +921,141 @@ let item_properties_alternative_grouping = item_properties .replace(/("Metadata\*":\s{\s*?"show":\s)false/, '$1' + true) .replace(/(("Metadata"|"Popularity"|"AllMusic"|"Last.fm"|"Wikipedia"):\s{\s*?"show":\s)true/g, '$1' + false); +let nowplaying = `Artist: %artist%$crlf() +$crlf() +Title: %title%$crlf() +$crlf() +[Album: %album%$crlf() +$crlf()] +$if2(%playback_time%,0:00)[ / %length%]`; + +let radioParser = `/* RadioStreamParser is written in javascript and can be user edited with care. +It's designed for use with internet radio streams that contain the artist name and song title in a non-standard format. +Before editing, make a backup copy in case things go wrong. + +1. To add a new radio stream, copy one of the case instances including the break. Paste under the last and within the switch statement. +2. For each, change the path. For this, open properties and copy and paste the File path. Retain the quotes below. Escape any backslashes: replace \\ with \\\\ +3. Extract artist and title from radio stream item. + In many cases this can be done simply by splitting the item, e.g. at -. The 1st item will then be item[0], the 2nd item[1] etc. + In more complex cases use RegExp or javascript string manipulation functions. Google for syntax. +4. Adjust the format (comment out if unwanted). This is aesthetic. It won't affect searching. +5. Use console.log traces to see what's going on and debug, e.g uncomment those below. + +This parser is also used by Find & Play provided the biography package id {BA9557CE-7B4B-4E0E-9373-99F511E81252} is unaltered. +*/ + +'use strict'; + +class radioStreamParser { + + static getStreamInfo(focus, ignoreLock) { + // don't alter the next 4 lines + const path = $.eval('%path%', focus, ignoreLock); + let artist = ''; + let item = $.eval('[$trim(' + (typeof cfg !== 'undefined' ? cfg.tf.title : ppt.tfTitle) + ')]', focus, ignoreLock); + let title = ''; + + switch (path) { + + case 'http://dieneuewelle.cast.addradio.de/dieneuewelle/simulcast/high/stream.mp3': + //console.log('original item', item); + item = item.split('-'); + //console.log('split item', item); + + artist = (item[1] || '').trim(); // always return empty string if no match + //console.log('artist', artist); + + title = (item[2] || '').trim(); + //console.log('title', title); + + break; + + case 'http://energyzuerich.ice.infomaniak.ch/energyzuerich-high.mp3': // items requiring same parsing can be grouped as shown + case 'http://vintageradio.ice.infomaniak.ch/vintageradio-high.mp3': + + //console.log('original item', item); + item = item.split('˗'); // use correct hyphen(s)!; it's safest to save file as utf-8-BOM especially if there are unicode characters + //console.log('split item', item); + + artist = (item[0] || '').trim(); + //console.log('artist', artist); + + title = (item[1] || '').trim(); + //console.log('title', title); + + break; + + case 'http://kohina.duckdns.org:8000/stream.ogg': + + //console.log('original item', item); + item = item.split('-'); + //console.log('split item', item); + + artist = (item[0] || '').trim(); // always return empty string if no match + //console.log('artist', artist); + + title = (item[1] || '').trim(); + if (item[2]) title = (title + ' - ' + item[2].trim()); + //console.log('title', title); + + break; + + case 'http://www.rcgoldserver.eu:8253/': // artist name and song title in standard format except title has year + case 'http://www.rmgoldserver.eu:8199/': + + //console.log('original item', item); + title = this.removeTrailingYear(item); // item is the original parsed title; trailing year removed as it interferes with searching + //console.log('title', title); + + // artist is correct: return will be '': as its empty original parsed artist is used without modification + + break; + + case 'artist and title are switched - a path would need to be put here': + + //console.log('original item', item); + artist = item; // item is the title, which is the artist as they're swapped + //console.log('artist', artist); + + title = $.eval('[$trim(' + (typeof cfg !== 'undefined' ? cfg.tf.artist : ppt.tfArtist) + ')]', focus, ignoreLock); + //console.log('title', title); + + break; + + } + + // adjust format + artist = artist.toLowerCase(); // toLowerCase() stops all uppercase being treated as abbreviation by $.titlecase + artist = $.titlecase(artist); + //console.log('formatted artist', artist); + + title = title.toLowerCase(); + title = $.titlecase(title); + //console.log('formatted title', title); + + // return object containing artist & title: don't alter + return { + artist: artist, + title: title + } + } + + static removeTrailingYear(title) { + const kw = '(-\\s*|\\s+)\\d\\d\\d\\d'; + let ix = -1; + let yr = title.match(RegExp(kw)); + if (yr) { + yr = yr[0].toString(); + ix = title.indexOf(yr); + } + if (ix != -1) title = title.slice(0, ix).trim(); + return title; + } +}` + cfg.init(settings); settings = undefined; item_properties = undefined; -item_properties_alternative_grouping = undefined; \ No newline at end of file +item_properties_alternative_grouping = undefined; +nowplaying = undefined; +radioParser = undefined; \ No newline at end of file diff --git a/scripts/tagger.js b/scripts/tagger.js index 305dcf3..14cd781 100644 --- a/scripts/tagger.js +++ b/scripts/tagger.js @@ -4,6 +4,7 @@ class Tagger { constructor() { this.arr1 = []; this.arr2 = []; + this.force = false; this.genres = []; this.simList = []; this.setGenres(); @@ -194,7 +195,7 @@ class Tagger { return out; } - write(handles, notify) { + write(handles, notify, notifyFocus) { if (!handles) return; const kww = 'Founded In: |Born In: |Gegr\\u00fcndet: |Formado en: |Fond\\u00e9 en: |Luogo di fondazione: |\\u51fa\\u8eab\\u5730: |Za\\u0142o\\u017cono w: |Local de funda\\u00e7\\u00e3o: |\\u041c\\u0435\\u0441\\u0442\\u043e \\u043e\\u0441\\u043d\\u043e\\u0432\\u0430\\u043d\\u0438\\u044f: |Grundat \\u00e5r: |Kuruldu\\u011fu tarih: |\\u521b\\u5efa\\u4e8e: |Geboren in: |Lugar de nacimiento: |N\\u00e9\\(e\\) en: |Luogo di nascita: |\\u51fa\\u8eab\\u5730: |Urodzony w: |Local de nascimento: |\\u041c\\u0435\\u0441\\u0442\\u043e \\u0440\\u043e\\u0436\\u0434\\u0435\\u043d\\u0438\\u044f: |F\\u00f6dd: |Do\\u011fum yeri: |\\u751f\\u4e8e: '; const lkw = 'Last.fm: '; @@ -216,6 +217,7 @@ class Tagger { const artListeners = []; const artTags = []; const cue = []; + const force = this.force && notify; const locale = []; const radioStream = notify && panel.isRadio(panel.id.focus); const rem = []; @@ -241,20 +243,20 @@ class Tagger { if (artist != cur_artist) { cur_artist = artist; similarArtists[i] = ''; - if (cfg.tagEnabled7 || cfg.tagEnabled8 || cfg.tagEnabled9 || cfg.tagEnabled10 && cfg.tagEnabled13 < 7) { + if (cfg.tagEnabled7 || cfg.tagEnabled8 || cfg.tagEnabled9 || cfg.tagEnabled10 && cfg.tagEnabled13 < 7 || force) { artTags[i] = ''; artListeners[i] = ''; locale[i] = ''; const lfmBio = panel.cleanPth(cfg.pth.foLfmBio, !radioStream ? handles[i] : panel.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $.clean(artist) + cfg.suffix.foLfmBio + '.txt'; if ($.file(lfmBio)) { const lBio = $.open(lfmBio); - if (cfg.tagEnabled7) { + if (cfg.tagEnabled7 || force) { artTags[i] = this.getTag(lBio, 'Top Tags: ').tag; if (artTags[i]) artTags[i] = this.lfmTidy(artTags[i]); } if (cfg.tagEnabled8) artListeners[i] = this.getTag(lBio, lkw, false, 1, 'artist').tag; - if (cfg.tagEnabled9) locale[i] = this.getTag(lBio, kww).tag; - if (cfg.tagEnabled10 && cfg.tagEnabled13 < 7) { + if (cfg.tagEnabled9 || force) locale[i] = this.getTag(lBio, kww).tag; + if ((cfg.tagEnabled10 || force) && cfg.tagEnabled13 < 7) { similarArtists[i] = this.getTag(lBio, panel.similarArtistsKey).tag; if (similarArtists[i].length > 6) { similarArtists[i] = ''; @@ -270,20 +272,26 @@ class Tagger { } } if (!similarArtists[i].length) similarArtists[i] = ''; - if (cfg.tagEnabled4) { - artGenre_am[i] = ''; - const amBio = panel.cleanPth(cfg.pth.foAmBio, !radioStream ? handles[i] : panel.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $.clean(artist) + cfg.suffix.foAmBio + '.txt'; - if ($.file(amBio)) { - const aBio = $.open(amBio); - artGenre_am[i] = this.getTag(aBio, 'Genre: ').tag; - } - } - if (cfg.tagEnabled12) { + if (cfg.tagEnabled12 || !locale[i] && notify || force) { artGenre_w[i] = ''; const wikiBio = panel.cleanPth(cfg.pth.foWikiBio, !radioStream ? handles[i] : panel.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $.clean(artist) + cfg.suffix.foWikiBio + '.txt'; if ($.file(wikiBio)) { const wBio = $.open(wikiBio); artGenre_w[i] = this.getTag(wBio, 'Genre: ').tag; + if (!locale[i] && notify) locale[i] = this.getTag(wBio, kww).tag; + } + } + if (cfg.tagEnabled4 || !locale[i] && notify || force) { + artGenre_am[i] = ''; + const amBio = panel.cleanPth(cfg.pth.foAmBio, !radioStream ? handles[i] : panel.id.focus, !radioStream ? 'tag' : 'notifyRadioStream') + $.clean(artist) + cfg.suffix.foAmBio + '.txt'; + if ($.file(amBio)) { + const aBio = $.open(amBio); + artGenre_am[i] = this.getTag(aBio, 'Genre: ').tag; + const localeTag = this.getTag(aBio, 'Formed: |Born: ').tag; + if (localeTag.length && !/\s*in\s/.test(localeTag[0])) localeTag.shift(); + if (localeTag.length && /\s*in\s/.test(localeTag[0])) localeTag[0] = localeTag[0].split(/\s*in\s/)[1].trim(); + if (!locale[i] && notify) locale[i] = localeTag; + if (!locale[i].length) locale[i] = ''; } } } else { @@ -297,7 +305,7 @@ class Tagger { if (albumArtist + album != cur_albumArtist + cur_album) { cur_albumArtist = albumArtist; cur_album = album; - if (cfg.tagEnabled0 || cfg.tagEnabled1 || cfg.tagEnabled2 || cfg.tagEnabled3) { + if (cfg.tagEnabled0 || cfg.tagEnabled1 || cfg.tagEnabled2 || cfg.tagEnabled3 || force) { albGenre_am[i] = ''; amMoods[i] = ''; amRating[i] = ''; @@ -309,8 +317,8 @@ class Tagger { } if ($.file(amRev)) { const aRev = $.open(amRev); - if (cfg.tagEnabled0) albGenre_am[i] = this.getTag(aRev, 'Genre: ').tag; - if (cfg.tagEnabled1) amMoods[i] = this.getTag(aRev, 'Album Moods: ').tag; + if (cfg.tagEnabled0 || force) albGenre_am[i] = this.getTag(aRev, 'Genre: ').tag; + if (cfg.tagEnabled1 || force) amMoods[i] = this.getTag(aRev, 'Album Moods: ').tag; if (cfg.tagEnabled2) { const b = aRev.indexOf('>> Album rating: ') + 17; const f = aRev.indexOf(' <<'); @@ -319,10 +327,10 @@ class Tagger { if (amRating[i] == 0) amRating[i] = ''; } } - if (cfg.tagEnabled3) amThemes[i] = this.getTag(aRev, 'Album Themes: ').tag; + if (cfg.tagEnabled3 || force) amThemes[i] = this.getTag(aRev, 'Album Themes: ').tag; } } - if (cfg.tagEnabled5 || cfg.tagEnabled6) { + if (cfg.tagEnabled5 || cfg.tagEnabled6 || force) { albTags[i] = ''; albListeners[i] = ''; let lfmRev = panel.cleanPth(cfg.pth.foLfmRev, handles[i], 'tag') + $.clean(albumArtist) + ' - ' + $.clean(album) + cfg.suffix.foLfmRev + '.txt'; @@ -332,14 +340,14 @@ class Tagger { } if ($.file(lfmRev)) { const lRev = $.open(lfmRev); - if (cfg.tagEnabled5) { + if (cfg.tagEnabled5 || force) { albTags[i] = this.getTag(lRev, 'Top Tags: ').tag; if (albTags[i]) albTags[i] = this.lfmTidy(albTags[i]); } if (cfg.tagEnabled6) albListeners[i] = this.getTag(lRev, lkw, false, 1, 'album').tag; } } - if (cfg.tagEnabled11) { + if (cfg.tagEnabled11 || force) { albGenre_w[i] = ''; let wikiRev = panel.cleanPth(cfg.pth.foWikiRev, handles[i], 'tag') + $.clean(albumArtist) + ' - ' + $.clean(album) + cfg.suffix.foWikiRev + '.txt'; if (wikiRev.length > 259) { @@ -387,8 +395,10 @@ class Tagger { while (i--) handles.RemoveById(rem[i]); if (handles.Count && tags.length && notify) { window.NotifyOthers('biographyTags', { + artist: artists[0], + album: albums[0], handle: handles[0], - selectionMode: !panel.id.focus ? 'Prefer nowplaying' : 'Follow selected track (playlist)', + selectionMode: !notifyFocus ? 'Prefer nowplaying' : 'Follow selected track (playlist)', tags: tags[0] }); } diff --git a/scripts/text.js b/scripts/text.js index 43392e4..020e3ee 100644 --- a/scripts/text.js +++ b/scripts/text.js @@ -98,6 +98,7 @@ class Text { }, sp: 0, subHeading: 0, + summaryEnd: 0, text: [], txt: '', txt_w: { @@ -155,8 +156,8 @@ class Text { } this.lyrics = { - lyrics3Installed: utils.CheckComponent('foo_uie_lyrics3'), - openLyricsInstalled: utils.CheckComponent('foo_openlyrics') + ESLyricInstalled: utils.CheckComponent('foo_uie_eslyric'), + lyrics3Installed: utils.CheckComponent('foo_uie_lyrics3') } this.mod = { @@ -174,6 +175,14 @@ class Text { curWikiRev: 0 } + this.rating = { + am: -1, + amStr: '', + lfm: -1, + lfmStr: '', + y: 0 + } + this.ratingPos = { heading: false, summary: false, @@ -184,11 +193,22 @@ class Text { this.reader = { items: [], - lyrics: false, - txtLyrics: false, + bio: { + lyrics: false, + nowplaying: false, + perSec: false, + props: false, + txtLyrics: false + }, + ESLyricSaved: false, lyrics3Saved: false, - openLyricsSaved: false, - props: false, + rev: { + lyrics: false, + nowplaying: false, + perSec: false, + props: false, + txtLyrics: false + }, trackStartTime: fb.PlaybackTime, tag: false, text: false, @@ -251,6 +271,7 @@ class Text { txt: ppt.sourcerev == 3 }, subHeading: 0, + summaryEnd: 0, text: [], txt: '', txt_w: { @@ -279,17 +300,15 @@ class Text { txt: ['textreader', 'textreader'] } - this.rating = { - am: -1, - amStr: '', - lfm: -1, - lfmStr: '', - y: 0 - } - this.currentTrackTags = $.debounce(() => { - const handle = $.handle(panel.id.focus); - if (handle) tag.write(new FbMetadbHandleList([handle]), true); + if (!$.server) return; + const handle1 = $.handle(panel.id.focus); + if (handle1) tag.write(new FbMetadbHandleList([handle1]), true, panel.id.focus); + if (tag.force) { + const handle2 = $.handle(!panel.id.focus); + const handlesSame = handle1 && handle2 && handle1.Compare(handle2); + if (handle2 && !handlesSame) tag.write(new FbMetadbHandleList([handle2]), true, !panel.id.focus); + } }, 2000, { 'leading': true, 'trailing': true @@ -312,17 +331,17 @@ class Text { albCalc() { if (!this.rev.text.length || ppt.img_only || this.lyricsDisplayed()) return; - let font = !this.reader.txtLyrics || ppt.sourceAll ? ui.font.main : ui.font.lyrics; + let font = !(this.reader.rev.txtLyrics || this.rev.loaded.txt && this.reader.rev.nowplaying) || ppt.sourceAll ? ui.font.main : ui.font.lyrics; let j = 0; - this.line.h.rev = this.rev.loaded.txt && !ppt.sourceAll && this.reader.props ? ui.font.main_h + 1 * $.scale : !this.reader.txtLyrics || ppt.sourceAll ? ui.font.main_h : ui.font.lyrics_h + 4 * $.scale; + this.line.h.rev = this.rev.loaded.txt && !ppt.sourceAll && this.reader.rev.props ? ui.font.main_h + 1 * $.scale : !this.reader.rev.txtLyrics || ppt.sourceAll ? ui.font.main_h : ui.font.lyrics_h + 4 * $.scale; this.rev.arr = []; - for (let k = 0; k < this.rev.text.length; k++) { - let arr = []; - let l = []; - let summaryEnd = 0; - let v = this.rev.text[k]; - $.gr(1, 1, false, g => { + $.gr(1, 1, false, g => { + for (let k = 0; k < this.rev.text.length; k++) { + let arr = []; + let l = []; + this.rev.summaryEnd = 0; + let v = this.rev.text[k]; if (panel.style.inclTrackRev && !$.isArray(v)) { let ti = v.match(/!\u00a6.+?$/gm); if (ti) { @@ -340,7 +359,7 @@ class Text { l = g.EstimateLineWrap(v, font, panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({text: l[i].trim()}); } - } else { + } else if (!this.lyricsArr('rev')){ arr = arr.concat(JSON.parse(JSON.stringify(v))); } } else { @@ -351,7 +370,7 @@ class Text { l = g.EstimateLineWrap(revSummary, ui.font.summary, panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({text: l[i].trim()}); for (let i = 0; i < arr.length; i++) arr[i].text = arr[i].text.replace(/^\u2219\s|^\|\s+/, '').replace(/\s*\|$/, ''); - summaryEnd = arr.length; + this.rev.summaryEnd = arr.length; if (revMain) arr.push({text: ''}); } if (revMain) { @@ -359,13 +378,15 @@ class Text { for (let i = 0; i < l.length; i += 2) arr.push({text: l[i].trim()}); } } - }); - $.gr(1, 1, false, g => { + + let y = this.rev.loaded.txt && this.reader.rev.nowplaying && !ppt.sourceAll && ppt.vCenter ? Math.max(panel.text.t + (panel.text.h - arr.length * this.line.h.rev) / 2, panel.text.t) : panel.text.t; arr.forEach((v, i, ary) => { if (v.sectionHeading) j = 0; - v.align = v.property ? (ppt.rowStripes ? this.lc : this.lp) : !this.reader.txtLyrics || ppt.sourceAll ? this.l : this.cc; - v.col = v.property && v.sectionHeading ? ui.col.accent : this.rev.subHeading && !i ? ui.col.source : i < summaryEnd ? ui.col.summary : ui.col.text; - v.font = !this.rev.subHeading || i ? (i < summaryEnd ? ui.font.summary : font) : ui.font.subHeadSource; + v.align = v.property ? (ppt.rowStripes ? this.lc : this.lp) : !(this.reader.rev.txtLyrics || this.rev.loaded.txt && this.reader.rev.nowplaying && ppt.hCenter) || ppt.sourceAll ? this.l : this.cc; + v.col = v.property && v.sectionHeading ? ui.col.accent : this.rev.subHeading && !i ? ui.col.source : i < this.rev.summaryEnd ? ui.col.summary : ui.col.text; + v.dropShadowLevel = !(this.reader.rev.txtLyrics || this.rev.loaded.txt && this.reader.rev.nowplaying) || ppt.sourceAll ? false : ppt.dropShadowLevel; + v.dropNegativeShadowLevel = v.dropShadowLevel && v.dropShadowLevel > 1 ? Math.floor(v.dropShadowLevel / 2) : 0; + v.font = !this.rev.subHeading || i ? (i < this.rev.summaryEnd ? ui.font.summary : font) : ui.font.subHeadSource; v.h1 = this.line.h.rev; v.h2 = this.line.h.rev + 1; $.source.amLfmWikiTxt.forEach((w, i) => { @@ -378,7 +399,7 @@ class Text { }); v.offset = 0; v.composersTooltip = this.rev.composersTooltip; - v.composers_tt_needed = v.composersTooltip && i < summaryEnd && v.text.startsWith('Composers: '); + v.composers_tt_needed = v.composersTooltip && i < this.rev.summaryEnd && v.text.startsWith('Composers: '); this.formatItemProperties(v, j); v.subHeading = this.rev.subHeading && !i ? this.rev.subHeading : 0; ['am', 'lfm'].forEach(w => { @@ -410,19 +431,20 @@ class Text { v.sectionHeading = true; } if (this.rev.subHeading && !i && ppt.heading) v.offset = ui.font.main_h * 0.2; + v.y = y; j++; }); - }); - if (this.rev.arr.length && arr.length) this.rev.arr.push({text: ''}); - this.rev.arr = this.rev.arr.concat(arr); - this.rev.ix = -1; - } + if (this.rev.arr.length && arr.length) this.rev.arr.push({text: ''}); + this.rev.arr = this.rev.arr.concat(arr); + this.rev.ix = -1; + } + }); this.tidyWiki('rev'); but.refresh(true); alb_scrollbar.reset(); - this.line.drawn.rev = this.reader.txtLyrics && !ppt.sourceAll || this.rev.loaded.txt && !ppt.sourceAll && this.reader.props ? Math.floor(panel.lines_drawn * ui.font.main_h / this.line.h.rev) : panel.lines_drawn; + this.line.drawn.rev = this.reader.rev.txtLyrics && !ppt.sourceAll || this.rev.loaded.txt && !ppt.sourceAll && this.reader.rev.props ? Math.floor(panel.lines_drawn * ui.font.main_h / this.line.h.rev) : panel.lines_drawn; alb_scrollbar.metrics(panel.sbar.x, panel.sbar.y, ui.sbar.w, panel.sbar.h, this.line.drawn.rev, this.line.h.rev, false); alb_scrollbar.setRows(this.rev.arr.length); if (this.ratingPos.subHeading || this.ratingPos.line) this.rating.y = Math.round((ui.font.main_h - but.rating.h1 / 2) / 1.8); @@ -593,14 +615,14 @@ class Text { artCalc() { if (!this.bio.text.length || ppt.img_only || this.lyricsDisplayed()) return; - let font = !this.reader.txtLyrics || ppt.sourceAll ? ui.font.main : ui.font.lyrics; + let font = !(this.reader.bio.txtLyrics || this.bio.loaded.txt && this.reader.bio.nowplaying) || ppt.sourceAll ? ui.font.main : ui.font.lyrics; let j = 0; - this.line.h.bio = this.bio.loaded.txt && !ppt.sourceAll && this.reader.props ? ui.font.main_h + 1 * $.scale : !this.reader.txtLyrics || ppt.sourceAll ? ui.font.main_h : ui.font.lyrics_h + 4 * $.scale; + this.line.h.bio = this.bio.loaded.txt && !ppt.sourceAll && this.reader.bio.props ? ui.font.main_h + 1 * $.scale : !this.reader.bio.txtLyrics || ppt.sourceAll ? ui.font.main_h : ui.font.lyrics_h + 4 * $.scale; this.bio.arr = []; - + this.bio.text.forEach(v => { let l = []; - let summaryEnd = 0; + this.bio.summaryEnd = 0; let arr = []; $.gr(1, 1, false, g => { if (!panel.summary.show || !v.includes('\u00a6End\u00a6')) { @@ -609,7 +631,7 @@ class Text { l = g.EstimateLineWrap(v, font, panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({text: l[i].trim()}); } - } else { + } else if (!this.lyricsArr('bio')) { arr = arr.concat(JSON.parse(JSON.stringify(v))); } } else { @@ -620,7 +642,7 @@ class Text { l = g.EstimateLineWrap(bioSummary, ui.font.summary, panel.text.w); for (let i = 0; i < l.length; i += 2) arr.push({text: l[i].trim()}); for (let i = 0; i < arr.length; i++) arr[i].text = arr[i].text.replace(/^\u2219\s|^\|\s+/, '').replace(/\s*\|$/, ''); - summaryEnd = arr.length; + this.bio.summaryEnd = arr.length; if (bioMain) arr.push({text: ''}); } if (bioMain) { @@ -629,12 +651,15 @@ class Text { } } }); - + + let y = this.bio.loaded.txt && this.reader.bio.nowplaying && !ppt.sourceAll && ppt.vCenter ? Math.max(panel.text.t + (panel.text.h - arr.length * this.line.h.bio) / 2, panel.text.t) : panel.text.t; arr.forEach((v, i) => { if (v.sectionHeading) j = 0; - v.align = v.property ? (ppt.rowStripes ? this.lc : this.lp) : !this.reader.txtLyrics || ppt.sourceAll ? this.l : this.cc; - v.col = v.property && v.sectionHeading ? ui.col.accent : this.bio.subHeading && !i ? ui.col.source : i < summaryEnd ? ui.col.summary : ui.col.text; - v.font = !this.bio.subHeading || i ? (i < summaryEnd ? ui.font.summary : font) : ui.font.subHeadSource; + v.align = v.property ? (ppt.rowStripes ? this.lc : this.lp) : !(this.reader.bio.txtLyrics || this.bio.loaded.txt && this.reader.bio.nowplaying && ppt.hCenter) || ppt.sourceAll ? this.l : this.cc; + v.col = v.property && v.sectionHeading ? ui.col.accent : this.bio.subHeading && !i ? ui.col.source : i < this.bio.summaryEnd ? ui.col.summary : ui.col.text; + v.dropShadowLevel = !(this.reader.bio.txtLyrics || this.bio.loaded.txt && this.reader.bio.nowplaying) || ppt.sourceAll ? false : ppt.dropShadowLevel; + v.dropNegativeShadowLevel = v.dropShadowLevel && v.dropShadowLevel > 1 ? Math.floor(v.dropShadowLevel / 2) : 0; + v.font = !this.bio.subHeading || i ? (i < this.bio.summaryEnd ? ui.font.summary : font) : ui.font.subHeadSource; v.h1 = this.line.h.bio; v.h2 = this.line.h.bio + 1; $.source.amLfmWikiTxt.forEach(w => { @@ -646,7 +671,7 @@ class Text { }); v.offset = 0; v.membersTooltip = this.bio.membersTooltip; - v.members_tt_needed = v.membersTooltip && i < summaryEnd && v.text.startsWith('Members: '); + v.members_tt_needed = v.membersTooltip && i < this.bio.summaryEnd && v.text.startsWith('Members: '); this.formatItemProperties(v, j); if ((this.bio.loaded.wiki || ppt.sourceAll) && (v.text.startsWith('==') || v.text.endsWith('=='))) { v.font = ui.font.subHeadWiki; @@ -654,6 +679,7 @@ class Text { v.sectionHeading = true; } if (this.bio.subHeading && !i && ppt.heading) v.offset = ui.font.main_h * 0.2; + v.y = y; j++; }); if (this.bio.arr.length && arr.length) this.bio.arr.push({text: ''}); @@ -663,7 +689,7 @@ class Text { this.tidyWiki('bio'); but.refresh(true); art_scrollbar.reset(); - this.line.drawn.bio = this.reader.txtLyrics && !ppt.sourceAll || this.bio.loaded.txt && !ppt.sourceAll && this.reader.props ? Math.floor(panel.lines_drawn * ui.font.main_h / this.line.h.bio) : panel.lines_drawn; + this.line.drawn.bio = this.reader.bio.txtLyrics && !ppt.sourceAll || this.bio.loaded.txt && !ppt.sourceAll && this.reader.bio.props ? Math.floor(panel.lines_drawn * ui.font.main_h / this.line.h.bio) : panel.lines_drawn; art_scrollbar.metrics(panel.sbar.x, panel.sbar.y, ui.sbar.w, panel.sbar.h, this.line.drawn.bio, this.line.h.bio, false); art_scrollbar.setRows(this.bio.arr.length); this.getScrollPos(); @@ -709,8 +735,8 @@ class Text { return {singleGenre: false, sub: ''}; } - checkLyrics(n) { - this.reader.txtLyrics = this.reader.lyrics && (ppt.sourceAll || (this.isSynced(n) ? !ppt.scrollSynced : !ppt.scrollUnsynced)); + checkLyrics(n, lyr) { + this.reader[n].txtLyrics = this.reader[n].lyrics && (ppt.sourceAll || (this.isSynced(lyr) ? !ppt.scrollSynced : !ppt.scrollUnsynced)); } checkNewLine(sub, n) { @@ -732,22 +758,19 @@ class Text { } cur_str = cur_str.split('\r\n'); cur_str = cur_str[1] || cur_str[0]; - let w = 0; - $.gr(1, 1, false, g => w = g.CalcTextWidth(cur_str, ui.font.summary)); - if (w < panel.text.w) return sub[n]; - - cur_str = ''; - for (let i = 1; i < n; i++) { - cur_str = cur_str && sub[i] ? cur_str + ' | ' + sub[i] : cur_str || sub[i]; - } + $.gr(1, 1, false, g => { + let w = g.CalcTextWidth(cur_str, ui.font.summary); + if (w < panel.text.w) return sub[n]; - w = 0; - $.gr(1, 1, false, g => w = g.CalcTextWidth(cur_str, ui.font.summary)); - const precedingSingleLineStr = w < panel.text.w; + cur_str = ''; + for (let i = 1; i < n; i++) { + cur_str = cur_str && sub[i] ? cur_str + ' | ' + sub[i] : cur_str || sub[i]; + } - w = 0; - $.gr(1, 1, false, g => w = g.CalcTextWidth(cur_str, ui.font.summary)); - return precedingSingleLineStr ? (w > panel.text.w ? sub[n] : '\r\n' + sub[n]) : sub[n]; + w = g.CalcTextWidth(cur_str, ui.font.summary); + const precedingSingleLineStr = w < panel.text.w; + return precedingSingleLineStr ? (w > panel.text.w ? sub[n] : '\r\n' + sub[n]) : sub[n]; + }); } check_tooltip(item, x, y, artistView, line_h) { @@ -795,7 +818,7 @@ class Text { let o = 0; switch (ppt.typeOverlay) { case 1: - gr.FillSolidRect(panel.tbox.l, panel.tbox.t, panel.tbox.w, panel.tbox.h, ui.col.rectOv); + gr.FillSolidRect(panel.tbox.l - 1, panel.tbox.t - 1, panel.tbox.w + 1, panel.tbox.h + 1, ui.col.rectOv); break; case 2: o = Math.round(ui.overlay.borderWidth / 2); @@ -818,7 +841,7 @@ class Text { this.bio.drawn = 0; for (let i = b; i < f; i++) { const item = this.bio.arr[i]; - const item_y = item.h1 * i + panel.text.t - art_scrollbar.delta; + const item_y = item.h1 * i + item.y - art_scrollbar.delta; if (item_y < panel.style.max_y) { this.bio.drawn++; const iy = Math.round(item_y + ui.font.main_h / 2); @@ -829,6 +852,13 @@ class Text { } if (!item.sectionHeading || (!item.property ? i < f - 2 : i < f - 1)) { if (!item.property) { + if (item.dropNegativeShadowLevel) { // nowplaying & lyrics only + gr.GdiDrawText(item.text, item.font, ui.col.dropShadow, panel.text.l - item.dropNegativeShadowLevel, item_y + item.offset, panel.text.w, item.h2, item.align); + gr.GdiDrawText(item.text, item.font, ui.col.dropShadow, panel.text.l, item_y - item.dropNegativeShadowLevel + item.offset, panel.text.w, item.h2, item.align); + } + if (item.dropShadowLevel) { // nowplaying & lyrics only + gr.GdiDrawText(item.text, item.font, ui.col.dropShadow, panel.text.l + item.dropShadowLevel, item_y + item.dropShadowLevel + item.offset, panel.text.w, item.h2, item.align); + } gr.GdiDrawText(item.text, item.font, item.col, panel.text.l, item_y + item.offset, panel.text.w, item.h2, item.align); if (item.members_tt_needed) this.checkTooltip(item, panel.text.l, true, panel.text.w, 0, 0, 0, item_y + item.offset); } @@ -850,7 +880,7 @@ class Text { this.rev.drawn = 0; for (let i = b; i < f; i++) { const item = this.rev.arr[i]; - const item_y = item.h1 * i + panel.text.t - alb_scrollbar.delta; + const item_y = item.h1 * i + item.y - alb_scrollbar.delta; if (item_y < panel.style.max_y) { this.rev.drawn++; const iy = Math.round(item_y + ui.font.main_h / 2); @@ -874,6 +904,13 @@ class Text { if (item.songLine) gr.DrawLine(item.songX1, iy, item.songX2, iy, ui.style.l_w, ui.col.centerLine); if (!item.sectionHeading || (!item.property ? i < f - 2 : i < f - 1)) { if (!item.property) { + if (item.dropNegativeShadowLevel) { // nowplaying & lyrics only + gr.GdiDrawText(item.text, item.font, ui.col.dropShadow, panel.text.l - item.dropNegativeShadowLevel, item_y + item.offset, panel.text.w, item.h2, item.align); + gr.GdiDrawText(item.text, item.font, ui.col.dropShadow, panel.text.l, item_y - item.dropNegativeShadowLevel + item.offset, panel.text.w, item.h2, item.align); + } + if (item.dropShadowLevel) { // nowplaying & lyrics only + gr.GdiDrawText(item.text, item.font, ui.col.dropShadow, panel.text.l + item.dropShadowLevel, item_y + item.dropShadowLevel + item.offset, panel.text.w, item.h2, item.align); + } gr.GdiDrawText(item.text, item.font, item.col, panel.text.l, item_y + item.offset, panel.text.w, item.h2, item.align); if (item.composers_tt_needed) this.checkTooltip(item, panel.text.l, true, panel.text.w, 0, 0, 0, item_y + item.offset); } @@ -957,7 +994,7 @@ class Text { let pths = !v.lyrics ? [v.pth] : [v.pth, pth]; const a = n == 'bio' ? $.clean(this.artist) : $.clean(this.albumartist); const l = $.clean(this.album); - const stndItem = v.lyrics || panel.stndItem(); + const stndItem = panel.isRadio(panel.id.focus) ? false : v.lyrics || panel.stndItem(); return pths.some(w => { const wildCard = /[*?]/.test(w); if (!wildCard) { @@ -1002,48 +1039,53 @@ class Text { let composersFound = false; let membersFound = false; if (prefix) { - sub[n] = s[n].key.replace(/(Album\s|Track\s)Moods: /g, 'Moods: ').replace(/Group Members: /g, 'Members: ') + sub[n]; - if (type == 'amBio') { - let tt_needed = false; - let members = ''; - if (/^Members:\s/.test(sub[n])) { - membersFound = true; - $.gr(1, 1, false, g => { - tt_needed = g.CalcTextWidth(sub[n], ui.font.summary, true) > panel.text.w; - if (tt_needed ) { - members = g.EstimateLineWrap(sub[n], ui.font.summary, panel.text.w - g.CalcTextWidth('... ', ui.font.summary))[0]; - members = members.replace(/\u2219\s?$/, '') + '...'; + $.gr(1, 1, false, g => { + sub[n] = s[n].key.replace(/(Album\s|Track\s)Moods: /g, 'Moods: ').replace(/Group Members: /g, 'Members: ') + sub[n]; + if (ppt.summaryCompact) { + if (type == 'amBio') { + let tt_needed = false; + let members = ''; + if (/^Members:\s/.test(sub[n])) { + membersFound = true; + tt_needed = g.CalcTextWidth(sub[n], ui.font.summary, true) > panel.text.w; + if (tt_needed ) { + members = g.EstimateLineWrap(sub[n], ui.font.summary, panel.text.w - g.CalcTextWidth('... ', ui.font.summary))[0]; + members = members.replace(/\u2219\s?$/, '') + '...'; + } } - }); - } - this.bio.membersTooltip = tt_needed ? sub[n] : ''; - if (tt_needed) sub[n] = members; - } - if (type == 'wikiRev') { - let tt_needed = false; - let composers = ''; - if (panel.style.inclTrackRev && /^Composers:\s/.test(sub[n])) { - composersFound = true; - $.gr(1, 1, false, g => { - tt_needed = g.CalcTextWidth(sub[n], ui.font.summary, true) > panel.text.w; - if (tt_needed ) { - composers = g.EstimateLineWrap(sub[n], ui.font.summary, panel.text.w - g.CalcTextWidth('... ', ui.font.summary))[0]; - composers = composers.replace(/\u2219\s?$/, '') + '...'; + this.bio.membersTooltip = tt_needed ? sub[n] : ''; + if (tt_needed) sub[n] = members; + } + if (type == 'wikiRev') { + let tt_needed = false; + let composers = ''; + if (panel.style.inclTrackRev && /^Composers:\s/.test(sub[n])) { + composersFound = true; + tt_needed = g.CalcTextWidth(sub[n], ui.font.summary, true) > panel.text.w; + if (tt_needed ) { + composers = g.EstimateLineWrap(sub[n], ui.font.summary, panel.text.w - g.CalcTextWidth('... ', ui.font.summary))[0]; + composers = composers.replace(/\u2219\s?$/, '') + '...'; + } } - }); + this.rev.composersTooltip = tt_needed ? sub[n] : ''; + if (tt_needed) sub[n] = composers; + } + } else { + this.bio.membersTooltip = ''; + this.rev.composersTooltip = ''; } - this.rev.composersTooltip = tt_needed ? sub[n] : ''; - if (tt_needed) sub[n] = composers; - } + }); } - if (!composersFound && !membersFound) { - const line1 = this.getLine(sub[n], limit, suffix); - let line2 = ''; - if (lines == 2) { - line2 = sub[n].replace(line1, ''); - line2 = this.getLine(line2, limit, suffix); + if (ppt.summaryCompact) { + if (!composersFound && !membersFound) { + const line1 = this.getLine(sub[n], limit, suffix); + let line2 = ''; + if (lines == 2) { + line2 = sub[n].replace(line1, ''); + line2 = this.getLine(line2, limit, suffix); + } + sub[n] = line1 + (line2 ? '\r\n' + line2 : ''); } - sub[n] = line1 + (line2 ? '\r\n' + line2 : ''); } if (sub[n]) sub[n] += '\r\n'; } else if (type == 'amBio') { @@ -1393,6 +1435,13 @@ class Text { }); return sub + (suffix ? end : ''); } + + getNowplaying(item, n) { + item = item.replace(/\r\n|\r|\n/g, ''); + this.reader[n].perSec = /%playback_time|%bitrate%|\$progress/i.test(item); + item = fb.IsPlaying ? (!this.reader[n].perSec ? $.eval('[$trim(' + item + ')]', false) : FbTitleFormat(item).Eval()) : $.eval('[$trim(' + item + ')]', panel.id.focus); + return item; + } getPlainTxtLyrics(item) { const isSynced = this.isSynced(item, true); @@ -1481,17 +1530,19 @@ class Text { } getScrollPos() { - let v; + let scrollPos, v; switch (ppt.artistView) { case true: v = this.artist + '-' + this.bio.loaded.ix + '-' + (!this.bio.loaded.txt ? '' : this.bio.readerItem); - if (!this.bio.scrollPos[v]) return art_scrollbar.setScroll(0); - art_scrollbar.setScroll(this.bio.scrollPos[v].scroll || 0); + scrollPos = $.jsonParse(ppt.bioScrollPos, {}); + if (!scrollPos[v]) return art_scrollbar.setScroll(0); + art_scrollbar.setScroll(scrollPos[v] || 0); break; case false: { v = (panel.style.inclTrackRev != 2 ? this.albumartist + this.album + this.composition + '-' : '') + '-' + this.rev.loaded.ix + '-' + ppt.inclTrackRev + (!this.rev.loaded.txt ? '' : this.rev.readerItem); - if (!this.rev.scrollPos[v]) return alb_scrollbar.setScroll(0); - alb_scrollbar.setScroll(this.rev.scrollPos[v].scroll || 0); + scrollPos = $.jsonParse(ppt.revScrollPos, {}); + if (!scrollPos[v]) return alb_scrollbar.setScroll(0); + alb_scrollbar.setScroll(scrollPos[v] || 0); break; } } @@ -1650,15 +1701,15 @@ class Text { if (panel.id.lyricsSource) lyrics.clear(); timer.clear(timer.lyrics); if (panel.id.lyricsSource) { - if (this[n].loaded.txt && this.reader.lyrics) { - if (!this.reader.txtLyrics) lyrics.load(this[n].txt); + if (this[n].loaded.txt && this.reader[n].lyrics) { + if (!this.reader[n].txtLyrics) lyrics.load(this[n].txt); else this.paint(); - } else if (fb.IsPlaying && this.artist && (!this.reader.lyrics3Saved || !this.reader.openLyricsSaved)) { + } else if (fb.IsPlaying && this.artist && (!this.reader.ESLyricSaved || !this.reader.lyrics3Saved)) { if (ppt.syncTxtReaderLyrics) this.lyricsSave(); } } - if (!this[n].loaded.txt || !this.reader.lyrics) this.reader.txtLyrics = false; + if (!this[n].loaded.txt || !this.reader[n].lyrics) this.reader[n].txtLyrics = false; this[n].subHeading = (ppt.sourceAll && ppt.sourceHeading || ppt.sourceHeading == 2) && this[n].text.length && this[n].text[0] != fallbackText ? 1 : 0; if (!ppt.heading && this[n].subHeading && this[n].loaded.ix != -1) { @@ -1679,8 +1730,8 @@ class Text { } if (ppt.text_only && !ui.style.isBlur || panel.alb.ix && panel.style.inclTrackRev) this.paint(); } - const lyricsProps = this[n].loaded.txt && (this.reader.lyrics || this.reader.props); - const flagArtist = ppt.artistView ? (!lyricsProps ? this.artist : name.artist(panel.id.focus)) : (!lyricsProps ? this.albumartist : name.albumArtist(panel.id.focus)); + const lyrPropsNowp = this[n].loaded.txt && (this.reader[n].lyrics || this.reader[n].props || this.reader[n].nowplaying); + const flagArtist = ppt.artistView ? (!lyrPropsNowp ? this.artist : name.artist(panel.id.focus)) : (!lyrPropsNowp ? this.albumartist : name.albumArtist(panel.id.focus)); this.getFlag(flagArtist, n); if (!ppt.heading) { this.newText = false; @@ -1952,6 +2003,7 @@ class Text { view: i < 4 ? 'bio' : 'rev', lyrics: ppt[`lyricsTxtReader${i}`] && !/item_properties/i.test(utils.SplitFilePath(item)[1]), name: ppt[`nmTxtReader${i}`], + nowplaying: /nowplaying/i.test(utils.SplitFilePath(item)[1]), props: /item_properties/i.test(utils.SplitFilePath(item)[1]), pth: item, tag: this.isTag(item) @@ -1964,22 +2016,24 @@ class Text { logScrollPos(n) { let keys = []; - let v; + let scrollPos, v; n = n == 'rev' ? false : n == 'bio' ? true : ppt.artistView; switch (n) { case true: - keys = Object.keys(this.bio.scrollPos); - if (keys.length > 70) delete this.bio.scrollPos[keys[0]]; + scrollPos = $.jsonParse(ppt.bioScrollPos, {}); + keys = Object.keys(scrollPos); + if (keys.length > 70) delete scrollPos[keys[0]]; v = this.artist + '-' + this.bio.loaded.ix + '-' + (!this.bio.loaded.txt ? '' : this.bio.readerItem); - this.bio.scrollPos[v] = {}; - this.bio.scrollPos[v].scroll = art_scrollbar.scroll; + scrollPos[v] = art_scrollbar.scroll; + ppt.bioScrollPos = JSON.stringify(scrollPos); break; case false: - keys = Object.keys(this.rev.scrollPos); - if (keys.length > 70) delete this.rev.scrollPos[keys[0]]; + scrollPos = $.jsonParse(ppt.revScrollPos, {}); + keys = Object.keys(scrollPos); + if (keys.length > 70) delete scrollPos[keys[0]]; v = (panel.style.inclTrackRev != 2 ? this.albumartist + this.album + this.composition + '-' : '') + '-' + this.rev.loaded.ix + '-' + ppt.inclTrackRev + (!this.rev.loaded.txt ? '' : this.rev.readerItem); - this.rev.scrollPos[v] = {}; - this.rev.scrollPos[v].scroll = alb_scrollbar.scroll; + scrollPos[v] = alb_scrollbar.scroll; + ppt.revScrollPos = JSON.stringify(scrollPos); break; } } @@ -1992,14 +2046,18 @@ class Text { } }); } - + + lyricsArr(n) { + return this.reader[n].lyrics && !this.reader[n].txtLyrics; + } + lyricsDisplayed() { const n = ppt.artistView ? 'bio' : 'rev'; - return this[n].loaded.txt && this.reader.lyrics && !this.reader.txtLyrics && !ppt.img_only; + return this[n].loaded.txt && this.lyricsArr(n) && !ppt.img_only; } lyricsSave() { - if (!this.lyrics.lyrics3Installed && !this.lyrics.openLyricsInstalled) return; + if (!this.lyrics.ESLyricInstalled && !this.lyrics.lyrics3Installed) return; let counter = 0; timer.clear(timer.lyrics); timer.lyrics.id = setInterval(() => { @@ -2010,9 +2068,11 @@ class Text { this.loadLyric(); } } - if (this.lyrics.openLyricsInstalled && !this.reader.openLyricsSaved) { - if (this.lyricExists()) { - this.reader.openLyricsSaved = true; + if (this.lyrics.ESLyricInstalled && !this.reader.ESLyricSaved) { + if (!this.lyricExists()) { + fb.RunMainMenuCommand('View/ESLyric/Panels/Save lyric'); + } else { + this.reader.ESLyricSaved = true; this.loadLyric(); } } @@ -2040,7 +2100,7 @@ class Text { } notifyTags() { - if (!cfg.notifyTags) return; + if (!cfg.notifyTags && !tag.force) return; this.currentTrackTags(); } @@ -2185,8 +2245,8 @@ class Text { if (!n) return ''; if (panel.lock) n = n.replace(/%artist%|\$meta\(artist,0\)/g, '#\u00a6#\u00a6#%artist%#\u00a6#\u00a6#').replace(/%title%|\$meta\(title,0\)/g, '#!#!#%title%#!#!#'); const b = ppt.artistView ? 'bio' : 'rev'; - let a = this[b].loaded.txt && (this.reader.lyrics || this.reader.props) ? $.tfEscape(name.artist(panel.id.focus)) : $.tfEscape(artistView ? this.artist : (!trackreview ? (panel.alb.ix ? this.albumartist : this.artist) : this.trackartist)); - let aa = this[b].loaded.txt && (this.reader.lyrics || this.reader.props) ? $.tfEscape(name.albumArtist(panel.id.focus)) : $.tfEscape(artistView ? (panel.art.ix ? this.artist : this.albumartist) : (!trackreview ? this.albumartist : this.trackartist)); + let a = this[b].loaded.txt && (this.reader[b].lyrics || this.reader[b].props || this.reader[b].nowplaying) ? $.tfEscape(name.artist(panel.id.focus)) : $.tfEscape(artistView ? this.artist : (!trackreview ? (panel.alb.ix ? this.albumartist : this.artist) : this.trackartist)); + let aa = this[b].loaded.txt && (this.reader[b].lyrics || this.reader[b].props || this.reader[b].nowplaying) ? $.tfEscape(name.albumArtist(panel.id.focus)) : $.tfEscape(artistView ? (panel.art.ix ? this.artist : this.albumartist) : (!trackreview ? this.albumartist : this.trackartist)); let composition = this.isCompositionLoaded(); let l = composition ? $.tfEscape(this.composition.replace('Album Unknown', '')) : $.tfEscape(this.album.replace('Album Unknown', '')); let tr = $.tfEscape(this.track); @@ -2200,7 +2260,17 @@ class Text { } tidyLyrics(n) { - return n.replace(/&(;|)/g, '&').replace(/"(;|)/g, '"').replace(/'(;|)/g, "'").replace(/’(;|)/g, "'").replace(/‘(;|)/g, "'").replace(/[\u0060\u00B4]/g, "'").replace(/
/gi, '').replace(/>(;|)/g, '>').replace(/<(;|)/g, '<').replace(/ (;|)/g, '').trim(); + return n.replace(/&(;|)/g, '&') + .replace(/>(;|)/g, '>') + .replace(/<(;|)/g, '<') + .replace(/ (;|)/g, '') + .replace(/"(;|)/g, '"') + .replace(/
/gi, '') + .replace(/\uFF1A/g, ':') + .replace(/\uFF08/g, '(') + .replace(/\uFF09/g, ')') + .replace(/\u00E2\u20AC\u2122|\u2019|\uFF07|[\u0060\u00B4]|’(;|)|‘(;|)|'(;|)|&apos(;|)/g, "'") // Apostrophe variants + .trim(); } tidyWiki(n) { @@ -2214,22 +2284,6 @@ class Text { } } - toString(arr) { - if (this.reader.props) { - arr = JSON.parse(JSON.stringify(arr)); - arr.forEach(v => { - if ($.isArray(v)) { - v.forEach(w => { - for (const [key, value] of Object.entries(w)) { - if (/\d/.test(value)) w[key] = ''; // stop changes to pb statistics or modified dates etc effecting scroll memory - } - }); - } - }); - } - return JSON.stringify(arr); - } - trackPth(n) { if (ppt.img_only || ppt.artistView) return ['', '', false, false]; return panel.getPth('track', panel.id.focus, this.artist, 'Track Reviews', '', '', $.clean(this.artist), '', 'Track Reviews', `fo${n}Rev`, false); @@ -2247,6 +2301,7 @@ class Text { } txtReader() { + let readerStart = Date.now(); const n = ppt.artistView ? 'bio' : 'rev'; this[n].readerTag = false; let found = -1; @@ -2270,15 +2325,18 @@ class Text { } }); if (found == -1) { - this.reader.lyrics = false; - this.reader.props = false; + this.reader[n].lyrics = false; + this.reader[n].nowplaying = false; + this.reader[n].perSec = false; + this.reader[n].props = false; this[n].readerItem = ''; this[n].txt = ''; but.createStars(); return; } - this.reader.lyrics = this.reader.items[found].lyrics; - this.reader.props = this.reader.items[found].props; + this.reader[n].lyrics = this.reader.items[found].lyrics; + this.reader[n].nowplaying = this.reader.items[found].nowplaying; + this.reader[n].props = this.reader.items[found].props; nm = this.upperCaseFirst(this.reader.items[found].name); this[n].subhead.txt = [nm, nm]; this[n].readerHeading = this.reader.items[found].heading; @@ -2286,8 +2344,8 @@ class Text { this.getSubHeadWidths(true); if (this.reader.items[found].tag) { this[n].readerTag = true; - this.checkLyrics(this[n].readerItem); - if (!this.reader.lyrics) { + this.checkLyrics(n, this[n].readerItem); + if (!this.reader[n].lyrics) { if (this[n].txt != this[n].readerItem) { this[n].txt = this[n].readerItem; this.newText = true; @@ -2297,7 +2355,7 @@ class Text { if (tFSplit.length === 1) { tFSplit = this[n].readerItem.split('\r'); } - if (this.reader.txtLyrics) tFSplit = this.getPlainTxtLyrics(tFSplit, !tFSplit.length); + if (this.reader[n].txtLyrics) tFSplit = this.getPlainTxtLyrics(tFSplit, !tFSplit.length); if (!$.equal(this[n].txt, tFSplit)) { this[n].txt = tFSplit; this.newText = true; @@ -2305,17 +2363,18 @@ class Text { } return; } else { - let item = !this.reader.lyrics ? $.open(this[n].readerItem).trim() : utils.ReadTextFile(this[n].readerItem, 65001); - if (this.reader.lyrics && item.includes('\ufffd')) item = $.open(this[n].readerItem); - if (this.reader.lyrics) item = this.tidyLyrics(item); - this.checkLyrics(item); - if (this.reader.lyrics) { + let item = !this.reader[n].lyrics ? $.open(this[n].readerItem).trim() : utils.ReadTextFile(this[n].readerItem, 65001); + if (this.reader[n].lyrics && item.includes('\ufffd')) item = $.open(this[n].readerItem); + if (this.reader[n].lyrics) item = this.tidyLyrics(item); + this.checkLyrics(n, item); + if (this.reader[n].lyrics) { item = item.split('\n'); - if (this.reader.txtLyrics) item = this.getPlainTxtLyrics(item); + if (this.reader[n].txtLyrics) item = this.getPlainTxtLyrics(item); } - if (this.reader.props) item = this.getItemProperties(item, n); + if (this.reader[n].props) item = this.getItemProperties(item, n); + if (this.reader[n].nowplaying) item = this.getNowplaying(item, n); - if (!this.reader.lyrics) { + if (!this.reader[n].lyrics) { if ($.isArray(this[n].txt)) { if (this.different(this[n].txt, item)) { this[n].txt = item; diff --git a/scripts/wikipedia.js b/scripts/wikipedia.js index cc83c50..f13f041 100644 --- a/scripts/wikipedia.js +++ b/scripts/wikipedia.js @@ -684,33 +684,37 @@ class DldWikipedia { if (this.type || !$.file(this.pth)) $.trace('wikipedia: ' + (this.title || this.artist) + ': not found', true); return; } - $.buildPth(this.fo); - - this.wiki = txt.add([`Wikipedia language: ${this.site.toUpperCase()}`], this.wiki); - $.save(this.pth, this.wiki, true); - server.res(); + if (this.fo) { + $.buildPth(this.fo); + + this.wiki = txt.add([`Wikipedia language: ${this.site.toUpperCase()}`], this.wiki); + $.save(this.pth, this.wiki, true); + server.res(); + } } else { const text = $.jsonParse(this.pth, {}, 'file'); - $.buildPth(this.fo); - if (this.site == 'en') { - text[this.title] = { - composer: this.info.composer, - date: this.info.released, - genre: genres, - length: this.info.length, - wiki: this.wiki, - lang: this.site.toUpperCase(), - update: Date.now() - }; - } else { - text[this.title] = { - genre: genres, - wiki: this.wiki, - lang: this.site.toUpperCase(), - update: Date.now() - }; + if (this.fo) { + $.buildPth(this.fo); + if (this.site == 'en') { + text[this.title] = { + composer: this.info.composer, + date: this.info.released, + genre: genres, + length: this.info.length, + wiki: this.wiki, + lang: this.site.toUpperCase(), + update: Date.now() + }; + } else { + text[this.title] = { + genre: genres, + wiki: this.wiki, + lang: this.site.toUpperCase(), + update: Date.now() + }; + } + $.save(this.pth, JSON.stringify($.sortKeys(text), null, 3), true); } - $.save(this.pth, JSON.stringify($.sortKeys(text), null, 3), true); if (genres.length || this.info.composer.length || this.info.released || this.info.length || this.wiki) server.res(); else $.trace('wikipedia: ' + this.name + ': not found', true); }