Spaces:
Running
Running
File size: 17,997 Bytes
6c3a31d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | """Auto formats in the app: the /start menu's Auto Format screen, Use for auto rename, copying a format to the other kind,
Apply default metadata, and what the files are renamed with."""
import pytest
from bot.db import MAX_FORMATS
from bot.formats import apply_format
from .appkit import ADMIN_CFG, EPISODE_NEW, OWNER, autofmt, click, data_of, jpeg, prefs, say, send_doc
from .test_formats_app import ONE_PIECE, labels_of, make_format
DEFAULT_META = {"title": "My Channel", "author": "@MyChannel", "artist": "", "audio": "{lang} Audio", "subtitle": "{lang}", "video": "HD"}
async def set_default_metadata(ctx, **over):
await ctx.db.update_settings(OWNER, meta=dict(DEFAULT_META), keep_audio=["japanese", "english"], keep_subtitle=["english"], **over)
def latest_job(ctx):
return list(ctx.app.queue.jobs.values())[-1]
# ---------------------------------------------------------------------------
# the Auto Format screen of the /start menu
# ---------------------------------------------------------------------------
async def test_the_start_menu_opens_the_auto_format_screen(ctx):
ev = await say(ctx, OWNER, "/start")
text, buttons, _kw = ev.out[0]
assert "<b>Auto format:</b> none saved" in text and "m:auto" in data_of(buttons) and "Auto Format" in labels_of(buttons)
cb = await click(ctx, OWNER, "m:auto")
text, buttons = cb.edits[-1]
assert "AUTO RENAME FORMAT" in text and "no auto formats yet" in text and cb.answers
data = data_of(buttons)
assert {"fm:new:auto", "fm:l:auto", "m:home", "x:close"} <= set(data) and not any(d.startswith("au:") for d in data)
async def test_the_screen_lists_the_auto_formats_and_marks_the_one_in_use(ctx):
a = await make_format(ctx, "One Piece", kind="auto", template="OP - E{episode}")
b = await make_format(ctx, "Dragon Ball", kind="auto", template="DB - E{episode}")
manual = await make_format(ctx, "Manual one")
cb = await click(ctx, OWNER, "m:auto")
text, buttons = cb.edits[-1]
data = data_of(buttons)
assert f"au:{a['id']}" in data and f"au:{b['id']}" in data and f"au:{manual['id']}" not in data
assert labels_of(buttons)[:2] == ["One Piece β
", "Dragon Ball"] # the first one is in use from the start
assert f"fm:v:{a['id']}" in data # "Edit the one in use"
cb = await click(ctx, OWNER, f"au:{b['id']}")
assert cb.toasts == ["Auto format: Dragon Ball"] and labels_of(cb.edits[-1][1])[:2] == ["One Piece", "Dragon Ball β
"]
assert (await prefs(ctx))["auto_format"] == b["id"] and (await autofmt(ctx))["id"] == b["id"]
data = data_of(cb.edits[-1][1])
assert f"fm:v:{b['id']}" in data and f"fm:v:{a['id']}" not in data # the edit button follows the choice
ev = await say(ctx, OWNER, "/start") # and the menu shows what is in use
assert "<b>Auto format:</b> <b>Dragon Ball</b>" in ev.texts[0] and "DB - E{episode}" in ev.texts[0]
async def test_a_stale_or_foreign_choice_is_refused(ctx):
manual = await make_format(ctx, "Manual one")
theirs = await make_format(ctx, "Theirs", uid=ADMIN_CFG, kind="auto")
await make_format(ctx, "Mine", kind="auto")
for data in (f"au:{manual['id']}", f"au:{theirs['id']}", "au:999999", "au:x", "au"):
cb = await click(ctx, OWNER, data)
assert cb.alerts == ["That format no longer exists."] and "AUTO RENAME FORMAT" in cb.edits[-1][0], data
assert (await autofmt(ctx))["name"] == "Mine"
async def test_each_user_has_their_own_auto_format(ctx):
mine = await make_format(ctx, "Mine", kind="auto", template="Mine - {title}")
theirs = await make_format(ctx, "Theirs", uid=ADMIN_CFG, kind="auto", template="Theirs - {title}")
await send_doc(ctx, OWNER, ONE_PIECE, mid=50)
await send_doc(ctx, ADMIN_CFG, ONE_PIECE, mid=51)
jobs = {j.user_id: j for j in ctx.app.queue.jobs.values()}
assert jobs[OWNER].new_name == "Mine - One Piece.mkv" and jobs[OWNER].fmt["id"] == mine["id"]
assert jobs[ADMIN_CFG].new_name == "Theirs - One Piece.mkv" and jobs[ADMIN_CFG].fmt["id"] == theirs["id"]
# ---------------------------------------------------------------------------
# what the files are renamed with
# ---------------------------------------------------------------------------
async def test_the_auto_format_in_use_renames_every_file_sent(ctx):
a = await make_format(ctx, "One Piece", kind="auto", template="OP - E{episode}")
b = await make_format(ctx, "Dragon Ball", kind="auto", template="DB - E{episode}")
ev = await send_doc(ctx, OWNER, ONE_PIECE, mid=50)
job = latest_job(ctx)
assert job.new_name == "OP - E1147.mkv" and job.fmt["id"] == a["id"] and job.fmt["kind"] == "auto"
assert "π Auto format: <b>One Piece</b>" in ev.texts[0]
await click(ctx, OWNER, f"au:{b['id']}")
ev = await send_doc(ctx, OWNER, ONE_PIECE, mid=51)
job = latest_job(ctx)
assert job.new_name == "DB - E1147.mkv" and job.fmt["id"] == b["id"] and "π Auto format: <b>Dragon Ball</b>" in ev.texts[0]
async def test_without_an_auto_format_files_get_the_built_in_template(ctx):
await make_format(ctx, "Manual one") # a manual format is never used by itself
ev = await send_doc(ctx, OWNER)
(job,) = ctx.app.queue.jobs.values()
assert job.fmt is None and job.new_name == EPISODE_NEW and "Auto format" not in ev.texts[0]
async def test_the_padded_episode_variables_in_an_auto_format(ctx):
await make_format(ctx, "Padded", kind="auto", template="{title} - {1episode} {2episode} {3episode}")
for mid, name, expected in (
(50, "One Piece - 4 [1080p].mkv", "One Piece - 04 004 0004.mkv"),
(51, "One Piece - 321 [1080p].mkv", "One Piece - 321 321 0321.mkv"),
(52, "One Piece - 1321 [1080p].mkv", "One Piece - 1321 1321 1321.mkv"),
):
await send_doc(ctx, OWNER, name, mid=mid)
assert latest_job(ctx).new_name == expected
async def test_the_first_auto_format_is_used_at_once_and_deleting_the_one_in_use_hands_over(ctx):
await click(ctx, OWNER, "fm:new:auto", msg_id=500)
await say(ctx, OWNER, "One Piece")
(a,) = await ctx.db.list_formats(OWNER, "auto")
_chat, text, kw = ctx.client.edited[-1]
assert "β
In use" in text and f"fm:use:{a['id']}" not in data_of(kw["buttons"])
assert (await autofmt(ctx))["id"] == a["id"]
await make_format(ctx, "Dragon Ball", kind="auto")
await click(ctx, OWNER, f"fm:del:{a['id']}")
cb = await click(ctx, OWNER, f"fm:ok:{a['id']}") # the one in use goes: the other takes over
assert "AUTO FORMATS" in cb.edits[-1][0] and "Dragon Ball β
" in labels_of(cb.edits[-1][1])
last = await autofmt(ctx)
assert last["name"] == "Dragon Ball"
await click(ctx, OWNER, f"fm:ok:{last['id']}") # the last one goes: back to the built-in defaults
assert await autofmt(ctx) is None
await send_doc(ctx, OWNER)
(job,) = ctx.app.queue.jobs.values()
assert job.fmt is None and job.new_name == EPISODE_NEW
async def test_use_for_auto_rename_button(ctx):
a = await make_format(ctx, "One Piece", kind="auto")
b = await make_format(ctx, "Dragon Ball", kind="auto")
cb = await click(ctx, OWNER, f"fm:v:{a['id']}")
assert "β
In use" in cb.edits[-1][0] and f"fm:use:{a['id']}" not in data_of(cb.edits[-1][1])
cb = await click(ctx, OWNER, f"fm:v:{b['id']}")
text, buttons = cb.edits[-1]
assert "Not in use" in text and f"fm:use:{b['id']}" in data_of(buttons) and "β
Use for auto rename" in labels_of(buttons)
cb = await click(ctx, OWNER, f"fm:use:{b['id']}")
text, buttons = cb.edits[-1]
assert cb.toasts == ["Auto format: Dragon Ball"] and "now renames your files" in text and "β
In use" in text
assert f"fm:use:{b['id']}" not in data_of(buttons) and (await autofmt(ctx))["id"] == b["id"]
cb = await click(ctx, OWNER, f"fm:v:{a['id']}") # the other one can be used again
assert f"fm:use:{a['id']}" in data_of(cb.edits[-1][1])
async def test_a_manual_format_cannot_be_used_for_auto_rename(ctx):
auto = await make_format(ctx, "Auto one", kind="auto")
manual = await make_format(ctx, "Manual one")
cb = await click(ctx, OWNER, f"fm:v:{manual['id']}")
assert f"fm:use:{manual['id']}" not in data_of(cb.edits[-1][1]) and "Use it with /rename" in cb.edits[-1][0]
cb = await click(ctx, OWNER, f"fm:use:{manual['id']}") # a stale button
assert cb.alerts == ["Only an auto format can be used for auto rename."] and cb.edits == []
assert (await autofmt(ctx))["id"] == auto["id"]
# ---------------------------------------------------------------------------
# copying a format to the other kind
# ---------------------------------------------------------------------------
@pytest.mark.parametrize("source, target", [("auto", "manual"), ("manual", "auto")])
async def test_a_format_can_be_copied_to_the_other_kind(ctx, source, target):
fmt = await make_format(ctx, "One Piece", kind=source, meta={"title": "OP"}, keep_audio=["japanese"], caption="<i>{filename}</i>")
await ctx.db.set_format_thumb(OWNER, fmt["id"], jpeg())
cb = await click(ctx, OWNER, f"fm:v:{fmt['id']}")
assert f"fm:cp:{fmt['id']}" in data_of(cb.edits[-1][1]) and f"π Copy to {target} formats" in labels_of(cb.edits[-1][1])
cb = await click(ctx, OWNER, f"fm:cp:{fmt['id']}")
(copy,) = await ctx.db.list_formats(OWNER, target)
assert copy["id"] != fmt["id"] and copy["kind"] == target and copy["name"] == "One Piece"
assert copy["template"] == "One Piece - E{episode}" and copy["meta"]["title"] == "OP" and copy["keep_audio"] == ["japanese"]
assert copy["caption"] == "<i>{filename}</i>" and await ctx.db.get_format_thumb(OWNER, copy["id"]) == jpeg()
text, buttons = cb.edits[-1]
assert cb.toasts == [f"Copied to your {target} formats."] and f"is copied to your {target} formats" in text
assert data_of(buttons) == [f"fm:v:{copy['id']}", f"fm:v:{fmt['id']}", "x:close"]
assert (await ctx.db.get_format(OWNER, fmt["id"]))["kind"] == source and await ctx.db.count_formats(OWNER, source) == 1
cb = await click(ctx, OWNER, f"fm:v:{copy['id']}") # "Open the copy": it is a format of the other kind
assert f"{'π€ Auto' if target == 'auto' else 'β Manual'}" in cb.edits[-1][0].splitlines()[0]
async def test_copies_are_independent_and_get_a_free_name(ctx):
fmt = await make_format(ctx, "One Piece", kind="auto")
await click(ctx, OWNER, f"fm:cp:{fmt['id']}")
await click(ctx, OWNER, f"fm:cp:{fmt['id']}")
first, second = await ctx.db.list_formats(OWNER, "manual")
assert [first["name"], second["name"]] == ["One Piece", "One Piece (2)"]
await click(ctx, OWNER, f"fm:cp:{first['id']}") # and back again: "One Piece" is taken among the auto formats
back = (await ctx.db.list_formats(OWNER, "auto"))[-1]
assert back["name"] == "One Piece (2)" and back["kind"] == "auto" and await ctx.db.count_formats(OWNER, "auto") == 2
await click(ctx, OWNER, f"fm:in:{first['id']}:template")
await say(ctx, OWNER, "Changed {title}")
assert (await ctx.db.get_format(OWNER, first["id"]))["template"] == "Changed {title}"
assert (await ctx.db.get_format(OWNER, fmt["id"]))["template"] == "One Piece - E{episode}"
assert (await ctx.db.get_format(OWNER, second["id"]))["template"] == "One Piece - E{episode}"
@pytest.mark.parametrize("kind", ["auto", "manual"])
async def test_copying_to_a_full_kind_is_refused_and_the_kinds_count_apart(ctx, kind):
other = "manual" if kind == "auto" else "auto"
fmt = await make_format(ctx, "One Piece", kind=kind)
for i in range(MAX_FORMATS):
await make_format(ctx, f"F{i}", kind=other)
cb = await click(ctx, OWNER, f"fm:cp:{fmt['id']}")
assert cb.alerts == [f"You already have {MAX_FORMATS} {other} formats. Delete one first."] and cb.edits == []
assert await ctx.db.count_formats(OWNER, other) == MAX_FORMATS and await ctx.db.count_formats(OWNER, kind) == 1
async def test_a_copy_shows_up_where_its_kind_is_used(ctx):
auto = await make_format(ctx, "From auto", kind="auto")
manual = await make_format(ctx, "From manual")
await click(ctx, OWNER, f"fm:cp:{auto['id']}") # a manual copy of the auto format
await click(ctx, OWNER, f"fm:cp:{manual['id']}") # an auto copy of the manual one
assert {"From manual", "From auto"} <= set(labels_of((await say(ctx, OWNER, "/rename")).out[0][1]))
labels = labels_of((await click(ctx, OWNER, "m:auto")).edits[-1][1])
assert "From auto β
" in labels and "From manual" in labels
async def test_an_auto_format_made_by_copying_is_the_first_one_and_renames_the_files(ctx):
manual = await make_format(ctx, "One Piece")
assert await autofmt(ctx) is None
await click(ctx, OWNER, f"fm:cp:{manual['id']}")
auto = await autofmt(ctx)
assert auto["kind"] == "auto" and auto["name"] == "One Piece"
ev = await send_doc(ctx, OWNER, ONE_PIECE, mid=50)
assert "π Auto format: <b>One Piece</b>" in ev.texts[0] and latest_job(ctx).new_name == "One Piece - E1147.mkv"
# ---------------------------------------------------------------------------
# Apply default metadata
# ---------------------------------------------------------------------------
@pytest.mark.parametrize("kind", ["auto", "manual"])
async def test_apply_default_metadata_copies_all_of_it_into_the_format(ctx, kind):
await set_default_metadata(ctx) # the metadata switch is Off: copying does not depend on it
fmt = await make_format(
ctx, "One Piece", kind=kind, meta={"title": "Old title", "artist": "Old artist"}, keep_audio=["french"], keep_subtitle=["2"]
)
cb = await click(ctx, OWNER, f"fm:dm:{fmt['id']}")
got = await ctx.db.get_format(OWNER, fmt["id"])
assert got["meta"] == DEFAULT_META and got["keep_audio"] == ["japanese", "english"] and got["keep_subtitle"] == ["english"]
assert got["template"] == "One Piece - E{episode}" and got["kind"] == kind # nothing else changed
text, buttons = cb.edits[-1]
assert cb.toasts == ["Default metadata applied."] and "Your default metadata is now in <b>One Piece</b>" in text
assert "<b>Title</b> βΈ <code>My Channel</code>" in text and "Japanese, English" in text and f"fm:dm:{fmt['id']}" in data_of(buttons)
s = await prefs(ctx) # the default metadata itself is untouched, and the format is not linked to it afterwards
assert s["meta"] == DEFAULT_META and s["keep_audio"] == ["japanese", "english"] and s["metadata_on"] is False
await ctx.db.update_settings(OWNER, meta={"title": "Changed later"})
assert (await ctx.db.get_format(OWNER, fmt["id"]))["meta"]["title"] == "My Channel"
@pytest.mark.parametrize("kind", ["auto", "manual"])
async def test_a_new_format_can_take_the_default_metadata_straight_away(ctx, kind):
await set_default_metadata(ctx)
await click(ctx, OWNER, f"fm:new:{kind}", msg_id=500)
await say(ctx, OWNER, "Fresh")
(fmt,) = await ctx.db.list_formats(OWNER)
_chat, text, kw = ctx.client.edited[-1]
assert "π· Apply default metadata" in labels_of(kw["buttons"]) and "Apply default metadata" in text
assert fmt["meta"]["title"] == ""
await click(ctx, OWNER, f"fm:dm:{fmt['id']}")
assert (await ctx.db.get_format(OWNER, fmt["id"]))["meta"] == DEFAULT_META
@pytest.mark.parametrize("kind", ["auto", "manual"])
async def test_apply_default_metadata_says_so_when_there_is_none(ctx, kind):
fmt = await make_format(ctx, "One Piece", kind=kind, meta={"title": "Mine"})
await ctx.db.update_settings(OWNER, metadata_on=True) # the switch alone is not metadata
cb = await click(ctx, OWNER, f"fm:dm:{fmt['id']}")
assert cb.alerts == ["Your default metadata is empty. Set it in /metadata first, then apply it."] and cb.edits == []
assert (await ctx.db.get_format(OWNER, fmt["id"]))["meta"]["title"] == "Mine" # nothing was wiped
async def test_track_lists_alone_count_as_default_metadata(ctx):
await ctx.db.update_settings(OWNER, keep_audio=["english"])
fmt = await make_format(ctx, kind="auto")
cb = await click(ctx, OWNER, f"fm:dm:{fmt['id']}")
assert cb.toasts == ["Default metadata applied."] and (await ctx.db.get_format(OWNER, fmt["id"]))["keep_audio"] == ["english"]
async def test_a_file_renamed_with_a_format_that_took_the_default_metadata_gets_it(ctx):
await set_default_metadata(ctx)
fmt = await make_format(ctx)
await click(ctx, OWNER, f"fm:dm:{fmt['id']}")
await click(ctx, OWNER, f"rn:p:{fmt['id']}")
await send_doc(ctx, OWNER, ONE_PIECE, mid=50)
job = latest_job(ctx)
assert job.fmt["meta"]["title"] == "My Channel" and job.fmt["keep_audio"] == ["japanese", "english"]
out = apply_format(await prefs(ctx), job.fmt)
assert out["metadata_on"] is True and out["meta"]["video"] == "HD" # written, although the default switch is Off
async def test_an_auto_format_says_whether_the_default_metadata_applies_to_it(ctx):
auto = await make_format(ctx, "Auto one", kind="auto")
manual = await make_format(ctx, "Manual one")
cb = await click(ctx, OWNER, f"fm:v:{auto['id']}")
assert "No metadata of its own: your default metadata is used while it is On" in cb.edits[-1][0]
assert "your default metadata is used" not in (await click(ctx, OWNER, f"fm:v:{manual['id']}")).edits[-1][0] # a manual one never uses it
await ctx.db.update_format(OWNER, auto["id"], meta={"title": "Own"})
assert "your default metadata is used" not in (await click(ctx, OWNER, f"fm:v:{auto['id']}")).edits[-1][0]
async def test_the_metadata_screen_calls_it_default_metadata_and_points_to_the_button(ctx):
cb = await click(ctx, OWNER, "m:meta")
text = cb.edits[-1][0]
assert "Your default metadata is currently: Off" in text and "Apply default metadata" in text and "global" not in text.lower()
await click(ctx, OWNER, "set:meta:on")
assert (await prefs(ctx))["metadata_on"] is True # the On/Off switch and the /metadata commands are unchanged
assert "currently: On" in (await say(ctx, OWNER, "/metadata")).texts[0]
|