Skip to content

Commit

Permalink
refactor OptimizeForNiconico
Browse files Browse the repository at this point in the history
  • Loading branch information
koizuka committed Oct 12, 2018
1 parent 7d8f743 commit c666525
Show file tree
Hide file tree
Showing 9 changed files with 345 additions and 183 deletions.
26 changes: 3 additions & 23 deletions app/components/windows/OptimizeForNiconico.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,9 @@

{{ $t('streaming.optimizationForNiconico.description') }}
<ul>
<li>
{{ $t('settings.Output.Streaming.VBitrate.name') }}: {{ settings.currentVideoBitrate }}
<span v-if="settings.optimizedVideoBitrate"> -&gt; {{ settings.optimizedVideoBitrate }}</span>
</li>
<li>
{{ $t('settings.Output.Streaming.ABitrate.name') }}: {{ settings.currentAudioBitrate }}
<span v-if="settings.optimizedAudioBitrate"> -&gt; {{ settings.optimizedAudioBitrate }}</span>
</li>
<li>
{{ $t('settings.Output.name') }}: {{ qualityName(settings.currentQuality) }}
<span v-if="settings.optimizedQuality"> -&gt; {{ qualityName(settings.optimizedQuality) }}</span>
</li>
<li>
{{ $t('settings.Advanced.Video.ColorSpace.name') }}: {{ settings.currentColorSpace }}
<span v-if="settings.optimizedColorSpace"> -&gt; {{ settings.optimizedColorSpace }}</span>
</li>
<li>
{{ $t('streaming.FPS') }}: {{ settings.currentFps }}
<span v-if="settings.optimizedFps"> -&gt; {{ settings.optimizedFps }}</span>
</li>
<li>
{{ $t('settings.Output.Untitled.Mode.name') }}: {{ outputModeName(settings.currentOutputMode) }}
<span v-if="settings.optimizedOutputMode"> -&gt; {{ outputModeName(settings.optimizedOutputMode) }}</span>
<li v-for="o in settings.info" :key="o.key">
{{ o.name }}: {{ o.currentValue }}
<span v-if="o.newValue"> -&gt; {{ o.newValue }}</span>
</li>
</ul>
<BoolInput :value="doNotShowAgain" @input="setDoNotShowAgain" />
Expand Down
19 changes: 4 additions & 15 deletions app/components/windows/OptimizeForNiconico.vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import { CustomizationService } from '../../services/customization';
import { IFormInput } from '../../components/shared/forms/Input';
import { StreamingService } from '../../services/streaming';
import { WindowsService } from '../../services/windows';
import { OptimizedSettings, SettingsService } from '../../services/settings';
import { SettingsService } from '../../services/settings';
import { $t } from '../../services/i18n';
import { OptimizedSettings } from 'services/settings/niconico-optimization';

@Component({
components: {
Expand All @@ -24,7 +25,7 @@ export default class OptimizeNiconico extends Vue {
@Inject() windowsService: WindowsService;
@Inject() settingsService: SettingsService;

settings: OptimizedSettings = this.windowsService.getChildWindowQueryParams();
settings: OptimizedSettings = this.windowsService.getChildWindowQueryParams() as any as OptimizedSettings;

get doNotShowAgain(): IFormInput<boolean> {
return {
Expand All @@ -34,24 +35,12 @@ export default class OptimizeNiconico extends Vue {
};
}

qualityName(value: string): string {
const arg = `settings.Video.Untitled.Output.${value}`;
const name = $t(arg);
return name !== arg ? name : value;
}

outputModeName(value: string): string {
const arg = `settings.Output.Untitled.Mode.${value}`;
const name = $t(arg);
return name !== arg ? name : value;
}

setDoNotShowAgain(model: IFormInput<boolean>) {
this.customizationService.setShowOptimizationDialogForNiconico(!model.value);
}

optimizeAndGoLive() {
this.settingsService.optimizeForNiconico(this.settings);
this.settingsService.optimizeForNiconico(this.settings.delta);
this.streamingService.toggleStreaming();
this.windowsService.closeChildWindow();
}
Expand Down
305 changes: 305 additions & 0 deletions app/services/settings/niconico-optimization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
import { $t } from 'services/i18n';
import { ISettingsSubCategory } from './settings-api';

export enum OptimizationKey {
outputMode = 'outputMode', // outputMode must be the first entry
videoBitrate = 'videoBitrate',
audioBitrate = 'audioBitrate',
quality = 'quality',
colorSpace = 'colorSpace',
fps = 'fps',
}

enum CategoryName {
output = 'Output',
video = 'Video',
advanced = 'Advanced',
}

type DefinitionParam = {
key: OptimizationKey,
category: CategoryName,
subCategory: string,
setting: string,
label?: string,
lookupValueName?: boolean,
};

const definitionParams: DefinitionParam[] = [
{
key: OptimizationKey.outputMode,
category: CategoryName.output,
subCategory: 'Untitled',
setting: 'Mode',
lookupValueName: true,
},
{
key: OptimizationKey.videoBitrate,
category: CategoryName.output,
subCategory: 'Streaming',
setting: 'VBitrate'
},
{
key: OptimizationKey.audioBitrate,
category: CategoryName.output,
subCategory: 'Streaming',
setting: 'ABitrate'
},
{
key: OptimizationKey.quality,
category: CategoryName.video,
subCategory: 'Untitled',
setting: 'Output',
label: 'settings.Output.name',
lookupValueName: true,
},
{
key: OptimizationKey.colorSpace,
category: CategoryName.advanced,
subCategory: 'Video',
setting: 'ColorSpace'
},
{
key: OptimizationKey.fps,
category: CategoryName.video,
subCategory: 'Untitled',
setting: 'FPSCommon',
label: 'streaming.FPS'
},
];

export type OptimizeSettings = Object;

export interface OptimizedSettings {
delta: OptimizeSettings;
current: OptimizeSettings;
info: {
key: string,
name: string,
currentValue: string,
newValue?: string
}[];
}

class OptKeyProperty {
key: OptimizationKey;
category: CategoryName;
subCategory: string;
setting: string;

private _label: string;
private lookupValueName: boolean;

constructor(options: DefinitionParam) {
this.key = options.key;
this.category = options.category;
this.subCategory = options.subCategory;
this.setting = options.setting;
if (options.label) {
this._label = options.label;
} else {
this._label = ['settings', options.category, options.subCategory, options.setting, 'name'].join('.');
}
this.lookupValueName = options.lookupValueName;
}

get label(): string {
const t = $t(this._label);
if (t !== this._label) {
return t;
}
return this._label;
}

value(v: any): string {
if (this.lookupValueName) {
const t = ['settings', this.category, this.subCategory, this.setting, v].join('.');
const name = $t(t);
if (t !== name) {
return name;
}
return t;
}
return v;
}
}

export class NiconicoOptimization {
private static definitions: OptKeyProperty[] = definitionParams.map(o => new OptKeyProperty(o));

static keys: OptimizationKey[] = Object.values(OptimizationKey);
static keyTable = new Map<OptimizationKey, OptKeyProperty>(
NiconicoOptimization.definitions.map((p) => [p.key, p]) as [OptimizationKey, OptKeyProperty][]
);

static optimizeInfo(current: OptimizeSettings, optimized: OptimizeSettings): {
key: string,
name: string,
currentValue: string,
newValue?: string
}[] {
return NiconicoOptimization.definitions.map(opt => {
const key = opt.key
if (key in optimized) {
return {
key,
name: opt.label,
currentValue: opt.value(current[key]),
newValue: opt.value(optimized[key])
}
} else {
return {
key,
name: opt.label,
currentValue: opt.value(current[key]),
}
}
});
}

static bestSettings(options: { bitrate: number }): OptimizeSettings {
let audioBitrate: number;
let quality: string;
if (options.bitrate >= 6000) {
audioBitrate = 192;
quality = '1280x720';
} else if (options.bitrate >= 2000) {
audioBitrate = 192;
quality = '800x450';
} else if (options.bitrate >= 1000) {
audioBitrate = 96;
quality = '800x450';
} else if (options.bitrate >= 384) {
audioBitrate = 48;
quality = '512x288';
} else {
audioBitrate = 48;
quality = '512x288';
}

return {
videoBitrate: (options.bitrate - audioBitrate),
audioBitrate: audioBitrate.toString(10),
quality: quality,
colorSpace: '709',
fps: '30',
outputMode: 'Simple',
};
}
}

export interface ISettingsAccessor {
getSettingsFormData(categoryName: string): ISettingsSubCategory[];
findSetting(settings: ISettingsSubCategory[], category: string, setting: string): any;
findSettingValue(settings: ISettingsSubCategory[], category: string, setting: string): any;
setSettings(categoryName: string, settingsData: ISettingsSubCategory[]): void;
}

export class NiconicoOptimizer {
private accessor: ISettingsAccessor;

constructor(accessor: ISettingsAccessor) {
this.accessor = accessor;
}

private categoryCache = new Map<CategoryName, ISettingsSubCategory[]>();

getCategory(category: CategoryName, reload: boolean = false): ISettingsSubCategory[] {
if (reload || !this.categoryCache.has(category)) {
this.categoryCache.set(category, this.accessor.getSettingsFormData(category));
}
return this.categoryCache.get(category);
}

updateCategory(category: CategoryName) {
if (this.categoryCache.has(category)) {
this.accessor.setSettings(category, this.getCategory(category));
}
}

findValue(key: OptimizationKey) {
const i = NiconicoOptimization.keyTable.get(key);
return this.accessor.findSettingValue(this.getCategory(i.category), i.subCategory, i.setting);
}

findSetting(key: OptimizationKey) {
const i = NiconicoOptimization.keyTable.get(key);
return this.accessor.findSetting(this.getCategory(i.category), i.subCategory, i.setting);
}

setValues(kvs: { key: OptimizationKey, value: any }[]) {
const om = kvs.find(i => i.key == OptimizationKey.outputMode);
if (om !== undefined) {
kvs = kvs.filter(i => i.key != OptimizationKey.outputMode);
kvs.unshift(om);
}

let modifiedCategories = new Set<CategoryName>();

for (const kv of kvs) {
const setting = this.findSetting(kv.key);
if (setting) {
if (setting.value !== kv.value) {
setting.value = kv.value;

const category = NiconicoOptimization.keyTable.get(kv.key).category;
modifiedCategories.add(category);

if (kv.key == OptimizationKey.outputMode) {
this.updateCategory(category);
this.getCategory(category, true); // reload
modifiedCategories.delete(category);
}
}
} else {
console.warn(`key not found: ${kv.key}`);
}
}

for (const category of modifiedCategories) {
if (this.categoryCache.has(category as CategoryName)) {
this.updateCategory(category as CategoryName);
}
}
}

setValue(key: OptimizationKey, value: any) {
this.setValues([{ key, value }]);
}

getCurrent(outputMode: string): OptimizeSettings {
// 出力モードが異なるときは変更した上で現在の値を取得する
const lastMode = this.findValue(OptimizationKey.outputMode);
if (lastMode !== outputMode) {
this.setValue(OptimizationKey.outputMode, outputMode);
}

// 値を取得する
const current = Object.assign(
{},
...NiconicoOptimization.keys.map((key: OptimizationKey) => {
if (key === OptimizationKey.outputMode) {
return { [key]: lastMode }; // 出力モードは以前の値
} else {
return { [key]: this.findValue(key) };
}
})
);

// 出力モードを元に戻す
if (lastMode !== outputMode) {
this.setValue(OptimizationKey.outputMode, lastMode);
}
return current;
}

optimize(optimized: OptimizeSettings) {
let kvs: { key: OptimizationKey, value: any }[] = [];
for (let key of NiconicoOptimization.keys) {
if (key in optimized) {
kvs.push({ key, value: optimized[key] });
}
}
this.setValues(kvs);
}
}
Loading

0 comments on commit c666525

Please sign in to comment.