diff --git a/Flaro/Views/Player/PlayerModel.swift b/Flaro/Views/Player/PlayerModel.swift index e831ae1..57e4966 100644 --- a/Flaro/Views/Player/PlayerModel.swift +++ b/Flaro/Views/Player/PlayerModel.swift @@ -528,6 +528,18 @@ final class PlayerModel { // nicht erreichbar ist — der eigentliche Sinn des Herunterladens. if let downloads, let localURL = downloads.localURL(forRatingKey: request.ratingKey) { await startLocal(url: localURL) + // **Die Angaben trotzdem holen, nur eben hinterher.** + // + // Hier wurde früher schlicht zurückgekehrt — und damit blieb + // `item` leer. Daran hing mehr, als es aussieht: keine Marken, + // also kein „Intro überspringen"; keine nächste Folge, also + // keine Knöpfe zum Blättern. Bei allem, was heruntergeladen + // war, fehlte das, und nur dort — weshalb es so lange aussah + // wie ein Fehler in der Erkennung. + // + // Nebenher und nicht davor: das Bild soll sofort laufen, und + // ohne Verbindung gibt es ohnehin nichts zu holen. + metadatenNachladen() return } @@ -747,6 +759,21 @@ final class PlayerModel { await load(PlaybackSource(url: url, origin: .remote), decision: decision) } + /// Holt Titelangaben nach, während die lokale Datei schon läuft. + /// + /// Scheitert es — kein Server, kein Netz —, bleibt es, wie es war: die + /// Datei läuft, es gibt bloss keine Marken und keine Nachbarfolgen. + private func metadatenNachladen() { + guard item == nil, let backend else { return } + Task { [weak self] in + guard let geholt = try? await backend.item(id: self?.request.ratingKey ?? "") else { return } + guard let self, self.item == nil else { return } + self.item = geholt + self.updateActiveMarker() + self.preloadNextEpisode() + } + } + private func startLocal(url: URL) async { // Eine Datei auf dem Gerät ist der ganze Film; es gibt keinen // Vorlauf, den der Spieler aufrechnen müsste. diff --git a/Flaro/Views/Player/PlayerView.swift b/Flaro/Views/Player/PlayerView.swift index 71cc860..d3eb375 100644 --- a/Flaro/Views/Player/PlayerView.swift +++ b/Flaro/Views/Player/PlayerView.swift @@ -746,12 +746,15 @@ struct PlayerView: View { Spacer() HStack { Spacer() - // **Im Abspann ist „überspringen" die falsche Frage.** + // **Im Abspann ist „überspringen" die falsche Frage — aber + // nicht die einzige.** // - // Was man dort will, ist die nächste Folge — ans Ende - // dieser zu springen bringt niemanden weiter. Deshalb steht - // derselbe Knopf schon im Abspann, nicht erst, wenn alles - // durchgelaufen ist. + // Was man dort meistens will, ist die nächste Folge; ans + // Ende dieser zu springen bringt niemanden weiter. Wer den + // Abspann aber sehen wollte und es sich anders überlegt, + // soll ihn trotzdem überspringen können. Zwei Antworten, + // zwei Knöpfe — welche gemeint ist, weiss nur der + // Zuschauer. if naechsteAnbieten(model) { Button { if let next = model.nextEpisodeRequest() { @@ -765,7 +768,9 @@ struct PlayerView: View { } .buttonStyle(.borderedProminent) .tint(accent) - } else if let marker = model.activeMarker { + } + + if let marker = model.activeMarker { Button { model.skipActiveMarker() } label: { diff --git a/server/tests/Flaro.Core.Tests/Abdruecke/folgeA.txt b/server/tests/Flaro.Core.Tests/Abdruecke/folgeA.txt new file mode 100644 index 0000000..e5cb507 --- /dev/null +++ b/server/tests/Flaro.Core.Tests/Abdruecke/folgeA.txt @@ -0,0 +1 @@ +4118107519,4118247293,4118239101,4126660477,4126537685,4160092117,4160092117,4126528983,4118270423,3581407703,3581407734,4118245862,4118246886,4117198198,4150779222,4159044950,4158975318,4160021887,4160091487,4151825759,4118271311,3044537663,3044406575,3052786991,3052754223,3052762431,3052725533,3187009373,3187144541,3178678101,3178743765,3176519671,3176527847,3176527735,3176658807,3210213207,3210212695,3210073431,3078083925,3052918141,3052926845,3052795887,3042236399,3075790335,3075757567,3075831263,3075962359,3086382551,3086374869,3052885973,3052754933,3052754421,3035977173,2515891669,2507498967,2507498999,2541188471,2541180279,2541212991,2541081887,2541081951,2549478751,2549609823,2549609823,2516055389,2516047325,2513819645,2513827837,2515926397,2507535743,2507527679,2506478847,2506477791,2507600343,2507600343,2507592663,2507658199,3051878007,3051742847,3051747197,3051683709,3051683325,3043292671,4117091839,4118136319,4151662078,4151703006,4118275294,4118279286,4118270007,4126658839,4118212871,4150784263,4150654261,4150588709,4285920565,3178755895,3187079031,3187070551,3187131991,3053307479,3078411990,3077896182,3044407294,3044505087,3045021087,3045021119,3053286829,3053269485,3044873469,3078427997,3043841311,3052238111,3051840799,3588714271,3588751135,3580221271,3581212119,3581212151,3562394103,3562525141,3579310549,3581375957,3614930389,3614930389,3614930293,3598022007,3606410623,3623122303,3623712094,3590288734,3590321502,3589272926,3588617567,3589657983,3715487743,3715487743,3716019199,3715953661,3715921917,3715458943,3715593591,3715620215,3581398391,4118271317,4118132181,4151686597,4151815637,3889610709,1733794805,1733663221,1695923445,1698020469,1697879093,1697878053,1696698407,1696722991,622982255,622972271,627165551,627169655,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627146103,627144183,1700884983,1700868607,1700869119,1684091902,1692481271,1692483319,3835771511,3852679703,3852679703,3886496263,3886378503,3064292871,3068487175,3086840646,3086832086,3086828022,3053274599,3051181543,3053286903,3053450695,3053385031,3053385029,3052787015,3044398423,3044398175,2507658367,2507633775,2507633791,2541171807,2541032799,2541033325,2549446253,3082253933,3048600285,3049059021,2516374493,2507986943,2503800319,2535314943,2535380447,2535355855,2537448927,2508088799,4127135231,4126496239,4126438895,4126438895,4126528991,4126660047,4126670287,4151820245,4152213493,4152202869,4148008789,4148007733,4146974997,4151177271,4151304231,4151304231,3078509687,3077981559,3044476799,3044482943,3027582591,3035905663,3052682879,3052672831,2511615262,3585369367,3585434967,2516051287,2515981639,2541737287,3613382135,3613382135,3596613111,3596613575,3613382613,3613251573,3581736933,3565020661,3565151701,3565147605,3581920501,3581793653,3615358325,3623874423,3623876183,3623867974,3615348310,3615282038,2541540710,3044406630,3044537463,4118279255,4118279511,4118279517,4118148445,4118148413,4118150461,4118150525,4118279519,4118271327,4118271487,4149598191,4149737470,4158126046,4158093270,3050665974,3042211702,3042211702,3042277174,3052892982,3052892991,3086446879,3086307599,3086308703,3086308733,2549448061,2513927517,2505428445,2505426429,3042301439,3042963935,3051353039,3034575581,3034445565,3034445549,3026055165,3023962109,3040696285,3074380255,3078574559,3078443519,3086840303,3086964205,3053409789,3053409741,2508027341,2508027341,2508158301,2508023165,2549966127,2549970735,2549970751,2533192990,3606967566,3606967646,3598701822,3615475198,3615475166,3581924831,3581867487,3565090271,3565147487,4101887359,4126471485,4126467373,4151633279,3077895551,3061306239,3075920895,3210204143,3176518639,3159750639,3161847165,3161847165,3161849629,3178757917,3178755869,3212269373,3212662077,3212670303,3204281727,3204412927,3203888639,3218561023,3218429919,3184838623,3176462303,3042240511,3042436479,3044472159,3078083679,3086472415,3086480637,3086480637,3086472413,3044406749,3044407261,3027728125,3027719919,3027588727,3027597142,3052696918,3052795254,3052795255,3044538199,3044538183,3044535877,3075864309,3076380645,3086861669,3086861685,3086996767,3086996767,3077434679,3043880247,3045059959,3045023095,3044886005,3044824565,3044889077,3052892567,3050729607,3050730647,3084342518,3077922022,3061079526,3061083622,3061278550,4135020102,4151801366,4125447958,4125390135,4125455679,3588707349,3589690373,3581367573,3581244725,3581241205,3581376309,3581335380,3587621700,3721910093,3721782621,3713394013,3713398077,3612669245,3612734783,3612865919,3587691902,3587659774,3587659502,3579139838,3579139806,3579139806,3714045918,3613251071,4158518783,4158452831,4160549981,4294307965,4294304125,4286046573,4286047213,4252488701,4250387327,4233451391,4233492286,4233493822,4260756862,4294835550,4160617822,4152218958,4150220766,4150228991,3579803645,3571406845,2499102684,2490718172,2490726356,2490886100,2507663188,2516055925,2516046711,2549470007,3061174551,3061117206,3061179702,3061310822,3061314934,3078018551,3044525559,3027617269,3025491125,3025499325,3025630717,4099405309,4126668119,4126668055,4151702807,4150584599,4150649143,4150784823,4118205223,4118267703,3044497237,3044505413,3044504901,3042268501,3042198901,3042237805,3220569453,3220696959,3204317023,3204314911,3204306206,3061699902,3057504574,3057504574,3074421118,3074388310,3042898423,3185505767,3168720373,3168749525,3168229316,3161806148,3161740661,3178525997,3178591549,3178571039,3178702175,3218486654,3219003390,4158588894,4158412494,4158420943,3051157471,3051157503,3051220479,3051224575,3028155871,2507658719,3577206239,4114075999,4151824735,4151701879,4143444263,3069572391,3069572903,3069568375,3075859807,3075990991,3075994831,3042432479,3042432511,3025623023,3027588863,3027588863,3027588735,3027588911,3035985215,3052762399,3052758287,2683692303,2675438943,2675430783,3212170751,3212310013,3220674557,3220670461,3086850045,3086329724,3077940572,3040212316,3040728444,3040826733,3040818277,3045012583,3078559079,3086815590,3086947158,3086955334,2516546374,2516547287,2516416455,2514288583,2514283991,2505964023,2640305655,2642402519,2508182613,2508181589,2541735957,2549608757,2549474661,2666915173,2658527077,2658662357,2675431111,2673333975,2673866326,2673735542,2648494902,2650657078,2650657078,2642243894,2507961639,2491213111,2491415933,2505498101,2513948117,2513951940,2538993092,2539123141,2539114997,2539090917,3579278327,3579278327,3579279350,3587536230,3570695526,3588054390,3588218182,3581869255,3615478983,3615347927,3615356151,3615352295,3615356407,3747469303,3713520503,3715617399,2507657845,2515923509,2515857981,2650076733,2683627117,2673143677,2673213309,2639781887,3210207199,3218601437,3084383709,3084776957,3076313469,3076321663,3044929919,3040802175,3044996991,3028186623,3027598838,3042278902,3042802903,3076347095,3059700951,3068089847,4141839863,4143412597,4151801685,4118247253,4101465941,4101470037,4109751637,4109751669,4107588727,4116501623,4116567415,4118803799,4118734647,3052861239,3052918583,3187005207,3178559319,3176458103,3176523639,3172423511,3172353623,3176548215,3210172271,3220469103,3220451711,3199561807,3203879117,2664912077,2673169917,2673169901 diff --git a/server/tests/Flaro.Core.Tests/Abdruecke/folgeB.txt b/server/tests/Flaro.Core.Tests/Abdruecke/folgeB.txt new file mode 100644 index 0000000..9bb754f --- /dev/null +++ b/server/tests/Flaro.Core.Tests/Abdruecke/folgeB.txt @@ -0,0 +1 @@ +3187635543,2650668375,2650668375,2650668119,3052795223,3052795255,3044398454,3077948278,3077952494,3077960695,3077961207,3077928279,3077928311,3044365687,3044349431,3044505087,3053352927,2516547039,2516407765,2516342229,2514253309,2505897469,2505963005,2541745533,3615487325,3615352157,3614307605,3614438677,3589263639,3581924119,3581924151,3581269815,3581278007,3581275959,3579313495,3612802391,4150254663,4150121687,4150056151,4150130167,4160746983,4160673765,3086997221,3042301685,3042309845,3042309077,3044402037,3044406655,3052795199,3052731775,3052731774,3044408686,3078094206,3078083934,3044529486,3044536655,3044405599,4118106495,4252455023,4252464367,4252448239,4286002687,4294391263,4292302815,4292302815,4292171615,4160091999,4160022047,4294239775,3715491615,3713398559,3713389951,2639615359,2639615343,2513917053,2513917053,2648167773,2648167773,2650264925,2683828061,2683692925,2683693055,2641754103,2641745382,2503329270,2503431638,3040245206,3042407894,3042398711,3052892535,3052761431,3086246229,3086247285,2541024765,2541084157,2541215229,2507595229,2507595213,3052918093,3052787039,2515857791,2507530543,2507536175,2507667239,3078092599,3078092599,3078084439,3078477655,3078616949,3087005685,3078616541,3078608349,3045053917,4118238461,4118115453,4151669887,4159993215,4160056686,4151668606,4151705214,4118273630,4118267614,4118271710,4118139854,4118115150,4118114654,4118114686,4118115686,4118246774,4126635319,3187103029,3187004789,3187012980,3187012948,3218470740,2673203164,2673203197,2673342461,2669150207,3210212863,3178714623,3178714495,3052787039,3187012959,3187013087,3052926815,3086473055,4151825887,4152219135,4152227071,4152226943,4152317055,4118762623,4118762871,4118771575,4127160183,4160190071,4160182231,4160051141,4160083909,4118149077,4118149109,4118149109,3044407285,4151703541,4151703541,4151695223,4151695223,4118107511,4118639990,4118771030,4100879702,4100871638,4101985783,4127151607,4127028597,4126996309,3590125333,3581211925,3581277461,3581269301,3589784885,3623867703,3623843191,3623843191,3615454679,3615315415,3615348215,3581924855,3581933559,4127193047,4126539735,3581208567,3581212151,4151703511,4151702487,4151702399,4151702399,4151703399,4160092007,4160091519,4160224575,4160216093,3078085647,2541737039,3615487071,3615487327,3581900159,4118796287,4126660607,4126660607,3052787711,3052722175,3052729855,3044406615,2490758487,2490725687,2489677183,2489808255,2507634047,2516539391,2549962751,2549962719,2549972959,2508054495,2508053847,3581934935,3590186325,3589662165,3614831061,3614822900,3614949876,3614884341,3614889845,3581310839,3581243255,3580128566,3580132662,3581244790,3589625206,4126659927,4126660439,2516047685,2516047445,2515924596,2541024868,2536830580,2536888149,3040335189,3044537717,3042436583,3050831079,3052920311,3052920279,2507658711,2641876295,2675439447,2675438391,2684221239,2684114215,2674742581,2641327381,2640737557,2641810773,3178747861,4252497815,4124440471,4116051895,4115988391,4115987878,3075863990,3075995094,3075994071,4152217975,4151636343,4150617463,4117194069,4125587317,4125579125,3052881783,3052754294,3044373886,3044373854,3043456270,3051877662,2515006751,2511730047,2499138901,2499073493,2507470837,2503276533,2503342079,2536962047,3078023159,4160222711,4143437271,4143437271,4160214469,4151702997,4118083061,4118671861,4118803445,4118729205,4118790647,4118663399,4118639735,4151141398,4151145494,4151276854,4126113574,4126039606,3589168663,3589101063,3589043719,2515334997,2515916757,2516043733,2507527605,2507470261,2507535797,2541221109,3078092021,3078088021,4151833943,4150785301,4150654261,4150621477,4117189941,4117189911,4117714263,4151268855,4150621687,4151670263,4160058711,3069703510,2683819350,2683688726,2675238710,2641684279,3187009335,3170366839,3170366967,3168261621,3042395637,3042395613,3042400221,2639624191,3713361919,4252329983,4152124799,3615257951,3615315295,3589625183,3589756253,3589764445,3589789519,3589789535,3589658495,3589666671,3614762863,3598054783,3598054878,3598177743,3606496735,3623867903,3581793511,2508059879,2491282933,2491413845,2490889557,2499147572,2515924788,2515859252,2549479220,2549601589,2541215029,2541092117,2507535703,2507666775,3044463991,3044463991,3052918135,3052885463,3044505031,3044505557,2541189093,3077928933,3077863285,3086250839,3086312279,3086313303,2549446014,2515883390,2507503102,2507527646,2507593054,3027686734,3036149598,3052791678,3052725631,3052729725,3077887357,2541081949,2654426444,2671146460,2673309181,2673309677,3185014781,3168106493,2631366621,2513926111,3589789527,3587569495,2549479287,2541024631,3614831991,3581277559,3581408759,3581408759,4116182519,4116182487,4116182471,4116174295,4116174327,4118271477,3581277685,3581408597,3623351637,3623220501,3606439221,3606439221,3606443319,2532693303,2549470575,2549470591,2507658615,2507666903,2490888671,3027752927,3028277215,3162371831,3187472247,3187472239,3221026799,3212670463,3212146143,3212146143,3212277247,3212277247,3187103231,3187144031,3052795231,2505438559,2505438551,2507666774,2507667286,2515990358,3052926806,3086350198,3086276455,3086271847,3077952885,3078092117,3078091861,3073889365,3073897845,3044435317,3044440055,3027720183,3035977695,3036018655,3035953151,3035953151,3052795871,3044374493,3044505565,3045021693,3045021055,3078444399,3077395583,3085792383,3085821023,3085952079,3085956447,3085881693,3045048797,3044921853,3044406253,3044407149,3044537725,3044496671,3043315031,4117069143,4117065079,4117194086,4126635894,4126626630,4126626759,4160091095,4151703543,4151629797,4151629301,4151703029,4151834101,4118271477,3042301269,3042309461,3078087959,3078088471,3086481215,3086473087,3086309247,3077920639,3077899774,3044349438,3044505086,3052893422,3052885502,3052885470,3052762446,2515920718,2541089566,2507666238,2516054847,2515859263,2507470143,2490692927,2490760477,2507594837,2505495877,2513884501,2507658709,2508027893,2508027381,2508027381,2508019189,2550093301,2541704599,2541188567,2524444663,2524313591,2524247543,2541024759,3614832087,3580360151,3580351991,3580351989,3580192245,3580192215,3580195319,2506446191,2514867567,3051741039,3051749214,3051745054,4117100311,4150785815,4150645087,3085291871,3076911485,3078616445,4118795629,4117218685,4117189981,4117198173,4117067101,4125447551,4126504831,4260855646,4260724574,4252335998,2641751934,3044403070,3052795222,3052781910,3052913015,3086471541,3085955349,2548953428,3078419796,3078419828,3078487524,3078586340,3042926565,3050788853,3050788311,3052762583,4151703007,4151694847,4151686647,4151819735,3883384151,3882335559,3848640855,3848648983,1701161015,1680325687,1680315429,1680052261,1696854117,1696845925,1696846949,622976109,627165549,627165559,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627177847,627145079,627146103,627144183,1700868599,1818309111,1818309111,1818309623,1826698999,1826701047,3835771639,3852548855,3886234327,3869719239,4138168005,3065374661,3069506887,3061634391,3078419831,3078481279,3078612855,3078584183,3078575998,3078575998,3061663599,3061663103,3078477087,3053442311,3053384967,2499798293,2491279125,2491250549,2491238261,2491369301,2508150613,2548004181,2682086773,3741184375,4275425399,4275425655,4275557759,4267177343,3193302911,3210079582,3210083678,3172324702,3155547646,3159871966,3159806943,3168162655,3168229245,3184874861,3184819565,3084221821,3084221821,3075833183,3042932046,3042964814,3086996990,3086996990,3086865919,3070088703,3059611037,3059606972,3076388284,3042792860,3045016988,3028178428,3036669949,3036538861,3036542591,3036542527,3027105310,3043817246,3043816766,3043882302,3076912510,3078092126,3078059486,3078084062,3044529630,3044529630,3077957118,3077957086,3078092254,3078084062,3077948927,3044406655,3044926815,3028248397,3027203965,3035453229,3035387693,3035395885,3053314879,3087003935,3060258079,3061273951,4151802975,4151671933,3614801405,3581369853,3587593695,3587593183,3621213007,3614921567,3614822775,3614766439,3614827893,3623350101,3623354325,3579312085,3581401045,3581396935,3581921223,3565143382,3565016422,3564958758,3044859943,3044929591,3077558295,3077567765,3077570389,3078553173,3078553301,3078610645,3086866071,3050624695,3042244534,3042239478,3042206198,3025495526,3027727734,3036116310,3052885271,3053405461,3044889877,2541583637,2541059349,2541186357,2549544245,2549601653,3086349653,3084248021,3075864565,3075987445,3042428917,3050823125,3033923031,3025532407,3025532407,3042308455,3044397422,3044398446,3077924863,3077926911,3078062077,3078049277,3078049277,3086308573,3086316637,3086315613,3086316637,3086382133,3086378295,2541188919,2541180535,2516014710,2516022998,2499237830,2499106758,2507494854,2507503062,3579180534,2505439207,2515861351,2549415765,2515926293,2515860757,2507465781,2507535383,2507591951,2507591967,2549535031,2549609831,2549478775,3052729719,3042244093,3042301437,2506082173,2508179293,2541712989,3074258525,3057481341,3056957309,4151670141,4149704189,4124562653,4107793615,4107793615,4099405151,4099396991,4116043135,4149540207,4149536111,4157993839,4158051167,4158112591,4157921246,4116506622,3042831854,3042967023,3042893311,3042958813,3042833869,3076388301,3076388348,3076519404,3076511724,3075856381,3075864573,3075864317,3077920493,3077920503,3077919583,3078058831,3077927262,3077928238,3084211262,3052885278,3052924190,3036146974,3028286814,3028155727,3026056655,4116567517,4116664829,4158603757,4158603757,4158411245,4150031725 diff --git a/server/tests/Flaro.Core.Tests/EchteAbdrueckeTests.cs b/server/tests/Flaro.Core.Tests/EchteAbdrueckeTests.cs new file mode 100644 index 0000000..dff193b --- /dev/null +++ b/server/tests/Flaro.Core.Tests/EchteAbdrueckeTests.cs @@ -0,0 +1,56 @@ +using Flaro.Core.Media; + +namespace Flaro.Core.Tests; + +/// Der Abgleich an echten Chromaprint-Abdrücken. +/// +/// +/// Bisher nur mit erfundenen Zahlen geprüft. Die sagen, ob die Suche +/// rechnet; sie sagen nicht, ob die Schwellen zu echtem Material passen. +/// Genau dazwischen kann alles liegen: zwei Aufnahmen desselben Stücks sind +/// nie bitgleich, und wie unähnlich sie sind, weiss man erst, wenn man es +/// misst. +/// +/// Die beiden Abdrücke liegen als Prüfdaten daneben: zwei Tonspuren mit +/// demselben dreissigsekündigen Stück an verschiedenen Stellen und in +/// verschiedener Bitrate, durch fpcalc gedreht. Zwanzig Kilobyte +/// Text — der Preis dafür, dass diese Zusage nicht bloss behauptet ist. +/// +public class EchteAbdrueckeTests +{ + private const double JeAbdruck = 4096.0 / 3.0 / 11025.0; + + private static IReadOnlyList? Lesen(string name) + { + var pfad = Path.Combine(TestContext.CurrentContext.TestDirectory, "Abdruecke", name); + if (!File.Exists(pfad)) return null; + + return File.ReadAllText(pfad) + .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Select(t => unchecked((uint)long.Parse(t))) + .ToList(); + } + + [Test] + public void Dasselbe_Stueck_wird_in_zwei_Aufnahmen_gefunden() + { + var a = Lesen("folgeA.txt"); + var b = Lesen("folgeB.txt"); + if (a is null || b is null) Assert.Ignore("Keine Abdrücke zum Prüfen da."); + + var treffer = Tonabgleich.Finden(a, b, JeAbdruck); + + TestContext.Out.WriteLine($"A {a.Count} Werte, B {b.Count} Werte"); + TestContext.Out.WriteLine(treffer is null + ? "NICHTS GEFUNDEN" + : $"A {treffer.AVon:0.0}–{treffer.ABis:0.0} s, B {treffer.BVon:0.0}–{treffer.BBis:0.0} s"); + + Assert.That(treffer, Is.Not.Null, "das Stück steckt in beiden"); + Assert.Multiple(() => + { + Assert.That(treffer!.AVon, Is.EqualTo(20).Within(3)); + Assert.That(treffer.BVon, Is.EqualTo(75).Within(3)); + Assert.That(treffer.Laenge, Is.EqualTo(30).Within(4)); + }); + } +} diff --git a/server/tests/Flaro.Core.Tests/Flaro.Core.Tests.csproj b/server/tests/Flaro.Core.Tests/Flaro.Core.Tests.csproj index 2dda915..d8ca55e 100644 --- a/server/tests/Flaro.Core.Tests/Flaro.Core.Tests.csproj +++ b/server/tests/Flaro.Core.Tests/Flaro.Core.Tests.csproj @@ -24,4 +24,8 @@ + + + + diff --git a/web/app/src/spieler.ts b/web/app/src/spieler.ts index ce390f0..df60642 100644 --- a/web/app/src/spieler.ts +++ b/web/app/src/spieler.ts @@ -178,7 +178,18 @@ export type Spieleranfrage = { dateiId: string; /** Die Kennung des Titels — dorthin geht der Fortschritt. */ titelId: string; + /** Wie der Titel heisst — bei einem Film reicht das. + * + * **Bei einer Folge nicht.** Dort braucht die Zeile über dem Spieler + * Serie, Staffel, Nummer und Folgentitel, und jede Startstelle hat sie + * anders zusammengesetzt: mal „S1 E6 · Name", mal nur „Wiedergabe", mal + * bloss den Serienamen. Deshalb kommen die Teile jetzt einzeln, und die + * Zeile entsteht genau einmal — hier. */ name: string; + serie?: string | null; + staffel?: number | null; + folge?: number | null; + folgentitel?: string | null; /** Ab welcher Sekunde des Films. */ ab: number; /** Wie lang der Film ist. **Aus der Mediathek, nicht aus dem Strom** — der @@ -274,7 +285,9 @@ export function spieler(anfrage: Spieleranfrage): void { kopf.className = 'pl-kopf'; const name = document.createElement('span'); name.className = 'pl-titel'; - name.textContent = anfrage.name; + name.textContent = anfrage.staffel != null || anfrage.folge != null + ? folgenname(anfrage.serie, anfrage.staffel, anfrage.folge, anfrage.folgentitel) + : anfrage.name; const kopfknoepfe = document.createElement('div'); kopfknoepfe.className = 'pl-gruppe'; @@ -370,10 +383,13 @@ export function spieler(anfrage: Spieleranfrage): void { // die Leiste gar nicht, und ein Knopf, von dem niemand weiss, dass es ihn // gibt, ist keiner. Als eigene Schicht über dem Bild bleibt er sichtbar, // solange die Marke läuft. - const ueberspringen = document.createElement('button'); - ueberspringen.className = 'btn btn-primary pl-ueberspringen hidden'; + // **Zwei Knöpfe, nicht einer.** Im Abspann einer Serie gibt es zwei + // vernünftige Antworten — weiterschauen oder den Abspann überspringen — + // und welche gemeint ist, weiss nur der Zuschauer. + const markenreihe = document.createElement('div'); + markenreihe.className = 'pl-marken hidden'; - schicht.append(video, dreher, mitte, kopf, bedienung, ueberspringen, meldungsfeld, kleinleiste); + schicht.append(video, dreher, mitte, kopf, bedienung, markenreihe, meldungsfeld, kleinleiste); document.body.appendChild(schicht); document.body.classList.add('pl-offen'); @@ -606,13 +622,6 @@ export function spieler(anfrage: Spieleranfrage): void { // // Jetzt stehen sie dort, wo man hinsieht: links und rechts vom Knopf, den // man drückt. - /** Wie die Serie heisst — für die Zeile über dem Spieler. - * - * Aus dem übergebenen Namen gewonnen: wer den Spieler startet, kennt sie, - * der Folgenvertrag nennt sie nicht. Steht dort nichts Brauchbares, - * bleibt die Zeile eben ohne Serie. */ - const serienname = anfrage.name.includes(' · ') ? anfrage.name.split(' · ')[0] : null; - /** Die Nachbarfolgen, sobald der Server sie genannt hat. */ let vorige: Folge | null = null; let naechste: Folge | null = null; @@ -702,7 +711,12 @@ export function spieler(anfrage: Spieleranfrage): void { spieler({ dateiId: datei.id, titelId: ziel.id, - name: folgenname(serienname, ziel.season, ziel.episode, ziel.title), + // Die Serie bleibt dieselbe; sie steht schon im laufenden Wunsch. + name: ziel.title ?? '', + serie: anfrage.serie, + staffel: ziel.season, + folge: ziel.episode, + folgentitel: ziel.title, ab: stelle, laenge: datei.durationSeconds, tonspur: ton, @@ -778,31 +792,45 @@ export function spieler(anfrage: Spieleranfrage): void { * **Keine eigene Uhr.** Die Zeit wird ohnehin bei jedem Fortschreiten * nachgeführt; ein zweiter Zeitgeber dafür liefe neben dem ersten her und * wäre eine zweite Stelle, an der etwas hängen bleibt. */ + /** Was gerade zu tun ist, als ein oder zwei Knöpfe. + * + * **Neu gezeichnet nur, wenn sich etwas ändert.** `zeitAuffrischen` läuft + * mehrmals je Sekunde; die Knöpfe jedesmal neu zu bauen hiesse, einen + * Klick mitten im Drücken zu verlieren. + */ + let markenstandJetzt = ''; + function markenstand(jetzt: number): void { const drin = marken.find(m => jetzt >= m.startSeconds && jetzt < m.endSeconds); - ueberspringen.classList.toggle('hidden', !drin); - if (!drin) return; - - // **Beim Abspann einer Serie ist „überspringen" die falsche Frage.** - // - // Was man dort will, ist die nächste Folge — ans Ende dieser zu - // springen bringt niemanden weiter. Bei einem Film gibt es nichts - // danach; dort heisst es, ihn zu beenden, und der Server hakt ihn - // beim Melden der Stelle ohnehin ab. - const abspann = drin.kind === 'credits'; + const abspann = drin?.kind === 'credits'; const weiter = abspann && naechste?.files[0] != null; + const kennung = `${drin?.startSeconds ?? ''}|${weiter}`; - const text = !abspann ? 'Intro überspringen' - : weiter ? 'Nächste Folge' - : 'Abspann überspringen'; - if (ueberspringen.textContent !== text) ueberspringen.textContent = text; + markenreihe.classList.toggle('hidden', !drin); + if (!drin || kennung === markenstandJetzt) return; + markenstandJetzt = kennung; + markenreihe.replaceChildren(); - ueberspringen.onclick = e => { + // **Im Abspann einer Serie zuerst die nächste Folge.** Das ist, was man + // dort will; „überspringen" steht daneben für den, der den Abspann + // sehen wollte und es sich anders überlegt. + if (weiter) { + const w = knopf('Nächste Folge', 'btn btn-primary'); + w.onclick = e => { e.stopPropagation(); folgeWechseln(naechste); }; + markenreihe.appendChild(w); + } + + const u = knopf( + abspann ? 'Abspann überspringen' : 'Intro überspringen', + weiter ? 'btn' : 'btn btn-primary'); + u.onclick = e => { e.stopPropagation(); - if (weiter) { folgeWechseln(naechste); return; } - if (abspann) { void beenden(); return; } + // Bei einem Film und bei der letzten Folge heisst „überspringen" + // Schluss: ans Ende zu spulen liesse ein stehendes Bild zurück. + if (abspann && !weiter) { void beenden(); return; } void springen(drin.endSeconds); }; + markenreihe.appendChild(u); } /** Ans Ende springen und zumachen — für den Abspann ohne Nachfolger. diff --git a/web/app/src/style.css b/web/app/src/style.css index 77c1589..da40379 100644 --- a/web/app/src/style.css +++ b/web/app/src/style.css @@ -928,13 +928,15 @@ textarea.feld { * Leiste gar nicht. Hier liegt er über dem Bild, rechts über der Stelle, * an der die Leiste erscheinen würde, und bleibt stehen, solange die Marke * läuft. */ -.pl-ueberspringen { +.pl-marken { position: absolute; right: var(--s-section); bottom: calc(var(--s-section) * 3.5); z-index: 3; - box-shadow: 0 2px 12px rgba(0, 0, 0, .6); + display: flex; + gap: var(--s-snug); } +.pl-marken .btn { box-shadow: 0 2px 12px rgba(0, 0, 0, .6); } .pl-unten { display: grid; grid-template-columns: 1fr auto 1fr; diff --git a/web/app/src/views/home.ts b/web/app/src/views/home.ts index 94fad34..2956c48 100644 --- a/web/app/src/views/home.ts +++ b/web/app/src/views/home.ts @@ -2,7 +2,7 @@ import { inhalt, weiterschauen, naechsteFolgen, empfehlungen, saisonal, type Bibliothek, type Angefangen, } from '../api'; -import { leer, fehlerzeile, meldung, folgenname } from '../util'; +import { leer, fehlerzeile, meldung } from '../util'; import { kachel, alsKachel, angefangenAlsKachel, reihe } from '../card'; import { hero, alsHero, angefangenAlsHero, type Heroeintrag } from '../hero'; import { spieler } from '../spieler'; @@ -85,9 +85,11 @@ export function startseite( spieler({ dateiId: e.fileId, titelId: e.id, - name: e.kind === 'episode' - ? folgenname(e.title, e.season, e.episode, e.subTitle) - : e.title, + name: e.title, + serie: e.kind === 'episode' ? e.title : null, + staffel: e.season, + folge: e.episode, + folgentitel: e.subTitle, ab: e.state.positionSeconds, laenge: e.durationSeconds ?? null, danach: () => void aufbauen(),