mirror of
https://github.com/9001/copyparty.git
synced 2025-08-18 09:22:31 -06:00
Merge remote-tracking branch 'upstream/hovudstraum' into nixos-module
This commit is contained in:
commit
6dd8ad56a2
39
README.md
39
README.md
|
@ -50,6 +50,8 @@ turn almost any device into a file server with resumable uploads/downloads using
|
||||||
* [rss feeds](#rss-feeds) - monitor a folder with your RSS reader
|
* [rss feeds](#rss-feeds) - monitor a folder with your RSS reader
|
||||||
* [recent uploads](#recent-uploads) - list all recent uploads
|
* [recent uploads](#recent-uploads) - list all recent uploads
|
||||||
* [media player](#media-player) - plays almost every audio format there is
|
* [media player](#media-player) - plays almost every audio format there is
|
||||||
|
* [playlists](#playlists) - create and play [m3u8](https://en.wikipedia.org/wiki/M3U) playlists
|
||||||
|
* [creating a playlist](#creating-a-playlist) - with a standalone mediaplayer or copyparty
|
||||||
* [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
|
* [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
|
||||||
* [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
|
* [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
|
||||||
* [markdown viewer](#markdown-viewer) - and there are *two* editors
|
* [markdown viewer](#markdown-viewer) - and there are *two* editors
|
||||||
|
@ -251,6 +253,7 @@ also see [comparison to similar software](./docs/versus.md)
|
||||||
* ☑ file manager (cut/paste, delete, [batch-rename](#batch-rename))
|
* ☑ file manager (cut/paste, delete, [batch-rename](#batch-rename))
|
||||||
* ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus/mp3 transcoding)
|
* ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus/mp3 transcoding)
|
||||||
* ☑ play video files as audio (converted on server)
|
* ☑ play video files as audio (converted on server)
|
||||||
|
* ☑ create and play [m3u8 playlists](#playlists)
|
||||||
* ☑ image gallery with webm player
|
* ☑ image gallery with webm player
|
||||||
* ☑ textfile browser with syntax hilighting
|
* ☑ textfile browser with syntax hilighting
|
||||||
* ☑ [thumbnails](#thumbnails)
|
* ☑ [thumbnails](#thumbnails)
|
||||||
|
@ -414,6 +417,9 @@ upgrade notes
|
||||||
|
|
||||||
"frequently" asked questions
|
"frequently" asked questions
|
||||||
|
|
||||||
|
* CopyParty?
|
||||||
|
* nope! the name is either copyparty (all-lowercase) or Copyparty -- it's [one word](https://en.wiktionary.org/wiki/copyparty) after all :>
|
||||||
|
|
||||||
* can I change the 🌲 spinning pine-tree loading animation?
|
* can I change the 🌲 spinning pine-tree loading animation?
|
||||||
* [yeah...](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#boring-loader-spinner) :-(
|
* [yeah...](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#boring-loader-spinner) :-(
|
||||||
|
|
||||||
|
@ -1044,6 +1050,7 @@ open the `[🎺]` media-player-settings tab to configure it,
|
||||||
* `[full]` does a full preload by downloading the entire next file; good for unreliable connections, bad for slow connections
|
* `[full]` does a full preload by downloading the entire next file; good for unreliable connections, bad for slow connections
|
||||||
* `[~s]` toggles the seekbar waveform display
|
* `[~s]` toggles the seekbar waveform display
|
||||||
* `[/np]` enables buttons to copy the now-playing info as an irc message
|
* `[/np]` enables buttons to copy the now-playing info as an irc message
|
||||||
|
* `[📻]` enables buttons to create an [m3u playlist](#playlists) with the selected songs
|
||||||
* `[os-ctl]` makes it possible to control audio playback from the lockscreen of your device (enables [mediasession](https://developer.mozilla.org/en-US/docs/Web/API/MediaSession))
|
* `[os-ctl]` makes it possible to control audio playback from the lockscreen of your device (enables [mediasession](https://developer.mozilla.org/en-US/docs/Web/API/MediaSession))
|
||||||
* `[seek]` allows seeking with lockscreen controls (buggy on some devices)
|
* `[seek]` allows seeking with lockscreen controls (buggy on some devices)
|
||||||
* `[art]` shows album art on the lockscreen
|
* `[art]` shows album art on the lockscreen
|
||||||
|
@ -1062,11 +1069,39 @@ open the `[🎺]` media-player-settings tab to configure it,
|
||||||
* "transcode to":
|
* "transcode to":
|
||||||
* `[opus]` produces an `opus` whenever transcoding is necessary (the best choice on Android and PCs)
|
* `[opus]` produces an `opus` whenever transcoding is necessary (the best choice on Android and PCs)
|
||||||
* `[awo]` is `opus` in a `weba` file, good for iPhones (iOS 17.5 and newer) but Apple is still fixing some state-confusion bugs as of iOS 18.2.1
|
* `[awo]` is `opus` in a `weba` file, good for iPhones (iOS 17.5 and newer) but Apple is still fixing some state-confusion bugs as of iOS 18.2.1
|
||||||
* `[caf]` is `opus` in a `caf` file, good for iPhones (iOS 11 through 17), technically unsupported by Apple but works for the mos tpart
|
* `[caf]` is `opus` in a `caf` file, good for iPhones (iOS 11 through 17), technically unsupported by Apple but works for the most part
|
||||||
* `[mp3]` -- the myth, the legend, the undying master of mediocre sound quality that definitely works everywhere
|
* `[mp3]` -- the myth, the legend, the undying master of mediocre sound quality that definitely works everywhere
|
||||||
* "tint" reduces the contrast of the playback bar
|
* "tint" reduces the contrast of the playback bar
|
||||||
|
|
||||||
|
|
||||||
|
### playlists
|
||||||
|
|
||||||
|
create and play [m3u8](https://en.wikipedia.org/wiki/M3U) playlists -- see example [text](https://a.ocv.me/pub/demo/music/?doc=example-playlist.m3u) and [player](https://a.ocv.me/pub/demo/music/#m3u=example-playlist.m3u)
|
||||||
|
|
||||||
|
click a file with the extension `m3u` or `m3u8` (for example `mixtape.m3u` or `touhou.m3u8` ) and you get two choices: Play / Edit
|
||||||
|
|
||||||
|
playlists can include songs across folders anywhere on the server, but filekeys/dirkeys are NOT supported, so the listener must have read-access or get-access to the files
|
||||||
|
|
||||||
|
|
||||||
|
### creating a playlist
|
||||||
|
|
||||||
|
with a standalone mediaplayer or copyparty
|
||||||
|
|
||||||
|
you can use foobar2000, deadbeef, just about any standalone player should work -- but you might need to edit the filepaths in the playlist so they fit with the server-URLs
|
||||||
|
|
||||||
|
alternatively, you can create the playlist using copyparty itself:
|
||||||
|
|
||||||
|
* open the `[🎺]` media-player-settings tab and enable the `[📻]` create-playlist feature -- this adds two new buttons in the bottom-right tray, `[📻add]` and `[📻copy]` which appear when you listen to music, or when you select a few audiofiles
|
||||||
|
|
||||||
|
* click the `📻add` button while a song is playing (or when you've selected some songs) and they'll be added to "the list" (you can't see it yet)
|
||||||
|
|
||||||
|
* at any time, click `📻copy` to send the playlist to your clipboard
|
||||||
|
* you can then continue adding more songs if you'd like
|
||||||
|
* if you want to wipe the playlist and start from scratch, just refresh the page
|
||||||
|
|
||||||
|
* create a new textfile, name it `something.m3u` and paste the playlist there
|
||||||
|
|
||||||
|
|
||||||
### audio equalizer
|
### audio equalizer
|
||||||
|
|
||||||
and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
|
and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
|
||||||
|
@ -2388,6 +2423,8 @@ copyparty returns a truncated sha512sum of your PUT/POST as base64; you can gene
|
||||||
|
|
||||||
you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, url-param `?pw=hunter2`, or with basic-authentication (either as the username or password)
|
you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, url-param `?pw=hunter2`, or with basic-authentication (either as the username or password)
|
||||||
|
|
||||||
|
> for basic-authentication, all of the following are accepted: `password` / `whatever:password` / `password:whatever` (the username is ignored)
|
||||||
|
|
||||||
NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
|
NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Maintainer: icxes <dev.null@need.moe>
|
# Maintainer: icxes <dev.null@need.moe>
|
||||||
pkgname=copyparty
|
pkgname=copyparty
|
||||||
pkgver="1.16.21"
|
pkgver="1.17.0"
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||||
arch=("any")
|
arch=("any")
|
||||||
|
@ -22,7 +22,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
||||||
)
|
)
|
||||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||||
backup=("etc/${pkgname}.d/init" )
|
backup=("etc/${pkgname}.d/init" )
|
||||||
sha256sums=("2e416e18dc854c65643b8aaedca56e0a5c5a03b0c3d45b7ff3f68daa38d8e9c6")
|
sha256sums=("d8a49b3398f4cdb0754dd8b9e3ab32544e44e2fee94c88903f65ffc003b6eeec")
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"url": "https://github.com/9001/copyparty/releases/download/v1.16.21/copyparty-sfx.py",
|
"url": "https://github.com/9001/copyparty/releases/download/v1.17.0/copyparty-sfx.py",
|
||||||
"version": "1.16.21",
|
"version": "1.17.0",
|
||||||
"hash": "sha256-+/f4g8J2Mv0l6ChXzbNJ84G8LeB+mP1UfkWzQxizd/g="
|
"hash": "sha256-iRqaXQvwX4DceOhZmI6g9KXeR+rFAWnNHK/GTHkoQ7Q="
|
||||||
}
|
}
|
|
@ -1025,7 +1025,7 @@ def add_upload(ap):
|
||||||
ap2.add_argument("--df", metavar="GiB", type=u, default="0", help="ensure \033[33mGiB\033[0m free disk space by rejecting upload requests; assumes gigabytes unless a unit suffix is given: [\033[32m256m\033[0m], [\033[32m4\033[0m], [\033[32m2T\033[0m] (volflag=df)")
|
ap2.add_argument("--df", metavar="GiB", type=u, default="0", help="ensure \033[33mGiB\033[0m free disk space by rejecting upload requests; assumes gigabytes unless a unit suffix is given: [\033[32m256m\033[0m], [\033[32m4\033[0m], [\033[32m2T\033[0m] (volflag=df)")
|
||||||
ap2.add_argument("--sparse", metavar="MiB", type=int, default=4, help="windows-only: minimum size of incoming uploads through up2k before they are made into sparse files")
|
ap2.add_argument("--sparse", metavar="MiB", type=int, default=4, help="windows-only: minimum size of incoming uploads through up2k before they are made into sparse files")
|
||||||
ap2.add_argument("--turbo", metavar="LVL", type=int, default=0, help="configure turbo-mode in up2k client; [\033[32m-1\033[0m] = forbidden/always-off, [\033[32m0\033[0m] = default-off and warn if enabled, [\033[32m1\033[0m] = default-off, [\033[32m2\033[0m] = on, [\033[32m3\033[0m] = on and disable datecheck")
|
ap2.add_argument("--turbo", metavar="LVL", type=int, default=0, help="configure turbo-mode in up2k client; [\033[32m-1\033[0m] = forbidden/always-off, [\033[32m0\033[0m] = default-off and warn if enabled, [\033[32m1\033[0m] = default-off, [\033[32m2\033[0m] = on, [\033[32m3\033[0m] = on and disable datecheck")
|
||||||
ap2.add_argument("--u2j", metavar="JOBS", type=int, default=2, help="web-client: number of file chunks to upload in parallel; 1 or 2 is good for low-latency (same-country) connections, 4-8 for android clients, 16 for cross-atlantic (max=64)")
|
ap2.add_argument("--u2j", metavar="JOBS", type=int, default=2, help="web-client: number of file chunks to upload in parallel; 1 or 2 is good when latency is low (same-country), 2~4 for android-clients, 2~6 for cross-atlantic. Max is 6 in most browsers. Big values increase network-speed but may reduce HDD-speed")
|
||||||
ap2.add_argument("--u2sz", metavar="N,N,N", type=u, default="1,64,96", help="web-client: default upload chunksize (MiB); sets \033[33mmin,default,max\033[0m in the settings gui. Each HTTP POST will aim for \033[33mdefault\033[0m, and never exceed \033[33mmax\033[0m. Cloudflare max is 96. Big values are good for cross-atlantic but may increase HDD fragmentation on some FS. Disable this optimization with [\033[32m1,1,1\033[0m]")
|
ap2.add_argument("--u2sz", metavar="N,N,N", type=u, default="1,64,96", help="web-client: default upload chunksize (MiB); sets \033[33mmin,default,max\033[0m in the settings gui. Each HTTP POST will aim for \033[33mdefault\033[0m, and never exceed \033[33mmax\033[0m. Cloudflare max is 96. Big values are good for cross-atlantic but may increase HDD fragmentation on some FS. Disable this optimization with [\033[32m1,1,1\033[0m]")
|
||||||
ap2.add_argument("--u2ow", metavar="NUM", type=int, default=0, help="web-client: default setting for when to replace/overwrite existing files; [\033[32m0\033[0m]=never, [\033[32m1\033[0m]=if-client-newer, [\033[32m2\033[0m]=always (volflag=u2ow)")
|
ap2.add_argument("--u2ow", metavar="NUM", type=int, default=0, help="web-client: default setting for when to replace/overwrite existing files; [\033[32m0\033[0m]=never, [\033[32m1\033[0m]=if-client-newer, [\033[32m2\033[0m]=always (volflag=u2ow)")
|
||||||
ap2.add_argument("--u2sort", metavar="TXT", type=u, default="s", help="upload order; [\033[32ms\033[0m]=smallest-first, [\033[32mn\033[0m]=alphabetical, [\033[32mfs\033[0m]=force-s, [\033[32mfn\033[0m]=force-n -- alphabetical is a bit slower on fiber/LAN but makes it easier to eyeball if everything went fine")
|
ap2.add_argument("--u2sort", metavar="TXT", type=u, default="s", help="upload order; [\033[32ms\033[0m]=smallest-first, [\033[32mn\033[0m]=alphabetical, [\033[32mfs\033[0m]=force-s, [\033[32mfn\033[0m]=force-n -- alphabetical is a bit slower on fiber/LAN but makes it easier to eyeball if everything went fine")
|
||||||
|
@ -1308,6 +1308,9 @@ def add_salt(ap, fk_salt, dk_salt, ah_salt):
|
||||||
ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt; used to generate unpredictable URLs for hidden files")
|
ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt; used to generate unpredictable URLs for hidden files")
|
||||||
ap2.add_argument("--dk-salt", metavar="SALT", type=u, default=dk_salt, help="per-directory accesskey salt; used to generate unpredictable URLs to share folders with users who only have the 'get' permission")
|
ap2.add_argument("--dk-salt", metavar="SALT", type=u, default=dk_salt, help="per-directory accesskey salt; used to generate unpredictable URLs to share folders with users who only have the 'get' permission")
|
||||||
ap2.add_argument("--warksalt", metavar="SALT", type=u, default="hunter2", help="up2k file-hash salt; serves no purpose, no reason to change this (but delete all databases if you do)")
|
ap2.add_argument("--warksalt", metavar="SALT", type=u, default="hunter2", help="up2k file-hash salt; serves no purpose, no reason to change this (but delete all databases if you do)")
|
||||||
|
ap2.add_argument("--show-ah-salt", action="store_true", help="on startup, print the effective value of \033[33m--ah-salt\033[0m (the autogenerated value in $XDG_CONFIG_HOME unless otherwise specified)")
|
||||||
|
ap2.add_argument("--show-fk-salt", action="store_true", help="on startup, print the effective value of \033[33m--fk-salt\033[0m (the autogenerated value in $XDG_CONFIG_HOME unless otherwise specified)")
|
||||||
|
ap2.add_argument("--show-dk-salt", action="store_true", help="on startup, print the effective value of \033[33m--dk-salt\033[0m (the autogenerated value in $XDG_CONFIG_HOME unless otherwise specified)")
|
||||||
|
|
||||||
|
|
||||||
def add_shutdown(ap):
|
def add_shutdown(ap):
|
||||||
|
@ -1349,7 +1352,7 @@ def add_admin(ap):
|
||||||
|
|
||||||
def add_thumbnail(ap):
|
def add_thumbnail(ap):
|
||||||
th_ram = (RAM_AVAIL or RAM_TOTAL or 9) * 0.6
|
th_ram = (RAM_AVAIL or RAM_TOTAL or 9) * 0.6
|
||||||
th_ram = int(max(min(th_ram, 6), 1) * 10) / 10
|
th_ram = int(max(min(th_ram, 6), 0.3) * 10) / 10
|
||||||
ap2 = ap.add_argument_group('thumbnail options')
|
ap2 = ap.add_argument_group('thumbnail options')
|
||||||
ap2.add_argument("--no-thumb", action="store_true", help="disable all thumbnails (volflag=dthumb)")
|
ap2.add_argument("--no-thumb", action="store_true", help="disable all thumbnails (volflag=dthumb)")
|
||||||
ap2.add_argument("--no-vthumb", action="store_true", help="disable video thumbnails (volflag=dvthumb)")
|
ap2.add_argument("--no-vthumb", action="store_true", help="disable video thumbnails (volflag=dvthumb)")
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (1, 16, 21)
|
VERSION = (1, 17, 0)
|
||||||
CODENAME = "COPYparty"
|
CODENAME = "mixtape.m3u"
|
||||||
BUILD_DT = (2025, 4, 20)
|
BUILD_DT = (2025, 4, 26)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|
|
@ -253,6 +253,14 @@ class SvcHub(object):
|
||||||
setattr(args, "ipu_iu", iu)
|
setattr(args, "ipu_iu", iu)
|
||||||
setattr(args, "ipu_nm", nm)
|
setattr(args, "ipu_nm", nm)
|
||||||
|
|
||||||
|
for zs in "ah_salt fk_salt dk_salt".split():
|
||||||
|
if getattr(args, "show_%s" % (zs,)):
|
||||||
|
self.log("root", "effective %s is %s" % (zs, getattr(args, zs)))
|
||||||
|
|
||||||
|
if args.ah_cli or args.ah_gen:
|
||||||
|
args.no_ses = True
|
||||||
|
args.shr = ""
|
||||||
|
|
||||||
if not self.args.no_ses:
|
if not self.args.no_ses:
|
||||||
self.setup_session_db()
|
self.setup_session_db()
|
||||||
|
|
||||||
|
|
|
@ -472,6 +472,8 @@ FN_EMB = set([".prologue.html", ".epilogue.html", "readme.md", "preadme.md"])
|
||||||
|
|
||||||
|
|
||||||
def read_ram() -> tuple[float, float]:
|
def read_ram() -> tuple[float, float]:
|
||||||
|
# NOTE: apparently no need to consider /sys/fs/cgroup/memory.max
|
||||||
|
# (cgroups2) since the limit is synced to /proc/meminfo
|
||||||
a = b = 0
|
a = b = 0
|
||||||
try:
|
try:
|
||||||
with open("/proc/meminfo", "rb", 0x10000) as f:
|
with open("/proc/meminfo", "rb", 0x10000) as f:
|
||||||
|
|
|
@ -1151,10 +1151,10 @@ html.y #widget.open {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
background: var(--bg-u3);
|
background: var(--bg-u3);
|
||||||
}
|
}
|
||||||
#wfs, #wfm, #wzip, #wnp {
|
#wfs, #wfm, #wzip, #wnp, #wm3u {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#wfs, #wzip, #wnp {
|
#wfs, #wzip, #wnp, #wm3u {
|
||||||
margin-right: .2em;
|
margin-right: .2em;
|
||||||
padding-right: .2em;
|
padding-right: .2em;
|
||||||
border: 1px solid var(--bg-u5);
|
border: 1px solid var(--bg-u5);
|
||||||
|
@ -1175,6 +1175,7 @@ html.y #widget.open {
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
}
|
}
|
||||||
#wtoggle.sel #wzip,
|
#wtoggle.sel #wzip,
|
||||||
|
#wtoggle.m3u #wm3u,
|
||||||
#wtoggle.np #wnp {
|
#wtoggle.np #wnp {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
@ -1183,6 +1184,7 @@ html.y #widget.open {
|
||||||
}
|
}
|
||||||
#wfm a,
|
#wfm a,
|
||||||
#wnp a,
|
#wnp a,
|
||||||
|
#wm3u a,
|
||||||
#wzip a {
|
#wzip a {
|
||||||
font-size: .5em;
|
font-size: .5em;
|
||||||
padding: 0 .3em;
|
padding: 0 .3em;
|
||||||
|
@ -1190,6 +1192,10 @@ html.y #widget.open {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
#wm3u a {
|
||||||
|
margin: -.2em .1em;
|
||||||
|
font-size: .45em;
|
||||||
|
}
|
||||||
#wfs {
|
#wfs {
|
||||||
font-size: .36em;
|
font-size: .36em;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
@ -1198,6 +1204,7 @@ html.y #widget.open {
|
||||||
border-width: 0 .25em 0 0;
|
border-width: 0 .25em 0 0;
|
||||||
}
|
}
|
||||||
#wfm span,
|
#wfm span,
|
||||||
|
#wm3u span,
|
||||||
#wnp span {
|
#wnp span {
|
||||||
font-size: .6em;
|
font-size: .6em;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -1205,6 +1212,10 @@ html.y #widget.open {
|
||||||
#wnp span {
|
#wnp span {
|
||||||
font-size: .7em;
|
font-size: .7em;
|
||||||
}
|
}
|
||||||
|
#wm3u span {
|
||||||
|
font-size: .77em;
|
||||||
|
padding-top: .2em;
|
||||||
|
}
|
||||||
#wfm a:not(.en) {
|
#wfm a:not(.en) {
|
||||||
opacity: .3;
|
opacity: .3;
|
||||||
color: var(--fm-off);
|
color: var(--fm-off);
|
||||||
|
|
|
@ -144,6 +144,8 @@ var Ls = {
|
||||||
"wt_seldl": "download selection as separate files$NHotkey: Y",
|
"wt_seldl": "download selection as separate files$NHotkey: Y",
|
||||||
"wt_npirc": "copy irc-formatted track info",
|
"wt_npirc": "copy irc-formatted track info",
|
||||||
"wt_nptxt": "copy plaintext track info",
|
"wt_nptxt": "copy plaintext track info",
|
||||||
|
"wt_m3ua": "add to m3u playlist (click <code>📻copy</code> later)",
|
||||||
|
"wt_m3uc": "copy m3u playlist to clipboard",
|
||||||
"wt_grid": "toggle grid / list view$NHotkey: G",
|
"wt_grid": "toggle grid / list view$NHotkey: G",
|
||||||
"wt_prev": "previous track$NHotkey: J",
|
"wt_prev": "previous track$NHotkey: J",
|
||||||
"wt_play": "play / pause$NHotkey: P",
|
"wt_play": "play / pause$NHotkey: P",
|
||||||
|
@ -280,6 +282,7 @@ var Ls = {
|
||||||
"mt_fau": "on phones, prevent music from stopping if the next song doesn't preload fast enough (can make tags display glitchy)\">☕️",
|
"mt_fau": "on phones, prevent music from stopping if the next song doesn't preload fast enough (can make tags display glitchy)\">☕️",
|
||||||
"mt_waves": "waveform seekbar:$Nshow audio amplitude in the scrubber\">~s",
|
"mt_waves": "waveform seekbar:$Nshow audio amplitude in the scrubber\">~s",
|
||||||
"mt_npclip": "show buttons for clipboarding the currently playing song\">/np",
|
"mt_npclip": "show buttons for clipboarding the currently playing song\">/np",
|
||||||
|
"mt_m3u_c": "show buttons for clipboarding the$Nselected songs as m3u8 playlist entries\">📻",
|
||||||
"mt_octl": "os integration (media hotkeys / osd)\">os-ctl",
|
"mt_octl": "os integration (media hotkeys / osd)\">os-ctl",
|
||||||
"mt_oseek": "allow seeking through os integration$N$Nnote: on some devices (iPhones),$Nthis replaces the next-song button\">seek",
|
"mt_oseek": "allow seeking through os integration$N$Nnote: on some devices (iPhones),$Nthis replaces the next-song button\">seek",
|
||||||
"mt_oscv": "show album cover in osd\">art",
|
"mt_oscv": "show album cover in osd\">art",
|
||||||
|
@ -305,6 +308,7 @@ var Ls = {
|
||||||
|
|
||||||
"mb_play": "play",
|
"mb_play": "play",
|
||||||
"mm_hashplay": "play this audio file?",
|
"mm_hashplay": "play this audio file?",
|
||||||
|
"mm_m3u": "press <code>Enter/OK</code> to Play\npress <code>ESC/Cancel</code> to Edit",
|
||||||
"mp_breq": "need firefox 82+ or chrome 73+ or iOS 15+",
|
"mp_breq": "need firefox 82+ or chrome 73+ or iOS 15+",
|
||||||
"mm_bload": "now loading...",
|
"mm_bload": "now loading...",
|
||||||
"mm_bconv": "converting to {0}, please wait...",
|
"mm_bconv": "converting to {0}, please wait...",
|
||||||
|
@ -435,6 +439,10 @@ var Ls = {
|
||||||
"tvt_sel": "select file ( for cut / copy / delete / ... )$NHotkey: S\">sel",
|
"tvt_sel": "select file ( for cut / copy / delete / ... )$NHotkey: S\">sel",
|
||||||
"tvt_edit": "open file in text editor$NHotkey: E\">✏️ edit",
|
"tvt_edit": "open file in text editor$NHotkey: E\">✏️ edit",
|
||||||
|
|
||||||
|
"m3u_add1": "song added to m3u playlist",
|
||||||
|
"m3u_addn": "{0} songs added to m3u playlist",
|
||||||
|
"m3u_clip": "m3u playlist now copied to clipboard\n\nyou should create a new textfile named something.m3u and paste the playlist in that document; this will make it playable",
|
||||||
|
|
||||||
"gt_vau": "don't show videos, just play the audio\">🎧",
|
"gt_vau": "don't show videos, just play the audio\">🎧",
|
||||||
"gt_msel": "enable file selection; ctrl-click a file to override$N$N<em>when active: doubleclick a file / folder to open it</em>$N$NHotkey: S\">multiselect",
|
"gt_msel": "enable file selection; ctrl-click a file to override$N$N<em>when active: doubleclick a file / folder to open it</em>$N$NHotkey: S\">multiselect",
|
||||||
"gt_crop": "center-crop thumbnails\">crop",
|
"gt_crop": "center-crop thumbnails\">crop",
|
||||||
|
@ -750,6 +758,8 @@ var Ls = {
|
||||||
"wt_seldl": "last ned de valgte filene$NSnarvei: Y",
|
"wt_seldl": "last ned de valgte filene$NSnarvei: Y",
|
||||||
"wt_npirc": "kopiér sang-info (irc-formatert)",
|
"wt_npirc": "kopiér sang-info (irc-formatert)",
|
||||||
"wt_nptxt": "kopiér sang-info",
|
"wt_nptxt": "kopiér sang-info",
|
||||||
|
"wt_m3ua": "legg til sang i m3u-spilleliste$N(husk å klikke på <code>📻copy</code> senere)",
|
||||||
|
"wt_m3uc": "kopiér m3u-spillelisten til utklippstavlen",
|
||||||
"wt_grid": "bytt mellom ikoner og listevisning$NSnarvei: G",
|
"wt_grid": "bytt mellom ikoner og listevisning$NSnarvei: G",
|
||||||
"wt_prev": "forrige sang$NSnarvei: J",
|
"wt_prev": "forrige sang$NSnarvei: J",
|
||||||
"wt_play": "play / pause$NSnarvei: P",
|
"wt_play": "play / pause$NSnarvei: P",
|
||||||
|
@ -886,6 +896,7 @@ var Ls = {
|
||||||
"mt_fau": "for telefoner: forhindre at avspilling stopper hvis nettet er for tregt til å laste neste sang i tide. Hvis påskrudd, kan forårsake at sang-info ikke vises korrekt i OS'et\">☕️",
|
"mt_fau": "for telefoner: forhindre at avspilling stopper hvis nettet er for tregt til å laste neste sang i tide. Hvis påskrudd, kan forårsake at sang-info ikke vises korrekt i OS'et\">☕️",
|
||||||
"mt_waves": "waveform seekbar:$Nvis volumkurve i avspillingsfeltet\">~s",
|
"mt_waves": "waveform seekbar:$Nvis volumkurve i avspillingsfeltet\">~s",
|
||||||
"mt_npclip": "vis knapper for å kopiere info om sangen du hører på\">/np",
|
"mt_npclip": "vis knapper for å kopiere info om sangen du hører på\">/np",
|
||||||
|
"mt_m3u_c": "vis knapper for å kopiere de valgte$Nsangene som innslag i en m3u8 spilleliste\">📻",
|
||||||
"mt_octl": "integrering med operativsystemet (fjernkontroll, info-skjerm)\">os-ctl",
|
"mt_octl": "integrering med operativsystemet (fjernkontroll, info-skjerm)\">os-ctl",
|
||||||
"mt_oseek": "tillat spoling med fjernkontroll$N$Nmerk: på noen enheter (iPhones) så vil$Ndette erstatte knappen for neste sang\">spoling",
|
"mt_oseek": "tillat spoling med fjernkontroll$N$Nmerk: på noen enheter (iPhones) så vil$Ndette erstatte knappen for neste sang\">spoling",
|
||||||
"mt_oscv": "vis album-cover på infoskjermen\">bilde",
|
"mt_oscv": "vis album-cover på infoskjermen\">bilde",
|
||||||
|
@ -911,6 +922,7 @@ var Ls = {
|
||||||
|
|
||||||
"mb_play": "lytt",
|
"mb_play": "lytt",
|
||||||
"mm_hashplay": "spill denne sangen?",
|
"mm_hashplay": "spill denne sangen?",
|
||||||
|
"mm_m3u": "trykk <code>Enter/OK</code> for å spille\ntrykk <code>ESC/Avbryt</code> for å redigere",
|
||||||
"mp_breq": "krever firefox 82+, chrome 73+, eller iOS 15+",
|
"mp_breq": "krever firefox 82+, chrome 73+, eller iOS 15+",
|
||||||
"mm_bload": "laster inn...",
|
"mm_bload": "laster inn...",
|
||||||
"mm_bconv": "konverterer til {0}, vent litt...",
|
"mm_bconv": "konverterer til {0}, vent litt...",
|
||||||
|
@ -1041,6 +1053,10 @@ var Ls = {
|
||||||
"tvt_sel": "markér filen ( for utklipp / sletting / ... )$NSnarvei: S\">merk",
|
"tvt_sel": "markér filen ( for utklipp / sletting / ... )$NSnarvei: S\">merk",
|
||||||
"tvt_edit": "redigér filen$NSnarvei: E\">✏️ endre",
|
"tvt_edit": "redigér filen$NSnarvei: E\">✏️ endre",
|
||||||
|
|
||||||
|
"m3u_add1": "sangen ble lagt til i m3u-spillelisten",
|
||||||
|
"m3u_addn": "{0} sanger ble lagt til i m3u-spillelisten",
|
||||||
|
"m3u_clip": "m3u-spillelisten ble kopiert til utklippstavlen\n\nneste steg er å opprette et tekstdokument med filnavn som slutter på <code>.m3u</code> og lime inn spillelisten der",
|
||||||
|
|
||||||
"gt_vau": "ikke vis videofiler, bare spill lyden\">🎧",
|
"gt_vau": "ikke vis videofiler, bare spill lyden\">🎧",
|
||||||
"gt_msel": "markér filer istedenfor å åpne dem; ctrl-klikk filer for å overstyre$N$N<em>når aktiv: dobbelklikk en fil / mappe for å åpne</em>$N$NSnarvei: S\">markering",
|
"gt_msel": "markér filer istedenfor å åpne dem; ctrl-klikk filer for å overstyre$N$N<em>når aktiv: dobbelklikk en fil / mappe for å åpne</em>$N$NSnarvei: S\">markering",
|
||||||
"gt_crop": "beskjær ikonene så de passer bedre\">✂",
|
"gt_crop": "beskjær ikonene så de passer bedre\">✂",
|
||||||
|
@ -1891,6 +1907,9 @@ ebi('widget').innerHTML = (
|
||||||
'</span><span id="wnp"><a' +
|
'</span><span id="wnp"><a' +
|
||||||
' href="#" id="npirc" tt="' + L.wt_npirc + '">📋<span>irc</span></a><a' +
|
' href="#" id="npirc" tt="' + L.wt_npirc + '">📋<span>irc</span></a><a' +
|
||||||
' href="#" id="nptxt" tt="' + L.wt_nptxt + '">📋<span>txt</span></a>' +
|
' href="#" id="nptxt" tt="' + L.wt_nptxt + '">📋<span>txt</span></a>' +
|
||||||
|
'</span><span id="wm3u"><a' +
|
||||||
|
' href="#" id="m3ua" tt="' + L.wt_m3ua + '">📻<span>add</span></a><a' +
|
||||||
|
' href="#" id="m3uc" tt="' + L.wt_m3uc + '">📻<span>copy</span></a>' +
|
||||||
'</span><a' +
|
'</span><a' +
|
||||||
' href="#" id="wtgrid" tt="' + L.wt_grid + '">田</a><a' +
|
' href="#" id="wtgrid" tt="' + L.wt_grid + '">田</a><a' +
|
||||||
' href="#" id="wtico">♫</a>' +
|
' href="#" id="wtico">♫</a>' +
|
||||||
|
@ -2306,6 +2325,7 @@ var mpl = (function () {
|
||||||
'<a href="#" class="tgl btn" id="au_fau" tt="' + L.mt_fau + '</a>' +
|
'<a href="#" class="tgl btn" id="au_fau" tt="' + L.mt_fau + '</a>' +
|
||||||
'<a href="#" class="tgl btn" id="au_waves" tt="' + L.mt_waves + '</a>' +
|
'<a href="#" class="tgl btn" id="au_waves" tt="' + L.mt_waves + '</a>' +
|
||||||
'<a href="#" class="tgl btn" id="au_npclip" tt="' + L.mt_npclip + '</a>' +
|
'<a href="#" class="tgl btn" id="au_npclip" tt="' + L.mt_npclip + '</a>' +
|
||||||
|
'<a href="#" class="tgl btn" id="au_m3u_c" tt="' + L.mt_m3u_c + '</a>' +
|
||||||
'<a href="#" class="tgl btn" id="au_os_ctl" tt="' + L.mt_octl + '</a>' +
|
'<a href="#" class="tgl btn" id="au_os_ctl" tt="' + L.mt_octl + '</a>' +
|
||||||
'<a href="#" class="tgl btn" id="au_os_seek" tt="' + L.mt_oseek + '</a>' +
|
'<a href="#" class="tgl btn" id="au_os_seek" tt="' + L.mt_oseek + '</a>' +
|
||||||
'<a href="#" class="tgl btn" id="au_osd_cv" tt="' + L.mt_oscv + '</a>' +
|
'<a href="#" class="tgl btn" id="au_osd_cv" tt="' + L.mt_oscv + '</a>' +
|
||||||
|
@ -2348,6 +2368,7 @@ var mpl = (function () {
|
||||||
"pb_mode": (sread('pb_mode', ['loop', 'next']) || 'next').split('-')[0],
|
"pb_mode": (sread('pb_mode', ['loop', 'next']) || 'next').split('-')[0],
|
||||||
"os_ctl": bcfg_get('au_os_ctl', have_mctl) && have_mctl,
|
"os_ctl": bcfg_get('au_os_ctl', have_mctl) && have_mctl,
|
||||||
'traversals': 0,
|
'traversals': 0,
|
||||||
|
'm3ut': '#EXTM3U\n',
|
||||||
};
|
};
|
||||||
bcfg_bind(r, 'loop', 'au_loop', false, function (v) {
|
bcfg_bind(r, 'loop', 'au_loop', false, function (v) {
|
||||||
if (mp.au)
|
if (mp.au)
|
||||||
|
@ -2377,6 +2398,9 @@ var mpl = (function () {
|
||||||
bcfg_bind(r, 'clip', 'au_npclip', false, function (v) {
|
bcfg_bind(r, 'clip', 'au_npclip', false, function (v) {
|
||||||
clmod(ebi('wtoggle'), 'np', v && mp.au);
|
clmod(ebi('wtoggle'), 'np', v && mp.au);
|
||||||
});
|
});
|
||||||
|
bcfg_bind(r, 'm3uen', 'au_m3u_c', false, function (v) {
|
||||||
|
clmod(ebi('wtoggle'), 'm3u', v && (mp.au || msel.getsel().length));
|
||||||
|
});
|
||||||
bcfg_bind(r, 'follow', 'au_follow', false, setaufollow);
|
bcfg_bind(r, 'follow', 'au_follow', false, setaufollow);
|
||||||
bcfg_bind(r, 'ac_flac', 'ac_flac', true);
|
bcfg_bind(r, 'ac_flac', 'ac_flac', true);
|
||||||
bcfg_bind(r, 'ac_aac', 'ac_aac', false);
|
bcfg_bind(r, 'ac_aac', 'ac_aac', false);
|
||||||
|
@ -2630,6 +2654,7 @@ if (can_owa && APPLE && / OS ([1-9]|1[0-7])_/.test(UA))
|
||||||
mpl.init_ac2();
|
mpl.init_ac2();
|
||||||
|
|
||||||
|
|
||||||
|
var re_m3u = /\.(m3u8?)$/i;
|
||||||
var re_au_native = (can_ogg || have_acode) ? /\.(aac|flac|m4a|mp3|ogg|opus|wav)$/i : /\.(aac|flac|m4a|mp3|wav)$/i,
|
var re_au_native = (can_ogg || have_acode) ? /\.(aac|flac|m4a|mp3|ogg|opus|wav)$/i : /\.(aac|flac|m4a|mp3|wav)$/i,
|
||||||
re_au_all = /\.(aac|ac3|aif|aiff|alac|alaw|amr|ape|au|dfpwm|dts|flac|gsm|it|itgz|itxz|itz|m4a|mdgz|mdxz|mdz|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|ogg|okt|opus|ra|s3m|s3gz|s3xz|s3z|tak|tta|ulaw|wav|wma|wv|xm|xmgz|xmxz|xmz|xpk|3gp|asf|avi|flv|m4v|mkv|mov|mp4|mpeg|mpeg2|mpegts|mpg|mpg2|nut|ogm|ogv|rm|ts|vob|webm|wmv)$/i;
|
re_au_all = /\.(aac|ac3|aif|aiff|alac|alaw|amr|ape|au|dfpwm|dts|flac|gsm|it|itgz|itxz|itz|m4a|mdgz|mdxz|mdz|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|ogg|okt|opus|ra|s3m|s3gz|s3xz|s3z|tak|tta|ulaw|wav|wma|wv|xm|xmgz|xmxz|xmz|xpk|3gp|asf|avi|flv|m4v|mkv|mov|mp4|mpeg|mpeg2|mpegts|mpg|mpg2|nut|ogm|ogv|rm|ts|vob|webm|wmv)$/i;
|
||||||
|
|
||||||
|
@ -2656,9 +2681,9 @@ function MPlayer() {
|
||||||
|
|
||||||
link = link[link.length - 1];
|
link = link[link.length - 1];
|
||||||
var url = link.getAttribute('href'),
|
var url = link.getAttribute('href'),
|
||||||
m = re_audio.exec(url.split('?')[0]);
|
fn = url.split('?')[0];
|
||||||
|
|
||||||
if (m) {
|
if (re_audio.exec(fn)) {
|
||||||
var tid = link.getAttribute('id');
|
var tid = link.getAttribute('id');
|
||||||
r.order.push(tid);
|
r.order.push(tid);
|
||||||
r.tracks[tid] = url;
|
r.tracks[tid] = url;
|
||||||
|
@ -2666,6 +2691,11 @@ function MPlayer() {
|
||||||
ebi('a' + tid).onclick = ev_play;
|
ebi('a' + tid).onclick = ev_play;
|
||||||
clmod(trs[a], 'au', 1);
|
clmod(trs[a], 'au', 1);
|
||||||
}
|
}
|
||||||
|
else if (re_m3u.exec(fn)) {
|
||||||
|
var tid = link.getAttribute('id');
|
||||||
|
tds[0].innerHTML = '<a id="a' + tid + '" href="#a' + tid + '" class="play">' + L.mb_play + '</a></td>';
|
||||||
|
ebi('a' + tid).onclick = ev_load_m3u;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.vol = clamp(fcfg_get('vol', IPHONE ? 1 : dvol / 100), 0, 1);
|
r.vol = clamp(fcfg_get('vol', IPHONE ? 1 : dvol / 100), 0, 1);
|
||||||
|
@ -2880,6 +2910,8 @@ var widget = (function () {
|
||||||
wtico = ebi('wtico'),
|
wtico = ebi('wtico'),
|
||||||
nptxt = ebi('nptxt'),
|
nptxt = ebi('nptxt'),
|
||||||
npirc = ebi('npirc'),
|
npirc = ebi('npirc'),
|
||||||
|
m3ua = ebi('m3ua'),
|
||||||
|
m3uc = ebi('m3uc'),
|
||||||
touchmode = false,
|
touchmode = false,
|
||||||
was_paused = true;
|
was_paused = true;
|
||||||
|
|
||||||
|
@ -2938,6 +2970,49 @@ var widget = (function () {
|
||||||
toast.ok(1, L.clipped, null, 'top');
|
toast.ok(1, L.clipped, null, 'top');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
m3ua.onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
|
var el,
|
||||||
|
files = [],
|
||||||
|
sel = msel.getsel();
|
||||||
|
|
||||||
|
for (var a = 0; a < sel.length; a++) {
|
||||||
|
el = ebi(sel[a].id).closest('tr');
|
||||||
|
if (clgot(el, 'au'))
|
||||||
|
files.push(el);
|
||||||
|
}
|
||||||
|
el = QS('#files tr.play');
|
||||||
|
if (!sel.length && el)
|
||||||
|
files.push(el);
|
||||||
|
|
||||||
|
for (var a = 0; a < files.length; a++) {
|
||||||
|
var md = ft2dict(files[a])[0],
|
||||||
|
dur = md['.dur'] || '1',
|
||||||
|
tag = '';
|
||||||
|
|
||||||
|
if (md.artist && md.title)
|
||||||
|
tag = md.artist + ' - ' + md.title;
|
||||||
|
else if (md.artist)
|
||||||
|
tag = md.artist + ' - ' + md.file;
|
||||||
|
else if (md.title)
|
||||||
|
tag = md.title;
|
||||||
|
|
||||||
|
if (dur.indexOf(':') > 0) {
|
||||||
|
dur = dur.split(':');
|
||||||
|
dur = 60 * parseInt(dur[0]) + parseInt(dur[1]);
|
||||||
|
}
|
||||||
|
else dur = parseInt(dur);
|
||||||
|
|
||||||
|
mpl.m3ut += '#EXTINF:' + dur + ',' + tag + '\n' + uricom_dec(get_evpath()) + md.file + '\n';
|
||||||
|
}
|
||||||
|
toast.ok(2, files.length == 1 ? L.m3u_add1 : L.m3u_addn.format(files.length), null, 'top');
|
||||||
|
};
|
||||||
|
m3uc.onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
|
cliptxt(mpl.m3ut, function () {
|
||||||
|
toast.ok(15, L.m3u_clip, null, 'top');
|
||||||
|
});
|
||||||
|
};
|
||||||
r.set(sread('au_open') == 1);
|
r.set(sread('au_open') == 1);
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
clmod(widget, 'anim', 1);
|
clmod(widget, 'anim', 1);
|
||||||
|
@ -4121,6 +4196,7 @@ function play(tid, is_ev, seek) {
|
||||||
clmod(ebi(oid), 'act', 1);
|
clmod(ebi(oid), 'act', 1);
|
||||||
clmod(ebi(oid).closest('tr'), 'play', 1);
|
clmod(ebi(oid).closest('tr'), 'play', 1);
|
||||||
clmod(ebi('wtoggle'), 'np', mpl.clip);
|
clmod(ebi('wtoggle'), 'np', mpl.clip);
|
||||||
|
clmod(ebi('wtoggle'), 'm3u', mpl.m3uen);
|
||||||
if (thegrid)
|
if (thegrid)
|
||||||
thegrid.loadsel();
|
thegrid.loadsel();
|
||||||
|
|
||||||
|
@ -4395,6 +4471,11 @@ function eval_hash() {
|
||||||
goto(v.slice(3));
|
goto(v.slice(3));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (v.startsWith("#m3u=")) {
|
||||||
|
load_m3u(v.slice(5));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4736,6 +4817,7 @@ var fileman = (function () {
|
||||||
clmod(bshr, 'hide', hshr);
|
clmod(bshr, 'hide', hshr);
|
||||||
|
|
||||||
clmod(ebi('wfm'), 'act', QS('#wfm a.en:not(.hide)'));
|
clmod(ebi('wfm'), 'act', QS('#wfm a.en:not(.hide)'));
|
||||||
|
clmod(ebi('wtoggle'), 'm3u', mpl.m3uen && (nsel || (mp && mp.au)));
|
||||||
|
|
||||||
var wfs = ebi('wfs'), h = '';
|
var wfs = ebi('wfs'), h = '';
|
||||||
try {
|
try {
|
||||||
|
@ -6900,7 +6982,7 @@ var ahotkeys = function (e) {
|
||||||
|
|
||||||
|
|
||||||
// search
|
// search
|
||||||
(function () {
|
var search_ui = (function () {
|
||||||
var sconf = [
|
var sconf = [
|
||||||
[
|
[
|
||||||
L.s_sz,
|
L.s_sz,
|
||||||
|
@ -6935,7 +7017,8 @@ var ahotkeys = function (e) {
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
var trs = [],
|
var r = {},
|
||||||
|
trs = [],
|
||||||
orig_url = null,
|
orig_url = null,
|
||||||
orig_html = null,
|
orig_html = null,
|
||||||
cap = 125;
|
cap = 125;
|
||||||
|
@ -7142,13 +7225,19 @@ var ahotkeys = function (e) {
|
||||||
search_in_progress = 0;
|
search_in_progress = 0;
|
||||||
srch_msg(false, '');
|
srch_msg(false, '');
|
||||||
|
|
||||||
var res = JSON.parse(this.responseText),
|
var res = JSON.parse(this.responseText);
|
||||||
tagord = res.tag_order;
|
r.render(res, this, true);
|
||||||
|
}
|
||||||
|
|
||||||
sortfiles(res.hits);
|
r.render = function (res, xhr, sort) {
|
||||||
|
var tagord = res.tag_order;
|
||||||
|
|
||||||
|
srch_msg(false, '');
|
||||||
|
if (sort)
|
||||||
|
sortfiles(res.hits);
|
||||||
|
|
||||||
var ofiles = ebi('files');
|
var ofiles = ebi('files');
|
||||||
if (ofiles.getAttribute('ts') > this.ts)
|
if (xhr && ofiles.getAttribute('ts') > xhr.ts)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
treectl.hide();
|
treectl.hide();
|
||||||
|
@ -7200,19 +7289,21 @@ var ahotkeys = function (e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ofiles = set_files_html(html.join('\n'));
|
ofiles = set_files_html(html.join('\n'));
|
||||||
ofiles.setAttribute("ts", this.ts);
|
ofiles.setAttribute("ts", xhr ? xhr.ts : 1);
|
||||||
ofiles.setAttribute("q_raw", this.q_raw);
|
ofiles.setAttribute("q_raw", xhr ? xhr.q_raw : 'playlist');
|
||||||
set_vq();
|
set_vq();
|
||||||
mukey.render();
|
mukey.render();
|
||||||
reload_browser();
|
reload_browser();
|
||||||
filecols.set_style(['File Name']);
|
filecols.set_style(['File Name']);
|
||||||
|
|
||||||
sethash('q=' + uricom_enc(this.q_raw));
|
if (xhr)
|
||||||
|
sethash('q=' + uricom_enc(xhr.q_raw));
|
||||||
|
|
||||||
ebi('unsearch').onclick = unsearch;
|
ebi('unsearch').onclick = unsearch;
|
||||||
var m = ebi('moar');
|
var m = ebi('moar');
|
||||||
if (m)
|
if (m)
|
||||||
m.onclick = moar;
|
m.onclick = moar;
|
||||||
}
|
};
|
||||||
|
|
||||||
function unsearch(e) {
|
function unsearch(e) {
|
||||||
ev(e);
|
ev(e);
|
||||||
|
@ -7229,9 +7320,98 @@ var ahotkeys = function (e) {
|
||||||
cap *= 2;
|
cap *= 2;
|
||||||
do_search();
|
do_search();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
function ev_load_m3u(e) {
|
||||||
|
ev(e);
|
||||||
|
var id = this.getAttribute('id').slice(1),
|
||||||
|
url = ebi(id).getAttribute('href').split('?')[0];
|
||||||
|
|
||||||
|
modal.confirm(L.mm_m3u,
|
||||||
|
function () { load_m3u(url); },
|
||||||
|
function () {
|
||||||
|
if (has(perms, 'write') && has(perms, 'delete'))
|
||||||
|
window.location = url + '?edit';
|
||||||
|
else
|
||||||
|
showfile.show(url);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function load_m3u(url) {
|
||||||
|
var xhr = new XHR();
|
||||||
|
xhr.open('GET', url, true);
|
||||||
|
xhr.onload = render_m3u;
|
||||||
|
xhr.url = url;
|
||||||
|
xhr.send();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function render_m3u() {
|
||||||
|
if (!xhrchk(this, L.tv_xe1, L.tv_xe2))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var evp = get_evpath(),
|
||||||
|
m3u = this.responseText,
|
||||||
|
xtd = m3u.slice(0, 12).indexOf('#EXTM3U') + 1,
|
||||||
|
lines = m3u.replace(/\r/g, '\n').split('\n'),
|
||||||
|
dur = 1,
|
||||||
|
artist = '',
|
||||||
|
title = '',
|
||||||
|
ret = {'hits': [], 'tag_order': ['artist', 'title', '.dur'], 'trunc': false};
|
||||||
|
|
||||||
|
for (var a = 0; a < lines.length; a++) {
|
||||||
|
var ln = lines[a].trim();
|
||||||
|
if (xtd && ln.startsWith('#')) {
|
||||||
|
var m = /^#EXTINF:([0-9]+)[, ](.*)/.exec(ln);
|
||||||
|
if (m) {
|
||||||
|
dur = m[1];
|
||||||
|
title = m[2];
|
||||||
|
var ofs = title.indexOf(' - ');
|
||||||
|
if (ofs > 0) {
|
||||||
|
artist = title.slice(0, ofs);
|
||||||
|
title = title.slice(ofs + 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ln.indexOf('.') < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var n = ret.hits.length + 1,
|
||||||
|
url = ln;
|
||||||
|
|
||||||
|
if (url.indexOf(':\\')) // C:\
|
||||||
|
url = url.split(/\\/g).pop();
|
||||||
|
|
||||||
|
url = url.replace(/\\/g, '/');
|
||||||
|
url = uricom_enc(url).replace(/%2f/gi, '/')
|
||||||
|
|
||||||
|
if (!url.startsWith('/'))
|
||||||
|
url = vjoin(evp, url);
|
||||||
|
|
||||||
|
ret.hits.push({
|
||||||
|
"ts": 946684800 + n,
|
||||||
|
"sz": 100000 + n,
|
||||||
|
"rp": url,
|
||||||
|
"tags": {".dur": dur, "artist": artist, "title": title}
|
||||||
|
});
|
||||||
|
dur = 1;
|
||||||
|
artist = title = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
search_ui.render(ret, null, false);
|
||||||
|
sethash('m3u=' + this.url.split('?')[0].split('/').pop());
|
||||||
|
goto();
|
||||||
|
|
||||||
|
var el = QS('#files>tbody>tr.au>td>a.play');
|
||||||
|
if (el)
|
||||||
|
el.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function aligngriditems() {
|
function aligngriditems() {
|
||||||
if (!treectl)
|
if (!treectl)
|
||||||
return;
|
return;
|
||||||
|
@ -8133,7 +8313,7 @@ var treectl = (function () {
|
||||||
document.documentElement.scrollLeft = 0;
|
document.documentElement.scrollLeft = 0;
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
r.gentab(get_evpath(), r.lsc);
|
r.gentab(get_evpath(), r.lsc);
|
||||||
ebi('wrap').style.opacity = 'unset';
|
ebi('wrap').style.opacity = CLOSEST ? 'unset' : 1;
|
||||||
}, 1);
|
}, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8286,7 +8466,7 @@ var wfp_debounce = (function () {
|
||||||
if (--r.n <= 0) {
|
if (--r.n <= 0) {
|
||||||
r.n = 0;
|
r.n = 0;
|
||||||
clearTimeout(r.t);
|
clearTimeout(r.t);
|
||||||
ebi('wfp').style.opacity = 'unset';
|
ebi('wfp').style.opacity = CLOSEST ? 'unset' : 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
r.reset = function () {
|
r.reset = function () {
|
||||||
|
@ -9457,6 +9637,11 @@ function sandbox(tgt, rules, allow, cls, html) {
|
||||||
clmod(tgt, 'sb');
|
clmod(tgt, 'sb');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!CLOSEST) {
|
||||||
|
tgt.textContent = html;
|
||||||
|
clmod(tgt, 'sb');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
clmod(tgt, 'sb', 1);
|
clmod(tgt, 'sb', 1);
|
||||||
|
|
||||||
var tid = tgt.getAttribute('id'),
|
var tid = tgt.getAttribute('id'),
|
||||||
|
@ -9524,7 +9709,7 @@ window.addEventListener("message", function (e) {
|
||||||
el.parentNode.removeChild(el.previousSibling);
|
el.parentNode.removeChild(el.previousSibling);
|
||||||
|
|
||||||
el.style.height = (parseInt(t[2]) + SBH) + 'px';
|
el.style.height = (parseInt(t[2]) + SBH) + 'px';
|
||||||
el.style.visibility = 'unset';
|
el.style.visibility = CLOSEST ? 'unset' : 'block';
|
||||||
wfp_debounce.show();
|
wfp_debounce.show();
|
||||||
}
|
}
|
||||||
else if (t[0] == 'iscroll') {
|
else if (t[0] == 'iscroll') {
|
||||||
|
|
|
@ -364,7 +364,8 @@ if (!Element.prototype.matches)
|
||||||
Element.prototype.mozMatchesSelector ||
|
Element.prototype.mozMatchesSelector ||
|
||||||
Element.prototype.webkitMatchesSelector;
|
Element.prototype.webkitMatchesSelector;
|
||||||
|
|
||||||
if (!Element.prototype.closest)
|
var CLOSEST = !!Element.prototype.closest;
|
||||||
|
if (!CLOSEST)
|
||||||
Element.prototype.closest = function (s) {
|
Element.prototype.closest = function (s) {
|
||||||
var el = this;
|
var el = this;
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -1,3 +1,26 @@
|
||||||
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
|
# 2025-0420-1836 `v1.16.21` unzip-compat
|
||||||
|
|
||||||
|
a couple guys have been asking if I accept donations -- thanks a lot!! added a few options on [my github page](https://github.com/9001/) :>
|
||||||
|
|
||||||
|
## 🧪 new features
|
||||||
|
|
||||||
|
* #156 add button to loop/repeat music 71c55659
|
||||||
|
|
||||||
|
## 🩹 bugfixes
|
||||||
|
|
||||||
|
* #155 download-as-zip: increase compatibility with the unix `unzip` command db33d68d
|
||||||
|
* this unfortunately reduces support for huge zipfiles on old software (WinXP and such)
|
||||||
|
* and makes it less safe to stream zips into unzippers, so use tar.gz instead
|
||||||
|
* and is perhaps not even a copyparty bug; see commit-message for the full story
|
||||||
|
|
||||||
|
## 🔧 other changes
|
||||||
|
|
||||||
|
* show warning on Ctrl-A in lazy-loaded folders 5b3a5fe7
|
||||||
|
* docker: hide keepalive pings from logs d5a9bd80
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
# 2025-0413-2151 `v1.16.20` all sorted
|
# 2025-0413-2151 `v1.16.20` all sorted
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue