Merge pull request #293 from Materialious/update/1.0.8

Update/1.0.9
This commit is contained in:
Ward
2024-05-13 15:41:27 +12:00
committed by GitHub
44 changed files with 161 additions and 96 deletions
+2 -2
View File
@@ -7,8 +7,8 @@ android {
applicationId "us.materialio.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 8
versionName "1.0.8"
versionCode 9
versionName "1.0.9"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "Materialious",
"version": "1.0.8",
"version": "1.0.9",
"description": "Modern material design for Invidious.",
"author": {
"name": "Ward Pearce",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "materialious",
"version": "1.0.8",
"version": "1.0.9",
"private": true,
"scripts": {
"dev": "vite dev",
+1 -1
View File
@@ -6,7 +6,7 @@ import {
instanceStore,
returnYTDislikesInstanceStore,
synciousInstanceStore
} from '../../store';
} from '../store';
import type {
Channel,
ChannelContentPlaylists,
+17 -2
View File
@@ -7,7 +7,7 @@
export let comment: Comment;
export let videoId: string;
let replies: Comments;
let replies: Comments | undefined = undefined;
async function loadReplies(continuation: string) {
try {
@@ -18,6 +18,16 @@
});
} catch {}
}
function commentTimestamps(html: string): string {
const regex =
/<a href="([^"]+)" data-onclick="jump_to_time" data-jump-time="(\d+)">(\d+:\d+(?::\d+)?)<\/a>\s*(.+)/;
const replacement = `<a href="/watch/${videoId}?time=$2" data-sveltekit-preload-data="off" class="link">$3</a>`;
const processedHtml = html.replace(regex, replacement);
return processedHtml;
}
</script>
<div class="comment">
@@ -42,7 +52,7 @@
{/if}
</div>
<p style="margin-bottom: 0;">
{@html comment.contentHtml}
{@html commentTimestamps(comment.contentHtml)}
</p>
<div style="display: flex;">
<p><i>thumb_up</i> {numberWithCommas(comment.likeCount)}</p>
@@ -75,6 +85,11 @@
<i class="primary-text">expand_more</i>
<span class="primary-text">{comment.replies.replyCount} {$_('replies')}</span>
</button>
{:else if replies}
<button on:click={() => (replies = undefined)} class="transparent replies">
<i class="primary-text">expand_less</i>
<span class="primary-text">{$_('hideReplies')}</span>
</button>
{/if}
</div>
</div>
+1 -1
View File
@@ -1,8 +1,8 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { get } from 'svelte/store';
import { miniPlayerSrcStore, playerProxyVideosStore } from '../store';
import { proxyVideoUrl } from './misc';
import { miniPlayerSrcStore, playerProxyVideosStore } from './store';
let currentTime: number = 0;
+32 -33
View File
@@ -1,19 +1,22 @@
<script lang="ts">
import 'vidstack/bundle';
import { App } from '@capacitor/app';
import { page } from '$app/stores';
import type { Page } from '@sveltejs/kit';
import { SponsorBlock, type Category } from 'sponsorblock-api';
import { onDestroy, onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import type { MediaTimeUpdateEvent, PlayerSrc, VideoSrc } from 'vidstack';
import type { MediaPlayerElement } from 'vidstack/elements';
import { deleteVideoProgress, getVideoProgress, saveVideoProgress } from './Api';
import type { VideoPlay } from './Api/model';
import { getBestThumbnail, proxyVideoUrl, videoLength, type PhasedDescription } from './misc';
import {
authStore,
instanceStore,
miniPlayerSrcStore,
playerAlwaysLoopStore,
playerAndroidBackgroundPlayStore,
playerAutoPlayStore,
playerDashStore,
playerProxyVideosStore,
@@ -21,22 +24,34 @@
sponsorBlockCategoriesStore,
sponsorBlockUrlStore,
synciousStore
} from '../store';
import { deleteVideoProgress, getVideoProgress, saveVideoProgress } from './Api';
import type { VideoPlay } from './Api/model';
import { getBestThumbnail, proxyVideoUrl, videoLength, type PhasedDescription } from './misc';
} from './store';
import { getDynamicTheme } from './theme';
export let data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null };
export let audioMode = false;
export let player: MediaPlayerElement;
export let isSyncing: boolean = false;
export let isEmbed: boolean = false;
let src: PlayerSrc = [];
let categoryBeingSkipped = '';
let playerIsLive = false;
let playerPosSet = false;
function loadTimeFromUrl(page: Page): boolean {
if (player) {
const timeGivenUrl = page.url.searchParams.get('time');
if (timeGivenUrl && !isNaN(parseFloat(timeGivenUrl))) {
player.currentTime = Number(timeGivenUrl);
return true;
}
}
return false;
}
page.subscribe((pageUpdate) => loadTimeFromUrl(pageUpdate));
export function seekTo(time: number) {
if (typeof player !== 'undefined') {
player.currentTime = time;
@@ -123,11 +138,11 @@
}
if (get(playerDashStore)) {
src = [{ src: data.video.dashUrl + '?local=true', type: 'application/dash+xml' }];
player.addEventListener('dash-can-play', async () => {
await loadPlayerPos();
});
src = [{ src: data.video.dashUrl + '?local=true', type: 'application/dash+xml' }];
} else {
let formattedSrc;
src = data.video.formatStreams.map((format) => {
@@ -193,12 +208,7 @@
if (playerPosSet) return;
playerPosSet = true;
const paramTime = new URLSearchParams(window.location.search).get('time');
if (paramTime && !isNaN(parseFloat(paramTime))) {
player.currentTime = Number(paramTime);
return;
}
if (loadTimeFromUrl($page)) return;
let toSetTime = 0;
@@ -250,19 +260,6 @@
player.destroy();
playerPosSet = false;
});
// Background play for Android.
App.addListener('appStateChange', (state) => {
// Very much backwards logic, but for whatever reason
// Calling play() within a conditional breaks background play.
// Maybe a race condition.
player.play();
if (!get(playerAndroidBackgroundPlayStore)) {
player.pause();
}
});
</script>
{#if audioMode}
@@ -294,9 +291,11 @@
{/if}
</media-player>
<div class="snackbar" id="sponsorblock-alert">
<span
>{$_('skipping')}
<span class="bold" style="text-transform: capitalize;">{categoryBeingSkipped}</span></span
>
</div>
{#if !isEmbed}
<div class="snackbar" id="sponsorblock-alert">
<span
>{$_('skipping')}
<span class="bold" style="text-transform: capitalize;">{categoryBeingSkipped}</span></span
>
</div>
{/if}
+3 -1
View File
@@ -3,8 +3,8 @@
import Mousetrap from 'mousetrap';
import { createEventDispatcher, onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { interfaceSearchSuggestionsStore } from '../store';
import { getSearchSuggestions } from './Api';
import { interfaceSearchSuggestionsStore } from './store';
const dispatch = createEventDispatcher();
@@ -32,6 +32,8 @@
}
function handleSubmit() {
if (search.trim() === '') return;
selectedSuggestionIndex = -1;
goto(`/search/${encodeURIComponent(search)}`);
dispatch('searchSubmitted');
+2 -2
View File
@@ -3,6 +3,7 @@
import { Capacitor } from '@capacitor/core';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from './misc';
import {
authStore,
darkModeStore,
@@ -30,8 +31,7 @@
synciousInstanceStore,
synciousStore,
themeColorStore
} from '../store';
import { ensureNoTrailingSlash } from './misc';
} from './store';
let sponsorCategoriesList: string[] = [];
sponsorBlockCategoriesStore.subscribe((value) => (sponsorCategoriesList = value));
+1 -1
View File
@@ -7,7 +7,7 @@
import { onDestroy, onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { syncPartyConnectionsStore, syncPartyPeerStore } from '../store';
import { syncPartyConnectionsStore, syncPartyPeerStore } from './store';
function changeVideoEvent(conn: DataConnection) {
conn.on('data', (data) => {
+11 -12
View File
@@ -2,6 +2,11 @@
import { createEventDispatcher, onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { getDeArrow, getThumbnail, getVideo, getVideoProgress } from './Api';
import type { Notification, PlaylistPageVideo, Video, VideoBase, VideoPlay } from './Api/model';
import ShareVideo from './ShareVideo.svelte';
import { cleanNumber, getBestThumbnail, proxyVideoUrl, videoLength } from './misc';
import type { PlayerEvents } from './player';
import {
authStore,
deArrowEnabledStore,
@@ -11,12 +16,7 @@
syncPartyConnectionsStore,
syncPartyPeerStore,
synciousStore
} from '../store';
import { getDeArrow, getThumbnail, getVideo, getVideoProgress } from './Api';
import type { Notification, PlaylistPageVideo, Video, VideoBase, VideoPlay } from './Api/model';
import ShareVideo from './ShareVideo.svelte';
import { cleanNumber, getBestThumbnail, proxyVideoUrl, videoLength } from './misc';
import type { PlayerEvents } from './player';
} from './store';
const dispatch = createEventDispatcher();
@@ -238,12 +238,7 @@
</video>
</div>
{:else}
<img
class="responsive"
style="max-width: 100%;min-height: 160px;"
src={img.src}
alt="Thumbnail for video"
/>
<img class="responsive" src={img.src} alt="Thumbnail for video" />
{/if}
{:else}
<p>{$_('thumbnail.failedToLoadImage')}</p>
@@ -339,6 +334,10 @@
max-height: 160px;
}
.thumbnail img {
object-fit: contain;
}
@media screen and (max-width: 650px) {
.thumbnail {
max-height: 100%;
+1 -1
View File
@@ -1,10 +1,10 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { authStore } from '../store';
import { removePlaylistVideo } from './Api';
import type { Notification, PlaylistPageVideo, Video, VideoBase } from './Api/model';
import Thumbnail from './Thumbnail.svelte';
import { authStore } from './store';
export let videos: VideoBase[] | Video[] | Notification[] | PlaylistPageVideo[] = [];
export let oneItemPerRow: boolean = false;
+2 -2
View File
@@ -1,5 +1,4 @@
import { page } from '$app/stores';
import { get } from 'svelte/store';
import {
darkModeStore,
deArrowEnabledStore,
@@ -24,7 +23,8 @@ import {
synciousInstanceStore,
synciousStore,
themeColorStore
} from '../store';
} from '$lib/store';
import { get } from 'svelte/store';
const persistedStores = [
{
@@ -12,6 +12,7 @@
"deleteAllHistory": "Delete all history",
"skipping": "Skipping",
"replies": "replies",
"hideReplies": "Hide replies",
"hideVideo": "Hide video",
"syncParty": {
"userJoined": "User joined your watch party.",
+1 -1
View File
@@ -3,8 +3,8 @@ import { page } from '$app/stores';
import humanNumber from 'human-number';
import type Peer from 'peerjs';
import { get } from 'svelte/store';
import { instanceStore } from '../store';
import type { Image } from './Api/model';
import { instanceStore } from './store';
export function truncate(value: string, maxLength: number = 50): string {
return value.length > maxLength ? `${value.substring(0, maxLength)}...` : value;
@@ -1,6 +1,6 @@
<script src="ts">
import { page } from '$app/stores';
import { activePageStore } from '../store';
import { activePageStore } from '$lib/store';
activePageStore.set(null);
</script>
@@ -11,6 +11,14 @@
import SyncParty from '$lib/SyncParty.svelte';
import Thumbnail from '$lib/Thumbnail.svelte';
import { bookmarkletLoadFromUrl, loadSettingsFromEnv } from '$lib/externalSettings';
import {
activePageStore,
authStore,
darkModeStore,
instanceStore,
syncPartyPeerStore,
themeColorStore
} from '$lib/store';
import { App } from '@capacitor/app';
import { Browser } from '@capacitor/browser';
import { Capacitor } from '@capacitor/core';
@@ -20,14 +28,6 @@
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { pwaInfo } from 'virtual:pwa-info';
import {
activePageStore,
authStore,
darkModeStore,
instanceStore,
syncPartyPeerStore,
themeColorStore
} from '../store';
let mobileSearchShow = false;
@@ -191,8 +191,10 @@
><i>menu</i></button
>
<Logo classes="m l" />
<h6 class="l">Materialious</h6>
<nav on:click={() => goto('/')} style="cursor: pointer;" class="m l">
<Logo />
<h6 class="l">Materialious</h6>
</nav>
<div class="max m l"></div>
@@ -271,7 +273,7 @@
<dialog class="left small" id="menu-expanded">
<header class="fixed">
<nav>
<nav on:click={() => goto('/')} style="cursor: pointer;">
<button class="transparent circle large" data-ui="#menu-expanded"><i>menu</i></button>
<div style="width: 20%;">
<Logo />
@@ -1,6 +1,6 @@
<script lang="ts">
import VideoList from '$lib/VideoList.svelte';
import { activePageStore } from '../store';
import { activePageStore } from '$lib/store';
export let data;
@@ -2,8 +2,8 @@
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import PageLoading from '$lib/PageLoading.svelte';
import { authStore } from '$lib/store';
import { onMount } from 'svelte';
import { authStore } from '../../store';
// Auth response handling for Desktop
onMount(() => {
@@ -5,12 +5,12 @@
import PlaylistThumbnail from '$lib/PlaylistThumbnail.svelte';
import Thumbnail from '$lib/Thumbnail.svelte';
import { cleanNumber, getBestThumbnail } from '$lib/misc';
import { activePageStore, authStore } from '$lib/store';
import { Capacitor } from '@capacitor/core';
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
import { get } from 'svelte/store';
import { activePageStore, authStore } from '../../../store';
export let data;
@@ -3,12 +3,12 @@
import type { VideoPlay } from '$lib/Api/model';
import PageLoading from '$lib/PageLoading.svelte';
import VideoList from '$lib/VideoList.svelte';
import { activePageStore, synciousStore } from '$lib/store';
import { error } from '@sveltejs/kit';
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
import { get } from 'svelte/store';
import { activePageStore, synciousStore } from '../../store';
activePageStore.set('history');
@@ -3,10 +3,10 @@
import type { PlaylistPageVideo } from '$lib/Api/model.js';
import VideoList from '$lib/VideoList.svelte';
import { cleanNumber, unsafeRandomItem } from '$lib/misc.js';
import { activePageStore, playlistSettingsStore } from '$lib/store';
import { Capacitor } from '@capacitor/core';
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { activePageStore, playlistSettingsStore } from '../../../store';
export let data;
@@ -1,8 +1,8 @@
<script lang="ts">
import { deletePersonalPlaylist, getPersonalPlaylists, postPersonalPlaylist } from '$lib/Api';
import PlaylistThumbnail from '$lib/PlaylistThumbnail.svelte';
import { activePageStore } from '$lib/store';
import { _ } from 'svelte-i18n';
import { activePageStore } from '../../store';
export let data;
@@ -4,9 +4,9 @@
import PageLoading from '$lib/PageLoading.svelte';
import PlaylistThumbnail from '$lib/PlaylistThumbnail.svelte';
import Thumbnail from '$lib/Thumbnail.svelte';
import { activePageStore } from '$lib/store';
import { _ } from 'svelte-i18n';
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
import { activePageStore } from '../../../store';
export let data;
@@ -1,9 +1,9 @@
<script lang="ts">
import { getFeed } from '$lib/Api/index.js';
import VideoList from '$lib/VideoList.svelte';
import { activePageStore } from '$lib/store';
import { _ } from 'svelte-i18n';
import InfiniteLoading, { type InfiniteEvent } from 'svelte-infinite-loading';
import { activePageStore } from '../../store';
export let data;
@@ -1,8 +1,8 @@
<script lang="ts">
import { deleteUnsubscribe } from '$lib/Api';
import { activePageStore } from '$lib/store';
import Fuse from 'fuse.js';
import { _ } from 'svelte-i18n';
import { activePageStore } from '../../../store';
activePageStore.set(null);
@@ -48,11 +48,12 @@
{#each subscriptions as sub}
<article>
<nav>
<a href={`/channel/${sub.authorId}`} style="flex: 1;"
<a href={`/channel/${sub.authorId}`}
><h6>
{sub.author}
</h6></a
>
<div class="max"></div>
<button on:click={async () => unsubscribe(sub.authorId)} class="border">Unsubscribe</button>
</nav>
</article>
@@ -1,6 +1,6 @@
<script lang="ts">
import VideoList from '$lib/VideoList.svelte';
import { activePageStore } from '../../store';
import { activePageStore } from '$lib/store';
export let data;
@@ -4,12 +4,19 @@ import { error } from '@sveltejs/kit';
export async function load({ url }) {
const videoId = url.searchParams.get('v');
const playlistId = url.searchParams.get('list');
const timestamp = url.searchParams.get('t');
if (videoId) {
let goToUrl = `/watch/${videoId}`;
let goToUrl = new URL(`${location.origin}/watch/${videoId}`);
if (playlistId) {
goToUrl += `?playlist=${playlistId}`;
goToUrl.searchParams.set('playlist', playlistId);
}
if (timestamp) {
goToUrl.searchParams.set('time', timestamp);
}
goto(goToUrl);
} else {
error(404);
@@ -16,11 +16,6 @@
import Thumbnail from '$lib/Thumbnail.svelte';
import { cleanNumber, getBestThumbnail, numberWithCommas, unsafeRandomItem } from '$lib/misc';
import type { PlayerEvents } from '$lib/player';
import type { DataConnection } from 'peerjs';
import { onDestroy, onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import type { MediaPlayerElement } from 'vidstack/elements';
import {
activePageStore,
authStore,
@@ -32,7 +27,12 @@
playlistSettingsStore,
syncPartyConnectionsStore,
syncPartyPeerStore
} from '../../../store';
} from '$lib/store';
import type { DataConnection } from 'peerjs';
import { onDestroy, onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import type { MediaPlayerElement } from 'vidstack/elements';
export let data;
@@ -461,6 +461,11 @@
<span>{cleanNumber(returnYTDislikes.dislikes)}</span>
</button>
</nav>
{:else}
<button style="cursor: default;" class="border">
<i class="small">thumb_up</i>
<span>{cleanNumber(data.video.likeCount)}</span>
</button>
{/if}
{/await}
@@ -7,14 +7,14 @@ import {
postHistory
} from '$lib/Api/index.js';
import { phaseDescription } from '$lib/misc';
import { error } from '@sveltejs/kit';
import { get } from 'svelte/store';
import {
authStore,
playerProxyVideosStore,
returnYTDislikesInstanceStore,
returnYtDislikesStore
} from '../../../store';
} from '$lib/store';
import { error } from '@sveltejs/kit';
import { get } from 'svelte/store';
export async function load({ params, url }) {
let video;
@@ -0,0 +1,23 @@
import { getVideo } from '$lib/Api/index.js';
import { error } from '@sveltejs/kit';
import { get } from 'svelte/store';
import type { PhasedDescription } from '../../../../lib/misc.js';
import { playerProxyVideosStore } from '../../../../lib/store.js';
export async function load({ params }) {
let video;
try {
video = await getVideo(params.slug, get(playerProxyVideosStore));
} catch (errorMessage: any) {
error(500, errorMessage);
}
return {
video: video,
content: {
description: '',
timestamps: []
} as PhasedDescription,
playlistId: null
};
}
@@ -0,0 +1,11 @@
<script lang="ts">
import Player from '$lib/Player.svelte';
import type { MediaPlayerElement } from 'vidstack/elements';
export let data;
export let layout = false;
let player: MediaPlayerElement;
</script>
<Player bind:player isEmbed={true} {data} />
+1 -1
View File
@@ -5,7 +5,7 @@ import json
import os
import re
LATEST_VERSION = "1.0.8"
LATEST_VERSION = "1.0.9"
WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")
ROOT_PACKAGE = os.path.join(WORKING_DIR, "package.json")