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]