Skip to content

Commit

Permalink
Ignore lists and other tweaks
Browse files Browse the repository at this point in the history
Closes #43
  • Loading branch information
Rafiuth committed Aug 11, 2022
1 parent 6f421ce commit 7aa9ee5
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 44 deletions.
4 changes: 2 additions & 2 deletions SpotifyOggDumper/Data/Install.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$SpotifyInstallerUrl = "https://upgrade.scdn.co/upgrade/client/win32-x86/spotify_installer-1.1.90.855.ga73cc2ce-7.exe"
$SpotifyVersion = "1.1.90.855"
$SpotifyInstallerUrl = "https://upgrade.scdn.co/upgrade/client/win32-x86/spotify_installer-1.1.90.859.gf1bb1e36-11.exe"
$SpotifyVersion = "1.1.90.859"
$BtsUrl = "https://github.com/mrpond/BlockTheSpot/releases/download/2021.10.29.44/chrome_elf.zip"

function InstallSpotify
Expand Down
17 changes: 13 additions & 4 deletions SpotifyOggDumper/StateManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,23 @@ struct StateManagerImpl : public StateManager
_dataDir(dataDir),
_ctrlSv(std::bind(&StateManagerImpl::HandleMessage, this, std::placeholders::_1, std::placeholders::_2))
{
_configPath = Utils::GetAppDataFolder() / "Soggfy/config.json";
_configPath = _dataDir / "config.json";

fs::path currConfigPath = _dataDir / "config.json";
std::ifstream configFile(fs::exists(currConfigPath) ? currConfigPath : _configPath);
auto appDataConfigPath = Utils::GetAppDataFolder() / "Soggfy/config.json";
if (fs::exists(appDataConfigPath)) {
fs::copy_file(appDataConfigPath, _configPath);

std::error_code error;
fs::remove_all(appDataConfigPath.parent_path(), error);
}

std::ifstream configFile(_configPath);
if (configFile.good()) {
_config = json::parse(configFile, nullptr, true, true);
} else {
_config = json::object();
fs::create_directories(_configPath.parent_path());
}

fs::path baseSavePath = _config.value("/savePaths/basePath"_json_pointer, fs::path());
if (!fs::exists(baseSavePath)) {
baseSavePath = Utils::GetMusicFolder() / "Soggfy";
Expand Down Expand Up @@ -133,6 +139,9 @@ struct StateManagerImpl : public StateManager
auto playback = GetPlayback(content["playbackId"]);
if (!playback) break;

if (content.value("ignore", false)) {
DiscardTrack(*playback, "Ignored");
}
conn->Send(MessageType::DOWNLOAD_STATUS, {
{ "playbackId", playback->Id },
{ "status", playback->Status.first },
Expand Down
3 changes: 2 additions & 1 deletion Sprinkles/dev_build.bat
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
npm run build & copy dist\bundle.js %appdata%\Spotify\Apps\xpui\extensions\Soggfy.js /y
rem npm run build & copy dist\bundle.js %appdata%\Spotify\Apps\xpui\extensions\Soggfy.js /y
npm run build & copy dist\bundle.js ..\build\Debug\Soggfy.js /y & cmd /c "cd ..\build\Debug & Injector.exe -d"
15 changes: 14 additions & 1 deletion Sprinkles/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ let config = {
canvas: "{artist_name}/{album_name}{multi_disc_path}/Canvas/{track_num}. {track_name}.mp4",
invalidCharRepl: "unicode", //nasty global, used in PathTemplate.escapePath()
},
//Resource URIs to be ignored by the downloader.
ignorelist: {
},
blockAds: true
};
export default config;
export default config;

export function isTrackIgnored(track) {
let tieUris = [
track.uri,
track.album?.uri ?? track.albumUri,
track.metadata?.context_uri ?? track.contextUri,
...(track.artists ?? [])
];
return tieUris.some(res => config.ignorelist[res?.uri ?? res]);
}
6 changes: 3 additions & 3 deletions Sprinkles/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function onMessage(type: MessageType, payload: any)
failed: true,
message: ex.message
});
setPlaybackStatusInd(payload.playbackId, { status: DownloadStatus.ERROR, message: ex.message });
setPlaybackStatusInd(payload.playbackId, { status: DownloadStatus.Error, message: ex.message });
})
.finally(() => {
playbackTracker.remove(payload.playbackId);
Expand All @@ -48,7 +48,7 @@ function onMessage(type: MessageType, payload: any)

let val = {
...results[key],
status: DownloadStatus.WARN,
status: DownloadStatus.Warn,
message: "Different tracks mapping to the same file name"
};
for (let subkey of key.split(',')) {
Expand All @@ -68,7 +68,7 @@ function setPlaybackStatusInd(playbackId: string, data: TrackStatus)
if (info) {
statusIndicator.updateRows({ [info.uri]: data });
}
if (data.status === DownloadStatus.ERROR || data.status === DownloadStatus.DONE) {
if (data.status === DownloadStatus.Error || data.status === DownloadStatus.Done) {
playbackTracker.remove(playbackId);
}
}
4 changes: 2 additions & 2 deletions Sprinkles/src/player-state-tracker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Utils from "./utils";
import { Player, PlayerState, TrackInfo, SpotifyUtils } from "./spotify-apis";
import Resources from "./resources";
import config from "./config";
import config, { isTrackIgnored } from "./config";
import { PathTemplate, TemplatedSearchTree } from "./path-template";
import { Connection, MessageType } from "./connection";

Expand All @@ -18,7 +18,7 @@ export default class PlayerStateTracker
if (!data.playbackId) return;

if (!this._playbacks.has(data.playbackId)) {
conn.send(MessageType.DOWNLOAD_STATUS, { playbackId: data.playbackId });
conn.send(MessageType.DOWNLOAD_STATUS, { playbackId: data.playbackId, ignore: isTrackIgnored(data.item) });
}
this._playbacks.set(data.playbackId, data);
});
Expand Down
34 changes: 20 additions & 14 deletions Sprinkles/src/ui/status-indicator.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import config from "../config"
import config, { isTrackIgnored } from "../config"
import { Connection, MessageType } from "../connection";
import { Icons, Selectors } from "./ui";
import Utils from "../utils";
import { TemplatedSearchTree } from "../path-template";

export enum DownloadStatus
export const enum DownloadStatus
{
ERROR = "ERROR",
IN_PROGRESS = "IN_PROGRESS",
CONVERTING = "CONVERTING",
WARN = "WARN",
DONE = "DONE"
Error = "ERROR",
InProgress = "IN_PROGRESS",
Converting = "CONVERTING",
Warn = "WARN",
Done = "DONE",
Ignored = "IGNORED",
}
export interface TrackStatus
{
status: DownloadStatus,
path?: string; //if status == DONE
message?: string;
}
//From https://fonts.google.com/icons
const StatusIcons = {
[DownloadStatus.ERROR]: Icons.Error,
[DownloadStatus.IN_PROGRESS]: Icons.InProgress,
[DownloadStatus.CONVERTING]: Icons.Processing,
[DownloadStatus.WARN]: Icons.Warning,
[DownloadStatus.DONE]: Icons.Done
[DownloadStatus.Error]: Icons.Error,
[DownloadStatus.InProgress]: Icons.InProgress,
[DownloadStatus.Converting]: Icons.Processing,
[DownloadStatus.Warn]: Icons.Warning,
[DownloadStatus.Done]: Icons.Done,
[DownloadStatus.Ignored]: Icons.SyncDisabled
};

export class StatusIndicator
Expand Down Expand Up @@ -69,6 +70,10 @@ export class StatusIndicator
let row = rowWrapper.firstElementChild;
let trackInfo = this.getRowTrackInfo(row, listSection);
let info = map[trackInfo?.uri];

if (!info && trackInfo && isTrackIgnored(trackInfo.extraProps)) {
info = { status: DownloadStatus.Ignored, message: "Ignored" };
}
if (!info) continue;

let node = row["__sgf_status_ind"] ??= document.createElement("div");
Expand All @@ -80,7 +85,7 @@ export class StatusIndicator
node.className = "sgf-status-indicator";
node.innerHTML = `
<div class="sgf-status-indicator-card">
${info.status == DownloadStatus.DONE
${info.status == DownloadStatus.Done
? `
<div class="sgf-status-browse-button">
<svg width="24" height="24" viewBox="0 0 24 24" fill="#ddd">
Expand Down Expand Up @@ -138,6 +143,7 @@ ${StatusIcons[info.status]}`;

return {
uri: extraProps.uri,
extraProps,
vars: {
track_name: row.querySelector(Selectors.rowTitle).innerText,
artist_name: extraProps.artists[0].name,
Expand Down
54 changes: 37 additions & 17 deletions Sprinkles/src/ui/ui.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import config from "../config";
import config, { isTrackIgnored } from "../config";
import { Connection, MessageType } from "../connection";
import Utils from "../utils";
import UIC from "./components";
import { Platform, SpotifyUtils } from "../spotify-apis";
import { Platform, Player, SpotifyUtils } from "../spotify-apis";
import Resources from "../resources";

import ComponentsStyle from "./css/components.css";
Expand All @@ -12,6 +12,7 @@ import { PathTemplate, TemplatedSearchTree } from "../path-template";

const MergedStyles = [ComponentsStyle, SettingsStyle, StatusIndicatorStyle].join('\n'); //TODO: find a better way to do this

//From https://fonts.google.com/icons
export const Icons = {
Folder: `<svg width="24px" height="24px" viewBox="0 0 24 24" fill="currentColor"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"></path></svg>`,
Sliders: `<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z"></path></svg>`,
Expand All @@ -21,7 +22,8 @@ export const Icons = {
InProgress: `<svg width="18" height="18" viewBox="0 0 24 24" fill="#29f"><path d="M18.32,4.26C16.84,3.05,15.01,2.25,13,2.05v2.02c1.46,0.18,2.79,0.76,3.9,1.62L18.32,4.26z M19.93,11h2.02 c-0.2-2.01-1-3.84-2.21-5.32L18.31,7.1C19.17,8.21,19.75,9.54,19.93,11z M18.31,16.9l1.43,1.43c1.21-1.48,2.01-3.32,2.21-5.32 h-2.02C19.75,14.46,19.17,15.79,18.31,16.9z M13,19.93v2.02c2.01-0.2,3.84-1,5.32-2.21l-1.43-1.43 C15.79,19.17,14.46,19.75,13,19.93z M15.59,10.59L13,13.17V7h-2v6.17l-2.59-2.59L7,12l5,5l5-5L15.59,10.59z M11,19.93v2.02 c-5.05-0.5-9-4.76-9-9.95s3.95-9.45,9-9.95v2.02C7.05,4.56,4,7.92,4,12S7.05,19.44,11,19.93z"/></svg>`,
Processing: `<svg width="18" height="18" viewBox="0 0 24 24" fill="#ddd"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"><animateTransform attributeName="transform" attributeType="XML" type="rotate" from="360 12 12" to="0 12 12" dur="3s" repeatCount="indefinite"/></path></svg>`,
Warning: `<svg width="18" height="18" viewBox="0 0 24 24" fill="#fbd935"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>`,

SyncDisabled: `<svg height="20" width="20" viewBox="0 0 24 25" fill="#bbb"><path d="m19.8 22.6-3.725-3.725q-.475.275-.987.5-.513.225-1.088.375v-2.1q.15-.05.3-.112.15-.063.3-.138l-8-8q-.275.625-.438 1.288Q6 11.35 6 12.05q0 1.125.425 2.187Q6.85 15.3 7.75 16.2l.25.25V14h2v6H4v-2h2.75l-.4-.35q-1.225-1.225-1.788-2.662Q4 13.55 4 12.05q0-1.125.287-2.163.288-1.037.838-1.962L1.4 4.2l1.425-1.425 18.4 18.4Zm-.875-6.575-1.5-1.5q.275-.6.425-1.25.15-.65.15-1.325 0-1.125-.425-2.188Q17.15 8.7 16.25 7.8L16 7.55V10h-2V4h6v2h-2.75l.4.35q1.225 1.225 1.788 2.662Q20 10.45 20 11.95q0 1.125-.288 2.137-.287 1.013-.787 1.938Zm-9.45-9.45-1.5-1.5Q8.45 4.8 8.95 4.6q.5-.2 1.05-.35v2.1q-.125.05-.262.1-.138.05-.263.125Z"/></svg>`,

DoneBig: `<svg width="24" height="24" viewBox="0 0 24 24" fill="#3f3"><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>`,
ErrorBig: `<svg width="24" height="24" viewBox="0 0 24 24" fill="#f22"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>`,

Expand Down Expand Up @@ -78,7 +80,7 @@ export default class UI
return div;
}

private async createM3U(uri: string)
private async createM3U(uri: string, trackUris?: string[])
{
let info = await Resources.getTracks(uri);
let tracks = info.tracks;
Expand All @@ -93,6 +95,7 @@ export default class UI

let tree = new TemplatedSearchTree(config.savePaths.track);
for (let track of tracks) {
if (trackUris?.length >= 2 && !trackUris.includes(track.uri)) continue;
tree.add(track.uri, track.vars);
}

Expand Down Expand Up @@ -258,34 +261,51 @@ export default class UI
private onContextMenuOpened(menuList: Element)
{
const HookDescs = [
(uri) => ({
(contextUri, trackUris) => ({
text: "Export M3U",
offset: "beforebegin",
onClick: () => this.createM3U(uri)
})
onClick: () => this.createM3U(contextUri, trackUris)
}),
(contextUri, trackUris) => {
let uris = trackUris?.length > 0 ? trackUris : [contextUri];
let ignored = uris.some(uri => config.ignorelist[uri]);

return {
text: `${ignored ? "Unignore" : "Ignore"} ${uris[0].split(':')[1]}${uris.length > 1 ? "s" : ""}`,
onClick: () => {
for (let uri of uris) {
ignored ? delete config.ignorelist[uri] : config.ignorelist[uri] = 1;
}
this._conn.send(MessageType.DOWNLOAD_STATUS, {
playbackId: Player.getState().playbackId,
ignore: isTrackIgnored(Player.getState().item)
});
this.updateConfig("ignorelist", config.ignorelist);
}
}
}
];

for (let menuItem of menuList.children) {
let props = Utils.getReactProps(menuList, menuItem);
let isTarget = props && (
(props.contextUri && props.highlightedUri) || //Track: Show credits
(props.uri && props.hasOwnProperty("onRemoveCallback")) || //Album: Add/remove to library
(props.uri && props.description != null) //Playlist: Go to playlist radio
(props.contextUri && (props.highlightedUri || props.uris)) || //Track: Show credits
(props.uri && props.hasOwnProperty("onRemoveCallback")) || //Album: Add/remove to library
(props.uri && props.description != null) //Playlist: Go to playlist radio
);
if (isTarget) {
let uri = props.contextUri ?? props.uri;
let contextUri = props.contextUri ?? props.uri;
let trackUris = props.highlightedUri ? [props.highlightedUri] : props.uris;

for (let descFactory of HookDescs) {
var desc = descFactory(uri);

let itemTemplate = menuList.querySelector("li button[aria-disabled='false'] span").parentElement.parentElement;
let item = itemTemplate.cloneNode(true) as HTMLLIElement;
let desc = descFactory(contextUri, trackUris);
let item = menuList.querySelector("li button:not([aria-disabled='true']) span").parentElement.parentElement.cloneNode(true) as HTMLLIElement;
item.querySelector("span").innerText = desc.text;
item.querySelector("button").classList.remove("QgtQw2NJz7giDZxap2BB"); //separator class
item.querySelector("button").onclick = () => {
desc.onClick();
menuList.parentElement.parentElement["_tippy"]?.props?.onClickOutside();
};
menuItem.insertAdjacentElement(desc.offset as any ?? "afterend", item);
menuItem.insertAdjacentElement("beforebegin", item);
}
menuList["_sgf_handled"] = true; //add mark to prevent this method from being fired multiple times
break;
Expand Down

0 comments on commit 7aa9ee5

Please sign in to comment.