Files
furumusic/templates/player/modals.html
T
2026-05-28 23:38:07 +03:00

651 lines
53 KiB
HTML

<!-- Info Modal -->
<template x-if="$store.info.modal">
<div class="modal-overlay" @click.self="$store.info.close()">
<div class="modal-box info-modal">
<div class="info-modal-head">
<h3 x-text="$store.info.modal.title"></h3>
<button class="mobile-list-action" @click="$store.info.close()" title="{{ t.player_close }}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<div class="info-modal-body">
<template x-if="$store.info.modal.rows && $store.info.modal.rows.length">
<table class="info-table">
<tbody>
<template x-for="(row, idx) in $store.info.modal.rows" :key="row.label + '-' + idx">
<tr>
<th x-text="row.label"></th>
<td>
<template x-if="row.links && row.links.length">
<div class="info-link-list">
<template x-for="link in row.links" :key="link.type + '-' + link.id + '-' + link.label">
<button class="info-link" type="button" @click="$store.info.navigate(link)" x-text="link.label"></button>
</template>
</div>
</template>
<template x-if="!row.links || !row.links.length">
<span x-text="row.value"></span>
</template>
</td>
</tr>
</template>
</tbody>
</table>
</template>
<pre class="info-modal-plain" x-show="!$store.info.modal.rows || !$store.info.modal.rows.length" x-text="$store.info.modal.body"></pre>
</div>
</div>
</div>
</template>
<!-- Create / Rename Playlist Modal -->
<template x-if="$store.playlists.modal">
<div class="modal-overlay" @click.self="$store.playlists.modal = null">
<div class="modal-box">
<h3 x-text="$store.playlists.modal.mode === 'create' ? '{{ t.player_new_playlist }}' : '{{ t.player_rename_playlist }}'"></h3>
<input type="text" x-model="$store.playlists.modal.title" placeholder="{{ t.player_playlist_name }}"
@keydown.enter="$store.playlists.submitModal()" x-init="$nextTick(() => $el.focus())">
<div class="modal-footer">
<button class="modal-btn modal-btn-ghost" @click="$store.playlists.modal = null">{{ t.player_cancel }}</button>
<button class="modal-btn modal-btn-primary" @click="$store.playlists.submitModal()"
x-text="$store.playlists.modal.mode === 'create' ? '{{ t.player_create }}' : '{{ t.player_save }}'"></button>
</div>
</div>
</div>
</template>
<!-- Add to Playlist Modal -->
<template x-if="$store.playlists.picker">
<div class="modal-overlay" @click.self="$store.playlists.picker = null">
<div class="modal-box">
<h3>{{ t.player_add_to_playlist }}</h3>
<div class="modal-playlist-list">
<template x-for="pl in $store.playlists.list.filter(p => p.kind === 'user' && p.is_own)" :key="pl.id">
<div class="modal-playlist-item" @click="$store.playlists.addToPicked(pl.id)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>
<span x-text="pl.title"></span>
</div>
</template>
</div>
<div class="modal-footer">
<button class="modal-btn modal-btn-ghost" @click="$store.playlists.picker = null">{{ t.player_cancel }}</button>
<button class="modal-btn modal-btn-primary" @click="$store.playlists.picker = null; $store.playlists.showCreate()">{{ t.player_new_playlist }}</button>
</div>
</div>
</div>
</template>
<!-- Torrent Import Modal -->
<template x-if="$store.torrents.modal">
<div class="modal-overlay" @click.self="$store.torrents.close()">
<div class="modal-box torrent-modal">
<div class="torrent-modal-head">
<div>
<h3>{{ t.player_torrent_manager }}</h3>
<p class="torrent-message" style="margin:4px 0 0"
:class="{ error: $store.torrents.error }"
x-text="$store.torrents.message"></p>
</div>
<button class="torrent-modal-close"
@click="$store.torrents.close()"
title="{{ t.player_close }}"
aria-label="{{ t.player_close }}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
<div class="torrent-client-status">
<span class="torrent-status-pill"
:class="{ active: $store.torrents.activeCount() > 0 }"
x-text="$store.torrents.clientSummary()"></span>
<span class="torrent-status-pill torrent-agent-pill"
:class="{ active: $store.torrents.agentBusy() }">
<span class="torrent-agent-dot"></span>
<span x-text="$store.torrents.agentSummary()"></span>
</span>
<span class="torrent-status-pill"
x-text="$store.torrents.sessions.length + ' ' + T.saved"></span>
</div>
</div>
<div class="torrent-tabs">
<button class="torrent-tab-btn"
:class="{ active: $store.torrents.activeTab === 'import' }"
@click="$store.torrents.showImportTab()">Import</button>
<button class="torrent-tab-btn"
:class="{ active: $store.torrents.activeTab === 'uploads' }"
@click="$store.torrents.showUploadsTab()">
<span>My uploads</span>
<span class="torrent-tab-count"
x-show="$store.torrents.uploadPendingTotal + $store.torrents.uploadQueuedTotal > 0"
x-text="$store.torrents.uploadPendingTotal + $store.torrents.uploadQueuedTotal"></span>
</button>
</div>
<template x-if="$store.torrents.activeTab === 'import'">
<div class="torrent-manager-layout">
<aside class="torrent-manager-sidebar">
<div class="torrent-manager-title">
<span>{{ t.player_saved_torrents }}</span>
<button class="modal-btn modal-btn-ghost" style="padding:4px 8px"
@click="$store.torrents.loadSessions()"
:disabled="$store.torrents.loading">{{ t.player_refresh }}</button>
</div>
<div class="torrent-session-list">
<template x-if="!$store.torrents.loadingSessions && $store.torrents.sessions.length === 0">
<div class="empty-state" style="padding:28px 12px">
<p>{{ t.player_no_saved_torrents }}</p>
</div>
</template>
<template x-for="job in $store.torrents.sessions" :key="job.id">
<div class="torrent-session-row"
:class="{ active: $store.torrents.workspaceMode === 'session' && $store.torrents.previewData && $store.torrents.previewData.id === job.id }"
@click="$store.torrents.openSession(job.id)">
<div class="torrent-session-main">
<div class="torrent-session-topline">
<div class="torrent-session-name" x-text="job.name"></div>
<span class="torrent-status-badge"
:class="$store.torrents.statusBadgeClass(job)"
x-text="$store.torrents.statusLabel(job)"></span>
</div>
<div class="torrent-session-meta" x-text="$store.torrents.sessionMeta(job)"></div>
<div class="torrent-session-progress">
<div class="torrent-session-progress-bar"
:style="'width:' + $store.torrents.progressValue(job) + '%'"></div>
</div>
</div>
</div>
</template>
<button type="button"
class="torrent-session-row torrent-session-add"
:class="{ active: $store.torrents.isImporting() }"
@click="$store.torrents.addNew()"
:disabled="$store.torrents.loading">
<span class="torrent-session-add-icon">+</span>
<span>{{ t.player_upload }}</span>
</button>
</div>
</aside>
<section class="torrent-workspace">
<template x-if="$store.torrents.workspaceMode === 'empty'">
<div class="empty-state torrent-workspace-empty">
<p x-text="T.chooseSavedOrAddTorrent"></p>
</div>
</template>
<template x-if="$store.torrents.isImporting()">
<div class="torrent-import-panel">
<div class="torrent-modal-grid">
<div>
<label for="local-file-input">{{ t.player_local_files }}</label>
<input id="local-file-input" type="file" multiple accept="audio/*,.mp3,.flac,.wav,.m4a,.ogg,.opus,.aac"
@change="$store.torrents.setLocalFiles($event.target.files)">
<div class="torrent-upload-summary" x-text="$store.torrents.localUploadSummary()"></div>
</div>
<div>
<label for="torrent-magnet-input">{{ t.player_magnet_link }}</label>
<input id="torrent-magnet-input" type="text"
x-model="$store.torrents.magnet"
placeholder="magnet:?xt=urn:btih:...">
</div>
<div>
<label for="torrent-file-input">{{ t.player_torrent_file }}</label>
<input id="torrent-file-input" type="file" accept=".torrent,application/x-bittorrent"
@change="$store.torrents.file = $event.target.files[0] || null">
</div>
</div>
<div class="torrent-upload-progress"
x-show="$store.torrents.uploadProgress > 0 || ($store.torrents.localFiles.length > 0 && $store.torrents.loading)">
<div class="torrent-progress-head">
<span x-text="$store.torrents.uploadProgress >= 100 ? T.uploadComplete : T.uploadingFiles"></span>
<span x-text="$store.torrents.uploadProgressText"></span>
</div>
<div class="torrent-progress-track">
<div class="torrent-progress-bar"
:style="'width:' + $store.torrents.uploadProgress + '%'"></div>
</div>
</div>
<div class="torrent-actions">
<button class="modal-btn modal-btn-primary" @click="$store.torrents.preview()" :disabled="$store.torrents.loading">
{{ t.player_upload_content }}
</button>
</div>
</div>
</template>
<template x-if="$store.torrents.currentJob">
<div class="torrent-progress-card">
<div class="torrent-progress-head">
<span x-text="$store.torrents.statusText($store.torrents.currentJob)"></span>
<span x-text="$store.torrents.progressValue($store.torrents.currentJob).toFixed(1) + '%'"></span>
</div>
<div class="torrent-progress-track">
<div class="torrent-progress-bar"
:style="'width:' + $store.torrents.progressValue($store.torrents.currentJob) + '%'"></div>
</div>
<div class="torrent-progress-details"
:class="{ completed: $store.torrents.isCompleted($store.torrents.currentJob) }">
<span class="torrent-progress-metric">
<span class="torrent-progress-label"
x-text="$store.torrents.isCompleted($store.torrents.currentJob) ? T.size : T.downloaded"></span>
<span class="torrent-progress-value"
x-text="$store.torrents.progressDetailText($store.torrents.currentJob)"></span>
</span>
<span class="torrent-progress-metric"
x-show="!$store.torrents.isCompleted($store.torrents.currentJob)">
<span class="torrent-progress-label" x-text="T.speed"></span>
<span class="torrent-progress-value"
x-text="$store.torrents.speedText($store.torrents.currentJob)"></span>
</span>
<span class="torrent-progress-metric"
x-show="!$store.torrents.isCompleted($store.torrents.currentJob)">
<span class="torrent-progress-label" x-text="T.peers"></span>
<span class="torrent-progress-value"
x-text="$store.torrents.peerText($store.torrents.currentJob)"></span>
</span>
<span class="torrent-progress-metric"
x-show="!$store.torrents.isCompleted($store.torrents.currentJob) && $store.torrents.etaText($store.torrents.currentJob)">
<span class="torrent-progress-label" x-text="T.eta"></span>
<span class="torrent-progress-value"
x-text="$store.torrents.etaText($store.torrents.currentJob)"></span>
</span>
</div>
</div>
</template>
<template x-if="$store.torrents.previewData">
<div class="torrent-preview-panel">
<div class="torrent-preview-head">
<div style="min-width:0">
<div class="torrent-preview-title" x-text="$store.torrents.previewData.name"></div>
<div class="torrent-preview-meta"
x-text="$store.torrents.previewData.files.length + ' {{ t.player_files_count }} - ' + $store.torrents.bytes($store.torrents.previewData.total_size)"></div>
</div>
<div class="torrent-preview-actions">
<button class="modal-btn"
:class="$store.torrents.actionButtonClass()"
@click="$store.torrents.toggleDownloadAction()"
:disabled="$store.torrents.actionButtonDisabled()">
<span x-text="$store.torrents.actionButtonText()"></span>
</button>
<button class="modal-btn modal-btn-danger"
@click="$store.torrents.removeSession($store.torrents.previewData.id)"
:disabled="$store.torrents.loading">
{{ t.player_delete }}
</button>
</div>
</div>
<div class="torrent-tree-toolbar">
<div class="torrent-selected-summary"
x-text="$store.torrents.selected.size + ' {{ t.player_selected }} - ' + $store.torrents.bytes($store.torrents.selectedBytes())"></div>
<div class="torrent-actions" style="margin-top:0">
<button class="modal-btn modal-btn-ghost" @click="$store.torrents.expandAll(true)">{{ t.player_expand_all }}</button>
<button class="modal-btn modal-btn-ghost" @click="$store.torrents.expandAll(false)">{{ t.player_collapse }}</button>
</div>
</div>
<div class="torrent-file-tree">
<template x-for="node in $store.torrents.visibleNodes()" :key="node.key">
<div class="torrent-tree-row" :style="'--indent:' + $store.torrents.rowIndent(node) + 'px'">
<button class="torrent-tree-toggle"
:class="{ expanded: $store.torrents.expanded.has(node.key) }"
@click="$store.torrents.toggleExpand(node)"
:style="node.type === 'folder' ? '' : 'visibility:hidden'">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="9 18 15 12 9 6"/>
</svg>
</button>
<button class="torrent-tree-check"
:class="$store.torrents.nodeCheckClass(node)"
@click="$store.torrents.toggleNode(node)">
<template x-if="$store.torrents.nodeState(node) === 'checked'">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
<polyline points="20 6 9 17 4 12"/>
</svg>
</template>
<template x-if="$store.torrents.nodeState(node) === 'partial'">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
<line x1="5" y1="12" x2="19" y2="12"/>
</svg>
</template>
</button>
<div class="torrent-tree-label" :title="node.name">
<template x-if="node.type === 'folder'">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 7a2 2 0 012-2h5l2 2h7a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"/>
</svg>
</template>
<template x-if="node.type === 'file'">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
</svg>
</template>
<span class="torrent-file-name" x-text="node.name"></span>
</div>
<span class="torrent-file-size" x-text="$store.torrents.bytes(node.size)"></span>
</div>
</template>
</div>
</div>
</template>
</section>
</div>
</template>
<template x-if="$store.torrents.activeTab === 'uploads'">
<section class="upload-manager-panel">
<div class="upload-manager-head">
<div>
<h4>My uploaded tracks</h4>
<p x-text="$store.torrents.uploadSummary()"></p>
</div>
<button class="modal-btn modal-btn-ghost"
@click="$store.torrents.loadUploads()"
:disabled="$store.torrents.uploadHasEditorOpen()">{{ t.player_refresh }}</button>
</div>
<template x-if="$store.torrents.uploadLoaded && $store.torrents.uploadTracks.length === 0 && $store.torrents.uploadPending.length === 0 && $store.torrents.uploadQueued.length === 0">
<div class="empty-state torrent-workspace-empty">
<p>No uploaded tracks yet</p>
</div>
</template>
<div class="upload-manager-grid">
<aside class="upload-review-column">
<div class="upload-panel-card">
<div class="upload-panel-title">Needs approval</div>
<p class="upload-panel-subtitle" x-text="$store.torrents.uploadPendingTotal + ' pending or failed'"></p>
<template x-if="$store.torrents.uploadPending.length === 0">
<div class="upload-mini-empty">No tracks need approval</div>
</template>
<div class="upload-review-list">
<template x-for="item in $store.torrents.uploadPending" :key="item.id">
<button class="upload-review-row" :class="{ active: $store.torrents.uploadReviewEditId === item.id, failed: item.status === 'failed' }" @click="$store.torrents.editUploadReview(item)">
<span class="torrent-status-badge" :class="'status-' + item.status" x-text="item.status"></span>
<span class="upload-review-name" x-text="item.filename"></span>
<span class="upload-review-error" x-show="item.error_message" x-text="item.error_message"></span>
</button>
</template>
</div>
</div>
<template x-if="$store.torrents.uploadQueuedTotal > 0">
<div class="upload-panel-card upload-queue-panel">
<div class="upload-panel-title">Queued / processing</div>
<template x-for="item in $store.torrents.compactQueuedUploads()" :key="item.id">
<div class="upload-queue-row">
<span class="torrent-status-badge" :class="'status-' + item.status" x-text="item.status"></span>
<span class="upload-queue-name" x-text="item.filename"></span>
</div>
</template>
<div class="upload-mini-empty" x-show="$store.torrents.uploadQueuedTotal > $store.torrents.uploadQueued.length" x-text="'Showing ' + $store.torrents.uploadQueued.length + ' of ' + $store.torrents.uploadQueuedTotal"></div>
</div>
</template>
</aside>
<section class="upload-library-column">
<div class="upload-bulk-bar" x-show="$store.torrents.uploadSelectedCount() > 0">
<div class="upload-bulk-title" x-text="$store.torrents.uploadSelectedCount() + ' selected'"></div>
<input type="text" placeholder="Artists" x-model="$store.torrents.uploadBulkDraft.artists">
<input type="text" placeholder="Featured" x-model="$store.torrents.uploadBulkDraft.featured_artists">
<input type="text" placeholder="Album" x-model="$store.torrents.uploadBulkDraft.release_title">
<input type="number" placeholder="Year" x-model="$store.torrents.uploadBulkDraft.release_year">
<select x-model="$store.torrents.uploadBulkDraft.release_type">
<option value="">Type unchanged</option><option value="album">Album</option><option value="single">Single</option><option value="ep">EP</option><option value="compilation">Compilation</option><option value="mixtape">Mixtape</option><option value="live">Live</option><option value="soundtrack">Soundtrack</option><option value="remix">Remix</option><option value="demo">Demo</option>
</select>
<select x-model="$store.torrents.uploadBulkDraft.hidden">
<option value="">Visibility unchanged</option><option value="false">Visible</option><option value="true">Hidden</option>
</select>
<button class="modal-btn modal-btn-primary" @click="$store.torrents.saveUploadBulkEdit()" :disabled="$store.torrents.uploadBulkSaving">Apply</button>
<button class="modal-btn modal-btn-ghost" @click="$store.torrents.clearUploadSelection()">Clear</button>
</div>
<div class="upload-release-tree">
<template x-for="release in $store.torrents.uploadReleases" :key="release.id">
<div class="upload-release-node" :class="{ hidden: release.is_hidden }">
<div class="upload-release-row">
<button class="torrent-tree-check" :class="$store.torrents.uploadReleaseSelectionState(release)" @click="$store.torrents.toggleUploadReleaseSelection(release)">
<template x-if="$store.torrents.uploadReleaseSelectionState(release) === 'checked'"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></template>
<template x-if="$store.torrents.uploadReleaseSelectionState(release) === 'partial'"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><line x1="5" y1="12" x2="19" y2="12"/></svg></template>
</button>
<button class="torrent-tree-toggle" :class="{ expanded: $store.torrents.uploadReleaseExpanded(release.id) }" @click="$store.torrents.toggleUploadRelease(release.id)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg></button>
<div class="upload-release-main">
<div class="upload-release-title"><span x-text="release.title"></span><span class="upload-hidden-pill" x-show="release.is_hidden">hidden</span></div>
<div class="upload-track-meta"><span x-text="$store.torrents.uploadReleaseArtistsText(release)"></span><span>-</span><span x-text="release.year || 'no year'"></span><span>-</span><span x-text="release.tracks.length + ' tracks'"></span></div>
</div>
<button class="modal-btn modal-btn-ghost" @click="$store.torrents.editUploadRelease(release)">Edit release</button>
</div>
<div class="upload-track-children" x-show="$store.torrents.uploadReleaseExpanded(release.id)">
<template x-for="item in release.tracks" :key="item.track.id">
<div class="upload-tree-track" :class="{ hidden: item.is_hidden, selected: $store.torrents.selectedUploadTracks.has(item.track.id) }">
<button class="torrent-tree-check" :class="{ checked: $store.torrents.selectedUploadTracks.has(item.track.id) }" @click="$store.torrents.toggleUploadTrackSelection(item.track.id)">
<template x-if="$store.torrents.selectedUploadTracks.has(item.track.id)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg></template>
</button>
<button class="track-action-btn play-btn" @click="$store.player.play(item.track)" title="{{ t.player_play }}"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg></button>
<div class="upload-track-main">
<div class="upload-track-title"><span x-text="item.track.track_number ? item.track.track_number + '. ' + item.track.title : item.track.title"></span><span class="upload-hidden-pill" x-show="item.is_hidden">hidden</span></div>
<div class="upload-track-meta"><span x-text="$store.torrents.uploadArtistsText(item)"></span><span x-show="$store.torrents.uploadFeaturedArtistsText(item)">feat.</span><span x-show="$store.torrents.uploadFeaturedArtistsText(item)" x-text="$store.torrents.uploadFeaturedArtistsText(item)"></span></div>
</div>
<div class="upload-track-actions">
<button class="track-action-btn" @click="$store.queue.addNextInQueue([item.track])" title="{{ t.player_play_next }}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 6h14M5 12h8M5 18h14"/><path d="M17 10l4 3-4 3" fill="currentColor" stroke="none"/></svg></button>
<button class="modal-btn modal-btn-ghost" @click="$store.torrents.editUpload(item)">Edit</button>
</div>
</div>
</template>
</div>
</div>
</template>
</div>
</section>
</div>
<template x-if="$store.torrents.uploadHasEditorOpen()">
<div class="upload-editor-backdrop" @click.self="$store.torrents.closeUploadEditor()">
<aside class="upload-editor-drawer">
<div class="upload-editor-head">
<div>
<div class="upload-panel-title" x-text="$store.torrents.uploadEditorKicker()"></div>
<h4 x-text="$store.torrents.uploadEditorTitle()"></h4>
</div>
<button class="track-action-btn" @click="$store.torrents.closeUploadEditor()" title="Close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>
</button>
</div>
<template x-if="$store.torrents.uploadReviewDraft">
<div class="upload-editor-form">
<label class="upload-field upload-field-half"><span>Title</span><input type="text" x-model="$store.torrents.uploadReviewDraft.title"></label>
<label class="upload-field upload-field-half"><span>Artist</span><input type="text" x-model="$store.torrents.uploadReviewDraft.artist"></label>
<label class="upload-field upload-field-half"><span>Album</span><input type="text" x-model="$store.torrents.uploadReviewDraft.album"></label>
<label class="upload-field upload-field-half"><span>Featured</span><input type="text" x-model="$store.torrents.uploadReviewDraft.featured_artists" placeholder="Artist, Artist"></label>
<label class="upload-field upload-field-compact"><span>Year</span><input type="number" x-model="$store.torrents.uploadReviewDraft.year"></label>
<label class="upload-field upload-field-compact"><span>Track #</span><input type="number" min="1" x-model="$store.torrents.uploadReviewDraft.track_number"></label>
<label class="upload-field upload-field-compact"><span>Genre</span><input type="text" x-model="$store.torrents.uploadReviewDraft.genre"></label>
<label class="upload-field upload-field-compact">
<span>Type</span>
<select x-model="$store.torrents.uploadReviewDraft.release_type">
<option value="album">Album</option><option value="single">Single</option><option value="ep">EP</option><option value="compilation">Compilation</option><option value="mixtape">Mixtape</option><option value="live">Live</option><option value="soundtrack">Soundtrack</option><option value="remix">Remix</option><option value="demo">Demo</option>
</select>
</label>
<label class="upload-field upload-field-wide"><span>Notes</span><textarea rows="4" x-model="$store.torrents.uploadReviewDraft.notes"></textarea></label>
<div class="upload-editor-actions">
<button class="modal-btn modal-btn-danger" @click="$store.torrents.deleteUploadReview()" :disabled="$store.torrents.uploadReviewSavingId === $store.torrents.uploadReviewEditId">Delete review</button>
<button class="modal-btn modal-btn-primary" @click="$store.torrents.approveUploadReview()" :disabled="$store.torrents.uploadReviewSavingId === $store.torrents.uploadReviewEditId">Approve</button>
</div>
</div>
</template>
<template x-if="$store.torrents.uploadReleaseDraft">
<div class="upload-editor-form">
<label class="upload-field upload-field-wide"><span>Album</span><input type="text" x-model="$store.torrents.uploadReleaseDraft.title"></label>
<label class="upload-field upload-field-wide"><span>Album artists</span><input type="text" x-model="$store.torrents.uploadReleaseDraft.artists"></label>
<label class="upload-field upload-field-half"><span>Year</span><input type="number" x-model="$store.torrents.uploadReleaseDraft.year"></label>
<label class="upload-field upload-field-half"><span>Type</span><select x-model="$store.torrents.uploadReleaseDraft.release_type"><option value="album">Album</option><option value="single">Single</option><option value="ep">EP</option><option value="compilation">Compilation</option><option value="mixtape">Mixtape</option><option value="live">Live</option><option value="soundtrack">Soundtrack</option><option value="remix">Remix</option><option value="demo">Demo</option></select></label>
<label class="upload-field upload-field-toggle"><input type="checkbox" x-model="$store.torrents.uploadReleaseDraft.is_hidden"><span>Hidden</span></label>
<div class="upload-editor-actions">
<button class="modal-btn modal-btn-primary" @click="$store.torrents.saveUploadReleaseEdit()" :disabled="$store.torrents.uploadReleaseSavingId === $store.torrents.uploadReleaseEditId">Save release</button>
</div>
</div>
</template>
<template x-if="$store.torrents.uploadDraft">
<div class="upload-editor-form">
<label class="upload-field upload-field-wide"><span>Title</span><input type="text" x-model="$store.torrents.uploadDraft.title"></label>
<label class="upload-field upload-field-half"><span>Artists</span><input type="text" x-model="$store.torrents.uploadDraft.artists"></label>
<label class="upload-field upload-field-half"><span>Featured</span><input type="text" x-model="$store.torrents.uploadDraft.featured_artists"></label>
<label class="upload-field upload-field-half"><span>Album</span><input type="text" x-model="$store.torrents.uploadDraft.release_title"></label>
<label class="upload-field upload-field-half"><span>Type</span><select x-model="$store.torrents.uploadDraft.release_type"><option value="album">Album</option><option value="single">Single</option><option value="ep">EP</option><option value="compilation">Compilation</option><option value="mixtape">Mixtape</option><option value="live">Live</option><option value="soundtrack">Soundtrack</option><option value="remix">Remix</option><option value="demo">Demo</option></select></label>
<label class="upload-field upload-field-compact"><span>Year</span><input type="number" x-model="$store.torrents.uploadDraft.release_year"></label>
<label class="upload-field upload-field-compact"><span>Track #</span><input type="number" min="1" x-model="$store.torrents.uploadDraft.track_number"></label>
<label class="upload-field upload-field-compact"><span>Disc #</span><input type="number" min="1" x-model="$store.torrents.uploadDraft.disc_number"></label>
<label class="upload-field upload-field-toggle"><input type="checkbox" x-model="$store.torrents.uploadDraft.is_hidden"><span>Hidden</span></label>
<div class="upload-editor-actions">
<button class="modal-btn modal-btn-primary" @click="$store.torrents.saveUploadEdit()" :disabled="$store.torrents.uploadSavingId === $store.torrents.uploadEditId">Save track</button>
</div>
</div>
</template>
</aside>
</div>
</template>
<div class="upload-track-list" x-show="false">
<template x-for="item in $store.torrents.uploadTracks" :key="item.track.id">
<div class="upload-track-card" :class="{ hidden: item.is_hidden }">
<template x-if="$store.torrents.uploadEditId !== item.track.id">
<div class="upload-track-display">
<button class="track-action-btn play-btn"
@click="$store.player.play(item.track)"
title="{{ t.player_play }}">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
</button>
<div class="upload-track-main">
<div class="upload-track-title">
<span x-text="item.track.title"></span>
<span class="upload-hidden-pill" x-show="item.is_hidden">hidden</span>
</div>
<div class="upload-track-meta">
<span x-text="$store.torrents.uploadArtistsText(item)"></span>
<span>·</span>
<span x-text="item.track.release_title"></span>
<span x-show="item.track.track_number">·</span>
<span x-show="item.track.track_number" x-text="'#' + item.track.track_number"></span>
</div>
</div>
<div class="upload-track-actions">
<button class="track-action-btn" @click="$store.queue.addNextInQueue([item.track])" title="{{ t.player_play_next }}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 6h14M5 12h8M5 18h14"/><path d="M17 10l4 3-4 3" fill="currentColor" stroke="none"/></svg>
</button>
<button class="modal-btn modal-btn-ghost" @click="$store.torrents.editUpload(item)">Edit</button>
</div>
</div>
</template>
<template x-if="$store.torrents.uploadEditId === item.track.id">
<div class="upload-edit-form">
<label>
<span>Title</span>
<input type="text" x-model="$store.torrents.uploadDraft.title">
</label>
<label>
<span>Artists</span>
<input type="text" x-model="$store.torrents.uploadDraft.artists" placeholder="Artist, Featured Artist">
</label>
<label>
<span>Release</span>
<input type="text" x-model="$store.torrents.uploadDraft.release_title">
</label>
<label>
<span>Type</span>
<select x-model="$store.torrents.uploadDraft.release_type">
<option value="album">Album</option>
<option value="single">Single</option>
<option value="ep">EP</option>
<option value="compilation">Compilation</option>
<option value="mixtape">Mixtape</option>
<option value="live">Live</option>
<option value="soundtrack">Soundtrack</option>
<option value="remix">Remix</option>
<option value="demo">Demo</option>
</select>
</label>
<label>
<span>Year</span>
<input type="number" x-model="$store.torrents.uploadDraft.release_year">
</label>
<label>
<span>Track #</span>
<input type="number" min="1" x-model="$store.torrents.uploadDraft.track_number">
</label>
<label>
<span>Disc #</span>
<input type="number" min="1" x-model="$store.torrents.uploadDraft.disc_number">
</label>
<label class="upload-hidden-toggle">
<input type="checkbox" x-model="$store.torrents.uploadDraft.is_hidden">
<span>Hidden</span>
</label>
<div class="upload-edit-actions">
<button class="modal-btn modal-btn-primary"
@click="$store.torrents.saveUploadEdit()"
:disabled="$store.torrents.uploadSavingId === item.track.id">Save</button>
<button class="modal-btn modal-btn-ghost" @click="$store.torrents.cancelUploadEdit()">Cancel</button>
</div>
</div>
</template>
</div>
</template>
</div>
</section>
</template>
</div>
</div>
</template>
<!-- Play History Modal -->
<template x-if="$store.history.modal">
<div class="modal-overlay" @click.self="$store.history.close()">
<div class="modal-box history-modal">
<h3>{{ t.player_play_history }}</h3>
<p class="torrent-message" :class="{ error: $store.history.error }"
x-text="$store.history.message"></p>
<div class="history-list">
<template x-if="!$store.history.loading && $store.history.items.length === 0">
<div class="empty-state" style="padding:32px 16px">
<p>{{ t.player_no_plays_yet }}</p>
</div>
</template>
<template x-for="item in $store.history.items" :key="item.id">
<div class="history-row">
<div style="min-width:0">
<div class="history-title" x-text="item.track_title"></div>
<div class="history-release" x-text="item.release_title || '{{ t.player_unknown_release }}'"></div>
</div>
<div>
<div class="history-date" x-text="$store.history.date(item.played_at)"></div>
<div class="history-duration" x-text="$store.history.duration(item.duration_listened)"></div>
</div>
</div>
</template>
</div>
<div class="history-pager">
<button class="modal-btn modal-btn-ghost"
@click="$store.history.load($store.history.page - 1)"
:disabled="$store.history.loading || $store.history.page <= 1">
{{ t.player_previous }}
</button>
<span class="history-release"
x-text="'{{ t.player_page }} ' + $store.history.page + ' {{ t.player_of }} ' + $store.history.totalPages()"></span>
<button class="modal-btn modal-btn-primary"
@click="$store.history.load($store.history.page + 1)"
:disabled="$store.history.loading || $store.history.page >= $store.history.totalPages()">
{{ t.player_next }}
</button>
</div>
</div>
</div>
</template>
</div>